diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/qmail-smtpc/deps-exe/qmail-smtpc | 1 | ||||
| -rw-r--r-- | src/qmail-smtpc/deps-lib/qmailr | 3 | ||||
| -rw-r--r-- | src/qmail-smtpc/dns.c | 177 | ||||
| -rw-r--r-- | src/qmail-smtpc/qmail-smtpc.c | 81 | ||||
| -rw-r--r-- | src/qmail-smtpc/qmail-smtpc.h | 35 | ||||
| -rw-r--r-- | src/qmail-smtpc/qmailr.h | 31 | ||||
| -rw-r--r-- | src/qmail-smtpc/qmailr_control.c | 81 | ||||
| -rw-r--r-- | src/qmail-smtpc/qmailr_tcpto.c | 16 | ||||
| -rw-r--r-- | src/qmail-smtpc/qmailr_tls.c | 42 | ||||
| -rw-r--r-- | src/qmail-smtpc/qmailr_utils.c | 15 | ||||
| -rw-r--r-- | src/qmail-smtpc/smtproutes.c | 140 |
11 files changed, 549 insertions, 73 deletions
diff --git a/src/qmail-smtpc/deps-exe/qmail-smtpc b/src/qmail-smtpc/deps-exe/qmail-smtpc index 98605d9..b0b5a08 100644 --- a/src/qmail-smtpc/deps-exe/qmail-smtpc +++ b/src/qmail-smtpc/deps-exe/qmail-smtpc @@ -1,3 +1,4 @@ +dns.o smtproutes.o libqmailr.a.xyzzy -lskarnet diff --git a/src/qmail-smtpc/deps-lib/qmailr b/src/qmail-smtpc/deps-lib/qmailr index 294fde8..c1095ed 100644 --- a/src/qmail-smtpc/deps-lib/qmailr +++ b/src/qmail-smtpc/deps-lib/qmailr @@ -1,3 +1,6 @@ +qmailr_control.o qmailr_error.o qmailr_tcpto.o +qmailr_tls.o +qmailr_utils.o -lskarnet diff --git a/src/qmail-smtpc/dns.c b/src/qmail-smtpc/dns.c new file mode 100644 index 0000000..a469019 --- /dev/null +++ b/src/qmail-smtpc/dns.c @@ -0,0 +1,177 @@ +/* ISC license. */ + +#include <string.h> +#include <stdlib.h> + +#include <skalibs/tai.h> +#include <skalibs/stralloc.h> +#include <skalibs/genalloc.h> +#include <skalibs/ip46.h> +#include <skalibs/random.h> + +#include <s6-dns/s6dns.h> + +#include "qmailr.h" +#include "qmail-smtpc.h" + +static int mx_cmp (void const *a, void const *b) +{ + s6dns_message_rr_mx_t const *aa = a ; + s6dns_message_rr_mx_t const *bb = b ; + return aa->preference < bb-> preference ? -1 : aa->preference > bb->preference ; +} + +void dns_init (void) +{ + if (!s6dns_init_options(0)) qmail_tempsys("Unable to init DNS") ; +} + +void dns_canon (char const *host, char const *const *recip, unsigned int n, size_t *recippos, genalloc *mxpos, stralloc *storage) +{ + genalloc mx = GENALLOC_ZERO ; /* s6dns_message_rr_mx_t */ + size_t atpos[n] ; + s6dns_dpag_t cnames[n] ; + s6dns_resolve_t info[n + !!mxpos] ; + + for (unsigned int i = 0 ; i < n ; i++) + { + char const *at = strrchr(recip[i], '@') ; + if (!at) qmailr_perm("Invalid recipient") ; + atpos[i] = at - recip[i] ; + if (!s6dns_domain_fromstring_noqualify_encode(&info[i].q, at+1, strlen(at+1)) + qmailr_tempsys("Unable to DNS-encode recipient domain") ; + cnames[i].ds = genalloc_zero ; + cnames[i].rtype = S6DNS_T_CNAME ; + info[i].qtype = S6DNS_T_CNAME ; + info[i].options = S6DNS_O_RECURSIVE ; + info[i].deadline = tain_infinite ; + info[i].parsefunc = &s6dns_message_parse_answer_domain ; + info[i].data = cnames + i ; + } + if (!s6dns_domain_fromstring_noqualify_encode(&info[n].q, host, strlen(host)) + qmailr_tempsys("Unable to DNS-encode recipient domain") ; + + if (mxpos) + { + info[n].qtype = S6DNS_T_MX ; + info[n].options = S6DNS_O_RECURSIVE ; + info[n].deadline = tain_infinite ; + info[n].parsefunc = &s6dns_message_parse_answer_mx ; + info[n].data = &mx ; + } + + if (!s6dns_resolven_parse_g(info, n + !!mxpos, 0)) + qmailr_tempsys("Unable to perform DNS resolutions") ; + + for (unsigned int i = 0 ; i < n ; i++) + { + recippos[i] = storage->len ; + box_encode(recip[i], atpos[i], storage) ; + if (!stralloc_catb(storage, "@", 1)) dienomem() ; + if (!info[i].status && genalloc_len(s6dns_domain_t, &cnames[i].ds)) + { + if (!s6dns_domain_decode(genalloc_s(s6dns_domain_t, &cnames[i].ds)) + qmailr_tempsys("Unable to parse CNAME") ; + if (!stralloc_readyplus(storage, 256)) dienomem() ; + recippos[i] = storage->len ; + storage->len += s6dns_domain_tostring(storage->s + storage->len, 255, genalloc_s(s6dns_domain_t, &cnames[i].ds)) ; + if (storage->s[storage->len-1] == '.') --storage->len ; + storage->s[storage->len++] = 0 ; + } + else if (!stralloc_cats(storage, recip[i] + atpos[i] + 1)) dienomem() ; + if (!stralloc_0(storage)) dienomem() ; + } + + if (mxpos && !info[n].status && genalloc_len(s6dns_message_rr_mx_t, &mx)) + { + s6dns_message_rr_mx_t *mxs = genalloc_s(s6dns_message_rr_mx_t, &mx) ; + size_t mxlen = genalloc_len(s6dns_message_rr_mx_t, &mx) ; + qsort(mxs, mxlen, sizeof(s6dns_message_rr_mx_t), &mx_cmp) ; + if (!genalloc_readyplus(size_t, mxpos, mxlen)) dienomem() ; + for (size_t i = 0 ; i < mxlen ; i++) + { + if (!s6dns_domain_decode(&mxs[i].exchange)) qmailr_tempsys("Unable to parse MX record") ; + if (!stralloc_readyplus(storage, 256)) dienomem() ; + genalloc_catb(size_t, mxpos, storage->len) ; + storage->len += s6dns_domain_tostring(storage->s + storage->len, 255, &mxs[i].exchange) ; + storage->s[storage->len++] = 0 ; + } + genalloc_free(s6dns_message_rr_mx_t, &mx) ; + } +} + +void dns_ip_of_mx (size_t const *pos, unsigned int n, mxip *tab, stralloc *storage, char const *ipme4, unsigned int n4, char const *ipme6, unsigned int n6) +{ +#ifdef SKALIBS_IPV6_ENABLED + unsigned int const N = n << 1 ; + stralloc ip6[n] ; +#else + unsigned int const N = n ; +#endif + stralloc ip4[n] ; + s6dns_resolve_t info[N] ; + for (unsigned int i = 0 ; i < n ; i++) + { + if (!s6dns_domain_fromstring_noqualify_encode(&info[i].q, storage->s + pos[i], strlen(storage->s + pos[i])) + qmailr_tempsys("Unable to DNS-encode MX") ; + ip4[i] = stralloc_zero ; + info[i].qtype = S6DNS_T_A ; + info[i].options = S6DNS_O_RECURSIVE ; + info[i].deadline = tain_infinite ; + info[i].parsefunc = &s6dns_message_parse_answer_a ; + info[i].data = ip4 + i ; + +#ifdef SKALIBS_IPV6_ENABLED + ip6[i] = stralloc_zero ; + info[n+i].q = info[i].q ; + info[n+i].qtype = S6DNS_T_A ; + info[n+i].options = S6DNS_O_RECURSIVE ; + info[n+i].deadline = tain_infinite ; + info[n+i].parsefunc = &s6dns_message_parse_answer_aaaa ; + info[n+i].data = ip6 + i ; +#endif + } + + if (!s6dns_resolven_parse_g(info, N, 0)) + qmailr_tempsys("Unable to perform DNS resolutions") ; + + for (unsigned int i = 0 ; i < n ; i++) + { + if (!info[i].status) + { + for (unsigned int j = 0 ; j < ip4[i].len ; j += 4) + { + if (bsearch(ip4.s + j, ipme4, n4, 4, &qmailr_memcmp4)) + { + memmove(ip4[i].s + j, ip4[i].s + ip4[i].len - 4, 4) ; + ip4[i].len -= 4 ; + } + } + random_unsort(ip4.s, ip4.len >> 2, 4) ; + tab[i].pos4 = storage->len ; + tab[i].n4 = ip4.len >> 2 ; + if (!stralloc_catb(&storage, ip4.s, ip4.len)) dienomem() ; + stralloc_free(ip4 + i) ; + } + +#ifdef SKALIBS_IPV6_ENABLED + if (!info[n+i].status) + { + for (unsigned int j = 0 ; j < ip6[i].len ; j += 16) + { + if (bsearch(ip6.s + j, ipme6, n6, 16, &qmailr_memcmp16)) + { + memmove(ip6[i].s + j, ip6[i].s + ip6[i].len - 16, 16) ; + ip6[i].len -= 16 ; + } + } + random_unsort(ip6.s, ip6.len >> 4, 16) ; + tab[i].pos6 = storage->len ; + tab[i].n6 = ip6.len >> 4 ; + if (!stralloc_catb(&storage, ip6.s, ip6.len)) dienomem() ; + stralloc_free(ip6 + i) ; + } +#endif + + } +} diff --git a/src/qmail-smtpc/qmail-smtpc.c b/src/qmail-smtpc/qmail-smtpc.c index b6a7c47..6496be7 100644 --- a/src/qmail-smtpc/qmail-smtpc.c +++ b/src/qmail-smtpc/qmail-smtpc.c @@ -1,11 +1,92 @@ /* ISC license. */ +#include <string.h> +#include <stdint.h> #include <unistd.h> +#include <skalibs/cdb.h> +#include <skalibs/stralloc.h> +#include <skalibs/sig.h> +#include <skalibs/tai.h> +#include <skalibs/ip46.h> + #include <smtpd-starttls-proxy/config.h> #include "qmailr.h" +#include "qmail-smtpc.h" + +#define dieusage() qmailr_perm("qmail-remote was invoked improperly") int main (int argc, char const *const *argv) { + stralloc storage = STRALLOC_ZERO ; + stralloc ipme4 = STRALLOC_ZERO ; + stralloc ipme6 = STRALLOC_ZERO ; + qmailr_tls qt = QMAILR_TLS_ZERO ; + smtproutes routes = SMTPROUTES_ZERO ; + unsigned int timeoutconnect = 60, timeoutremote = 1200 ; + char const *host ; + size_t mepos, helopos, hostpos = 0, senderpos ; + uint16_t port = 25 ; + int r ; + + if (argc-- < 4) dieusage() ; argv++ ; + if (chdir(SMTPD_STARTTLS_PROXY_QMAIL_HOME) == -1) qmailr_temp("Unable to chdir to " SMTPD_STARTTLS_PROXY_QMAIL_HOME) ; + if (sig_altignore(SIGPIPE) == -1) qmailr_tempsys("Unable to ignore SIGPIPE") ; + host = *argv++ ; argc-- ; + tain_now_set_stopwatch_g() ; + qmailr_dns_init() ; + + /* init control */ + + r = qmailr_control_read("control/me", &storage, &mepos) ; + if (r == -1) qmailr_tempsys("Unable to read control/me") ; + else if (!r) qmailr_temp("Invalid control/me") ; + + r = qmail_control_read("control/helohost", &storage, &helopos) ; + if (r == -1) qmailr_tempsys("Unable to read control/helohost") ; + else if (!r) helopos = mepos ; + + r = qmail_control_readint("control/timeoutconnect", &timeoutconnect, &storage) ; + if (r == -1) qmailr_tempsys("Unable to read control/timeoutconnect") ; + r = qmail_control_readint("control/timeoutremote", &timeoutremote, &storage) ; + if (r == -1) qmailr_tempsys("Unable to read control/timeoutremote") ; + + if (!qmailr_control_readiplist("control/ipme", &ipme4, &ipme6)) + qmailr_tempsys("Unable to read control/ipme") ; + stralloc_shrink(&ipme4) ; + stralloc_shrink(&ipme6) ; + qsort(ipme4.s, ipme4.len >> 2, 4, &qmailr_memcmp4) ; + qsort(ipme6.s, ipme6.len >> 4, 16, &qmailr_memcmp16) ; + + if (!qmailr_tls_init(&qt, &storage)) + qmailr_tempsys("Unable to read TLS control files") ; + + if (smtproutes_init(&routes)) + { + if (!smtproutes_match(&routes, host, &storage, &hostpos, &port)) + { + size_t hostlen = strlen(host) ; + for (size_t i = 0 ; i < hostlen ; i++) if (host[i] == '.') + if (smtproutes_match(&routes, argv[1], &storage, &hostpos, &port)) break ; + if (!hostpos) smtproutes_match(&routes, "", &storage, &hostpos, &port) ; + } + smtproutes_free(&routes) ; + } + + { + genalloc mxpos = GENALLOC_ZERO ; + int usehost ; + size_t recippos[argc] ; + + dns_canon(host, argv, argc, recippos, hostpos ? 0 : &mxpos, &storage) ; + usehost = hostpos || !genalloc_len(size_t, &mxpos) ; + + unsigned int mxn = usehost ? 1 : genalloc_len(size_t, &mxpos) ; + mxip mxind[mxn] ; + dns_ip_of_mx(usehost ? &hostpos : genalloc_s(size_t, &mxpos), mxn, mxip, storage, ipme4.s, ipme4.len >> 2, ipme6.s, ipme6.len >> 4) ; + genalloc_free(size_t, &mxpos) ; + } + + _exit(0) ; } diff --git a/src/qmail-smtpc/qmail-smtpc.h b/src/qmail-smtpc/qmail-smtpc.h index 7c178bb..a109d07 100644 --- a/src/qmail-smtpc/qmail-smtpc.h +++ b/src/qmail-smtpc/qmail-smtpc.h @@ -1,9 +1,42 @@ /* ISC license. */ +#include <stddef.h> +#include <stdint.h> + #include <skalibs/cdb.h> +#include <skalibs/stralloc.h> + +#include "qmailr.h" + +#define dienomem() qmailr_tempsys("Unable to grow stralloc") + + +/* dns */ + +typedef struct mxip_s mxip, *mxip_ref ; +struct mxip_s +{ + size_t pos4 ; + size_t pos6 ; + uint16_t n4 ; + uint16_t n6 ; +} ; +#define MXIP_ZERO { 0 } + +extern void dns_init (void) ; +extern void dns_canon (char const *, char const *const *, unsigned int, size_t *, genalloc *, stralloc *) ; +extern void dns_ip_of_mx (size_t const *, unsigned int, mxip *, stralloc *, char const *, unsigned int, char const *, unsigned int) ; /* smtproutes */ -extern int smtproutes_init (cdb *) ; +typedef struct smtproutes_s smtproutes ; +struct smtproutes_s +{ + cdb map ; +} ; +#define SMTPROUTES_ZERO { .map = CDB_ZERO } +extern int smtproutes_init (smtproutes *) ; +extern int smtproutes_match (smtproutes const *, char const *, stralloc *, size_t *, uint16_t *) ; +extern void smtproutes_free (smtproutes *) ; diff --git a/src/qmail-smtpc/qmailr.h b/src/qmail-smtpc/qmailr.h index 6ee9bfc..d623274 100644 --- a/src/qmail-smtpc/qmailr.h +++ b/src/qmail-smtpc/qmailr.h @@ -1,9 +1,12 @@ /* ISC license. */ +#include <stddef.h> #include <stdint.h> #include <skalibs/gccattributes.h> #include <skalibs/tai.h> +#include <skalibs/stralloc.h> + /* qmailr_error */ @@ -20,8 +23,36 @@ extern void qmailr_diesys (char const *) gccattr_noreturn ; #define qmailr_permv(v, n) qmailr_diev(1, (v), n) +/* qmailr_utils */ + +extern int qmailr_memcmp4 (void const *, void const *) ; +extern int qmailr_memcmp16 (void const *, void const *) ; + + /* qmailr_tcpto */ extern int qmailr_tcpto_match (char const *, int) ; extern int qmailr_tcpto_update (char const *, int, int) ; + +/* qmailr_control */ + +extern int qmailr_control_read (char const *, stralloc *, size_t *) ; +extern int qmailr_control_readint (char const *file, unsigned int *, stralloc *) ; + + +/* qmailr_tls */ + +typedef struct qmailr_tls_s qmailr_tls, *qmailr_tls_ref ; +struct qmailr_tls_s +{ + size_t tapos ; + size_t certpos ; + size_t keypos ; + uint8_t flagtls : 1 ; + uint8_t flagtadir : 1 ; + uint8_t flagclientcert : 1 ; +} ; +#define QMAILR_TLS_ZERO { 0 } + +extern int qmailr_tls_init (qmailr_tls *, stralloc *) ; diff --git a/src/qmail-smtpc/qmailr_control.c b/src/qmail-smtpc/qmailr_control.c new file mode 100644 index 0000000..a46cb22 --- /dev/null +++ b/src/qmail-smtpc/qmailr_control.c @@ -0,0 +1,81 @@ +/* ISC license. */ + +#include <stddef.h> +#include <errno.h> + +#include <skalibs/types.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/buffer.h> +#include <skalibs/fmtscan.h> +#include <skalibs/stralloc.h> +#include <skalibs/djbunix.h> + +#include "qmailr.h" + +#include <skalibs/posixishard.h> + +int qmailr_control_read (char const *file, stralloc *sa, size_t *pos) +{ + int fd = openc_readb(file) ; + if (fd == -1) return errno == ENOENT ? (errno = 0, 0) : -1 ; + + char buf[4096] ; + size_t r = allread(fd, buf, 4096) ; + fd_close(fd) ; + if (r == 4096) return (errno = ENAMETOOLONG, -1) ; + if (!r) return 0 ; + if (buf[r-1] == '\n') r-- ; + if (!r) return 0 ; + if (!stralloc_readyplus(sa, r+1)) return -1 ; + *pos = sa->len ; + stralloc_catb(sa, buf, r) ; stralloc_0(sa) ; + return 1 ; +} + +int qmailr_control_readint (char const *file, unsigned int *x, stralloc *sa) +{ + size_t pos ; + int r = qmailr_control_readfile(file, sa, &pos) ; + if (r <= 0) return r ; + sa->len = pos ; + if (!uint0_scan(sa->s + sa->len, x)) return (errno = EPROTO, 0) ; + return 1 ; +} + +int qmailr_control_readiplist (char const *file, stralloc *ip4, stralloc *ip6) +{ + int fd = openc_readb(file) ; + if (fd == -1) return errno == ENOENT ? (errno = 0, 0) : -1 ; + + size_t pos4 = ip4->len, pos6 = ip6->len ; + char buf[4096] ; + buffer b = BUFFER_INIT(&buffer_read, fd, buf, 4096) ; + + for (;;) + { + char line[128] ; + char ip[16] ; + size_t len = 0 ; + int r = getlnmax(&b, line, 127, &len, '\n') ; + if (r == -1) goto err ; + if (!r) break ; + if (!len) continue ; + if (line[len-1] != '\n') line[len++] = '\n' ; + if (ip6_scan(line, ip) == len-1) + { + if (!stralloc_catb(ip6, ip, 16)) goto err ; + } + else if (ip4_scan(line, ip) == len-1) + { + if (!stralloc_catb(ip4, ip, 4)) goto err ; + } + else goto errinval ; + } + return 1 ; + + errinval: + errno = EINVAL ; + err: + ip4->len = pos4 ; ip6->len = pos6 ; + return 0 ; +} diff --git a/src/qmail-smtpc/qmailr_tcpto.c b/src/qmail-smtpc/qmailr_tcpto.c index fc8841b..f90d1d0 100644 --- a/src/qmail-smtpc/qmailr_tcpto.c +++ b/src/qmail-smtpc/qmailr_tcpto.c @@ -35,16 +35,6 @@ a shared lock for this (unsure why djb didn't). */ -static int memcmp4 (void const *a, void const *b) -{ - return memcmp(a, b, 4) ; -} - -static int memcmp16 (void const *a, void const *b) -{ - return memcmp(a, b, 16) ; -} - int qmailr_tcpto_match (char const *ip, int is6) { char const *file = is6 ? SMTPD_STARTTLS_PROXY_QMAIL_HOME "/run/qmail-remote/tcpto6" : SMTPD_STARTTLS_PROXY_QMAIL_HOME "/queue/lock/tcpto" ; @@ -59,7 +49,7 @@ int qmailr_tcpto_match (char const *ip, int is6) if (fd_lock(fd, 0, 0) == -1) goto err ; if (!cdb_init_fromfd(&c, fd)) goto err ; if (c.size % width) goto errproto ; - p = bsearch(ip, c.map, c.size / width, width, is6 ? &memcmp16 : &memcmp4) ; + p = bsearch(ip, c.map, c.size / width, width, is6 ? &qmailr_memcmp16 : &qmailr_memcmp4) ; if (p) { if (p[iplen] >= 2) @@ -108,7 +98,7 @@ int qmailr_tcpto_update (char const *ip, int is6, int problem) { if (allread(fdr, buf, st.st_size) < st.st_size) goto err0 ; memset(buf + st.st_size, 0, width) ; - p = bsearch(ip, buf, n, width, is6 ? &memcmp16 : &memcmp4) ; + p = bsearch(ip, buf, n, width, is6 ? &qmailr_memcmp16 : &qmailr_memcmp4) ; if (p) { if (problem) @@ -147,7 +137,7 @@ int qmailr_tcpto_update (char const *ip, int is6, int problem) memcpy(buf + i * width, buf + --n * width, width) ; if (n) { - qsort(buf, n, width, is6 ? &memcmp16 : &memcmp4) ; + qsort(buf, n, width, is6 ? &qmailr_memcmp16 : &qmailr_memcmp4) ; if (allwrite(fdw, buf, n * width) < n * width) goto err ; } if (ftruncate(fdw, n * width) == -1) goto err ; diff --git a/src/qmail-smtpc/qmailr_tls.c b/src/qmail-smtpc/qmailr_tls.c new file mode 100644 index 0000000..50625e2 --- /dev/null +++ b/src/qmail-smtpc/qmailr_tls.c @@ -0,0 +1,42 @@ +/* ISC license. */ + +#include <stddef.h> + +#include <smtpd-starttls-proxy/config.h> +#include "qmailr.h" + +int qmailr_tls_init (qmailr_tls *qt, stralloc *sa) +{ + static char const *tafile = SMTPD_STARTTLS_PROXY_QMAIL_HOME "/control/trustanchors" ; + static char const *certfile = SMTPD_STARTTLS_PROXY_QMAIL_HOME "/control/clientcert" ; + static char const *keyfile = SMTPD_STARTTLS_PROXY_QMAIL_HOME "/control/clientkey" ; + + qmailr_tls tmp = QMAILR_TLS_ZERO ; + size_t sabase = sa->len ; + int r = qmailr_control_read(tafile, sa, &tmp.tapos) ; + if (r == -1) return 0 ; + if (r) + { + tmp.flagtls = 1 ; + if (sa->s[sa->len - 2] == '/') + { + sa->s[--sa->len - 1] = 0 ; + tmp.flagtadir = 1 ; + } + r = qmailr_control_read(certfile, sa, &tmp.certpos) ; + if (r == -1) goto err ; + if (r) + { + r = qmailr_control_read(keyfile, sa, &tmp.keypos) ; + if (r == -1) goto err ; + if (r) tmp.flagclientcert = 1 ; + } + } + + *qt = tmp ; + return 1 ; + + err: + sa->len = sabase ; + return 0 ; +} diff --git a/src/qmail-smtpc/qmailr_utils.c b/src/qmail-smtpc/qmailr_utils.c new file mode 100644 index 0000000..177a356 --- /dev/null +++ b/src/qmail-smtpc/qmailr_utils.c @@ -0,0 +1,15 @@ +/* ISC license. */ + +#include <string.h> + +#include "qmailr.h" + +int qmailr_memcmp4 (void const *a, void const *b) +{ + return memcmp(a, b, 4) ; +} + +int qmailr_memcmp16 (void const *a, void const *b) +{ + return memcmp(a, b, 16) ; +} diff --git a/src/qmail-smtpc/smtproutes.c b/src/qmail-smtpc/smtproutes.c index 08829c9..bcea080 100644 --- a/src/qmail-smtpc/smtproutes.c +++ b/src/qmail-smtpc/smtproutes.c @@ -2,6 +2,7 @@ #include <skalibs/bsdsnowflake.h> +#include <string.h> #include <stdint.h> #include <unistd.h> #include <stdlib.h> @@ -31,81 +32,87 @@ directly) and RAM (the cdb is read-only and shared). The cdb is updated whenever control/smtproutes is newer. We have to lock around the test to avoid several - concurrent compilations; the lock feels a bit too big, + concurrent compilations; the current lock feels too big, the crit section can probably be made smaller, but the - current behaviour is safe and avoids retry heuristics. + behaviour is safe and avoids retry heuristics. */ /* - Key to the control/smtproutes parser: + Key to the control/smtproutes parser + [host]:[relay[:port]] + An ip in square brackets is acceptable in host and relay, even ipv6 - 0 1 2 3 4 5 -st\ev EOF # \n : 0-9 other -0 h n n -START END COMMENT START RELAY HOST HOST + 0 1 2 3 4 5 6 7 8 9 +st\ev EOF # \n : [ ] 0-9 a-f other special + +0 h n n n +START END COMMENT START RELAY QHOST X HOST HOST HOST X 1 -COMMENT END COMMENT START COMMENT COMMENT COMMENT +COMMENT END COMMENT START COMMENT COMMENT COMMENT COMMENT COMMENT COMMENT COMMENT + +2 n n n +QHOST X X X QHOST X EHOST QHOST QHOST X X + +3 n h n n n +HOST X HOST X RELAY X X HOST HOST HOST X + +4 h +EHOST X X X RELAY X X X X X X + +5 ra n ra r n n n +RELAY END INRELAY START PORT QRELAY X INRELAY INRELAY INRELAY X + +6 n n n +QRELAY X X X QRELAY X ERELAY QRELAY QRELAY X X -2 n h n n -HOST X HOST X RELAY HOST HOST +7 ra ra r n n n +INRELAY END X START PORT X X INRELAY INRELAY INRELAY X -3 ra n ra r n n -RELAY END RELAY START PORT RELAY RELAY +8 ra ra r +ERELAY END X START PORT X X X X X X -4 pa pa n -PORT END X START X PORT X +9 pa pa n +PORT END X START X X X PORT X X X -END=5, X=6 +END=a, X=b -0x08 n push character -0x10 h compute host length -0x20 r compute relay length -0x40 p compute port -0x80 a add route entry +0x0100 n push character +0x0200 h compute host length +0x0400 r compute relay length +0x0800 p compute port +0x1000 a add route entry */ static inline uint8_t cclass (char c) { - switch (c) - { - case 0 : return 0 ; - case '#' : return 1 ; - case '\n' : return 2 ; - case ':' : return 3 ; - case '0' : - case '1' : - case '2' : - case '3' : - case '4' : - case '5' : - case '6' : - case '7' : - case '8' : - case '9' : return 4 ; - default : break ; - } - return 5 ; + static uint8_t const table[128] = "0999999999299999999999999999999998918889999898786666666666399898877777788888888888888888884958898888888888888888888888888899999" ; + return c & 0x80 ? 9 : table[c] - '0' ; } static inline char getnext (buffer *b) { char c ; ssize_t r = buffer_get(b, &c, 1) ; - if (r == -1) qmailr_tempsys("unable to read from control/smtproutes") ; + if (r == -1) qmailr_tempsys("Unable to read from control/smtproutes") ; return r ? c : 0 ; } static inline void smtproutes_compile (int fdr, int fdw) { - static uint8_t const table[5][6] = + static uint16_t const table[10][9] = { - { 0x05, 0x01, 0x00, 0x13, 0x0a, 0x0a }, - { 0x05, 0x01, 0x00, 0x01, 0x01, 0x01 }, - { 0x06, 0x0a, 0x06, 0x13, 0x0a, 0x0a }, - { 0xa5, 0x0b, 0xa0, 0x24, 0x0b, 0x0b }, - { 0xc5, 0x06, 0xc0, 0x06, 0x0c, 0x06 } + { 0x000a, 0x0001, 0x0000, 0x0205, 0x0002, 0x000b, 0x0103, 0x0103, 0x0103, 0x000b }, + { 0x000a, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001 }, + { 0x000b, 0x000b, 0x000b, 0x0102, 0x000b, 0x0004, 0x0102, 0x0102, 0x0102, 0x000b }, + { 0x000b, 0x0103, 0x000b, 0x0205, 0x000b, 0x000b, 0x0103, 0x0103, 0x0103, 0x000b }, + { 0x000b, 0x000b, 0x000b, 0x0205, 0x000b, 0x000b, 0x000b, 0x000b, 0x000b, 0x000b }, + { 0x140a, 0x0107, 0x1400, 0x0409, 0x0006, 0x000b, 0x0107, 0x0107, 0x0107, 0x000b }, + { 0x000b, 0x000b, 0x000b, 0x0106, 0x000b, 0x0008, 0x0106, 0x0106, 0x0106, 0x000b }, + { 0x140a, 0x000b, 0x1400, 0x0409, 0x000b, 0x000b, 0x0107, 0x0107, 0x0107, 0x000b }, + { 0x140a, 0x000b, 0x1400, 0x0409, 0x000b, 0x000b, 0x000b, 0x000b, 0x000b, 0x000b }, + { 0x180a, 0x000b, 0x1800, 0x000b, 0x000b, 0x000b, 0x0109, 0x000b, 0x000b, 0x000b } } ; cdbmaker cm = CDBMAKER_ZERO ; stralloc sa = STRALLOC_ZERO ; @@ -115,45 +122,46 @@ static inline void smtproutes_compile (int fdr, int fdw) uint8_t state = 0 ; if (!cdbmake_start(&cm, fdw)) qmailr_tempsys("Unable to cdbmake_start") ; - while (state < 5) + while (state < 0x0a) { char c = getnext(&b) ; - uint8_t val = table[state][cclass(c)] ; - state = val & 0x07 ; - if (val & 0x08) + uint16_t val = table[state][cclass(c)] ; + state = val & 0x000f ; + if (val & 0x0100) { if (!stralloc_catb(&sa, &c, 1)) qmailr_tempsys("Unable to grow stralloc") ; } - if (val & 0x10) + if (val & 0x0200) { relaypos = sa.len + 1 ; if (!stralloc_catb(&sa, "\0\0\31", 3)) qmailr_tempsys("Unable to grow stralloc") ; } - if (val & 0x20) + if (val & 0x0400) { if (!stralloc_0(&sa)) qmailr_tempsys("Unable to grow stralloc") ; relayend = sa.len ; } - if (val & 0x40) + if (val & 0x0800) { uint16_t port ; if (!stralloc_0(&sa)) qmailr_tempsys("Unable to grow stralloc") ; if (!uint160_scan(sa.s + relayend, &port)) qmailr_temp("Invalid port in control/smtproutes") ; uint16_pack_big(sa.s + relaypos, port) ; } - if (val & 0x80) + if (val & 0x0100) { - if (!cdbmake_add(&cm, sa.s, relaypos, sa.s + relaypos, relayend - relaypos)) - qmailr_tempsys("Unable to cdbmake_add") ; + if (relaypos > 1 || relayend > 2 + relaypos) + if (!cdbmake_add(&cm, sa.s, relaypos, sa.s + relaypos, relayend - relaypos)) + qmailr_tempsys("Unable to cdbmake_add") ; sa.len = 0 ; } } - if (state != 5) qmailr_temp("Syntax error in control/smtproutes") ; + if (state != 0x0a) qmailr_temp("Syntax error in control/smtproutes") ; stralloc_free(&sa) ; if (!cdbmake_finish(&cm)) qmailr_tempsys("Unable to cdbmake_finish") ; } -int smtproutes_init (cdb *c) +int smtproutes_init (smtproutes *routes) { static char const *cdbfile = SMTPD_STARTTLS_PROXY_QMAIL_HOME "/run/qmail-remote/smtproutes.cdb" ; static char const *lckfile = SMTPD_STARTTLS_PROXY_QMAIL_HOME "/run/qmail-remote/smtproutes.lock" ; @@ -200,7 +208,7 @@ int smtproutes_init (cdb *c) } useit: - if (!cdb_init_fromfd(c, fdc)) qmailr_tempsys("Unable to mmap run/qmail-remote/smtproutes.cdb") ; + if (!cdb_init_fromfd(&routes->map, fdc)) qmailr_tempsys("Unable to mmap run/qmail-remote/smtproutes.cdb") ; fd_close(fdc) ; fd_close(fdl) ; return 1 ; @@ -210,3 +218,17 @@ int smtproutes_init (cdb *c) errno = 0 ; return 0 ; } + +int smtproutes_match (smtproutes const *routes, char const *s, stralloc *sa, size_t *pos, uint16_t *port) +{ + cdb_data data ; + int r = cdb_find(&routes->map, &data, s, strlen(s)) ; + if (r == -1) qmailr_temp("Invalid run/qmail-remote/smtproutes.cdb") ; + if (!r) return 0 ; + if (data.len < 3) return 0 ; + if (data.s[data.len - 1]) qmailr_temp("Invalid run/qmail-remote/smtproutes.cdb") ; + *pos = sa->len ; + uint16_unpack_big(data.s, port) ; + if (!stralloc_catb(sa, data.s + 2, data.len - 2)) qmailr_tempsys("Unable to grow stralloc") ; + return 1 ; +} |
