aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmail-smtpc
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2026-02-02 18:01:03 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2026-02-02 18:01:03 +0000
commit36491e787c8be5b32b93f5c3b20edd9aa9d4c4e9 (patch)
tree101b8cf332eb050cda394e11003cb3ee5dc2605b /src/qmail-smtpc
parent541b3256968eae128abd35e762f3e3a81885fa7c (diff)
downloadsmtpd-starttls-proxy-36491e787c8be5b32b93f5c3b20edd9aa9d4c4e9.tar.gz
More qmail-smtpc stuff
Diffstat (limited to 'src/qmail-smtpc')
-rw-r--r--src/qmail-smtpc/deps-exe/qmail-smtpc1
-rw-r--r--src/qmail-smtpc/deps-lib/qmailr3
-rw-r--r--src/qmail-smtpc/dns.c177
-rw-r--r--src/qmail-smtpc/qmail-smtpc.c81
-rw-r--r--src/qmail-smtpc/qmail-smtpc.h35
-rw-r--r--src/qmail-smtpc/qmailr.h31
-rw-r--r--src/qmail-smtpc/qmailr_control.c81
-rw-r--r--src/qmail-smtpc/qmailr_tcpto.c16
-rw-r--r--src/qmail-smtpc/qmailr_tls.c42
-rw-r--r--src/qmail-smtpc/qmailr_utils.c15
-rw-r--r--src/qmail-smtpc/smtproutes.c140
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 ;
+}