diff -ruN qmail-1.03/base64.c qmail-1.03.patched/base64.c --- qmail-1.03/base64.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/base64.c 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,123 @@ +#include "base64.h" +#include "stralloc.h" +#include "substdio.h" +#include "str.h" + +static char *b64alpha = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#define B64PAD '=' + +/* returns 0 ok, 1 illegal, -1 problem */ + +int b64decode(in,l,out) +const unsigned char *in; +int l; +stralloc *out; /* not null terminated */ +{ + int p = 0; + int n; + unsigned int x; + int i, j; + char *s; + unsigned char b[3]; + + if (l == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + while(in[l-1] == B64PAD) { + p ++; + l--; + } + + n = (l + p) / 4; + i = (n * 3) - p; + if (!stralloc_ready(out,i)) return -1; + out->len = i; + s = out->s; + + for(i = 0; i < n - 1 ; i++) { + x = 0; + for(j = 0; j < 4; j++) { + if(in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if(in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if(in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if(in[j] == '+') + x = (x << 6) + 62; + else if(in[j] == '/') + x = (x << 6) + 63; + else if(in[j] == '=') + x = (x << 6); + } + + s[2] = (unsigned char)(x & 255); x >>= 8; + s[1] = (unsigned char)(x & 255); x >>= 8; + s[0] = (unsigned char)(x & 255); x >>= 8; + s += 3; in += 4; + } + + x = 0; + for(j = 0; j < 4; j++) { + if(in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if(in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if(in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if(in[j] == '+') + x = (x << 6) + 62; + else if(in[j] == '/') + x = (x << 6) + 63; + else if(in[j] == '=') + x = (x << 6); + } + + b[2] = (unsigned char)(x & 255); x >>= 8; + b[1] = (unsigned char)(x & 255); x >>= 8; + b[0] = (unsigned char)(x & 255); x >>= 8; + + for(i = 0; i < 3 - p; i++) + s[i] = b[i]; + + return 0; +} + +int b64encode(in,out) +stralloc *in; +stralloc *out; /* not null terminated */ +{ + unsigned char a, b, c; + int i; + char *s; + + if (in->len == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + if (!stralloc_ready(out,in->len / 3 * 4 + 4)) return -1; + s = out->s; + + for (i = 0;i < in->len;i += 3) { + a = in->s[i]; + b = i + 1 < in->len ? in->s[i + 1] : 0; + c = i + 2 < in->len ? in->s[i + 2] : 0; + + *s++ = b64alpha[a >> 2]; + *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)]; + + if (i + 1 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)]; + + if (i + 2 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[c & 63]; + } + out->len = s - out->s; + return 0; +} diff -ruN qmail-1.03/base64.h qmail-1.03.patched/base64.h --- qmail-1.03/base64.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/base64.h 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,7 @@ +#ifndef BASE64_H +#define BASE64_H + +extern int b64decode(); +extern int b64encode(); + +#endif diff -ruN qmail-1.03/byte_cspn.c qmail-1.03.patched/byte_cspn.c --- qmail-1.03/byte_cspn.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/byte_cspn.c 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,11 @@ +#include "byte.h" + +unsigned int byte_cspn(s,n,c) +register char *s; +register unsigned int n; +register char *c; +{ + while(*c) + n = byte_chr(s,n,*c++); + return n; +} diff -ruN qmail-1.03/byte.h qmail-1.03.patched/byte.h --- qmail-1.03/byte.h 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/byte.h 2007-09-27 03:28:05.000000000 +0200 @@ -3,6 +3,8 @@ extern unsigned int byte_chr(); extern unsigned int byte_rchr(); +extern unsigned int byte_cspn(); +extern unsigned int byte_rcspn(); extern void byte_copy(); extern void byte_copyr(); extern int byte_diff(); diff -ruN qmail-1.03/byte_rcspn.c qmail-1.03.patched/byte_rcspn.c --- qmail-1.03/byte_rcspn.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/byte_rcspn.c 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,17 @@ +#include "byte.h" + +unsigned int byte_rcspn(s,n,c) +register char *s; +register unsigned int n; +register char *c; +{ + unsigned int ret,pos,i; + + for(ret = n,pos = 0;*c;++c) { + i = byte_rchr(s + pos,n - pos,*c) + pos; + if (i < n) ret = pos = i; + } + + return ret; +} + diff -ruN qmail-1.03/cdbmake_add.c qmail-1.03.patched/cdbmake_add.c --- qmail-1.03/cdbmake_add.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/cdbmake_add.c 1998-06-15 12:53:16.000000000 +0200 @@ -1,4 +1,3 @@ -#include "alloc.h" #include "cdbmake.h" void cdbmake_init(cdbm) diff -ruN qmail-1.03/cdb_seek.c qmail-1.03.patched/cdb_seek.c --- qmail-1.03/cdb_seek.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/cdb_seek.c 1998-06-15 12:53:16.000000000 +0200 @@ -1,5 +1,6 @@ #include #include +extern int errno; #include "cdb.h" #ifndef SEEK_SET diff -ruN qmail-1.03/chkspawn.c qmail-1.03.patched/chkspawn.c --- qmail-1.03/chkspawn.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/chkspawn.c 2007-09-27 03:28:05.000000000 +0200 @@ -22,8 +22,8 @@ _exit(1); } - if (auto_spawn > 255) { - substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 255.\n"); + if (auto_spawn > 65000) { + substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 65000.\n"); substdio_flush(subfderr); _exit(1); } diff -ruN qmail-1.03/conf-cc qmail-1.03.patched/conf-cc --- qmail-1.03/conf-cc 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/conf-cc 2007-09-27 03:28:05.000000000 +0200 @@ -1,3 +1,3 @@ -cc -O2 +cc -O2 -DTLS -DEXTERNAL_TODO -I/usr/local/ssl/include -I/usr/kerberos/include This will be used to compile .c files. diff -ruN qmail-1.03/config-fast.sh qmail-1.03.patched/config-fast.sh --- qmail-1.03/config-fast.sh 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/config-fast.sh 1998-06-15 12:53:16.000000000 +0200 @@ -1,29 +1,29 @@ fqdn="$1" echo Your fully qualified host name is "$fqdn". -echo Putting "$fqdn" into QMAIL/control/me... +echo Putting "$fqdn" into control/me... echo "$fqdn" > QMAIL/control/me chmod 644 QMAIL/control/me ( echo "$fqdn" | sed 's/^\([^\.]*\)\.\([^\.]*\)\./\2\./' | ( read ddom - echo Putting "$ddom" into QMAIL/control/defaultdomain... + echo Putting "$ddom" into control/defaultdomain... echo "$ddom" > QMAIL/control/defaultdomain chmod 644 QMAIL/control/defaultdomain ) ) ( echo "$fqdn" | sed 's/^.*\.\([^\.]*\)\.\([^\.]*\)$/\1.\2/' | ( read pdom - echo Putting "$pdom" into QMAIL/control/plusdomain... + echo Putting "$pdom" into control/plusdomain... echo "$pdom" > QMAIL/control/plusdomain chmod 644 QMAIL/control/plusdomain ) ) -echo Putting "$fqdn" into QMAIL/control/locals... +echo Putting "$fqdn" into control/locals... echo "$fqdn" >> QMAIL/control/locals chmod 644 QMAIL/control/locals -echo Putting "$fqdn" into QMAIL/control/rcpthosts... +echo Putting "$fqdn" into control/rcpthosts... echo "$fqdn" >> QMAIL/control/rcpthosts chmod 644 QMAIL/control/rcpthosts echo "Now qmail will refuse to accept SMTP messages except to $fqdn." diff -ruN qmail-1.03/config.sh qmail-1.03.patched/config.sh --- qmail-1.03/config.sh 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/config.sh 1998-06-15 12:53:16.000000000 +0200 @@ -6,18 +6,18 @@ if read fqdn then echo Your host\'s fully qualified name in DNS is "$fqdn". - echo Putting "$fqdn" into QMAIL/control/me ... + echo Putting "$fqdn" into control/me... echo "$fqdn" > QMAIL/control/me chmod 644 QMAIL/control/me ( echo "$fqdn" | sed 's/^\([^\.]*\)\.\([^\.]*\)\./\2\./' | ( read ddom - echo Putting "$ddom" into QMAIL/control/defaultdomain ... + echo Putting "$ddom" into control/defaultdomain... echo "$ddom" > QMAIL/control/defaultdomain chmod 644 QMAIL/control/defaultdomain ) ) ( echo "$fqdn" | sed 's/^.*\.\([^\.]*\)\.\([^\.]*\)$/\1.\2/' | ( read pdom - echo Putting "$pdom" into QMAIL/control/plusdomain ... + echo Putting "$pdom" into control/plusdomain... echo "$pdom" > QMAIL/control/plusdomain chmod 644 QMAIL/control/plusdomain ) ) @@ -34,7 +34,7 @@ ./dnsptr "$localip" 2>/dev/null | ( if read local then - echo Adding "$local" to QMAIL/control/locals... + echo Adding "$local" to control/locals... echo "$local" >> QMAIL/control/locals else echo PTR lookup failed. I assume this address has no DNS name. @@ -54,11 +54,11 @@ echo 'Make sure to change rcpthosts if you add hosts to locals or virtualdomains!' else echo Sorry, I couldn\'t find your host\'s canonical name in DNS. - echo You will have to set up QMAIL/control/me yourself. + echo You will have to set up control/me yourself. fi ) else echo Sorry, I couldn\'t find your hostname. - echo You will have to set up QMAIL/control/me yourself. + echo You will have to set up control/me yourself. fi ) diff -ruN qmail-1.03/conf-spawn qmail-1.03.patched/conf-spawn --- qmail-1.03/conf-spawn 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/conf-spawn 2007-09-27 03:28:05.000000000 +0200 @@ -1,5 +1,7 @@ 120 -This is a silent concurrency limit. You can't set it above 255. On some -systems you can't set it above 125. qmail will refuse to compile if the -limit is too high. +This is a silent concurrency limit. You can't set it above 65000. Many +systems have a "hidden limit" of 509, because a single process cannot +have more than 1023 handles open at once, and each concurrent delivery +uses two handles. If you set it any higher than your system's "hidden +limit", qmail will refuse to compile. diff -ruN qmail-1.03/dns.c qmail-1.03.patched/dns.c --- qmail-1.03/dns.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/dns.c 2007-09-27 03:28:05.000000000 +0200 @@ -7,8 +7,11 @@ #include extern int res_query(); extern int res_search(); +extern int errno; +extern int h_errno; #include "ip.h" #include "ipalloc.h" +#include "strsalloc.h" #include "fmt.h" #include "alloc.h" #include "str.h" @@ -29,6 +32,7 @@ static int numanswers; static char name[MAXDNAME]; static struct ip_address ip; +static stralloc txt = {0}; unsigned short pref; static stralloc glue = {0}; @@ -194,6 +198,49 @@ return 0; } +static int findtxt(wanttype) +int wanttype; +{ + unsigned short rrtype; + unsigned short rrdlen; + int i; + + if (numanswers <= 0) return 2; + --numanswers; + if (responsepos == responseend) return DNS_SOFT; + + i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); + if (i < 0) return DNS_SOFT; + responsepos += i; + + i = responseend - responsepos; + if (i < 4 + 3 * 2) return DNS_SOFT; + + rrtype = getshort(responsepos); + rrdlen = getshort(responsepos + 8); + responsepos += 10; + + if (rrtype == wanttype) + { + unsigned short txtpos; + unsigned char txtlen; + + txt.len = 0; + for (txtpos = 0;txtpos < rrdlen;txtpos += txtlen) + { + txtlen = responsepos[txtpos++]; + if (txtlen > rrdlen-txtpos) txtlen = rrdlen-txtpos; + if (!stralloc_catb(&txt,&responsepos[txtpos],txtlen)) return DNS_MEM; + } + + responsepos += rrdlen; + return 1; + } + + responsepos += rrdlen; + return 0; +} + void dns_init(flagsearch) int flagsearch; { @@ -211,7 +258,7 @@ if (!sa->len) return loop; if (sa->s[sa->len - 1] == ']') return loop; if (sa->s[sa->len - 1] == '.') { --sa->len; continue; } - switch(resolve(sa,T_ANY)) + switch(resolve(sa,T_CNAME)) { case DNS_MEM: return DNS_MEM; case DNS_SOFT: return DNS_SOFT; @@ -252,15 +299,18 @@ return len; } -int dns_ptr(sa,ip) -stralloc *sa; +static int dns_ptrplus(ssa,ip) +strsalloc *ssa; struct ip_address *ip; { + stralloc sa = {0}; int r; - if (!stralloc_ready(sa,iaafmt((char *) 0,ip))) return DNS_MEM; - sa->len = iaafmt(sa->s,ip); - switch(resolve(sa,T_PTR)) + if (!stralloc_ready(&sa,iaafmt((char *) 0,ip))) return DNS_MEM; + sa.len = iaafmt(sa.s,ip); + r = resolve(&sa,T_PTR); + alloc_free(sa.s); + switch(r) { case DNS_MEM: return DNS_MEM; case DNS_SOFT: return DNS_SOFT; @@ -271,13 +321,35 @@ if (r == DNS_SOFT) return DNS_SOFT; if (r == 1) { - if (!stralloc_copys(sa,name)) return DNS_MEM; - return 0; + stralloc sa2 = {0}; + if (!stralloc_copys(&sa2,name)) return DNS_MEM; + if (!strsalloc_append(ssa,&sa2)) return DNS_MEM; } } + if (ssa->len) return 0; return DNS_HARD; } +int dns_ptr(ssa,ip) +strsalloc *ssa; +struct ip_address *ip; +{ + int r; + int j; + + if (!strsalloc_readyplus(ssa,0)) return DNS_MEM; + ssa->len = 0; + r = dns_ptrplus(ssa,ip); + if (r < 0) + { + for (j = 0;j < ssa->len;++j) + alloc_free(ssa->sa[j].s); + ssa->len = 0; + } + return r; +} + + static int dns_ipplus(ia,sa,pref) ipalloc *ia; stralloc *sa; @@ -285,6 +357,14 @@ { int r; struct ip_mx ix; +#ifdef TLS + stralloc fqdn = {0}; + + if (!stralloc_copy(&fqdn,sa)) return DNS_MEM; + if (!stralloc_0(&fqdn)) return DNS_MEM; + ix.fqdn = fqdn.s; + alloc_free(fqdn); +#endif if (!stralloc_copy(&glue,sa)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; @@ -345,6 +425,9 @@ ix.pref = 0; if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) { +#ifdef TLS + ix.fqdn = NULL; +#endif if (!ipalloc_append(ia,&ix)) return DNS_MEM; return 0; } @@ -413,3 +496,49 @@ alloc_free(mx); return flagsoft; } + + +static int dns_txtplus(ssa,sa) +strsalloc *ssa; +stralloc *sa; +{ + int r; + + switch(resolve(sa,T_TXT)) + { + case DNS_MEM: return DNS_MEM; + case DNS_SOFT: return DNS_SOFT; + case DNS_HARD: return DNS_HARD; + } + while ((r = findtxt(T_TXT)) != 2) + { + if (r == DNS_SOFT) return DNS_SOFT; + if (r == 1) + { + stralloc sa = {0}; + if (!stralloc_copy(&sa,&txt)) return DNS_MEM; + if (!strsalloc_append(ssa,&sa)) return DNS_MEM; + } + } + if (ssa->len) return 0; + return DNS_HARD; +} + +int dns_txt(ssa,sa) +strsalloc *ssa; +stralloc *sa; +{ + int r; + int j; + + if (!strsalloc_readyplus(ssa,0)) return DNS_MEM; + ssa->len = 0; + r = dns_txtplus(ssa,sa); + if (r < 0) + { + for (j = 0;j < ssa->len;++j) + alloc_free(ssa->sa[j].s); + ssa->len = 0; + } + return r; +} diff -ruN qmail-1.03/dnsfq.c qmail-1.03.patched/dnsfq.c --- qmail-1.03/dnsfq.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/dnsfq.c 2007-09-27 03:28:05.000000000 +0200 @@ -5,15 +5,19 @@ #include "dnsdoe.h" #include "ip.h" #include "ipalloc.h" +#include "strsalloc.h" #include "exit.h" stralloc sa = {0}; +strsalloc ssa = {0}; ipalloc ia = {0}; void main(argc,argv) int argc; char **argv; { + int j; + if (!argv[1]) _exit(100); if (!stralloc_copys(&sa,argv[1])) @@ -25,8 +29,11 @@ { substdio_putsflush(subfderr,"no IP addresses\n"); _exit(100); } - dnsdoe(dns_ptr(&sa,&ia.ix[0].ip)); - substdio_putflush(subfdout,sa.s,sa.len); - substdio_putsflush(subfdout,"\n"); + dnsdoe(dns_ptr(&ssa,&ia.ix[0].ip)); + for(j = 0;j < ssa.len;++j) + { + substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len); + substdio_putsflush(subfdout,"\n"); + } _exit(0); } diff -ruN qmail-1.03/dns.h qmail-1.03.patched/dns.h --- qmail-1.03/dns.h 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/dns.h 2007-09-27 03:28:05.000000000 +0200 @@ -10,5 +10,6 @@ int dns_mxip(); int dns_ip(); int dns_ptr(); +int dns_txt(); #endif diff -ruN qmail-1.03/dnsptr.c qmail-1.03.patched/dnsptr.c --- qmail-1.03/dnsptr.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/dnsptr.c 2007-09-27 03:28:05.000000000 +0200 @@ -6,22 +6,28 @@ #include "dns.h" #include "dnsdoe.h" #include "ip.h" +#include "strsalloc.h" #include "exit.h" -stralloc sa = {0}; +strsalloc ssa = {0}; struct ip_address ip; void main(argc,argv) int argc; char **argv; { + int j; + if (!argv[1]) _exit(100); ip_scan(argv[1],&ip); dns_init(0); - dnsdoe(dns_ptr(&sa,&ip)); - substdio_putflush(subfdout,sa.s,sa.len); - substdio_putsflush(subfdout,"\n"); + dnsdoe(dns_ptr(&ssa,&ip)); + for(j = 0;j < ssa.len;++j) + { + substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len); + substdio_putsflush(subfdout,"\n"); + } _exit(0); } diff -ruN qmail-1.03/dnstxt.c qmail-1.03.patched/dnstxt.c --- qmail-1.03/dnstxt.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/dnstxt.c 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,32 @@ +#include "substdio.h" +#include "subfd.h" +#include "stralloc.h" +#include "str.h" +#include "scan.h" +#include "dns.h" +#include "dnsdoe.h" +#include "strsalloc.h" +#include "exit.h" + +strsalloc ssa = {0}; +stralloc sa = {0}; + +void main(argc,argv) +int argc; +char **argv; +{ + int j; + + if (!argv[1]) _exit(100); + + if (!stralloc_copys(&sa, argv[1])) + { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); } + dns_init(0); + dnsdoe(dns_txt(&ssa,&sa)); + for (j = 0;j < ssa.len;++j) + { + substdio_put(subfdout,ssa.sa[j].s,ssa.sa[j].len); + substdio_putsflush(subfdout,"\n"); + } + _exit(0); +} diff -ruN qmail-1.03/error.3 qmail-1.03.patched/error.3 --- qmail-1.03/error.3 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/error.3 1998-06-15 12:53:16.000000000 +0200 @@ -3,7 +3,8 @@ error \- syscall error codes .SH SYNTAX .B #include -.B #include + +extern int \fBerrno\fP; extern int \fBerror_intr\fP; .br diff -ruN qmail-1.03/error.c qmail-1.03.patched/error.c --- qmail-1.03/error.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/error.c 2007-09-27 03:28:05.000000000 +0200 @@ -93,3 +93,10 @@ #else -13; #endif + +int error_dquot = +#ifdef EDQUOT +EDQUOT; +#else +-14; +#endif diff -ruN qmail-1.03/error.h qmail-1.03.patched/error.h --- qmail-1.03/error.h 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/error.h 2007-09-27 03:28:05.000000000 +0200 @@ -1,7 +1,8 @@ #ifndef ERROR_H #define ERROR_H -#include +/* extern int errno; */ +#include extern int error_intr; extern int error_nomem; @@ -16,6 +17,7 @@ extern int error_pipe; extern int error_perm; extern int error_acces; +extern int error_dquot; extern char *error_str(); extern int error_temp(); diff -ruN qmail-1.03/EXTTODO qmail-1.03.patched/EXTTODO --- qmail-1.03/EXTTODO 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/EXTTODO 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,114 @@ +EXTTODO by Claudio Jeker and +Andre Oppermann +(c) 1998,1999,2000,2001,2002 Internet Business Solutions Ltd. + +The EXTTODO patch is a part of the qmail-ldap patch. +This patches for qmail come with NO WARRANTY. + +These patches are under the BSD license. + +RELEASE: 5. Jan. 2003 + +EXTTODO: +====================== + +TOC: + WHAT DOES IT DO + INSTALL + CONFIG FILES + SETUP + BIG PICTURE + +NEWS: + + This is the first release of the EXTTODO patch. + +================================================================================ + +WHAT DOES IT DO + + The exttodo patch addresses a problem known as the silly qmail (queue) + problem. This problem is found only on system with high injection rates. + + qmail with a big local and remote concurrency could deliver a tremendous + amount of messages but normally this can not be achieved because qmail-send + becomes a bottleneck on those high volumes servers. + qmail-send preprocesses all new messages before distributing them for local + or remote delivering. In one run qmail-send does one todo run but has the + ability to close multiple jobs. Because of this layout qmail-send can not + feed all the new available (local/remote) delivery slots and therefor it is + not possible to achieve the maximum throughput. + This would be a minor problem if one qmail-send run could be done in extreme + short time but because of many file system calls (fsync and (un)link) a todo + run is expensive and throttles the throughput. + + The exttodo patch tries to solve the problem by moving the todo routine into + an external program. This reduces the run time in qmail-send. + + exttodo adds a new program to qmail called qmail-todo. qmail-todo prepares + incoming messages for local and remote delivering (by creating info/ + local/ and remote/ and removing todo/). See also + INTERNALS. As next qmail-todo transmits the to qmail-send which will + add this message into the priority queue which schedules the message for + delivery. + +INSTALL + + To enable the exttodo patch you need to define EXTERNAL_TODO while compiling + qmail(-ldap) this can be done with the -D flag of cc (e.g. cc -DEXTERNAL_TODO). + + NOTE: the exttodo patch can also be used on qmail systems without the + qmail-ldap patch. + +================================================================================ + +CONFIG FILES + + No additional control files are used or needed. + +================================================================================ + +SETUP + + qmail-todo will be started by qmail-start and therefor no additional setup + is needed. + + To verify that exttodo is running just check if qmail-todo is running. + +================================================================================ + +BIG PICTURE + + +-------+ +-------+ + | clean | | clean | + +--0-1--+ +--0-1--+ +-----------+ + trigger ^ | ^ | +->0,1 lspawn | + | | v | v / +-----------+ + +-------+ v +--2-3--+ +--5-6--+ / + | | | | 0<--7 1,2<-+ + | queue |--+--| todo | | send | + | | | | 1-->8 3,4<-+ + +-------+ +-------+ +---0---+ \ + | \ +-----------+ + v +->0,1 rspwan | + +---0---+ +-----------+ + | logger| + +-------+ + +Communication between qmail-send and qmail-todo + +todo -> send: + D[LRB]\0 + Start delivery for new message with id . + the character L, R or B defines the type + of delivery, local, remote or both respectively. + L\0 + Dump string to the logger without adding additional \n or similar. +send -> todo: + H Got a SIGHUP reread ~/control/locals and ~/control/virtualdomains + X Quit ASAP. + +qmail-todo sends "\0" terminated messages whereas qmail-send just send one +character to qmail-todo. + + diff -ruN qmail-1.03/EXTTODO-INFO qmail-1.03.patched/EXTTODO-INFO --- qmail-1.03/EXTTODO-INFO 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/EXTTODO-INFO 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,11 @@ +Files modified: +Makefile +EXTTODO +FILES +TARGETS +qmail-send.c +qmail-todo.c +qmail-start.c +hier.c +install-big.c + diff -ruN qmail-1.03/FILES qmail-1.03.patched/FILES --- qmail-1.03/FILES 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/FILES 2007-09-27 04:18:34.000000000 +0200 @@ -98,6 +98,7 @@ qmail-start.9 qmail-tcpok.8 qmail-tcpto.8 +qmail-todo.8 qmail-users.9 qmail.7 qreceipt.1 @@ -135,6 +136,8 @@ dnsip.c dnsmxip.c dnsptr.c +dnstxt.c +spfquery.c hostname.c ipmeprint.c tcp-env.c @@ -335,13 +338,16 @@ byte.h byte_chr.c byte_copy.c +byte_cspn.c byte_cr.c byte_diff.c byte_rchr.c +byte_rcspn.c byte_zero.c str.h str_chr.c str_cpy.c +str_cpyb.c str_diff.c str_diffn.c str_len.c @@ -401,6 +407,8 @@ date822fmt.c dns.h dns.c +spf.h +spf.c trylsock.c tryrsolv.c ip.h @@ -431,3 +439,4 @@ tcp-environ.5 constmap.h constmap.c +qmail-todo.c diff -ruN qmail-1.03/hier.c qmail-1.03.patched/hier.c --- qmail-1.03/hier.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/hier.c 2007-09-27 04:18:56.000000000 +0200 @@ -109,6 +109,9 @@ c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711); +#ifdef EXTERNAL_TODO + c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711); +#endif c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700); c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700); @@ -236,6 +239,8 @@ c(auto_qmail,"man/cat8","qmail-tcpok.0",auto_uido,auto_gidq,0644); c(auto_qmail,"man/man8","qmail-tcpto.8",auto_uido,auto_gidq,0644); c(auto_qmail,"man/cat8","qmail-tcpto.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-todo.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-todo.0",auto_uido,auto_gidq,0644); c(auto_qmail,"man/man8","qmail-pop3d.8",auto_uido,auto_gidq,0644); c(auto_qmail,"man/cat8","qmail-pop3d.0",auto_uido,auto_gidq,0644); c(auto_qmail,"man/man8","qmail-popup.8",auto_uido,auto_gidq,0644); diff -ruN qmail-1.03/install-big.c qmail-1.03.patched/install-big.c --- qmail-1.03/install-big.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/install-big.c 2007-09-27 03:28:05.000000000 +0200 @@ -109,6 +109,9 @@ c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711); +#ifdef EXTERNAL_TODO + c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711); +#endif c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700); c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700); diff -ruN qmail-1.03/ipalloc.h qmail-1.03.patched/ipalloc.h --- qmail-1.03/ipalloc.h 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/ipalloc.h 2007-09-27 03:28:05.000000000 +0200 @@ -3,7 +3,12 @@ #include "ip.h" +#ifdef TLS +#include "stralloc.h" +struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ; +#else struct ip_mx { struct ip_address ip; int pref; } ; +#endif #include "gen_alloc.h" diff -ruN qmail-1.03/ipme.c qmail-1.03.patched/ipme.c --- qmail-1.03/ipme.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/ipme.c 2007-09-27 03:28:05.000000000 +0200 @@ -53,7 +53,7 @@ if (!ipalloc_append(&ipme,&ix)) { return 0; } if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1; - len = 256; + len = 8192; /* any value big enough to get all the interfaces in one read is good */ for (;;) { if (!stralloc_ready(&buf,len)) { close(s); return 0; } buf.len = 0; @@ -65,7 +65,7 @@ break; } if (len > 200000) { close(s); return -1; } - len += 100 + (len >> 2); + len *= 2; } x = buf.s; while (x < buf.s + buf.len) { diff -ruN qmail-1.03/Makefile qmail-1.03.patched/Makefile --- qmail-1.03/Makefile 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/Makefile 2007-09-27 04:18:21.000000000 +0200 @@ -1,5 +1,7 @@ # Don't edit Makefile! Use conf-* for configuration. +DEFINES=-DEXTERNAL_TODO # use to enable external todo + SHELL=/bin/sh default: it @@ -136,6 +138,10 @@ compile auto_usera.c ./compile auto_usera.c +base64.o: \ +compile base64.c base64.h stralloc.h substdio.h str.h + ./compile base64.c + binm1: \ binm1.sh conf-qmail cat binm1.sh \ @@ -203,6 +209,10 @@ compile byte_cr.c byte.h ./compile byte_cr.c +byte_cspn.o: \ +compile byte_cspn.c byte.h + ./compile byte_cspn.c + byte_diff.o: \ compile byte_diff.c byte.h ./compile byte_diff.c @@ -211,6 +221,10 @@ compile byte_rchr.c byte.h ./compile byte_rchr.c +byte_rcspn.o: \ +compile byte_rcspn.c byte.h + ./compile byte_rcspn.c + byte_zero.o: \ compile byte_zero.c byte.h ./compile byte_zero.c @@ -263,7 +277,7 @@ cdbmake_add.o cdbmake_add.o: \ -compile cdbmake_add.c cdbmake.h alloc.h uint32.h +compile cdbmake_add.c cdbmake.h uint32.h ./compile cdbmake_add.c cdbmake_hash.o: \ @@ -393,84 +407,96 @@ rm -f trydrent.o dns.lib: \ -tryrsolv.c compile load socket.lib dns.o ipalloc.o ip.o stralloc.a \ -alloc.a error.a fs.a str.a +tryrsolv.c compile load socket.lib dns.o ipalloc.o strsalloc.o ip.o \ +stralloc.a alloc.a error.a fs.a str.a ( ( ./compile tryrsolv.c && ./load tryrsolv dns.o \ - ipalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \ + ipalloc.o strsalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \ -lresolv `cat socket.lib` ) >/dev/null 2>&1 \ && echo -lresolv || exit 0 ) > dns.lib rm -f tryrsolv.o tryrsolv dns.o: \ -compile dns.c ip.h ipalloc.h ip.h gen_alloc.h fmt.h alloc.h str.h \ -stralloc.h gen_alloc.h dns.h case.h +compile dns.c ip.h ipalloc.h strsalloc.h gen_alloc.h fmt.h alloc.h \ +str.h stralloc.h dns.h case.h ./compile dns.c dnscname: \ -load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ +load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ substdio.a error.a str.a fs.a dns.lib socket.lib - ./load dnscname dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ + ./load dnscname dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ socket.lib` dnscname.o: \ -compile dnscname.c substdio.h subfd.h substdio.h stralloc.h \ +compile dnscname.c substdio.h subfd.h stralloc.h \ gen_alloc.h dns.h dnsdoe.h readwrite.h exit.h ./compile dnscname.c dnsdoe.o: \ -compile dnsdoe.c substdio.h subfd.h substdio.h exit.h dns.h dnsdoe.h +compile dnsdoe.c substdio.h subfd.h exit.h dns.h dnsdoe.h ./compile dnsdoe.c dnsfq: \ -load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ +load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ substdio.a error.a str.a fs.a dns.lib socket.lib - ./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ + ./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ socket.lib` dnsfq.o: \ -compile dnsfq.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ -dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h +compile dnsfq.c substdio.h subfd.h stralloc.h gen_alloc.h \ +dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h exit.h ./compile dnsfq.c dnsip: \ -load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ +load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ substdio.a error.a str.a fs.a dns.lib socket.lib - ./load dnsip dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ + ./load dnsip dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ socket.lib` dnsip.o: \ -compile dnsip.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ -dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h +compile dnsip.c substdio.h subfd.h stralloc.h gen_alloc.h \ +dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h exit.h ./compile dnsip.c dnsmxip: \ -load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o now.o stralloc.a alloc.a \ -substdio.a error.a str.a fs.a dns.lib socket.lib - ./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o now.o \ +load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o stralloc.a \ +alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib + ./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o \ stralloc.a alloc.a substdio.a error.a str.a fs.a `cat \ dns.lib` `cat socket.lib` dnsmxip.o: \ -compile dnsmxip.c substdio.h subfd.h substdio.h stralloc.h \ -gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h \ +compile dnsmxip.c substdio.h subfd.h stralloc.h \ +gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h \ now.h datetime.h exit.h ./compile dnsmxip.c dnsptr: \ -load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ +load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ substdio.a error.a str.a fs.a dns.lib socket.lib - ./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ + ./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ socket.lib` dnsptr.o: \ -compile dnsptr.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ +compile dnsptr.c substdio.h subfd.h stralloc.h gen_alloc.h \ str.h scan.h dns.h dnsdoe.h ip.h exit.h ./compile dnsptr.c +dnstxt: \ +load dnstxt.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ +substdio.a error.a str.a fs.a dns.lib socket.lib + ./load dnstxt dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \ + alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ + socket.lib` + +dnstxt.o: \ +compile dnstxt.c substdio.h subfd.h stralloc.h gen_alloc.h \ +str.h scan.h dns.h dnsdoe.h ip.h exit.h + ./compile dnstxt.c + dot-qmail.0: \ dot-qmail.5 nroff -man dot-qmail.5 > dot-qmail.0 @@ -703,7 +729,7 @@ hier.o: \ compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h - ./compile hier.c + ./compile $(DEFINES) hier.c home: \ home.sh conf-qmail @@ -755,7 +781,7 @@ install-big.o: \ compile install-big.c auto_qmail.h auto_split.h auto_uids.h fmt.h \ fifo.h - ./compile install-big.c + ./compile $(DEFINES) install-big.c install.o: \ compile install.c substdio.h strerr.h error.h open.h readwrite.h \ @@ -777,24 +803,24 @@ ./compile ip.c ipalloc.o: \ -compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h ip.h \ +compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h \ gen_alloc.h ./compile ipalloc.c ipme.o: \ -compile ipme.c hassalen.h byte.h ip.h ipalloc.h ip.h gen_alloc.h \ -stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h +compile ipme.c hassalen.h byte.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h \ +stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h strsalloc.h ./compile ipme.c ipmeprint: \ -load ipmeprint.o ipme.o ip.o ipalloc.o stralloc.a alloc.a substdio.a \ -error.a str.a fs.a socket.lib - ./load ipmeprint ipme.o ip.o ipalloc.o stralloc.a alloc.a \ - substdio.a error.a str.a fs.a `cat socket.lib` +load ipmeprint.o ipme.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ +substdio.a error.a str.a fs.a socket.lib + ./load ipmeprint ipme.o ip.o ipalloc.o strsalloc.o stralloc.a \ + alloc.a substdio.a error.a str.a fs.a `cat socket.lib` ipmeprint.o: \ compile ipmeprint.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \ -ipalloc.h ip.h gen_alloc.h exit.h +ipalloc.h strsalloc.h ip.h gen_alloc.h exit.h ./compile ipmeprint.c it: \ @@ -804,11 +830,11 @@ qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \ qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \ qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \ -dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \ +dnsptr dnsip dnsmxip dnsfq dnstxt hostname ipmeprint qreceipt qsmhook qbiff \ forward preline condredirect bouncesaying except maildirmake \ maildir2mbox maildirwatch qail elq pinq idedit install-big install \ instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ -binm3 binm3+df +binm3 binm3+df spfquery qmail-todo load: \ make-load warn-auto.sh systype @@ -935,7 +961,7 @@ maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \ qmail-control.0 qmail-header.0 qmail-users.0 dot-qmail.0 \ qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 \ -envelopes.0 forgeries.0 +envelopes.0 forgeries.0 qmail-todo.0 mbox.0: \ mbox.5 @@ -1333,10 +1359,10 @@ qmail-qmqpc: \ load qmail-qmqpc.o slurpclose.o timeoutread.o timeoutwrite.o \ -timeoutconn.o ip.o control.o auto_qmail.o sig.a ndelay.a open.a \ +timeoutconn.o constmap.o case.a ip.o control.o auto_qmail.o sig.a ndelay.a open.a \ getln.a substdio.a stralloc.a alloc.a error.a str.a fs.a socket.lib ./load qmail-qmqpc slurpclose.o timeoutread.o \ - timeoutwrite.o timeoutconn.o ip.o control.o auto_qmail.o \ + timeoutwrite.o timeoutconn.o constmap.o case.a ip.o control.o auto_qmail.o \ sig.a ndelay.a open.a getln.a substdio.a stralloc.a alloc.a \ error.a str.a fs.a `cat socket.lib` @@ -1419,13 +1445,14 @@ nroff -man qmail-qstat.8 > qmail-qstat.0 qmail-queue: \ -load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o \ -datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \ -str.a fs.a auto_qmail.o auto_split.o auto_uids.o +load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o qregex.o \ +datetime.a seek.a case.a ndelay.a open.a sig.a getln.a stralloc.a alloc.a \ +substdio.a error.a control.o constmap.o str.a fs.a auto_qmail.o \ +auto_split.o auto_uids.o ./load qmail-queue triggerpull.o fmtqfn.o now.o \ - date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \ - alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ - auto_split.o auto_uids.o + date822fmt.o qregex.o control.o constmap.o datetime.a case.a seek.a \ + ndelay.a open.a sig.a getln.a stralloc.a alloc.a substdio.a error.a \ + str.a fs.a auto_qmail.o auto_split.o auto_uids.o qmail-queue.0: \ qmail-queue.8 @@ -1439,14 +1466,16 @@ qmail-remote: \ load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \ -timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o ipme.o quote.o \ +timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o strsalloc.o ipme.o quote.o \ ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \ -substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib +substdio.a error.a str.a fs.a auto_qmail.o base64.o qregex.o dns.lib \ +socket.lib ./load qmail-remote control.o constmap.o timeoutread.o \ timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \ - ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ + ipalloc.o strsalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \ - str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` + str.a fs.a auto_qmail.o base64.o qregex.o `cat dns.lib` \ + `cat socket.lib` -lssl -lcrypto qmail-remote.0: \ qmail-remote.8 @@ -1455,7 +1484,7 @@ qmail-remote.o: \ compile qmail-remote.c sig.h stralloc.h gen_alloc.h substdio.h \ subfd.h substdio.h scan.h case.h error.h auto_qmail.h control.h dns.h \ -alloc.h quote.h ip.h ipalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h \ +alloc.h quote.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h strsalloc.h \ gen_alloc.h gen_allocdefs.h str.h now.h datetime.h exit.h constmap.h \ tcpto.h readwrite.h timeoutconn.h timeoutread.h timeoutwrite.h ./compile qmail-remote.c @@ -1509,7 +1538,7 @@ scan.h case.h auto_qmail.h trigger.h newfield.h stralloc.h quote.h \ qmail.h substdio.h qsutil.h prioq.h datetime.h gen_alloc.h constmap.h \ fmtqfn.h readsubdir.h direntry.h - ./compile qmail-send.c + ./compile $(DEFINES) qmail-send.c qmail-showctl: \ load qmail-showctl.o auto_uids.o control.o open.a getln.a stralloc.a \ @@ -1528,21 +1557,23 @@ compile qmail-showctl.c substdio.h subfd.h substdio.h exit.h fmt.h \ str.h control.h constmap.h stralloc.h gen_alloc.h direntry.h \ auto_uids.h auto_qmail.h auto_break.h auto_patrn.h auto_spawn.h \ -auto_split.h +auto_split.h spf.h ./compile qmail-showctl.c qmail-smtpd: \ -load qmail-smtpd.o rcpthosts.o qregex.o commands.o timeoutread.o \ -timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ -date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ -open.a sig.a case.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \ -fs.a auto_qmail.o socket.lib +load qmail-smtpd.o qregex.o rcpthosts.o commands.o timeoutread.o \ +timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o constmap.o \ +received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a fd.a wait.a \ +datetime.a getln.a open.a sig.a case.a env.a stralloc.a alloc.a strerr.a \ +substdio.a error.a str.a fs.a auto_qmail.o base64.o socket.lib dns.lib \ +auto_break.o ./load qmail-smtpd qregex.o rcpthosts.o commands.o timeoutread.o \ - timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ - received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ - datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ - alloc.a strerr.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ - socket.lib` + timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o \ + constmap.o received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a \ + fd.a wait.a datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ + alloc.a strerr.a substdio.a error.a str.a fs.a auto_qmail.o \ + auto_break.o base64.o `cat socket.lib` `cat dns.lib` \ + -lssl -lcrypto -lcrypt qmail-smtpd.0: \ qmail-smtpd.8 @@ -1551,9 +1582,10 @@ qmail-smtpd.o: \ compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \ substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \ -error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \ +error.h ipme.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h ip.h qmail.h \ substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \ -exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h +exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h spf.h \ +cdb.h uint32.h auto_break.h ./compile qmail-smtpd.c qmail-start: \ @@ -1574,7 +1606,7 @@ qmail-start.o: \ compile qmail-start.c fd.h prot.h exit.h fork.h auto_uids.h - ./compile qmail-start.c + ./compile $(DEFINES) qmail-start.c qmail-tcpok: \ load qmail-tcpok.o open.a lock.a strerr.a substdio.a error.a str.a \ @@ -1606,6 +1638,24 @@ fmt.h ip.h lock.h error.h exit.h datetime.h now.h datetime.h ./compile qmail-tcpto.c +qmail-todo: \ +load qmail-todo.o control.o constmap.o trigger.o fmtqfn.o now.o \ +readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a alloc.a \ +substdio.a error.a str.a fs.a auto_qmail.o auto_split.o + ./load qmail-todo control.o constmap.o trigger.o fmtqfn.o now.o \ + readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a \ + alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_split.o + +qmail-todo.0: \ +qmail-todo.8 + nroff -man qmail-todo.8 > qmail-todo.0 + +qmail-todo.o: \ +compile alloc.h auto_qmail.h byte.h constmap.h control.h direntry.h error.h \ +exit.h fmt.h fmtqfn.h getln.h open.h ndelay.h now.h readsubdir.h readwrite.h \ +scan.h select.h str.h stralloc.h substdio.h trigger.h + ./compile $(DEFINES) qmail-todo.c + qmail-upq: \ warn-auto.sh qmail-upq.sh conf-qmail conf-break conf-split cat warn-auto.sh qmail-upq.sh \ @@ -1775,7 +1825,7 @@ qmail-popup.8 qmail-pw2u.9 qmail-qmqpc.8 qmail-qmqpd.8 qmail-qmtpd.8 \ qmail-qread.8 qmail-qstat.8 qmail-queue.8 qmail-remote.8 \ qmail-rspawn.8 qmail-send.9 qmail-showctl.8 qmail-smtpd.8 \ -qmail-start.9 qmail-tcpok.8 qmail-tcpto.8 qmail-users.9 qmail.7 \ +qmail-start.9 qmail-tcpok.8 qmail-tcpto.8 qmail-todo.8 qmail-users.9 qmail.7 \ qreceipt.1 splogger.8 tcp-env.1 config.sh config-fast.sh \ qmail-clean.c qmail-getpw.c qmail-inject.c qmail-local.c \ qmail-lspawn.c qmail-newmrh.c qmail-newu.c qmail-pop3d.c \ @@ -1783,7 +1833,7 @@ qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \ qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \ qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \ -dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \ +dnsip.c dnsmxip.c dnsptr.c dnstxt.c hostname.c ipmeprint.c tcp-env.c \ sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \ except.c bouncesaying.c condredirect.c maildirmake.c maildir2mbox.c \ maildirwatch.c splogger.c qail.sh elq.sh pinq.sh qmail-upq.sh \ @@ -1817,8 +1867,9 @@ trywaitp.c sig.h sig_alarm.c sig_block.c sig_catch.c sig_pause.c \ sig_pipe.c sig_child.c sig_term.c sig_hup.c sig_misc.c sig_bug.c \ trysgact.c trysgprm.c env.3 env.h env.c envread.c byte.h byte_chr.c \ -byte_copy.c byte_cr.c byte_diff.c byte_rchr.c byte_zero.c str.h \ -str_chr.c str_cpy.c str_diff.c str_diffn.c str_len.c str_rchr.c \ +byte_copy.c byte_cr.c byte_cspn.c byte_diff.c byte_rchr.c byte_rcspn.c \ +byte_zero.c str.h spf.c spf.h spfquery.c \ +str_chr.c str_cpy.c str_cpyb.c str_diff.c str_diffn.c str_len.c str_rchr.c \ str_start.c lock.h lock_ex.c lock_exnb.c lock_un.c tryflock.c getln.3 \ getln.h getln.c getln2.3 getln2.c sgetopt.3 sgetopt.h sgetopt.c \ subgetopt.3 subgetopt.h subgetopt.c error.3 error_str.3 error_temp.3 \ @@ -1828,7 +1879,7 @@ headerbody.h headerbody.c token822.h token822.c control.h control.c \ datetime.3 datetime.h datetime.c datetime_un.c prioq.h prioq.c \ date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \ -ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \ +ipalloc.h strsalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \ ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \ prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \ maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c @@ -1896,11 +1947,29 @@ spawn.o: \ compile chkspawn spawn.c sig.h wait.h substdio.h byte.h str.h \ -stralloc.h gen_alloc.h select.h exit.h alloc.h coe.h open.h error.h \ +stralloc.h gen_alloc.h select.h exit.h coe.h open.h error.h \ auto_qmail.h auto_uids.h auto_spawn.h ./chkspawn ./compile spawn.c +spf.o: \ +compile spf.c stralloc.h gen_alloc.h alloc.h ipme.h ip.h ipalloc.h \ +strsalloc.h str.h fmt.h scan.h byte.h now.h case.h qregex.h + ./compile spf.c + +spfquery: \ +load spfquery.o spf.o ip.o ipme.o ipalloc.o strsalloc.o now.o dns.o env.o \ +datetime.a stralloc.a alloc.a str.a substdio.a error.a fs.a case.a dns.lib \ +socket.lib envread.o qregex.o + ./load spfquery spf.o ip.o ipme.o ipalloc.o strsalloc.o \ + now.o dns.o env.o datetime.a stralloc.a alloc.a str.a substdio.a \ + case.a error.a fs.a `cat dns.lib` `cat socket.lib` envread.o qregex.o + +spfquery.o: \ +compile spfquery.c substdio.h subfd.h stralloc.h gen_alloc.h alloc.h \ +spf.h exit.h + ./compile spfquery.c + splogger: \ load splogger.o substdio.a error.a str.a fs.a syslog.lib socket.lib ./load splogger substdio.a error.a str.a fs.a `cat \ @@ -1916,12 +1985,12 @@ ./compile splogger.c str.a: \ -makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \ -str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_diff.o byte_copy.o \ -byte_cr.o byte_zero.o - ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o \ - str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o \ - byte_diff.o byte_copy.o byte_cr.o byte_zero.o +makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o str_chr.o \ +str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o byte_rcspn.o \ +byte_diff.o byte_copy.o byte_cr.o byte_zero.o + ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o \ + str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o \ + byte_rcspn.o byte_diff.o byte_copy.o byte_cr.o byte_zero.o str_chr.o: \ compile str_chr.c str.h @@ -1931,6 +2000,10 @@ compile str_cpy.c str.h ./compile str_cpy.c +str_cpyb.o: \ +compile str_cpyb.c str.h + ./compile str_cpyb.c + str_diff.o: \ compile str_diff.c str.h ./compile str_diff.c @@ -2010,6 +2083,11 @@ compile strerr_sys.c error.h strerr.h ./compile strerr_sys.c +strsalloc.o: \ +compile strsalloc.c alloc.h gen_allocdefs.h stralloc.h strsalloc.h \ +gen_alloc.h + ./compile strsalloc.c + subfderr.o: \ compile subfderr.c readwrite.h substdio.h subfd.h substdio.h ./compile subfderr.c @@ -2070,11 +2148,13 @@ tcp-env: \ load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \ -timeoutconn.o ip.o ipalloc.o case.a ndelay.a sig.a env.a getopt.a \ -stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib +constmap.o control.o open.a getln.a \ +timeoutconn.o ip.o ipalloc.o strsalloc.o case.a ndelay.a sig.a env.a \ +getopt.a stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib ./load tcp-env dns.o remoteinfo.o timeoutread.o \ - timeoutwrite.o timeoutconn.o ip.o ipalloc.o case.a ndelay.a \ - sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \ + constmap.o control.o open.a getln.a \ + timeoutwrite.o timeoutconn.o ip.o ipalloc.o strsalloc.o case.a \ + ndelay.a sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \ str.a fs.a `cat dns.lib` `cat socket.lib` tcp-env.0: \ @@ -2143,3 +2223,23 @@ wait_pid.o: \ compile wait_pid.c error.h haswaitp.h ./compile wait_pid.c + +cert: + openssl req -new -x509 -nodes \ + -out /var/qmail/control/servercert.pem -days 366 \ + -keyout /var/qmail/control/servercert.pem + chmod 640 /var/qmail/control/servercert.pem + chown qmaild.qmail /var/qmail/control/servercert.pem + ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem + +cert-req: + openssl req -new -nodes \ + -out req.pem \ + -keyout /var/qmail/control/servercert.pem + chmod 640 /var/qmail/control/servercert.pem + chown qmaild.qmail /var/qmail/control/servercert.pem + ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem + @echo + @echo "Send req.pem to your CA to obtain signed_req.pem, and do:" + @echo "cat signed_req.pem >> /var/qmail/control/servercert.pem" + diff -ruN qmail-1.03/make-makelib.sh qmail-1.03.patched/make-makelib.sh --- qmail-1.03/make-makelib.sh 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/make-makelib.sh 2007-09-27 03:28:05.000000000 +0200 @@ -3,6 +3,9 @@ echo 'ar cr "$main" ${1+"$@"}' case "$1" in +darwin-*) + echo 'ranlib -c "$main"' + ;; sunos-5.*) ;; unix_sv*) ;; irix64-*) ;; diff -ruN qmail-1.03/msg00151.txt qmail-1.03.patched/msg00151.txt --- qmail-1.03/msg00151.txt 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/msg00151.txt 1970-01-01 01:00:00.000000000 +0100 @@ -1,209 +0,0 @@ ---- Makefile Mon Jun 15 06:53:16 1998 -+++ ../qmail-1.03-modified/Makefile Tue Sep 8 15:36:43 1998 -@@ -803,6 +803,7 @@ - predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \ - qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \ - qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \ -+qmail-popbull \ - qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \ - dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \ - forward preline condredirect bouncesaying except maildirmake \ -@@ -930,6 +931,7 @@ - qmail-queue.0 qmail-inject.0 mailsubj.0 qmail-showctl.0 qmail-newu.0 \ - qmail-pw2u.0 qmail-qread.0 qmail-qstat.0 qmail-tcpto.0 qmail-tcpok.0 \ - qmail-pop3d.0 qmail-popup.0 qmail-qmqpc.0 qmail-qmqpd.0 qmail-qmtpd.0 \ -+qmail-popbull.0 \ - qmail-smtpd.0 tcp-env.0 qmail-newmrh.0 qreceipt.0 qbiff.0 forward.0 \ - preline.0 condredirect.0 bouncesaying.0 except.0 maildirmake.0 \ - maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \ -@@ -1286,6 +1288,18 @@ - timeoutwrite.h - ./compile qmail-pop3d.c - -+qmail-popbull: \ -+load qmail-popbull.o now.o substdio.a stralloc.a alloc.a str.a error.a -+ ./load qmail-popbull now.o substdio.a stralloc.a alloc.a fs.a str.a err -or.a -+ -+qmail-popbull.0: \ -+qmail-popbull.8 -+ nroff -man qmail-popbull.8 > qmail-popbull.0 -+ -+qmail-popbull.o: \ -+compile qmail-popbull.c direntry.h substdio.h stralloc.h fmt.h str.h -+ ./compile qmail-popbull.c -+ - qmail-popup: \ - load qmail-popup.o commands.o timeoutread.o timeoutwrite.o now.o \ - case.a fd.a sig.a wait.a stralloc.a alloc.a substdio.a error.a str.a \ - -diff -u -P ./qmail-popbull.8 new/qmail-popbull.8 ---- ./qmail-popbull.8 Wed Dec 31 19:00:00 1969 -+++ new/qmail-popbull.8 Wed Nov 19 08:53:24 1997 -@@ -0,0 +1,50 @@ -+.TH qmail-popbull 8 -+.SH NAME -+qmail-popbull \- insert bulletins -+.SH SYNOPSIS -+.B qmail-popbull -+.I bulletin-directory -+.I pop3d-program -+.I Maildir -+.SH DESCRIPTION -+.B qmail-popbull -+examines a directory of bulletins whenever a user checks their pop3 mail. -+When a new bulletin is seen, it inserts the bulletin into the user's -+.I Maildir . -+ -+It compares the time on the -+.I Maildir 's -+.B cur -+directory and compares it to the time on the bulletin. If the bulletin -+is newer, -+.B qmail-popbull -+inserts a symlink to the bulletin into the user's Maildir/new directory. -+The bulletin should be an ordinary email message. One reasonable use -+of -+.B qmail-popbull -+is to point it to the -+.B new -+directory of a special user's Maildir. When you send that user mail, -+it becomes a bulletin. -+ -+When it is finished, it execs the -+.I pop3d-program -+with the remainder of its arguments as arguments to -+.I pop3d-program . -+ -+.I pop3d-program -+can be any program, but that program's first argument must be the user's -+.I Maildir . -+ -+Suggested invocation: -+.EX -+ qmail-popup YOURHOST /bin/checkpassword /var/qmail/bin/qmail-popbull -+.br -+ /var/spool/bulletins /var/qmail/bin/qmail-pop3d Maildir -+.br -+ (all on one line) -+.EE -+ -+.SH "SEE ALSO" -+qmail-popup(8), -+qmail-pop3d(8) -diff -u -P ./qmail-popbull.c new/qmail-popbull.c ---- ./qmail-popbull.c Wed Dec 31 19:00:00 1969 -+++ new/qmail-popbull.c Wed Nov 19 08:40:07 1997 -@@ -0,0 +1,108 @@ -+#include -+#include -+#include "direntry.h" -+#include "substdio.h" -+#include "stralloc.h" -+#include "subfd.h" -+#include "fmt.h" -+#include "error.h" -+#include "datetime.h" -+#include "now.h" -+#include "str.h" -+ -+void die() { _exit(100); } -+void die_temp() { _exit(111); } -+void die_usage() { -+ substdio_putsflush(subfderr,"qmail-popbull: usage: qmail-popbull bulldir pop3 -d maildir\n"); die_temp(); } -+void die_nobulldir() { -+ substdio_putsflush(subfderr,"qmail-popbull: fatal: unable to read bulldir\n") -; die_temp(); } -+void die_nomaildir() { -+ substdio_putsflush(subfderr,"qmail-popbull: fatal: unable to write to maildir -\n"); die_temp(); } -+void die_nomem() { -+ substdio_putsflush(subfderr,"qmail-popbull: fatal: out of memory\n"); die_tem -p(); } -+ -+stralloc cur_name = {0}; -+stralloc fn = {0}; -+stralloc fn2 = {0}; -+char fntmptph[80 + FMT_ULONG * 2]; -+ -+void fnmake_maildir(dir) -+char *dir; -+{ -+ unsigned long pid; -+ unsigned long time; -+ char host[64]; -+ char *s; -+ int loop; -+ struct stat st; -+ int fd; -+ -+ pid = getpid(); -+ host[0] = 0; -+ gethostname(host,sizeof(host)); -+ for (loop = 0;;++loop) -+ { -+ time = now(); -+ s = fntmptph; -+ s += fmt_str(s,"new/"); -+ s += fmt_ulong(s,time); *s++ = '.'; -+ s += fmt_ulong(s,pid); *s++ = '.'; -+ s += fmt_strn(s,host,sizeof(host)); *s++ = 0; -+ if (stat(fntmptph,&st) == -1) if (errno == error_noent) break; -+ /* really should never get to this point */ -+ if (loop == 2) _exit(1); -+ sleep(2); -+ } -+} -+ -+ -+void main(argc,argv) -+int argc; -+char **argv; -+{ -+ int fd; -+ struct stat st; -+ datetime_sec cur_date; -+ char *bulldirname; -+ char *programname; -+ char *maildirname; -+ DIR *bulldir; -+ direntry *d; -+ char **childargs; -+ -+ if (!(bulldirname = argv[1])) die_usage(); -+ if (!(programname = argv[2])) die_usage(); -+ if (!(maildirname = argv[3])) die_usage(); -+ -+ if (!stralloc_copys(&cur_name, maildirname)) die_nomem(); -+ if (!stralloc_cats(&cur_name, "/cur")) die_nomem(); -+ if (!stralloc_0(&cur_name)) die_nomem(); -+ -+ if (stat(cur_name.s, &st) == -1) die_nomaildir(); -+ cur_date = st.st_atime; -+ -+ bulldir = opendir(bulldirname); -+ if (!bulldir) die_nobulldir(); -+ while(d = readdir(bulldir)) -+ { -+ if (str_equal(d->d_name,".")) continue; -+ if (str_equal(d->d_name,"..")) continue; -+ if (!stralloc_copys(&fn, bulldirname)) die_nomem(); -+ if (!stralloc_cats(&fn, "/")) die_nomem(); -+ if (!stralloc_cats(&fn, d->d_name)) die_nomem(); -+ if (!stralloc_0(&fn)) die_nomem(); -+ if (stat(fn.s, &st) == -1) die(); -+ if (st.st_mtime > cur_date) -+ { -+ fnmake_maildir(); -+ if (!stralloc_copys(&fn2, maildirname)) die_nomem(); -+ if (!stralloc_cats(&fn2, "/")) die_nomem(); -+ if (!stralloc_cats(&fn2, fntmptph)) die_nomem(); -+ if (!stralloc_0(&fn2)) die_nomem(); -+ symlink(fn.s, fn2.s); -+ } -+ } -+ closedir(bulldir); -+ childargs = argv + 2; -+ execvp(*childargs,childargs); -+} diff -ruN qmail-1.03/qmail.c qmail-1.03.patched/qmail.c --- qmail-1.03/qmail.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/qmail.c 2007-09-27 03:28:56.000000000 +0200 @@ -15,7 +15,7 @@ if(!binqqargs[0]) binqqargs[0] = env_get("QMAILQUEUE"); if(!binqqargs[0]) - binqqargs[0] = "bin/qmail-queue"; + binqqargs[0] = "/var/qmail/bin/qmail-queue"; } int qmail_open(qq) @@ -23,22 +23,32 @@ { int pim[2]; int pie[2]; + int pierr[2]; setup_qqargs(); if (pipe(pim) == -1) return -1; if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; } - + if (pipe(pierr) == -1) { + close(pim[0]); close(pim[1]); + close(pie[0]); close(pie[1]); + close(pierr[0]); close(pierr[1]); + return -1; + } + switch(qq->pid = vfork()) { case -1: + close(pierr[0]); close(pierr[1]); close(pim[0]); close(pim[1]); close(pie[0]); close(pie[1]); return -1; case 0: close(pim[1]); close(pie[1]); + close(pierr[0]); /* we want to receive data */ if (fd_move(0,pim[0]) == -1) _exit(120); if (fd_move(1,pie[0]) == -1) _exit(120); + if (fd_move(4,pierr[1]) == -1) _exit(120); if (chdir(auto_qmail) == -1) _exit(61); execv(*binqqargs,binqqargs); _exit(120); @@ -46,6 +56,7 @@ qq->fdm = pim[1]; close(pim[0]); qq->fde = pie[1]; close(pie[0]); + qq->fderr = pierr[0]; close(pierr[1]); substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf)); qq->flagerr = 0; return 0; @@ -93,10 +104,22 @@ { int wstat; int exitcode; + int match; + char ch; + static char errstr[256]; + int len = 0; qmail_put(qq,"",1); if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1; close(qq->fde); + substdio_fdbuf(&qq->ss,read,qq->fderr,qq->buf,sizeof(qq->buf)); + while( substdio_bget(&qq->ss,&ch,1) && len < 255){ + errstr[len]=ch; + len++; + } + if (len > 0) errstr[len]='\0'; /* add str-term */ + + close(qq->fderr); if (wait_pid(&wstat,qq->pid) != qq->pid) return "Zqq waitpid surprise (#4.3.0)"; @@ -105,9 +128,11 @@ exitcode = wait_exitcode(wstat); switch(exitcode) { + case 1: return "IYour SPAM has been ignored."; case 115: /* compatibility */ case 11: return "Denvelope address too long for qq (#5.1.3)"; case 31: return "Dmail server permanently rejected message (#5.3.0)"; + case 32: return "Dwe do not accept SPAM (#5.3.0)"; case 51: return "Zqq out of memory (#4.3.0)"; case 52: return "Zqq timeout (#4.3.0)"; case 53: return "Zqq write error or disk full (#4.3.0)"; @@ -129,8 +154,11 @@ case 81: return "Zqq internal bug (#4.3.0)"; case 120: return "Zunable to exec qq (#4.3.0)"; default: + if (exitcode == 82 && len > 2){ + return errstr; + } if ((exitcode >= 11) && (exitcode <= 40)) - return "Dqq permanent problem (#5.3.0)"; + return "Dqq permanent problem (#5.3.0)"; return "Zqq temporary problem (#4.3.0)"; } } diff -ruN qmail-1.03/qmail-control.9 qmail-1.03.patched/qmail-control.9 --- qmail-1.03/qmail-control.9 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/qmail-control.9 2007-09-27 03:28:05.000000000 +0200 @@ -23,8 +23,8 @@ .IR badhelo , .IR badmailfrom , .IR badmailfromnorelay , -.IR badmailto , -.IR badmailtonorelay , +.IR badrcpto , +.IR badrcptonorelay , .IR locals , .IR percenthack , .IR qmqpservers , @@ -47,8 +47,8 @@ .I badhelo \fR(none) \fRqmail-smtpd .I badmailfrom \fR(none) \fRqmail-smtpd .I badmailfromnorelay \fR(none) \fRqmail-smtpd -.I badmailto \fR(none) \fRqmail-smtpd -.I badmailtonorelay \fR(none) \fRqmail-smtpd +.I badrcptto \fR(none) \fRqmail-smtpd +.I badrcpttonorelay \fR(none) \fRqmail-smtpd .I bouncefrom \fRMAILER-DAEMON \fRqmail-send .I bouncehost \fIme \fRqmail-send .I concurrencylocal \fR10 \fRqmail-send @@ -63,6 +63,7 @@ .I idhost \fIme \fRqmail-inject .I localiphost \fIme \fRqmail-smtpd .I locals \fIme \fRqmail-send +.I mfcheck \fR0 \fRqmail-smtpd .I morercpthosts \fR(none) \fRqmail-smtpd .I percenthack \fR(none) \fRqmail-send .I plusdomain \fIme \fRqmail-inject @@ -71,6 +72,11 @@ .I rcpthosts \fR(none) \fRqmail-smtpd .I smtpgreeting \fIme \fRqmail-smtpd .I smtproutes \fR(none) \fRqmail-remote +.I spfbehavior \fR0 \fRqmail-smtpd +.I spfexp \fR(default) \fRqmail-smtpd +.I spfguess \fR(none) \fRqmail-smtpd +.I spfrules \fR(none) \fRqmail-smtpd +.I taps \fR(none) \fRqmail-queue .I timeoutconnect \fR60 \fRqmail-remote .I timeoutremote \fR1200 \fRqmail-remote .I timeoutsmtpd \fR1200 \fRqmail-smtpd @@ -80,6 +86,7 @@ .SH "SEE ALSO" qmail-inject(8), qmail-qmqpc(8), +qmail-queue(8), qmail-remote(8), qmail-send(8), qmail-showctl(8), diff -ruN qmail-1.03/qmail.h qmail-1.03.patched/qmail.h --- qmail-1.03/qmail.h 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/qmail.h 2007-09-27 03:28:05.000000000 +0200 @@ -8,6 +8,7 @@ unsigned long pid; int fdm; int fde; + int fderr; substdio ss; char buf[1024]; } ; diff -ruN qmail-1.03/qmail-local.c qmail-1.03.patched/qmail-local.c --- qmail-1.03/qmail-local.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/qmail-local.c 2007-09-27 03:28:05.000000000 +0200 @@ -1,6 +1,5 @@ #include #include -#include #include "readwrite.h" #include "sig.h" #include "env.h" @@ -42,6 +41,9 @@ void temp_qmail(fn) char *fn; { strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.3.0)"); } +char *overquota = + "Recipient's mailbox is full, message returned to sender. (#5.2.2)"; + int flagdoit; int flag99; @@ -111,7 +113,12 @@ alarm(86400); fd = open_excl(fntmptph); - if (fd == -1) _exit(1); + if (fd == -1) { + if (errno == error_dquot) + _exit(5); + else + _exit(1); + } substdio_fdbuf(&ss,read,0,buf,sizeof(buf)); substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf)); @@ -129,13 +136,15 @@ if (close(fd) == -1) goto fail; /* NFS dorks */ if (link(fntmptph,fnnewtph) == -1) goto fail; - if ((fd = open(fnnewtph, O_RDONLY)) < 0 || - fsync(fd) < 0 || close(fd) < 0) goto fail; - /* if it was error_exist, almost certainly successful; i hate NFS */ tryunlinktmp(); _exit(0); - fail: tryunlinktmp(); _exit(1); + fail: + if (errno == error_dquot) { + tryunlinktmp(); _exit(5); + } else { + tryunlinktmp(); _exit(1); + } } /* end child process */ @@ -166,6 +175,7 @@ case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)"); case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)"); case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)"); + case 5: strerr_die1x(100,overquota); default: strerr_die1x(111,"Temporary error on maildir delivery. (#4.3.0)"); } } @@ -225,7 +235,12 @@ return; writeerrs: - strerr_warn5("Unable to write ",fn,": ",error_str(errno),". (#4.3.0)",0); + if (errno == error_dquot) { + if (flaglocked) seek_trunc(fd,pos); + close(fd); + strerr_die1x(100,overquota); + } else + strerr_warn5("Unable to write ",fn,": ",error_str(errno),". (#4.3.0)",0); if (flaglocked) seek_trunc(fd,pos); close(fd); _exit(111); diff -ruN qmail-1.03/qmail-lspawn.c qmail-1.03.patched/qmail-lspawn.c --- qmail-1.03/qmail-lspawn.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/qmail-lspawn.c 2007-09-27 03:29:27.000000000 +0200 @@ -139,7 +139,7 @@ } if (pipe(pi) == -1) _exit(QLX_SYS); - args[0] = "/usr/sbin/qmail-getpw"; + args[0] = "/var/qmail/bin/qmail-getpw"; args[1] = local; args[2] = 0; switch(gpwpid = vfork()) @@ -191,7 +191,7 @@ x = nughde.s; xlen = nughde.len; - args[0] = "/usr/sbin/qmail-local"; + args[0] = "/var/qmail/bin/qmail-local"; args[1] = "--"; args[2] = x; n = byte_chr(x,xlen,0); if (n++ == xlen) _exit(QLX_USAGE); x += n; xlen -= n; diff -ruN qmail-1.03/qmail-pop3d.c qmail-1.03.patched/qmail-pop3d.c --- qmail-1.03/qmail-pop3d.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/qmail-pop3d.c 1998-06-15 12:53:16.000000000 +0200 @@ -66,14 +66,14 @@ void die_scan() { err("unable to scan $HOME/Maildir"); die(); } void err_syntax() { err("syntax error"); } -void err_unimpl(arg) char *arg; { err("unimplemented"); } +void err_unimpl() { err("unimplemented"); } void err_deleted() { err("already deleted"); } void err_nozero() { err("messages are counted from 1"); } void err_toobig() { err("not that many messages"); } void err_nosuch() { err("unable to open that message"); } void err_nounlink() { err("unable to unlink all deleted messages"); } -void okay(arg) char *arg; { puts("+OK \r\n"); flush(); } +void okay() { puts("+OK \r\n"); flush(); } void printfn(fn) char *fn; { @@ -146,7 +146,7 @@ } } -void pop3_stat(arg) char *arg; +void pop3_stat() { int i; unsigned long total; @@ -161,15 +161,15 @@ flush(); } -void pop3_rset(arg) char *arg; +void pop3_rset() { int i; for (i = 0;i < numm;++i) m[i].flagdeleted = 0; last = 0; - okay(0); + okay(); } -void pop3_last(arg) char *arg; +void pop3_last() { puts("+OK "); put(strnum,fmt_uint(strnum,last)); @@ -177,7 +177,7 @@ flush(); } -void pop3_quit(arg) char *arg; +void pop3_quit() { int i; for (i = 0;i < numm;++i) @@ -192,7 +192,7 @@ if (!stralloc_0(&line)) die_nomem(); rename(m[i].fn,line.s); /* if it fails, bummer */ } - okay(0); + okay(); die(); } @@ -214,7 +214,7 @@ if (i == -1) return; m[i].flagdeleted = 1; if (i + 1 > last) last = i + 1; - okay(0); + okay(); } void list(i,flaguidl) @@ -238,7 +238,7 @@ list(i,flaguidl); } else { - okay(0); + okay(); for (i = 0;i < numm;++i) if (!m[i].flagdeleted) list(i,flaguidl); @@ -267,11 +267,7 @@ fd = open_read(m[i].fn); if (fd == -1) { err_nosuch(); return; } - /* okay(); */ - puts("+OK "); - put(strnum,fmt_ulong(strnum,m[i].size)); - puts(" octets\r\n"); - flush(); + okay(); substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf)); blast(&ssmsg,limit); close(fd); @@ -303,7 +299,7 @@ getlist(); - okay(0); + okay(); commands(&ssin,pop3commands); die(); } diff -ruN qmail-1.03/qmail-popup.c qmail-1.03.patched/qmail-popup.c --- qmail-1.03/qmail-popup.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/qmail-popup.c 1998-06-15 12:53:16.000000000 +0200 @@ -64,10 +64,10 @@ void err_syntax() { err("syntax error"); } void err_wantuser() { err("USER first"); } -void err_authoriz(arg) char *arg; { err("authorization first"); } +void err_authoriz() { err("authorization first"); } -void okay(arg) char *arg; { puts("+OK \r\n"); flush(); } -void pop3_quit(arg) char *arg; { okay(0); die(); } +void okay() { puts("+OK \r\n"); flush(); } +void pop3_quit() { okay(); die(); } char unique[FMT_ULONG + FMT_ULONG + 3]; @@ -136,7 +136,7 @@ void pop3_user(arg) char *arg; { if (!*arg) { err_syntax(); return; } - okay(0); + okay(); seenuser = 1; if (!stralloc_copys(&username,arg)) die_nomem(); if (!stralloc_0(&username)) die_nomem(); diff -ruN qmail-1.03/qmail-queue.8 qmail-1.03.patched/qmail-queue.8 --- qmail-1.03/qmail-queue.8 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/qmail-queue.8 2007-09-27 03:28:05.000000000 +0200 @@ -40,6 +40,12 @@ However, the recipients probably expect to see a proper header, as described in .BR qmail-header(5) . +.SH "CONTROL FILES" +.TP 5 +.I taps +Should contain regex syntax of email addresses to tap and +the associated email address to send the copy to. The two +fields should be separated by a colon. .SH "FILESYSTEM RESTRICTIONS" .B qmail-queue imposes two constraints on the queue structure: diff -ruN qmail-1.03/qmail-queue.c qmail-1.03.patched/qmail-queue.c --- qmail-1.03/qmail-queue.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/qmail-queue.c 2007-09-27 03:28:05.000000000 +0200 @@ -1,6 +1,5 @@ #include #include -#include #include "readwrite.h" #include "sig.h" #include "exit.h" @@ -17,6 +16,8 @@ #include "auto_uids.h" #include "date822fmt.h" #include "fmtqfn.h" +#include "stralloc.h" +#include "constmap.h" #define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */ #define ADDR 1003 @@ -26,6 +27,13 @@ char outbuf[256]; struct substdio ssout; +int tapok = 0; +stralloc tap = {0}; +struct constmap maptap; +stralloc chkaddr = {0}; +int tapped; +stralloc tapaddr = {0}; + datetime_sec starttime; struct datetime dt; unsigned long mypid; @@ -156,11 +164,15 @@ { unsigned int len; char ch; - int fd; sig_blocknone(); umask(033); if (chdir(auto_qmail) == -1) die(61); + + tapok = control_readfile(&tap,"control/taps",0); + if (tapok == -1) die(65); + if (!constmap_init(&maptap,tap.s,tap.len,0)) die(65); + if (chdir("queue") == -1) die(62); mypid = getpid(); @@ -185,7 +197,7 @@ todofn = fnnum("todo/",0); intdfn = fnnum("intd/",0); - if (link(pidfn,messfn) == -1) die(64); + if (link(pidfn,messfn) == -1) die(64); if (unlink(pidfn) == -1) die(63); flagmademess = 1; @@ -221,14 +233,28 @@ if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (ch != 'F') die(91); if (substdio_bput(&ssout,&ch,1) == -1) die_write(); + stralloc_0(&chkaddr); for (len = 0;len < ADDR;++len) { + if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1); + else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1); if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (substdio_put(&ssout,&ch,1) == -1) die_write(); if (!ch) break; } if (len >= ADDR) die(11); + /* check the from address */ + stralloc_0(&chkaddr); + if (tapped == 0 && tapcheck()==1 ) { + tapped = 1; + if ( tapaddr.len > 0 ) { + if (substdio_bput(&ssout,"T",1) == -1) die_write(); + if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write(); + if (substdio_bput(&ssout,"",1) == -1) die_write(); + } + } + if (substdio_bput(&ssout,QUEUE_EXTRA,QUEUE_EXTRALEN) == -1) die_write(); for (;;) @@ -239,10 +265,24 @@ if (substdio_bput(&ssout,&ch,1) == -1) die_write(); for (len = 0;len < ADDR;++len) { + if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1); + else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1); if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (substdio_bput(&ssout,&ch,1) == -1) die_write(); if (!ch) break; } + + /* check the to address */ + stralloc_0(&chkaddr); + if (tapped == 0 && tapcheck()==1 ) { + tapped = 1; + if ( tapaddr.len > 0 ) { + if (substdio_bput(&ssout,"T",1) == -1) die_write(); + if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write(); + if (substdio_bput(&ssout,"",1) == -1) die_write(); + } + } + if (len >= ADDR) die(11); } @@ -250,9 +290,46 @@ if (fsync(intdfd) == -1) die_write(); if (link(intdfn,todofn) == -1) die(66); - if ((fd = open(todofn, O_RDONLY)) < 0 || - fsync(fd) < 0 || close(fd) < 0) die(66); triggerpull(); die(0); } + +int tapcheck() +{ + int i = 0; + int j = 0; + int x = 0; + int negate = 0; + stralloc curregex = {0}; + char tmpbuf[200]; + + while (j < tap.len) { + i = j; + while ((tap.s[i] != ':') && (i < tap.len)) i++; + if (tap.s[j] == '!') { + negate = 1; + j++; + } + stralloc_copys(&tapaddr, &tap.s[i+1]); + + stralloc_copyb(&curregex,tap.s + j,(i - j)); + stralloc_0(&curregex); + x = matchregex(chkaddr.s, curregex.s, tmpbuf); + + while ((tap.s[i] != '\0') && (i < tap.len)) i++; + + if ((negate) && (x == 0)) { + return 1; + } + if (!(negate) && (x > 0)) { + return 1; + } + j = i + 1; + negate = 0; + + + } + return 0; +} + diff -ruN qmail-1.03/qmail-remote.8 qmail-1.03.patched/qmail-remote.8 --- qmail-1.03/qmail-remote.8 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/qmail-remote.8 2007-09-27 03:28:05.000000000 +0200 @@ -127,8 +127,15 @@ .I smtproutes Artificial SMTP routes. Each route has the form -.IR domain\fB:\fIrelay , -without any extra spaces. + +.EX + \fIdomain\fB:\fIrelay user pass +.EE + +without any extra spaces between +.I domain +and +.I relay. If .I domain matches @@ -174,6 +181,35 @@ any other address is artificially routed to .BR heaven.af.mil . +.I user +and +.I pass +may be empty; if present they will be used as the userid and password +for an AUTH command when connecting to the remote SMTP server. Note that +in order to be used, both +.I user +and +.I pass +must be present, and must be separated from the prior items on the +line by exactly one space. This means that +.I user +cannot contain any spaces, although +.I pass +may contain spaces, and in fact if any "extra" spaces are present at the +end of the line, they will be considered part of the password. (You have +been warned.) + +Note that +.B qmail-remote +will not send AUTH commands over a non-secured connection (i.e. if a +STARTTLS command has not been successfully sent.) This is done on +purpose, to prevent your server from sending a password across the +internet in clear text. If +.I user +starts with a "-" character, the "-" will be removed and this security +check will be bypassed. Think of this as qmail-remote's version of the +ALLOW_INSECURE_AUTH variable used by qmail-smtpd. + The .B qmail system does not protect you if you create an artificial diff -ruN qmail-1.03/qmail-remote.c qmail-1.03.patched/qmail-remote.c --- qmail-1.03/qmail-remote.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/qmail-remote.c 2007-09-27 03:28:05.000000000 +0200 @@ -26,14 +26,29 @@ #include "tcpto.h" #include "readwrite.h" #include "timeoutconn.h" +#include "qregex.h" +#ifndef TLS #include "timeoutread.h" #include "timeoutwrite.h" +#endif + +#ifdef TLS +#include +#include +#include +SSL *ssl = NULL; + +stralloc tlsclientciphers = {0}; +int can_tls = 1; +#endif #define HUGESMTPTEXT 5000 #define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ unsigned long port = PORT_SMTP; +int allow_insecure_auth = 0; + GEN_ALLOC_typedef(saa,stralloc,sa,len,a) GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus) static stralloc sauninit = {0}; @@ -43,6 +58,9 @@ struct constmap maproutes; stralloc host = {0}; stralloc sender = {0}; +stralloc auth_smtp_user = {0}; +stralloc auth_smtp_pass = {0}; +stralloc auth_smtp_plain = {0}; saa reciplist = {0}; @@ -107,17 +125,94 @@ int smtpfd; int timeout = 1200; +#ifdef TLS +int flagtimedout = 0; +void sigalrm() +{ + flagtimedout = 1; +} + +int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) { + while(((r = SSL_read(ssl,buf,n)) <= 0) + && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ)); + if (SSL_get_error(ssl, r) != SSL_ERROR_NONE) + {char buf[1024]; + + out("ZTLS connection to "); outhost(); out(" died: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + }else r = read(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} + +int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) { + while(((r = SSL_write(ssl,buf,n)) <= 0) + && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_WRITE)); + if (SSL_get_error(ssl, r) != SSL_ERROR_NONE) + {char buf[1024]; + + out("ZTLS connection to "); outhost(); out(" died: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + }else r = write(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} + +static int client_cert_cb(SSL *s,X509 **x509, EVP_PKEY **pkey) +{ + out("ZTLS found no client cert in control/clientcert.pem\n"); + zerodie(NULL,NULL); +} + +static int verify_cb(int ok, X509_STORE_CTX * ctx) +{ + return (1); +} +#endif + int saferead(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + r = ssl_timeoutread(timeout,smtpfd,buf,len); +#else r = timeoutread(timeout,smtpfd,buf,len); +#endif if (r <= 0) dropped(); return r; } int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + r = ssl_timeoutwrite(timeout,smtpfd,buf,len); +#else r = timeoutwrite(timeout,smtpfd,buf,len); +#endif if (r <= 0) dropped(); return r; } @@ -186,6 +281,34 @@ out(append); out(".\n"); outsmtptext(); + +/* TAG */ +#if defined(TLS) && defined(DEBUG) +#define ONELINE_NAME(X) X509_NAME_oneline(X,NULL,0) + + if(ssl){ + X509 *peer; + + out("STARTTLS proto="); out(SSL_get_version(ssl)); + out("; cipher="); out(SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); + + /* we want certificate details */ + peer=SSL_get_peer_certificate(ssl); + if (peer != NULL) { + char *str; + + str=ONELINE_NAME(X509_get_subject_name(peer)); + out("; subject="); out(str); + OPENSSL_free(str); + str=ONELINE_NAME(X509_get_issuer_name(peer)); + out("; issuer="); out(str); + OPENSSL_free(str); + X509_free(peer); + } + out(";\n"); + } +#endif + zerodie(); } @@ -216,24 +339,208 @@ stralloc recip = {0}; +#ifdef TLS +void smtp(fqdn) +char *fqdn; +#else void smtp() +#endif { unsigned long code; int flagbother; + int try_plain=0; + int try_login=0; + int authd=0; int i; + stralloc slop = {0}; +#ifdef TLS + int needtlsauth = 0; + SSL_CTX *ctx; + int saveerrno, r; + + stralloc servercert = {0}; + struct stat st; + if(fqdn){ + if(!stralloc_copys(&servercert, "control/tlshosts/")) temp_nomem(); + if(!stralloc_catb(&servercert, fqdn, str_len(fqdn))) temp_nomem(); + if(!stralloc_catb(&servercert, ".pem", 4)) temp_nomem(); + if(!stralloc_0(&servercert)) temp_nomem(); + if (stat(servercert.s,&st) == 0) needtlsauth = 1; + } +#endif + + code = smtpcode(); + if (code >= 400 && code < 600) return; /* try next MX, see RFC 2821 */ + if (code != 220) quit("ZConnected to "," but greeting failed"); - if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); - - substdio_puts(&smtpto,"HELO "); + substdio_puts(&smtpto,"EHLO "); substdio_put(&smtpto,helohost.s,helohost.len); substdio_puts(&smtpto,"\r\n"); substdio_flush(&smtpto); - if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); - - substdio_puts(&smtpto,"MAIL FROM:<"); - substdio_put(&smtpto,sender.s,sender.len); - substdio_puts(&smtpto,">\r\n"); - substdio_flush(&smtpto); + if (smtpcode() != 250){ + substdio_puts(&smtpto,"HELO "); + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); + } + +#ifdef TLS + if(can_tls) { + i = 0; + while((i += str_chr(smtptext.s+i,'\n') + 1) && (i+12 < smtptext.len) && + str_diffn(smtptext.s+i+4,"STARTTLS\n",9)); + if (i+12 < smtptext.len) + { + substdio_puts(&smtpto,"STARTTLS\r\n"); + substdio_flush(&smtpto); + if (smtpcode() == 220) + { + SSL_library_init(); + if(!(ctx=SSL_CTX_new(SSLv23_client_method()))) + {char buf[1024]; + + out("ZTLS not available: error initializing ctx: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); + out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + if((stat("control/clientcert.pem", &st) == 0) && + ((SSL_CTX_use_RSAPrivateKey_file(ctx, "control/clientcert.pem", SSL_FILETYPE_PEM) <= 0) || + (SSL_CTX_use_certificate_chain_file(ctx, "control/clientcert.pem") <= 0) || + (SSL_CTX_check_private_key(ctx) <= 0))) + /* if there is a cert and it is bad, I fail + if there is no cert, I leave it to the other side to complain */ + SSL_CTX_set_client_cert_cb(ctx, client_cert_cb); + + /*SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);*/ + SSL_CTX_set_cipher_list(ctx,tlsclientciphers.s); + + if (needtlsauth){ + if (!SSL_CTX_load_verify_locations(ctx, servercert.s, NULL)) + {out("ZTLS unable to load "); out(servercert.s); out("\n"); + zerodie();} + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); + } + + if(!(ssl=SSL_new(ctx))) + {char buf[1024]; + + out("ZTLS not available: error initializing ssl: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); + out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + SSL_set_fd(ssl,smtpfd); + + alarm(timeout); + r = SSL_connect(ssl); saveerrno = errno; + alarm(0); + if (flagtimedout) + {out("ZTLS not available: connect timed out\n"); + zerodie();} + errno = saveerrno; + if (r<=0) + {char buf[1024]; + + out("ZTLS not available: connect failed: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); + out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + if (needtlsauth) + /* should also check alternate names */ + {char commonName[256]; + + if ((r=SSL_get_verify_result(ssl)) != X509_V_OK) + {out("ZTLS unable to verify server with "); + out(servercert.s); out(": "); + out(X509_verify_cert_error_string(r)); out("\n"); + zerodie(); + } + X509_NAME_get_text_by_NID(X509_get_subject_name( + SSL_get_peer_certificate(ssl)), + NID_commonName, commonName, 256); + if (strcasecmp(fqdn,commonName)){ + out("ZTLS connection to "); out(fqdn); + out(" wanted, certificate for "); out(commonName); + out(" received\n"); + zerodie();} + } + + substdio_puts(&smtpto,"EHLO "); + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + + if (smtpcode() != 250) + { + quit("ZTLS connected to "," but my name was rejected"); + } + } + } + if ((!ssl) && needtlsauth) + {out("ZNo TLS achieved while "); out(servercert.s); out(" exists.\n"); + quit();} + } /* ends if(can_tls) block */ +#endif + + if (allow_insecure_auth || ssl) { + stralloc_0(&smtptext); + if(auth_smtp_user.len && auth_smtp_pass.len) { + try_plain=matchregex(smtptext.s,"AUTH[^\n]+PLAIN"); + try_login=matchregex(smtptext.s,"AUTH[^\n]+LOGIN"); + } + } + + if (try_plain>0) { + if (!stralloc_0(&auth_smtp_plain)) temp_nomem(); + if (!stralloc_cat(&auth_smtp_plain,&auth_smtp_user)) temp_nomem(); + if (!stralloc_0(&auth_smtp_plain)) temp_nomem(); + if (!stralloc_cat(&auth_smtp_plain,&auth_smtp_pass)) temp_nomem(); + if (b64encode(&auth_smtp_plain,&slop)) temp_nomem(); + substdio_puts(&smtpto,"AUTH PLAIN "); + substdio_put(&smtpto,slop.s,slop.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 235) quit("ZConnected to "," but AUTH PLAIN was rejected"); + authd=1; + } + if ((!authd) && (try_login>0)) { + substdio_puts(&smtpto,"AUTH LOGIN\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 334) quit("ZConnected to "," but AUTH LOGIN command was rejected"); + if (b64encode(&auth_smtp_user,&slop) < 0) temp_nomem(); + substdio_put(&smtpto,slop.s,slop.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 334) quit("ZConnected to "," but AUTH LOGIN username was rejected"); + if (b64encode(&auth_smtp_pass,&slop) < 0) temp_nomem(); + substdio_put(&smtpto,slop.s,slop.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 235) quit("ZConnected to "," but AUTH LOGIN password was rejected"); + authd=1; + } + if (authd) { + substdio_puts(&smtpto,"MAIL FROM:<"); + substdio_put(&smtpto,sender.s,sender.len); + substdio_puts(&smtpto,"> AUTH=<"); + substdio_put(&smtpto,auth_smtp_user.s,auth_smtp_user.len); + substdio_puts(&smtpto,">\r\n"); + substdio_flush(&smtpto); + } else { + substdio_puts(&smtpto,"MAIL FROM:<"); + substdio_put(&smtpto,sender.s,sender.len); + substdio_puts(&smtpto,">\r\n"); + substdio_flush(&smtpto); + } code = smtpcode(); if (code >= 500) quit("DConnected to "," but sender was rejected"); if (code >= 400) quit("ZConnected to "," but sender was rejected"); @@ -324,6 +631,11 @@ case 1: if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; } +#ifdef TLS + if (control_rldef(&tlsclientciphers,"control/tlsclientciphers",0,"DEFAULT") != 1) + temp_control(); + if(!stralloc_0(&tlsclientciphers)) temp_nomem(); +#endif } void main(argc,argv) @@ -331,22 +643,34 @@ char **argv; { static ipalloc ip = {0}; - int i; + int i,j; unsigned long random; char **recips; unsigned long prefme; int flagallaliases; int flagalias; char *relayhost; - +#ifdef TLS + int zfd; + + sig_alarmcatch(sigalrm); +#endif + sig_pipeignore(); if (argc < 4) perm_usage(); if (chdir(auto_qmail) == -1) temp_chdir(); getcontrols(); - + +#ifdef TLS + if(-1==(zfd=open("control/clientcert.pem",O_RDONLY))) can_tls=0; + else close(zfd); +#endif if (!stralloc_copys(&host,argv[1])) temp_nomem(); + if (!stralloc_copys(&auth_smtp_user,"")) temp_nomem(); + if (!stralloc_copys(&auth_smtp_pass,"")) temp_nomem(); + relayhost = 0; for (i = 0;i <= host.len;++i) if ((i == 0) || (i == host.len) || (host.s[i] == '.')) @@ -355,6 +679,21 @@ if (relayhost && !*relayhost) relayhost = 0; if (relayhost) { + i = str_chr(relayhost,' '); + if (relayhost[i]) { + relayhost[i]=0; + if ('-' == relayhost[i+1]) { + allow_insecure_auth=1; + can_tls=0; + i++; + } + j = str_chr(relayhost + i + 1,' '); + if (relayhost[j]) { + relayhost[i + j + 1] = 0; + if (!stralloc_copys(&auth_smtp_user,relayhost + i + 1)) temp_nomem(); + if (!stralloc_copys(&auth_smtp_pass,relayhost + i + j + 2)) temp_nomem(); + } + } i = str_chr(relayhost,':'); if (relayhost[i]) { scan_ulong(relayhost + i + 1,&port); @@ -417,7 +756,11 @@ if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { tcpto_err(&ip.ix[i].ip,0); partner = ip.ix[i].ip; - smtp(); /* does not return */ +#ifdef TLS + smtp(ip.ix[i].fqdn); /* does not return */ +#else + smtp(); /* only returns when the next MX is to be tried */ +#endif } tcpto_err(&ip.ix[i].ip,errno == error_timeout); close(smtpfd); diff -ruN qmail-1.03/qmail-send.c qmail-1.03.patched/qmail-send.c --- qmail-1.03/qmail-send.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/qmail-send.c 2007-09-27 03:28:05.000000000 +0200 @@ -44,6 +44,8 @@ int lifetime = 604800; +int bouncemaxbytes = 0; + stralloc percenthack = {0}; struct constmap mappercenthack; stralloc locals = {0}; @@ -262,6 +264,8 @@ while (!stralloc_copys(&comm_buf[c],"")) nomem(); ch = delnum; while (!stralloc_append(&comm_buf[c],&ch)) nomem(); + ch = delnum >> 8; + while (!stralloc_append(&comm_buf[c],&ch)) nomem(); fnmake_split(id); while (!stralloc_cats(&comm_buf[c],fn.s)) nomem(); while (!stralloc_0(&comm_buf[c])) nomem(); @@ -740,9 +744,26 @@ qmail_fail(&qqt); else { - substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf)); - while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0) - qmail_put(&qqt,buf,r); + if(bouncemaxbytes) + { + int bytestogo = bouncemaxbytes; + int bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf; + substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf)); + while (bytestoget > 0 && (r = substdio_get(&ssread,buf,bytestoget)) > 0) { + qmail_put(&qqt,buf,r); + bytestogo -= bytestoget; + bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf; + } + if (r > 0) { + qmail_puts(&qqt,"\n\n--- End of message stripped.\n"); + } + } + else + { + substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf)); + while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0) + qmail_put(&qqt,buf,r); + } close(fd); if (r == -1) qmail_fail(&qqt); @@ -906,41 +927,42 @@ dline[c].len = REPORTMAX; /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */ /* but from a security point of view, we don't trust rspawn */ - if (!ch && (dline[c].len > 1)) + if (!ch && (dline[c].len > 2)) { delnum = (unsigned int) (unsigned char) dline[c].s[0]; + delnum += (unsigned int) ((unsigned int) dline[c].s[1]) << 8; if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used) log1("warning: internal error: delivery report out of range\n"); else { strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0; - if (dline[c].s[1] == 'Z') + if (dline[c].s[2] == 'Z') if (jo[d[c][delnum].j].flagdying) { - dline[c].s[1] = 'D'; + dline[c].s[2] = 'D'; --dline[c].len; while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem(); while (!stralloc_0(&dline[c])) nomem(); } - switch(dline[c].s[1]) + switch(dline[c].s[2]) { case 'K': log3("delivery ",strnum3,": success: "); - logsafe(dline[c].s + 2); + logsafe(dline[c].s + 3); log1("\n"); markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); --jo[d[c][delnum].j].numtodo; break; case 'Z': log3("delivery ",strnum3,": deferral: "); - logsafe(dline[c].s + 2); + logsafe(dline[c].s + 3); log1("\n"); break; case 'D': log3("delivery ",strnum3,": failure: "); - logsafe(dline[c].s + 2); + logsafe(dline[c].s + 3); log1("\n"); - addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2); + addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 3); markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); --jo[d[c][delnum].j].numtodo; break; @@ -1215,6 +1237,7 @@ /* this file is too long ---------------------------------------------- TODO */ +#ifndef EXTERNAL_TODO datetime_sec nexttodorun; DIR *tododir; /* if 0, have to opendir again */ stralloc todoline = {0}; @@ -1438,10 +1461,148 @@ if (fdchan[c] != -1) close(fdchan[c]); } +#endif + +/* this file is too long ------------------------------------- EXTERNAL TODO */ + +#ifdef EXTERNAL_TODO +stralloc todoline = {0}; +char todobuf[2048]; +int todofdin; +int todofdout; +int flagtodoalive; + +void tododied() { log1("alert: oh no! lost qmail-todo connection! dying...\n"); + flagexitasap = 1; flagtodoalive = 0; } + +void todo_init() +{ + todofdout = 7; + todofdin = 8; + flagtodoalive = 1; + /* sync with external todo */ + if (write(todofdout, "S", 1) != 1) tododied(); + + return; +} + +void todo_selprep(nfds,rfds,wakeup) +int *nfds; +fd_set *rfds; +datetime_sec *wakeup; +{ + if (flagexitasap) { + if (flagtodoalive) { + write(todofdout, "X", 1); + } + } + if (flagtodoalive) { + FD_SET(todofdin,rfds); + if (*nfds <= todofdin) + *nfds = todofdin + 1; + } +} + +void todo_del(char* s) +{ + int flagchan[CHANNELS]; + struct prioq_elt pe; + unsigned long id; + unsigned int len; + int c; + + for (c = 0;c < CHANNELS;++c) flagchan[c] = 0; + switch(*s++) { + case 'L': + flagchan[0] = 1; + break; + case 'R': + flagchan[1] = 1; + break; + case 'B': + flagchan[0] = 1; + flagchan[1] = 1; + break; + case 'X': + break; + default: + log1("warning: qmail-send unable to understand qmail-todo\n"); + return; + } + + len = scan_ulong(s,&id); + if (!len || s[len]) { + log1("warning: qmail-send unable to understand qmail-todo\n"); + return; + } + + pe.id = id; pe.dt = now(); + for (c = 0;c < CHANNELS;++c) + if (flagchan[c]) + while (!prioq_insert(&pqchan[c],&pe)) nomem(); + + for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break; + if (c == CHANNELS) + while (!prioq_insert(&pqdone,&pe)) nomem(); + + return; +} + +void todo_do(rfds) +fd_set *rfds; +{ + int r; + char ch; + int i; + + if (!flagtodoalive) return; + if (!FD_ISSET(todofdin,rfds)) return; + + r = read(todofdin,todobuf,sizeof(todobuf)); + if (r == -1) return; + if (r == 0) { + if (flagexitasap) + flagtodoalive = 0; + else + tododied(); + return; + } + for (i = 0;i < r;++i) { + ch = todobuf[i]; + while (!stralloc_append(&todoline,&ch)) nomem(); + if (todoline.len > REPORTMAX) + todoline.len = REPORTMAX; + /* qmail-todo is responsible for keeping it short */ + if (!ch && (todoline.len > 1)) { + switch (todoline.s[0]) { + case 'D': + if (flagexitasap) break; + todo_del(todoline.s + 1); + break; + case 'L': + log1(todoline.s + 1); + break; + case 'X': + if (flagexitasap) + flagtodoalive = 0; + else + tododied(); + break; + default: + log1("warning: qmail-send unable to understand qmail-todo: report mangled\n"); + break; + } + todoline.len = 0; + } + } +} + +#endif /* this file is too long ---------------------------------------------- MAIN */ int getcontrols() { if (control_init() == -1) return 0; + if (control_readint(&bouncemaxbytes,"control/bouncemaxbytes") == -1) return 0; if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0; if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0; if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0; @@ -1504,6 +1665,9 @@ log1("alert: unable to reread controls: unable to switch to home directory\n"); return; } +#ifdef EXTERNAL_TODO + write(todofdout, "H", 1); +#endif regetcontrols(); while (chdir("queue") == -1) { @@ -1544,7 +1708,7 @@ numjobs = 0; for (c = 0;c < CHANNELS;++c) { - char ch; + char ch, ch1; int u; int r; do @@ -1552,7 +1716,13 @@ while ((r == -1) && (errno == error_intr)); if (r < 1) { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); } + do + r = read(chanfdin[c],&ch1,1); + while ((r == -1) && (errno == error_intr)); + if (r < 1) + { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); } u = (unsigned int) (unsigned char) ch; + u += (unsigned int) ((unsigned char) ch1) << 8; if (concurrency[c] > u) concurrency[c] = u; numjobs += concurrency[c]; } @@ -1568,8 +1738,12 @@ todo_init(); cleanup_init(); +#ifdef EXTERNAL_TODO + while (!flagexitasap || !del_canexit() || flagtodoalive) +#else while (!flagexitasap || !del_canexit()) - { +#endif + { recent = now(); if (flagrunasap) { flagrunasap = 0; pqrun(); } diff -ruN qmail-1.03/qmail-showctl.c qmail-1.03.patched/qmail-showctl.c --- qmail-1.03/qmail-showctl.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/qmail-showctl.c 2007-09-27 03:28:05.000000000 +0200 @@ -15,6 +15,7 @@ #include "auto_patrn.h" #include "auto_spawn.h" #include "auto_split.h" +#include "spf.h" stralloc me = {0}; int meok; @@ -217,8 +218,9 @@ do_lst("badhelo","Any HELO host name is allowed.",""," HELO host name denied if it matches this pattern."); do_lst("badmailfrom","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern."); do_lst("badmailfromnorelay","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern and RELAYCLIENT is not set."); - do_lst("badmailto","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern."); - do_lst("badmailtonorelay","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern and RELAYCLIENT is not set."); + do_lst("badrcptto","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern."); + do_lst("badrcpttonorelay","No RCPT TO are specifically denied.","", + " RCPT TO denied if it matches this pattern, RELAYCLIENT is not set, and client has not done a successful AUTH."); do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is "); do_str("bouncehost",1,"bouncehost","Bounce host name is "); do_int("concurrencylocal","10","Local concurrency is ",""); @@ -261,6 +263,10 @@ do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 "); do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ",""); + do_int("spfbehavior","0","The SPF behavior is ",""); + do_str("spfexp",0,SPF_DEFEXP,"The SPF default explanation is: 550 "); + do_str("spfguess",0,"","The guess SPF rules are: "); + do_str("spfrules",0,"","The local SPF rules are: "); do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds"); do_int("timeoutremote","1200","SMTP client data timeout is "," seconds"); do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds"); @@ -274,8 +280,8 @@ if (str_equal(d->d_name,"badhelo")) continue; if (str_equal(d->d_name,"badmailfrom")) continue; if (str_equal(d->d_name,"badmailfromnorelay")) continue; - if (str_equal(d->d_name,"badmailto")) continue; - if (str_equal(d->d_name,"badmailtonorelay")) continue; + if (str_equal(d->d_name,"badrcptto")) continue; + if (str_equal(d->d_name,"badrcpttonorelay")) continue; if (str_equal(d->d_name,"bouncefrom")) continue; if (str_equal(d->d_name,"bouncehost")) continue; if (str_equal(d->d_name,"concurrencylocal")) continue; @@ -300,6 +306,10 @@ if (str_equal(d->d_name,"rcpthosts")) continue; if (str_equal(d->d_name,"smtpgreeting")) continue; if (str_equal(d->d_name,"smtproutes")) continue; + if (str_equal(d->d_name,"spfbehavior")) continue; + if (str_equal(d->d_name,"spfexp")) continue; + if (str_equal(d->d_name,"spfguess")) continue; + if (str_equal(d->d_name,"spfrules")) continue; if (str_equal(d->d_name,"timeoutconnect")) continue; if (str_equal(d->d_name,"timeoutremote")) continue; if (str_equal(d->d_name,"timeoutsmtpd")) continue; diff -ruN qmail-1.03/qmail-smtpd.8 qmail-1.03.patched/qmail-smtpd.8 --- qmail-1.03/qmail-smtpd.8 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/qmail-smtpd.8 2007-09-27 03:28:05.000000000 +0200 @@ -3,6 +3,11 @@ qmail-smtpd \- receive mail via SMTP .SH SYNOPSIS .B qmail-smtpd +[ +.I hostname +.I checkprogram +.I subprogram +] .SH DESCRIPTION .B qmail-smtpd receives mail messages via the Simple Mail Transfer Protocol (SMTP) @@ -23,7 +28,36 @@ header fields. .B qmail-smtpd -supports ESMTP, including the 8BITMIME and PIPELINING options. +has been modified to include many capabilities which are not present in +a standard qmail installation. The modifications are briefly described +below, or you can visit the web page listed at the end of this document +for a more complete description of how this version of qmail-smtpd differs +from the standard qmail-smtpd. + +.B qmail-smtpd +supports ESMTP, including the 8BITMIME, PIPELINING, and AUTH options. + +.B qmail-smtpd +can accept LOGIN, PLAIN, and optionally CRAM-MD5 AUTH types. It invokes +.IR checkprogram , +which reads on file descriptor 3 the username, a 0 byte, the password +or CRAM-MD5 response derived from +.IR hostname , +another 0 byte, a CRAM-MD5 challenge (if applicable to the AUTH type), +and a final 0 byte. +.I checkprogram +invokes +.I subprogram +upon successful authentication, which should in turn return 0 to +.BR qmail-smtpd , +effectively setting the environment variables RELAYCLIENT and TCPREMOTEINFO +(any supplied value replaced with the authenticated username). +.B qmail-smtpd +will reject the authentication attempt if it receives a nonzero return +value from +.I checkprogram +or +.IR subprogram . .SH TRANSPARENCY .B qmail-smtpd converts the SMTP newline convention into the UNIX newline convention @@ -49,6 +83,10 @@ environment variable is set, then the contents of .IR badhelo will be ignored. +If the +.B LOGREGEX +environment variable is set, then the log entry generated by a match +will include the regular expression which was matched. For more information, please have a look at doc/README.qregex. .TP 5 .I badmailfrom @@ -64,6 +102,10 @@ .BR @\fIhost , meaning every address at .IR host . +If the +.B LOGREGEX +environment variable is set, then the log entry generated by a match +will include the regular expression which was matched. For more information, please have a look at doc/README.qregex. .TP 5 .I badmailfromnorelay @@ -74,21 +116,26 @@ environment variable is not set. For more information, please have a look at doc/README.qregex. .TP 5 -.I badmailto +.I badrcptto Unacceptable envelope recipient addresses. .B qmail-smtpd will reject every recipient address for a message if the recipient address is listed in, or matches a POSIX regular expression pattern listed in, -.IR badmailto . +.IR badrcptto . +If the +.B LOGREGEX +environment variable is set, then the log entry generated by a match +will include the regular expression which was matched. For more information, please have a look at doc/README.qregex. .TP 5 -.I badmailtonorelay +.I badrcpttonorelay Functions the same as the -.IR badmailto +.IR badrcptto control file but is read only if the .B RELAYCLIENT -environment variable is not set. +environment variable is not set (and the client has not done a successful +AUTH command.) For more information, please have a look at doc/README.qregex. .TP 5 .I databytes @@ -117,6 +164,9 @@ .B DATABYTES is set, it overrides .IR databytes . +In addition, the environment variable is re-checked after a successful +AUTH command, which makes it possible to change its value using an +AUTH_SET_DATABYTES environment variable. .TP 5 .I localiphost Replacement host name for local IP addresses. @@ -138,6 +188,32 @@ This is done before .IR rcpthosts . .TP 5 +.I maxrcpt +If this file exists and contains a non-zero value, qmail-smtpd will +not accept more than this number of recipients for any single +message. + +This value may be overridden using the +.B MAXRCPT +environment variable, which may itself be changed in the event of +a successful AUTH command using an +.B AUTH_SET_MAXRCPT +environment variable. +.TP 5 +.I mfcheck +If this file exists and contains a non-zero value, the MAIL FROM +argument will be checked to make sure that the domain portion has +an MX record. This can help prevent spammers using bogus return +addresses. If the value is greater than 1, the results of this check +will be logged. + +If the environment variable +.B MFCHECK +is set, its value overrides the value in this file. In addition, the +environment variable is re-checked after a successful AUTH command, +which makes it possible to change its value using an AUTH_SET_MFCHECK +environment variable. +.TP 5 .I morercpthosts Extra allowed RCPT domains. If @@ -204,12 +280,506 @@ The first word of .I smtpgreeting should be the current host's name. + +Note that this value can be overridden by setting an +.B SMTPGREETING environment variable. +.TP 5 +.I spfbehavior +Set to a value between 1 and 6 to enable SPF checks; 0 to disable. +1 selects 'annotate-only' mode, where +.B qmail-smtpd +will annotate incoming email with +.B Received-SPF +fields, but will not reject any messages. 2 will produce temporary +failures on DNS lookup problems so you can make sure you always have +meaningful Received-SPF headers. 3 selects 'reject' mode, +where incoming mail will be rejected if the SPF record says 'fail'. 4 +selects a more stricter rejection mode, which is like 'reject' mode, +except that incoming mail will also be rejected when the SPF record +says 'softfail'. 5 will also reject when the SPF record says 'neutral', +and 6 if no SPF records are available at all (or a syntax error was +encountered). The contents of this file are overridden by the value of +the +.B SPFBEHAVIOR +environment variable, if set. +Default: 0. + +Note that the environment variable is re-checked after a successful AUTH +command, which makes it possible to change its value using an +AUTH_SET_SPFBEHAVIOR environment variable. +.TP 5 +.I spfexp +You can add a line with a an SPF explanation that will be shown to the +sender in case of a reject. It will override the default one. You can +use SPF macro expansion. +.TP 5 +.I spfguess +You can add a line with SPF rules that will be checked if a sender +domain doesn't have a SPF record. The local rules will also be used +in this case. +.TP 5 +.I spfrules +You can add a line with SPF rules that will be checked before other SPF +rules would fail. This can be used to always allow certain machines to +send certain mails. +.TP 5 +.I servercert.pem +This file should contain a PEM-encoded key and certificate which will be +used in order to create the server end of an SSL connection when processing +the STARTTLS command. This file should be readable by +.B qmail-smtpd +but not readable to the entire world. + +Note that this filename may be overridden by setting a +.B TLS_SERVER_CERT environment variable. .TP 5 .I timeoutsmtpd Number of seconds .B qmail-smtpd will wait for each new buffer of data from the remote SMTP client. Default: 1200. +.TP 5 +.SH "ENVIRONMENT VARIABLES" +There are several environment variables which may be set in order to +modify how qmail-smtpd works. +.TP 5 +.I ALLOW_CRAM +The CRAM-MD5 authentication method is not normally supported, because it +requires the server to have a list of plain-text passwords. +.B This is a bad thing. +However, if you understand the problem and are willing to accept the risk +that somebody cracking into your server might get all of your mailbox +passwords very easily, you can set this variable to a non-zero value and +.B qmail-smtpd +will advertise and support CRAM-MD5 as an authentication method. + +This option also requires that your checkpassword program know how to +process a CRAM authentication request. The +.B vchkpw +program, part of the vpopmail package (version 5.4 and above) is able +to handle CRAM-MD5 if you compile it with the option to store plain-text +passwords. Earlier versions of vpopmail had a bug where it was reading +the challenge and response in the wrong order, because the original SMTP +AUTH patch sent the values in the wrong order. The +.B 6c +version of the combined patch (described at the end of this document) fixed +the order. +.TP 5 +.I ALLOW_INSECURE_AUTH +The AUTH command is not normally advertised in response to the EHLO +command, or accepted from the client, unless the connection is known to be +secure (because STARTTLS has been successfully processed, or because the +.B SSL +environment variable has a non-zero value.) Setting +.B ALLOW_INSECURE_AUTH +to a non-zero value will make +.B qmail-smtpd +advertise the AUTH capabilities in the EHLO response, and accept the +AUTH command from the client, regardless of whether or not the connection +is secure. + +.B This is a very bad idea. +This allows your users to send their passwords across the Internet in +plain text when they AUTH in order to relay outbound mail. Anybody with +a packet sniffer could get your user's password, and then have their way +with the user's mailbox, or anything else where the user may be using +the same password. They would also be able to use that password to use +your server as a relay for spam... +.TP 5 +.I AUTH_CDB +If present, this variable contains the name of a cdb file which contains +valid userid/password combinations to satisfy the AUTH command. Each key +in this file should be a valid system userid, virtual email address, or +other value which passes for a "userid" in the AUTH command. The value +linked to each key should be the ENCRYPTED password for that userid. + +Note that the passwords in this file MUST BE ENCRYPTED using an encryption +or hashing method supported by your system's +.B crypt(3) +function. This is almost always the same set of encryption methods used +for the system's password database. +.TP 5 +.I AUTH_SET_ and AUTH_UNSET_ +When the first DATA command is sent during a session, if any environment +variables with names like AUTH_SET_{something} exist, a corresponding +environment variable called {something} will be created with the value +from the AUTH_SET_{something} variable, or if such a variable already +exists, the existing value will be replaced with the new value. In +either case, the AUTH_SET_{something} variable will then be deleted. + +At the same time, if any environment variables with names like +AUTH_UNSET_{something} exist, any existing environment variable called +{something} will be deleted from the environment, and then the +AUTH_UNSET_{something} variable will be deleted. + +These types of variables can be useful in situations where changes need +to be made to the environment when a user successfully sends an AUTH +command, in order to modify the behavior of a program called through the +.B QMAILQUEUE +mechanism (see below.) +.TP 5 +.I DENY_TLS +Setting this variable to a non-zero value will cause qmail-smtpd to not +support the STARTTLS command under any circumstances. + +This value is internally set to 1 if the +.B servercert.pem +control file (or whatever filename the +.B TLS_SERVER_CERT +variable points to) is not present or not readable. +.TP 5 +.I DROP_PRE_GREET +Many spammers will try to send commands to SMTP servers before the server +has sent its inital greeting, even though this violates RFC 821. Setting +this variable to a non-zero value will cause +.b qmail-smtpd +to pause for one second before sending the initial greeting, and drop any +client connection which tries to send commands before the greeting has +been sent. + +Note that if you also use the +.B GREETDELAY +variable, the one second delay that DROP_PRE_GREET uses is taken from that +number- so the value of GREETDELAY will be the total used by both features. +.TP 5 +.I FORCE_TLS +Setting this variable to a non-zero value will cause +.B qmail-smtpd +not to accept the MAIL FROM command unless STARTTLS has been successfully +processed. This makes it possible to create an SMTP server which can only +be used by authorized users. + +.B WARNING: do not do this for a standard port 25 SMTP service +which handles incoming mail for any real domains, or you will prevent the +domain from being able to receive any mail unless it happens to come from +one of your authorized users. + +This value is internally set to zero if the +.B SSL +environment variable is set to a non-zero value. + +If you try to combine +.B FORCE_TLS +with +.B DENY_TLS +(either by manually setting the +.B DENY_TLS +variable, or by not having a readable +.B servercert.pem +control file) the client will receive an error message and +.B qmail-smtpd +will exit without accepting any message from the client. +.TP 5 +.I GREETDELAY +Many spammers use programs which will give up on an SMTP server if +the server answers but doesn't send its initial banner in a timely +manner. However, legitimate mail servers will wait for the banner. +If you set this variable to a non-zero value, +.B qmail-smtpd +will pause that many seconds before sending the banner. + +Note that if you also use the +.B DROP_PRE_GREET +variable, the extra one-second delay that DROP_PRE_GREET introduces +is taken from this total, so that if both variables are used, the +value of GREETDELAY is the total delay introduced by both features. +.TP 5 +.I LOGREGEX +The badhelo, badmailfrom, badmailfromnorelay, badrcptto, and +badrcpttonorelay control files contain regular expressions which will +cause HELO, MAIL FROM, or RCPT TO commands whose arguments match a +pattern listed in the file to be rejected. + +If this variable exists, the log messages generated by these mecahnisms +will include the regular expression which was matched. +.TP 5 +.I MAXRCPT +If this variable is set, its value will override the value from the +.B maxrcpt +control file. It sets the maximum number of recipients for any one +message. + +This variable is re-checked after a successful AUTH command, which +makes it possible to change its value using an AUTH_SET_MAXRCPT +environment variable. +.TP 5 +.I MFCHECK +If this variable is set and has a non-zero value, the MAIL FROM +argument will be checked to make sure that the domain portion has +an MX record. This can help prevent spammers using bogus return +addresses. Note that this is the same check which is done if the +.b mfcheck +control file contains a non-zero value, although the value in the +environment variable overrides the value in the file. + +If the value is greater than 1, the results of this check will be +logged. + +This variable is re-checked after a successful AUTH command, which +makes it possible to change its value using an AUTH_SET_MFCHECK +environment variable. +.TP 5 +.I QMAILQUEUE +If this variable is set, it should point to a program which will be +executed instead of the +.B qmail-queue +program, in order to add a received message to the queue. This program +should read its input and return the values exactly as the original +.B qmail-queue +program does. This version of the patch adds two additional return +values which are not present in the original +.B qmail-queue +program: +.RS 5 +.TP 5 +.B 1 +Tells the remote SMTP client "Your spam has been ignored", with the same +status code as a successful delivery. This tells the remote SMTP client +that the message was delivered, while in fact the message was not added +to the queue. This can be used in cases where you know the message is +SPAM and you know that if you refuse the message, the remote SMTP client +is only going to keep trying over and over. +.TP 5 +.B 32 +Tells the remote SMTP client "we do not accept SPAM", with a status code +indicating permanent refusal of the message (i.e. a "hard error".) This +can be used if you know that the message is SPAM and want this message +to show up in the remote server's log (instead of the standard "mail +server permanently rejected message" caused by return value 31.) +.RE +.TP 5 +.I RELAYCLIENT +If this variable exists when +.B qmail-smtpd +starts, the remote SMTP client will be allowed to relay mail, ignoring +the +.B rcpthosts +and +.B morercpthosts.cdb +files. This is normally only done for IP addresses which belong to you +or your clients, and which could not be used by anybody who should not +be authorized to relay. Some sites don't use this at all, preferring to +grant relay access only to users who send a successful AUTH command. +.TP 5 +.I RELAYREJ +If this variable exists and has a non-zero value, +.B qmail-smtpd +will do a sanity check each recipient address, to ensure that the +address does not contain multiple "@" characters, or that it does not +contain any "%" or "!" characters before the "@" character. + +This check is only useful when dealing with broken relay testers that +try to use these characters to trigger old sendmail bugs and force your +server to be an open relay. Without these checks, qmail-smtpd may accept +messages but then not be able to deliver them- however some broken relay +testers consider this to be evidence of an open relay. + +This check is normally not needed, and in fact it can break some types +of gateway systems (such as SMTP-to-UUCP.) However, some people seem to +get a warm fuzzy feeling if they know that it's being done, so I've +included it for their benefit. +.B Please don't use it. + +This variable is re-checked after a successful AUTH command, which +makes it possible to change its value using an AUTH_SET_RELAYREJ +environment variable. +.TP 5 +.I REQUIRE_AUTH +Setting this variable to a non-zero value will cause +.B qmail-smtpd +to not accept the MAIL FROM command until the user has successfully +authenticated (using the AUTH command.) Note that this may be combined +with +.B FORCE_TLS +to create a secure server which your users can use to relay mail. + +If you try to set this without properly specifying an authentication +program on the command line, the client will receive an error message and +.B qmail-smtpd +will exit without accepting any message from the client. +.TP 5 +.I SMTP_AUTH_USER +When the remote SMTP client sends a successful AUTH command, the +.B SMTP_AUTH_USER +variable will be set to the userid from the credentials. This variable +can be tested by any scripts called through the +.B QMAILQUEUE +mechanism to determine whether or not the remote SMTP client has sent a +successful AUTH command or not. +.TP 5 +.I QMAILSMTPD_HELP_VERSION +If this variable is set to a non-zero value, the output of the HELP command +will include the version and URL of the jms1 combined patch you are using. + +Note that this variable is re-checked after a successful AUTH command, +which makes it possible to change its value using an +AUTH_SET_QMAILSMTPD_HELP_VERSION environment variable. +.TP 5 +.I QMAILSMTPD_LOG_MAIL +If this variable is set to a non-zero value, qmail-smtpd will log the sender +addresses specified in all successful MAIL FROM commands received from the +client. +.TP 5 +.I QMAILSMTPD_LOG_RCPT +If this variable is set to a non-zero value, qmail-smtpd will log the +recipient addresses specified in all successful RCPT TO commands received +from the client. +.TP 5 +.I SMTPGREETING +This variable overrides the value stored in the +.B smtpgreeting +control file (see above.) +.TP 5 +.I SPFBEHAVIOR +This variable overrides the value stored in the +.B spfbehavior +control file (see above.) + +Note that this variable is re-checked after a successful AUTH command, +which makes it possible to change its value using an +AUTH_SET_SPFBEHAVIOR environment variable. +.TP 5 +.I SPF_BLOCK_PLUS_ALL +Setting this variable to a non-zero value will cause any "+all" term +found in a domain's SPF record to be interpreted as if it said +"-all". This can be useful if you have a problem with spammers who have +discovered that by creating an SPF record with "+all" in it, they can +basically "walk around" any SPF-based filtering you may be doing. + +This variable is checked during the SPF check, which happens after the +client sends a MAIL FROM command. This makes it possible to change its +value using an AUTH_SET_SPF_BLOCK_PLUS_ALL environment variable. +.TP 5 +.I SPF_LOG +Setting this variable to a non-zero value will cause the result of every +SPF check to be logged. The log entries will be identical to the +"Received-SPF" header which is added to each incoming message. + +This variable is re-checked after a successful AUTH command, which +makes it possible to change its value using an AUTH_SET_SPF_LOG +environment variable. +.TP 5 +.I SSL +This variable should be set to a non-zero value if the SMTP service is +running +.B qmail-smtpd +under an SSL-secured socket. This tells +.B qmail-smtpd +that the connection is secure, which enables the AUTH command and disables +the STARTTLS command. +.TP 5 +.I TCPREMOTEINFO +This variable is normally set by +.B tcpserver +if run without the -R option, and will contain the data (if any) +returned by the +.B identd +service on the remote SMTP client's IP address. A successful AUTH +command will add or replace this value with the userid which was part of +the credentials. The data is the same as what you would find in the +.B SMTP_AUTH_USER +variable, with the exception that this variable may exist without a +successful AUTH command. +.TP 5 +.I TLS_SERVER_CERT +This variable contains the name of a file, which contains a PEM-encoded +private key and certificate used to encrypt the connection if a STARTTLS +command is received. If this variable is not present, the default name +.B control/servercert.pem +will be used. + +Note that if you wish to set up a service which will not accept the +STARTTLS command at all, you may either remove whatever file this variable +(or the default value) points to, or you can create a +.B DENY_TLS +environment variable with a non-zero value. +.TP 5 +.I VALIDRCPTTO_CDB +If this variable is present and not empty, qmail-smtpd will expect it to +contain the name of a cdb file. qmail-smtpd will check the argument of every +RCPT command against this file. It searches the file for a key which matches +the RCPT command. If a key is not found, the RCPT command will be rejected. +If a key is found, the RCPT command may be accepted or rejected, depending +on the value (if any) attached to that key. + +qmail-smtpd searches first for a key which matches the argument to the +RCPT command. If the address sent by the client is not found, and if the +mailbox portion of the address (the part to the left of the "@" sign) +contains one or more of qmail's "ext" character (usually "-", ASCII 45) +it will search for any appropriate entries of the form +.B user-default@domain +going from more to less specific, with +.B @domain +as a final resort. + +For example, if the address sent is +.B user-one-two-three@domain, +it will search for the following items: + +.EX + user-one-two-three@domain + user-one-two-default@domain + user-one-default@domain + user-default@domain + @domain +.EE + +The first key key it finds while searching is what will be used. + +Once an appropriate key is found, the value (if any) attached to that key +is checked. If the key has no value (as will be the case if you are using +an older version of the validrcptto.cdb patch) then the address will be +accepted. If there is a value attached, and if the first byte of that +value is a hyphen (i.e. "-", ASCII 45) then the address will be rejected. +Values other than "-" are reserved for future versions of this patch and +should not be used. + +In order to prevent "harvesting" attacks, +.B qmail-smtpd +counts how many invalid recipients have been attempted in each connection, +and when a certain limit is reached, +.B qmail-smtpd +will forcibly disconnect the client. This limit has a default 10, and can be +changed or disabled with the +.B VALIDRCPTTO_LIMIT +environment variable. + +This variable is re-checked after a successful AUTH command, which makes +it possible to change its value using an AUTH_SET_VALIDRCPTTO_CDB +or AUTH_UNSET_VALIDRCPTTO_CDB environment variable. +.TP 5 +.I VALIDRCPTTO_LIMIT +This variable sets the limit of how many invalid RCPT TO commands will be +treated as "soft errors". When this limit is reached, +.B qmail-smtpd +will forcibly disconnect from the client. The default limit is 10. Setting +this value to zero will disable the counting entirely, making it possible +for a spammer to "harvest" the valid email addresses on your server. + +This variable is re-checked after a successful AUTH command, which makes +it possible to change its value using an AUTH_SET_VALIDRCPTTO_LIMIT +environment variable. +.TP 5 +.I VALIDRCPTTO_LOG +Setting this variable to 1 will cause +.B qmail-smtpd +to log each RCPT TO command received from a client, along with what entry +from the +.B validrcptto.cdb +control file matched the address entered. If no match was found, that will +be logged as well. If a client is forcibly disconnected for trying more than +.B VALIDRCPTTO_LIMIT +invalid recipients, that will be logged as well. + +Setting this variable to 2 will cause +.B qmail-smtpd +to also log everything it searches for within the +.B validrcptto.cdb +file. This can be useful when debugging a problem. + +This variable is re-checked after a successful AUTH command, which makes +it possible to change its value using an AUTH_SET_VALIDRCPTTO_LOG +environment variable. .SH "SEE ALSO" tcp-env(1), tcp-environ(5), @@ -218,3 +788,10 @@ qmail-newmrh(8), qmail-queue(8), qmail-remote(8) +.SH "HISTORY" +See +.B http://qmail.jms1.net/patches/combined-details.shtml +for more detailed information about the differences between this instance +of qmail and the stock qmail. This man page is current as of the +.B 7.02 +version of the combined patch file. diff -ruN qmail-1.03/qmail-smtpd.c qmail-1.03.patched/qmail-smtpd.c --- qmail-1.03/qmail-smtpd.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/qmail-smtpd.c 2007-09-27 03:28:05.000000000 +0200 @@ -1,5 +1,7 @@ #include "sig.h" +#ifndef TLS #include "readwrite.h" +#endif #include "stralloc.h" #include "substdio.h" #include "alloc.h" @@ -20,27 +22,121 @@ #include "now.h" #include "exit.h" #include "rcpthosts.h" +#ifndef TLS #include "timeoutread.h" #include "timeoutwrite.h" +#endif #include "commands.h" #include "qregex.h" #include "strerr.h" +#include "wait.h" +#include "fd.h" +#include "dns.h" +#include "spf.h" +#include "cdb.h" +#include "auto_break.h" #define BMCHECK_BMF 0 #define BMCHECK_BMFNR 1 -#define BMCHECK_BMT 2 -#define BMCHECK_BMTNR 3 +#define BMCHECK_BRT 2 +#define BMCHECK_BRTNR 3 #define BMCHECK_BHELO 4 - + +#define _XOPEN_SOURCE +#include +#ifdef TLS +#include +SSL *ssl = NULL; + +stralloc clientcert = {0}; +stralloc tlsserverciphers = {0}; +#endif #define MAXHOPS 100 unsigned int databytes = 0; +unsigned int greetdelay = 0; +unsigned int drop_pre_greet = 0; +unsigned int mfchk = 0; int timeout = 1200; +int rcptcounter = 0; +int maxrcpt = -1; +unsigned int spfbehavior = 0; +int useauth = 0; +int useauth_cl = 0; +int useauth_cdb = 0; +int auth_cdb_fd = -1; +char *auth_cdb_file; +int usecram = 0; +unsigned int essl = 0; +char unique[FMT_ULONG + FMT_ULONG + 3]; +static stralloc authin = {0}; +static stralloc user = {0}; +static stralloc pass = {0}; +static stralloc chal = {0}; +static stralloc slop = {0}; +char *hostname; +char **childargs; +substdio ssup; +char upbuf[128]; +int authd = 0; +unsigned int allow_insecure_auth = 0; +unsigned int require_auth = 0; +char pid_buf[FMT_ULONG]; +stralloc title = {0}; +int log_mail = 0; +int log_rcpt = 0; + +#ifdef TLS +unsigned int force_tls = 0; +unsigned int deny_tls = 0; +int flagtimedout = 0; +char *servercert = "control/servercert.pem"; +void sigalrm() +{ + flagtimedout = 1; +} + +int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) { + while(((r = SSL_read(ssl,buf,n)) <= 0) + && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ)); + }else r = read(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} + +int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) { + while(((r = SSL_write(ssl,buf,n)) <= 0) + && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_WRITE)); + }else r = write(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} +#endif int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + r = ssl_timeoutwrite(timeout,fd,buf,len); +#else r = timeoutwrite(timeout,fd,buf,len); +#endif if (r <= 0) _exit(1); return r; } @@ -57,32 +153,69 @@ void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } +void die_cannot_auth() { out("421 REQUIRE_AUTH set without valid AUTH program.\r\n"); flush(); _exit(1); } +void die_cannot_cram() { out("421 ALLOW_CRAM not available\r\n"); flush(); _exit(1); } +void die_auth_cdb() { out("421 cannot read AUTH_CDB file\r\n"); flush(); _exit(1); } +void die_pre_greet() { out("554 SMTP protocol violation\r\n"); flush(); _exit(1); } void err_bmf() { out("553 sorry, your envelope sender has been denied (#5.7.1)\r\n"); } -void err_bmt() { out("553 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); } +void err_brt() { out("553 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); } void err_bhelo() { out("553 sorry, your HELO host name has been denied (#5.7.1)\r\n"); } +void err_hmf() { out("553 sorry, your envelope sender domain must exist (#5.7.1)\r\n"); } +void err_smf() { out("451 DNS temporary failure (#4.3.0)\r\n"); } void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } -void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); } +#ifdef TLS +void err_nogwcert() { out("553 no valid cert for gatewaying (#5.7.1)\r\n"); } +void err_tlsfirst() { out("503 STARTTLS first (#5.5.1)\r\n"); } +void die_forcedenytls() { out("421 FORCE_TLS and DENY_TLS both found (#4.3.0)\r\n"); flush(); _exit(1); } +#endif +void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); } void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } +void err_relay() { out("553 we don't relay (#5.7.1)\r\n"); } void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } +void err_seenmail() { out("503 only one MAIL command allowed (#5.5.1)\r\n"); } void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); } -void err_noop(arg) char *arg; { out("250 ok\r\n"); } -void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); } +void err_noop() { out("250 ok\r\n"); } +void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } +int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; } +int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; } +int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; } +int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; } +void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); } +void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); } +void err_noauthavail() { out("503 auth not available (#5.3.3)\r\n"); } +int err_noauthtype() { out("504 auth type not available (#5.5.1)\r\n"); return -1; } +int err_authabrt() { out("501 auth exchange cancelled (#5.0.0)\r\n"); return -1; } +int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } +int err_authfirst() { out("503 AUTH first (#5.5.1)\r\n"); } +int err_authmethod() { out("454 oops, unknown AUTH back-end (#4.3.0)\r\n"); return -1; } + +void err_vrt() { out("553 sorry, this recipient is not in my validrcptto list (#5.7.1)\r\n"); } +void die_vrt() { out("421 too many invalid addresses, goodbye (#4.3.0)\r\n"); flush(); _exit(1); } stralloc greeting = {0}; +stralloc spflocal = {0}; +stralloc spfguess = {0}; +stralloc spfexp = {0}; +int spf_log = 0; +int help_version = 0; void smtp_greet(code) char *code; { substdio_puts(&ssout,code); substdio_put(&ssout,greeting.s,greeting.len); } -void smtp_help(arg) char *arg; +void smtp_help() { - out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n"); + if(help_version) + out("214-qmail home page: http://pobox.com/~djb/qmail.html\r\n" + "214 jms1 combined patch v7.02 http://qmail.jms1.net/patches/combined.shtml\r\n"); + else + out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n"); } -void smtp_quit(arg) char *arg; +void smtp_quit() { smtp_greet("221 "); out("\r\n"); flush(); _exit(0); } @@ -92,6 +225,14 @@ char *remoteinfo; char *local; char *relayclient; +#ifdef TLS +char *tlsciphers; +#endif + +void err_size() { + out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); + strerr_warn4(title.s,"DATABYTES exceeded [",remoteip,"]",0); +} stralloc helohost = {0}; char *fakehelo; /* pointer into helohost, or 0 */ @@ -111,51 +252,144 @@ int bmfnrok = 0; stralloc bmfnr = {0}; -int bmtok = 0; -stralloc bmt = {0}; +int brtok = 0; +stralloc brt = {0}; -int bmtnrok = 0; -stralloc bmtnr = {0}; +int brtnrok = 0; +stralloc brtnr = {0}; int bhelook = 0; stralloc bhelo = {0}; +int vrtfd = -1; +int vrtcount = 0; +int vrtlimit = 10; +int vrtlog_do = 0; + +int relayrej = 0; + +void readenv() +{ + char *x; + unsigned long u; + + x = env_get("MFCHECK"); + if (x) { scan_ulong(x,&u); mfchk = u; } + + x = env_get("MAXRCPT"); + if (x) { scan_ulong(x,&u); maxrcpt = u; } + + /* RFC 2821 section 4.5.3.1 "recipients buffer" */ + if(maxrcpt<1) maxrcpt=-1; + else if(maxrcpt<100) maxrcpt=100 ; + + x = env_get("DATABYTES"); + if (x) { scan_ulong(x,&u); databytes = u; } + if (!(databytes + 1)) --databytes; + + x = env_get("SPFBEHAVIOR"); + if (x) { scan_ulong(x,&u); spfbehavior = u; } + + x = env_get("VALIDRCPTTO_LIMIT"); + if(x) { scan_ulong(x,&u); vrtlimit = (int) u; } + + x = env_get("VALIDRCPTTO_LOG"); + if(x) { scan_ulong(x,&u); vrtlog_do = (int) u; } + + x = env_get("SPF_LOG"); + if(x) { scan_ulong(x,&u); spf_log = (int) u; } + + x = env_get("RELAYREJ"); + if(x) { scan_ulong(x,&u); relayrej = (int) u; } + + x = env_get("VALIDRCPTTO_CDB"); + if(x) { + if (-1 != vrtfd) { close(vrtfd); vrtfd = -1; } + if(*x) { + vrtfd = open_read(x); + if (-1 == vrtfd) die_control(); + } + } + else if (-1 != vrtfd) { close(vrtfd); vrtfd = -1; } + + x = env_get("QMAILSMTPD_LOG_MAIL"); + if(x) { scan_ulong(x,&u); log_mail = (int) u; } + + x = env_get("QMAILSMTPD_LOG_RCPT"); + if(x) { scan_ulong(x,&u); log_rcpt = (int) u; } + + x = env_get("QMAILSMTPD_HELP_VERSION"); + if(x) { scan_ulong(x,&u); help_version = (int) u; } +} + +int logregex = 0; +stralloc matchedregex = {0}; + void setup() { char *x; unsigned long u; +#ifdef TLS + char *tlsciphers; +#endif if (control_init() == -1) die_control(); if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) die_control(); + x=env_get("SMTPGREETING"); + if(x) { if(!stralloc_copys(&greeting,x)) die_nomem(); } liphostok = control_rldef(&liphost,"control/localiphost",1,(char *) 0); if (liphostok == -1) die_control(); if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); if (timeout <= 0) timeout = 1; + if (control_readint(&maxrcpt,"control/maxrcpt") == -1) die_control(); if (rcpthosts_init() == -1) die_control(); + if (control_readint(&mfchk,"control/mfcheck") == -1) die_control(); + bmfok = control_readfile(&bmf,"control/badmailfrom",0); if (bmfok == -1) die_control(); bmfnrok = control_readfile(&bmfnr,"control/badmailfromnorelay",0); if (bmfnrok == -1) die_control(); - bmtok = control_readfile(&bmt,"control/badmailto",0); - if (bmtok == -1) die_control(); + brtok = control_readfile(&brt,"control/badrcptto",0); + if (brtok == -1) die_control(); + + brtnrok = control_readfile(&brtnr,"control/badrcpttonorelay",0); + if (brtnrok == -1) die_control(); - bmtnrok = control_readfile(&bmtnr,"control/badmailtonorelay",0); - if (bmtnrok == -1) die_control(); + bhelook = control_readfile(&bhelo,"control/badhelo",0); + if(bhelook == -1) die_control() ; + if(env_get("NOBADHELO")) bhelook = 0; - bhelook = control_readfile(&bhelo, "control/badhelo",0); - if (bhelook == -1) die_control(); - if (env_get("NOBADHELO")) bhelook = 0; + if(env_get("LOGREGEX")) logregex = 1; + + if (useauth_cdb) { + auth_cdb_fd = open_read(auth_cdb_file); + if(-1 == auth_cdb_fd) die_control(); + } if (control_readint(&databytes,"control/databytes") == -1) die_control(); - x = env_get("DATABYTES"); - if (x) { scan_ulong(x,&u); databytes = u; } - if (!(databytes + 1)) --databytes; + if (control_readint(&spfbehavior,"control/spfbehavior") == -1) + die_control(); + + if (control_readline(&spflocal,"control/spfrules") == -1) die_control(); + if (spflocal.len && !stralloc_0(&spflocal)) die_nomem(); + if (control_readline(&spfguess,"control/spfguess") == -1) die_control(); + if (spfguess.len && !stralloc_0(&spfguess)) die_nomem(); + if (control_rldef(&spfexp,"control/spfexp",0,SPF_DEFEXP) == -1) + die_control(); + if (!stralloc_0(&spfexp)) die_nomem(); + + x = env_get("GREETDELAY"); + if(x) { scan_ulong(x,&u); greetdelay = u; } + + x = env_get("DROP_PRE_GREET"); + if(x) { scan_ulong(x,&u); drop_pre_greet = u; } + remoteip = env_get("TCPREMOTEIP"); if (!remoteip) remoteip = "unknown"; local = env_get("TCPLOCALHOST"); @@ -165,6 +399,20 @@ if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); +#ifdef TLS + if (tlsciphers = env_get("TLSCIPHERS")){ + if (!stralloc_copys(&tlsserverciphers,tlsciphers)) die_nomem(); + } + else { + if (control_rldef(&tlsserverciphers,"control/tlsserverciphers",0,"DEFAULT") != 1) + die_control(); + } + if (!stralloc_0(&tlsserverciphers)) die_nomem(); + + x=env_get("TLS_SERVER_CERT"); + if(x) servercert=x; +#endif + readenv(); dohelo(remotehost); } @@ -231,6 +479,45 @@ return 1; } +int sizelimit(arg) /* modified SIZELIMIT function by Erwin Hoffmann (tx Uwe Ohse) */ +char *arg; +{ + int i; + long r; + char terminator; + unsigned long sizebytes = 0; + + terminator = '>'; + i = str_chr(arg,'<'); + if (arg[i]) + arg += i + 1; + else { + terminator = ' '; + arg += str_chr(arg,':'); + if (*arg == ':') ++arg; + while (*arg == ' ') ++arg; + } + + arg += str_chr(arg,terminator); + if (*arg && terminator == '>' ) ++arg; /* end of adddress */ + + while (*++arg) { + i = str_chr(arg,'='); + arg[i] = 0; + if (case_equals(arg,"SIZE")) { + arg += i; + while (*++arg && *arg > 47 && *arg < 58) { + sizebytes *= 10; + sizebytes += *arg - 48; } + r = databytes - sizebytes; + if (r < 0) return 0; + } + else + ++arg; + } + return 1; +} + int bmcheck(which) int which; { int i = 0; @@ -244,10 +531,10 @@ if (!stralloc_copy(&bmb,&bmf)) die_nomem(); } else if (which == BMCHECK_BMFNR) { if (!stralloc_copy(&bmb,&bmfnr)) die_nomem(); - } else if (which == BMCHECK_BMT) { - if (!stralloc_copy(&bmb,&bmt)) die_nomem(); - } else if (which == BMCHECK_BMTNR) { - if (!stralloc_copy(&bmb,&bmtnr)) die_nomem(); + } else if (which == BMCHECK_BRT) { + if (!stralloc_copy(&bmb,&brt)) die_nomem(); + } else if (which == BMCHECK_BRTNR) { + if (!stralloc_copy(&bmb,&brtnr)) die_nomem(); } else if (which == BMCHECK_BHELO) { if (!stralloc_copy(&bmb,&bhelo)) die_nomem(); } else { @@ -268,14 +555,138 @@ } else { x = matchregex(addr.s, curregex.s); } - if ((negate) && (x == 0)) return 1; - if (!(negate) && (x > 0)) return 1; + if ((negate) && (x == 0)) { + if (!stralloc_copyb(&matchedregex,bmb.s + j - 1,(i - j + 1))) die_nomem(); + if (!stralloc_0(&matchedregex)) die_nomem(); + return 1; + } + if (!(negate) && (x > 0)) { + if (!stralloc_copyb(&matchedregex,bmb.s + j,(i - j))) die_nomem(); + if (!stralloc_0(&matchedregex)) die_nomem(); + return 1; + } j = i + 1; negate = 0; } return 0; } +int mfcheck() +{ + stralloc sa = {0}; + ipalloc ia = {0}; + unsigned int random; + int j; + + if (!mfchk) return 0; + random = now() + (getpid() << 16); + j = byte_rchr(addr.s,addr.len,'@') + 1; + if (j < addr.len) { + stralloc_copys(&sa, addr.s + j); + dns_init(0); + j = dns_mxip(&ia,&sa,random); + stralloc_0(&sa); + if (j < 0) { + if(mfchk>1) strerr_warn5(title.s,"MFCHECK fail [",remoteip,"] ",sa.s,0); + return j; + } + if(mfchk>2) strerr_warn5(title.s,"MFCHECK pass [",remoteip,"] ",sa.s,0); + return 0; + } + if(mfchk>2) strerr_warn5(title.s,"MFCHECK bypassed [",remoteip,"] ",addr.s,0); + return 0; +} + +void vrtlog(l,a,b) +int l; +const char *a; +const char *b; +{ + if (l <= vrtlog_do) + strerr_warn6(title.s,"validrcptto [",remoteip,"] ",a,b,0); +} + +int vrtcheck() +{ + static char *rcptto = "RCPT TO: "; + static char *trying = "trying: "; + static char *found = "found: "; + static char *reject = "reject: "; + char *f = 0; + int j,k,r; + uint32 dlen; + stralloc laddr = {0}; + + stralloc user = {0}; + stralloc adom = {0}; + stralloc utry = {0}; + stralloc dval = {0}; + + if (-1 == vrtfd) return 1; + + /* lowercase whatever we were sent */ + if (!stralloc_copy(&laddr,&addr)) die_nomem() ; + case_lowerb(laddr.s,laddr.len); + + if ( !log_rcpt ) vrtlog ( 1 , rcptto , laddr.s ); + + /* exact match? */ + vrtlog ( 2 , trying , laddr.s ); + r = cdb_seek(vrtfd,laddr.s,laddr.len-1,&dlen) ; + if (r>0) f=laddr.s ; + else + { + j = byte_rchr(laddr.s,laddr.len,'@'); + if (j < laddr.len) + { + /* start "-default" search loop */ + stralloc_copyb(&user,laddr.s,j) ; + stralloc_copyb(&adom,laddr.s+j,laddr.len-j-1); + + while(1) + { + k = byte_rchr(user.s,user.len,*auto_break); + if (k >= user.len) break ; + + user.len = k+1; + stralloc_cats(&user,"default"); + + stralloc_copy(&utry,&user); + stralloc_cat (&utry,&adom); + stralloc_0(&utry); + + vrtlog ( 2 , trying , utry.s ); + r = cdb_seek(vrtfd,utry.s,utry.len-1,&dlen); + if (r>0) { f=utry.s; break; } + + user.len = k ; + } + + /* try "@domain" */ + if(!f) { + vrtlog ( 2 , trying , laddr.s+j ); + r = cdb_seek(vrtfd,laddr.s+j,laddr.len-j-1,&dlen) ; + if (r>0) f=laddr.s+j ; + } + } + } + + if(f) { + if(dlen) { + if(!stralloc_ready(&dval,dlen)) die_nomem(); + dval.len = read(vrtfd,dval.s,dlen); + if(dval.len>0) if(dval.s[0] == '-') { + vrtlog ( 1 , reject , f ) ; + return 0; + } + } + vrtlog ( 1 , found , f ) ; + return 1; + } + + return 0; +} + int addrallowed() { int r; @@ -284,11 +695,30 @@ return r; } +int addrrelay() +{ + int j; + if(relayrej) { + j = addr.len; + while(--j >= 0) + if (addr.s[j] == '@') break; + if (j < 0) j = addr.len; + while(--j >= 0) { + if (addr.s[j] == '@') return 1; + if (addr.s[j] == '%') return 1; + if (addr.s[j] == '!') return 1; + } + } + return 0; +} int seenmail = 0; +int flagbarf; /* defined if seenmail */ int flagbarfbmf; /* defined if seenmail */ -int flagbarfbmt; +int flagbarfbrt; int flagbarfbhelo; +int flagbarfspf; +stralloc spfbarfmsg = {0}; stralloc mailfrom = {0}; stralloc rcptto = {0}; @@ -298,60 +728,256 @@ seenmail = 0; dohelo(arg); if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); } +char size_buf[FMT_ULONG]; +void smtp_size() +{ + size_buf[fmt_ulong(size_buf,(unsigned long) databytes)] = 0; + out("\r\n250-SIZE "); out(size_buf); +} void smtp_ehlo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + smtp_greet("250-"); + if (useauth) + { + if ( essl || allow_insecure_auth ) + { + if (usecram) + { + out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN"); + out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN"); + } + else + { + out("\r\n250-AUTH LOGIN PLAIN"); + out("\r\n250-AUTH=LOGIN PLAIN"); + } + } + else if (usecram) + { + out("\r\n250-AUTH CRAM-MD5"); + out("\r\n250-AUTH=CRAM-MD5"); + } + } +#ifdef TLS + if (!(essl||deny_tls)) out("\r\n250-STARTTLS"); +#endif + smtp_size(); + out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg); if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); } -void smtp_rset(arg) char *arg; +void smtp_rset() { seenmail = 0; out("250 flushed\r\n"); } void smtp_mail(arg) char *arg; { + int r; + if (seenmail) { err_seenmail(); return; } +#ifdef TLS + if (force_tls) if (!ssl) { err_tlsfirst(); return; } +#endif + if (require_auth) if (!authd) { err_authfirst(); return; } if (!addrparse(arg)) { err_syntax(); return; } + if (databytes && !sizelimit(arg)) { err_size(); return; } flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */ - if ((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF); - if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient)) { + if((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF); + if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient) && (!authd)) { flagbarfbmf = bmcheck(BMCHECK_BMFNR); } + switch(mfcheck()) { /* make sure MAIL FROM domain has an MX record */ + case DNS_HARD: err_hmf(); return; + case DNS_SOFT: err_smf(); return; + case DNS_MEM: die_nomem(); + } + flagbarfspf = 0; + if (spfbehavior && !relayclient) + { + switch (r = spfcheck()) + { + case SPF_OK: env_put2("SPFRESULT","pass"); break; + case SPF_NONE: env_put2("SPFRESULT","none"); break; + case SPF_UNKNOWN: env_put2("SPFRESULT","unknown"); break; + case SPF_NEUTRAL: env_put2("SPFRESULT","neutral"); break; + case SPF_SOFTFAIL: env_put2("SPFRESULT","softfail"); break; + case SPF_FAIL: env_put2("SPFRESULT","fail"); break; + case SPF_ERROR: env_put2("SPFRESULT","error"); break; + } + if (spf_log) + { + stralloc si = {0}; + if (!spfinfo(&si)) die_nomem(); + if (!stralloc_0(&si)) die_nomem(); + strerr_warn3(title.s,"Received-SPF: ",si.s,0); + } + switch (r) + { + case SPF_NOMEM: + die_nomem(); + case SPF_ERROR: + if (spfbehavior < 2) break ; + out ("451 SPF lookup failure (#4.3.0)\r\n"); + return; + case SPF_NONE: + case SPF_UNKNOWN: + if (spfbehavior < 6) break ; + case SPF_NEUTRAL: + if (spfbehavior < 5) break ; + case SPF_SOFTFAIL: + if (spfbehavior < 4) break ; + case SPF_FAIL: + if (spfbehavior < 3) break ; + if (!spfexplanation(&spfbarfmsg)) die_nomem(); + if (!stralloc_0(&spfbarfmsg)) die_nomem(); + flagbarfspf = 1; + } + } + else + env_unset("SPFRESULT"); seenmail = 1; if (!stralloc_copys(&rcptto,"")) die_nomem(); if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); if (!stralloc_0(&mailfrom)) die_nomem(); + if (log_mail) { strerr_warn4(title.s,"MAIL FROM:<",mailfrom.s,">",0); } + rcptcounter = 0 ; out("250 ok\r\n"); } + +void err_spf() +{ + int i,j; + + for( i=0 ; i < spfbarfmsg.len ; i=j+1 ) { + j = byte_chr(spfbarfmsg.s + i,spfbarfmsg.len - i, '\n') + i; + if (j < spfbarfmsg.len){ + out("550-"); + spfbarfmsg.s[j] = 0; + out(spfbarfmsg.s); + spfbarfmsg.s[j] = '\n'; + } else { + out("550 "); + out(spfbarfmsg.s); + out(" (#5.7.1)\r\n"); + } + } +} + +#ifdef TLS +static int verify_cb(int ok, X509_STORE_CTX * ctx) +{ + return(1); +} +#endif + void smtp_rcpt(arg) char *arg; { + rcptcounter++; if (!seenmail) { err_wantmail(); return; } + if (checkrcptcount() == 1) { err_syntax(); return; } if (!addrparse(arg)) { err_syntax(); return; } + if (addrrelay()) { err_relay(); return; } + if (log_rcpt) { strerr_warn4(title.s,"RCPT TO:<",addr.s,">",0); } if (flagbarfbhelo) { - strerr_warn4("qmail-smtpd: badhelo: <",helohost.s,"> at ",remoteip,0); + if (logregex) { + strerr_warn7(title.s,"badhelo: <",helohost.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); + } else { + strerr_warn5(title.s,"badhelo: <",helohost.s,"> at ",remoteip,0); + } err_bhelo(); return; } if (flagbarfbmf) { - strerr_warn4("qmail-smtpd: badmailfrom: <",mailfrom.s,"> at ",remoteip,0); + if (logregex) { + strerr_warn7(title.s,"badmailfrom: <",mailfrom.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); + } else { + strerr_warn5(title.s,"badmailfrom: <",mailfrom.s,"> at ",remoteip,0); + } err_bmf(); return; } - if (bmtok) flagbarfbmt = bmcheck(BMCHECK_BMT); - if ((!flagbarfbmt) && (bmtnrok) && (!relayclient)) { - flagbarfbmt = bmcheck(BMCHECK_BMTNR); - } - if (flagbarfbmt) { - strerr_warn4("qmail-smtpd: badmailto: <",addr.s,"> at ",remoteip,0); - err_bmt(); + if (brtok) flagbarfbrt = bmcheck(BMCHECK_BRT); + if ((!flagbarfbrt) && (brtnrok) && (!relayclient) && (!authd)) { + flagbarfbrt = bmcheck(BMCHECK_BRTNR); + } + if (flagbarfbrt) { + if (logregex) { + strerr_warn7(title.s,"badrcptto: <",addr.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); + } else { + strerr_warn5(title.s,"badrcptto: <",addr.s,"> at ",remoteip,0); + } + err_brt(); return; } + + if (flagbarfspf) { err_spf(); return; } if (relayclient) { --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); if (!stralloc_0(&addr)) die_nomem(); } else +#ifndef TLS if (!addrallowed()) { err_nogateway(); return; } +#else + if (!addrallowed()) + { + if (ssl) + { STACK_OF(X509_NAME) *sk; + X509 *peercert; + stralloc tlsclients = {0}; + struct constmap maptlsclients; + int r; + + SSL_set_verify(ssl, + SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, + verify_cb); + if ((sk = SSL_load_client_CA_file("control/clientca.pem")) == NULL) + { err_nogateway(); return; } + SSL_set_client_CA_list(ssl, sk); + if((control_readfile(&tlsclients,"control/tlsclients",0) != 1) || + !constmap_init(&maptlsclients,tlsclients.s,tlsclients.len,0)) + { err_nogateway(); return; } + + SSL_renegotiate(ssl); + SSL_do_handshake(ssl); + ssl->state = SSL_ST_ACCEPT; + SSL_do_handshake(ssl); + if ((r = SSL_get_verify_result(ssl)) != X509_V_OK) + {out("553 no valid cert for gatewaying: "); + out(X509_verify_cert_error_string(r)); + out(" (#5.7.1)\r\n"); + return; + } + + if (peercert = SSL_get_peer_certificate(ssl)) + {char emailAddress[256]; + + X509_NAME_get_text_by_NID(X509_get_subject_name( + SSL_get_peer_certificate(ssl)), + NID_pkcs9_emailAddress, emailAddress, 256); + if (!stralloc_copys(&clientcert, emailAddress)) die_nomem(); + if (!constmap(&maptlsclients,clientcert.s,clientcert.len)) + { err_nogwcert(); return; } + relayclient = ""; + } + else { err_nogwcert(); return; } + } + else { err_nogateway(); return; } + } +#endif + + if (!relayclient && !vrtcheck()) { + strerr_warn5(title.s,"validrcptto [",remoteip,"] not found: ", + addr.s,0); + if(vrtlimit && (++vrtcount >= vrtlimit)) { + strerr_warn4(title.s,"validrcptto [",remoteip, + "] excessive violations, hanging up",0); + die_vrt(); + } + err_vrt(); + return; + } + if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); @@ -363,7 +989,11 @@ { int r; flush(); +#ifdef TLS + r = ssl_timeoutread(timeout,fd,buf,len); +#else r = timeoutread(timeout,fd,buf,len); +#endif if (r == -1) if (errno == error_timeout) die_alarm(); if (r <= 0) die_read(); return r; @@ -390,7 +1020,7 @@ char ch; int state; int flaginheader; - unsigned int pos; /* number of bytes since most recent \n, if fih */ + int pos; /* number of bytes since most recent \n, if fih */ int flagmaybex; /* 1 if this line might match RECEIVED, if fih */ int flagmaybey; /* 1 if this line might match \r\n, if fih */ int flagmaybez; /* 1 if this line might match DELIVERED, if fih */ @@ -410,8 +1040,8 @@ if (flagmaybex) if (pos == 7) ++*hops; if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; if (flagmaybey) if (pos == 1) flaginheader = 0; - ++pos; } + ++pos; if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } } switch(state) { @@ -445,6 +1075,25 @@ } } +void spfreceived() +{ + stralloc sa = {0}; + stralloc rcvd_spf = {0}; + + if (!spfbehavior || relayclient) return; + + if (!stralloc_copys(&rcvd_spf, "Received-SPF: ")) die_nomem(); + if (!spfinfo(&sa)) die_nomem(); + if (!stralloc_cat(&rcvd_spf, &sa)) die_nomem(); + if (!stralloc_append(&rcvd_spf, "\n")) die_nomem(); + if (bytestooverflow) { + bytestooverflow -= rcvd_spf.len; + if (bytestooverflow <= 0) qmail_fail(&qqt); + } + qmail_put(&qqt,rcvd_spf.s,rcvd_spf.len); +} + + char accept_buf[FMT_ULONG]; void acceptmessage(qp) unsigned long qp; { @@ -459,10 +1108,55 @@ out("\r\n"); } -void smtp_data(arg) char *arg; { +void auth_fixenv() +{ + int i,f; + char *envi,*eq; + stralloc work = {0}; + + do + { + f=0; + for (i=0;envi=environ[i];++i) + { + if (!str_diffn("AUTH_SET_",envi,9)) + { + stralloc_copys(&work,envi); + stralloc_0(&work); + eq = env_findeq(work.s); + *eq=0; + env_unset(work.s); + if(authd) + { + env_unset(work.s+9); + *eq='='; + env_put(work.s+9); + } + f=1; + break; + } + if (!str_diffn("AUTH_UNSET_",envi,11)) + { + stralloc_copys(&work,envi); + stralloc_0(&work); + eq = env_findeq(work.s); + *eq=0; + env_unset(work.s); + if(authd) env_unset(work.s+11); + f=1; + break; + } + } + } while (f); +} + +void smtp_data() { int hops; unsigned long qp; char *qqx; +#ifdef TLS + stralloc protocolinfo = {0}; +#endif if (!seenmail) { err_wantmail(); return; } if (!rcptto.len) { err_wantrcpt(); return; } @@ -471,8 +1165,23 @@ if (qmail_open(&qqt) == -1) { err_qqt(); return; } qp = qmail_qp(&qqt); out("354 go ahead\r\n"); - + +#ifdef TLS + if(ssl){ + if (!stralloc_copys(&protocolinfo, SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)))) die_nomem(); + if (!stralloc_catb(&protocolinfo, " encrypted SMTP", 15)) die_nomem(); + if (clientcert.len){ + if (!stralloc_catb(&protocolinfo," cert ", 6)) die_nomem(); + if (!stralloc_catb(&protocolinfo,clientcert.s, clientcert.len)) die_nomem(); + } + if (!stralloc_0(&protocolinfo)) die_nomem(); + } else if (!stralloc_copyb(&protocolinfo,"SMTP",5)) die_nomem(); + received(&qqt,protocolinfo.s,local,remoteip,remotehost,remoteinfo,case_diffs(remotehost,helohost.s) ? helohost.s : 0); +#else received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); +#endif + + spfreceived(); blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt); @@ -482,34 +1191,461 @@ qqx = qmail_close(&qqt); if (!*qqx) { acceptmessage(qp); return; } if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } - if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } - if (*qqx == 'D') out("554 "); else out("451 "); + if (databytes) if (!bytestooverflow) { err_size(); return; } + if (*qqx == 'I') out("250 ok "); else if (*qqx == 'D') out("554 "); else out("451 "); out(qqx + 1); out("\r\n"); } +#ifdef TLS +static RSA *tmp_rsa_cb(ssl,export,keylength) SSL *ssl; int export; int keylength; +{ + RSA* rsa; + BIO* in; + + if (!export || keylength == 512) + if (in=BIO_new(BIO_s_file_internal())) + if (BIO_read_filename(in,"control/rsa512.pem") > 0) + if (rsa=PEM_read_bio_RSAPrivateKey(in,NULL,NULL,NULL)) + return rsa; + return (RSA_generate_key(export?keylength:512,RSA_F4,NULL,NULL)); +} + +void smtp_tls(arg) char *arg; +{ + SSL_CTX *ctx; + + if (*arg) + {out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n"); + return;} + + if (ssl) + {out("454 TLS not available: TLS already active (4.3.0)\r\n"); + return;} + + if (essl) + {out("454 TLS not available: already SSL secured (4.3.0)\r\n"); + return;} + + if (deny_tls) + {out("454 TLS not available (4.3.0)\r\n"); + return;} + + SSL_library_init(); + if(!(ctx=SSL_CTX_new(SSLv23_server_method()))) + {out("454 TLS not available: unable to initialize ctx (#4.3.0)\r\n"); + return;} + if(!SSL_CTX_use_RSAPrivateKey_file(ctx, servercert, SSL_FILETYPE_PEM)) + {out("454 TLS not available: missing RSA private key (#4.3.0)\r\n"); + return;} + if(!SSL_CTX_use_certificate_chain_file(ctx, servercert)) + {out("454 TLS not available: missing certificate (#4.3.0)\r\n"); + return;} + SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb); + SSL_CTX_set_cipher_list(ctx,tlsserverciphers.s); + SSL_CTX_load_verify_locations(ctx, "control/clientca.pem",NULL); + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb); + + out("220 ready for tls\r\n"); flush(); + essl=1; + + if(!(ssl=SSL_new(ctx))) die_read(); + SSL_set_fd(ssl,0); + if(SSL_accept(ssl)<=0) die_read(); + substdio_fdbuf(&ssout,SSL_write,ssl,ssoutbuf,sizeof(ssoutbuf)); + + remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) remotehost = "unknown"; + dohelo(remotehost); +} +#endif + + +int authgetl(void) { + int i; + + if (!stralloc_copys(&authin, "")) die_nomem(); + + for (;;) { + if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ + i = substdio_get(&ssin,authin.s + authin.len,1); + if (i != 1) die_read(); + if (authin.s[authin.len] == '\n') break; + ++authin.len; + } + + if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len; + authin.s[authin.len] = 0; + + if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); } + if (authin.len == 0) { return err_input(); } + return authin.len; +} + +int authenticate_cl(void) +{ + int child; + int wstat; + int pi[2]; + + if (!stralloc_0(&user)) die_nomem(); + if (!stralloc_0(&pass)) die_nomem(); + if (!stralloc_0(&chal)) die_nomem(); + + if (pipe(pi) == -1) return err_pipe(); + switch(child = fork()) { + case -1: + return err_fork(); + case 0: + close(pi[1]); + if(fd_copy(3,pi[0]) == -1) return err_pipe(); + sig_pipedefault(); + execvp(*childargs, childargs); + _exit(1); + } + close(pi[0]); + + substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf); + if (substdio_put(&ssup,user.s,user.len) == -1) return err_write(); + if (substdio_put(&ssup,pass.s,pass.len) == -1) return err_write(); + if (substdio_put(&ssup,chal.s,chal.len) == -1) return err_write(); + if (substdio_flush(&ssup) == -1) return err_write(); + + close(pi[1]); + byte_zero(pass.s,pass.len); + byte_zero(upbuf,sizeof upbuf); + if (wait_pid(&wstat,child) == -1) return err_child(); + if (wait_crashed(wstat)) return err_child(); + if (wait_exitcode(wstat)) { + strerr_warn5(title.s,"AUTH failed [",remoteip,"] ",user.s,0); + sleep(5); + return 1; /* no */ + } + strerr_warn5(title.s,"AUTH successful [",remoteip,"] ",user.s,0); + return 0; /* yes */ +} + +void addvars(s) +char *s; +{ + char *n; + char *v; + int x; + + n = s; + + while (*n) + { + if (','==*n) n++ ; + x = str_chr(n,'='); + if (!n[x]) return ; + n[x]=0; + if (n[x+1]!='\"') return ; + v = n+x+2 ; + x = str_chr(v,'\"'); + if (!v[x]) return ; + v[x]=0; + env_put2(n,v); + n = v+x+1; + } +} + +int authenticate_cdb(void) +{ + int r,x; + uint32 dlen ; + stralloc epw = {0} ; + char *cr ; + + if (!stralloc_0(&user)) die_nomem(); + if (!stralloc_0(&pass)) die_nomem(); + + r = cdb_seek(auth_cdb_fd,user.s,user.len-1,&dlen); + if (r<=0) { + strerr_warn5(title.s,"AUTH failed (unknown user) [", + remoteip,"] ",user.s,0); + return 1 ; + } + + if (!stralloc_ready(&epw,dlen+1)) die_nomem() ; + epw.len = dlen ; + if (-1 == cdb_bread(auth_cdb_fd,epw.s,epw.len)) die_auth_cdb() ; + if (!stralloc_0(&epw)) die_nomem(); + + x = str_chr(epw.s,',') ; + epw.s[x] = 0 ; + + if ( str_diff ( crypt ( pass.s , epw.s ) , epw.s ) ) { + strerr_warn5(title.s,"AUTH failed (bad password) [", + remoteip,"] ",user.s,0); + return 1 ; + } + + if ( x < dlen ) + addvars(epw.s+x+1,dlen-x-1) ; + + strerr_warn5(title.s,"AUTH successful [",remoteip,"] ",user.s,0); + return 0 ; +} + +int authenticate(void) +{ + if(useauth_cdb) return authenticate_cdb(); + if(useauth_cl) return authenticate_cl(); + /* future auth back-end modules here */ + return err_authmethod(); +} + +int auth_login(arg) char *arg; +{ + int r; + + if ( ! ( essl || allow_insecure_auth ) ) return err_noauthtype() ; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); + } + else { + out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); + } + if (r == -1) die_nomem(); + + out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); + if (r == -1) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +int auth_plain(arg) char *arg; +{ + int r, id = 0; + + if ( ! ( essl || allow_insecure_auth ) ) return err_noauthtype() ; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&slop) == 1) return err_input(); + } + else { + out("334 \r\n"); flush(); + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input(); + } + if (r == -1 || !stralloc_0(&slop)) die_nomem(); + while (slop.s[id]) id++; /* ignore authorize-id */ + + if (slop.len > id + 1) + if (!stralloc_copys(&user,slop.s + id + 1)) die_nomem(); + if (slop.len > id + user.len + 2) + if (!stralloc_copys(&pass,slop.s + id + user.len + 2)) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +int auth_cram() +{ + int i, r; + char *s; + + if ( ! usecram ) return err_noauthtype(); + + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + + if (!stralloc_copys(&chal,"<")) die_nomem(); + if (!stralloc_cats(&chal,unique)) die_nomem(); + if (!stralloc_cats(&chal,hostname)) die_nomem(); + if (!stralloc_cats(&chal,">")) die_nomem(); + if (b64encode(&chal,&slop) < 0) die_nomem(); + if (!stralloc_0(&slop)) die_nomem(); + + out("334 "); + out(slop.s); + out("\r\n"); + flush(); + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input(); + if (r == -1 || !stralloc_0(&slop)) die_nomem(); + + i = str_chr(slop.s,' '); + s = slop.s + i; + while (*s == ' ') ++s; + slop.s[i] = 0; + if (!stralloc_copys(&user,slop.s)) die_nomem(); + if (!stralloc_copys(&pass,s)) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +struct authcmd { + char *text; + int (*fun)(); +} authcmds[] = { + { "login", auth_login } +, { "plain", auth_plain } +, { "cram-md5", auth_cram } +, { 0, err_noauthtype } +}; + +void smtp_auth(arg) +char *arg; +{ + int i; + char *cmd = arg; + + if (!useauth) { err_noauthavail(); return; } + if (authd) { err_authd(); return; } + if (seenmail) { err_authmail(); return; } + + if (!stralloc_copys(&user,"")) die_nomem(); + if (!stralloc_copys(&pass,"")) die_nomem(); + if (!stralloc_copys(&chal,"")) die_nomem(); + + i = str_chr(cmd,' '); + arg = cmd + i; + while (*arg == ' ') ++arg; + cmd[i] = 0; + + for (i = 0;authcmds[i].text;++i) + if (case_equals(authcmds[i].text,cmd)) break; + + switch (authcmds[i].fun(arg)) { + case 0: + authd = 1; + relayclient = ""; + auth_fixenv(); + readenv(); + remoteinfo = user.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + if (!env_unset("SMTP_AUTH_USER")) die_read(); + if (!env_put2("SMTP_AUTH_USER",remoteinfo)) die_nomem(); + out("235 ok, go ahead (#2.0.0)\r\n"); + break; + case 1: + out("535 authorization failed (#5.7.0)\r\n"); + } +} struct commands smtpcommands[] = { { "rcpt", smtp_rcpt, 0 } , { "mail", smtp_mail, 0 } , { "data", smtp_data, flush } +, { "auth", smtp_auth, flush } , { "quit", smtp_quit, flush } , { "helo", smtp_helo, flush } , { "ehlo", smtp_ehlo, flush } , { "rset", smtp_rset, 0 } , { "help", smtp_help, flush } +#ifdef TLS +, { "starttls", smtp_tls, flush } +#endif , { "noop", err_noop, flush } , { "vrfy", err_vrfy, flush } , { 0, err_unimpl, flush } } ; -void main() +void main(argc,argv) +int argc; +char **argv; { + char *x ; + unsigned long u ; + + if (argc>3) + { + hostname = argv[1]; + childargs = argv + 2; + useauth_cl = 1; + } + + pid_buf[fmt_ulong(pid_buf,getpid())]=0; + if (!stralloc_copys(&title,"qmail-smtpd[")) die_nomem(); + if (!stralloc_cats(&title,pid_buf)) die_nomem(); + if (!stralloc_cats(&title,"]: ")) die_nomem(); + if (!stralloc_0(&title)) die_nomem(); + +#ifdef TLS + sig_alarmcatch(sigalrm); +#endif sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); +#ifdef TLS + if(access(servercert,R_OK)) { deny_tls = 1; } + x = env_get("DENY_TLS"); + if(x) { scan_ulong(x,&u); if (u>0) deny_tls = 1; } + x = env_get("FORCE_TLS"); + if(x) { scan_ulong(x,&u); if (u>0) force_tls = 1; } +#endif + + x = env_get("SSL"); + if(x) { scan_ulong(x,&u); if (u>0) essl = 1; } +#ifdef TLS + if (essl) { force_tls = 0; } + if (force_tls && deny_tls) die_forcedenytls() ; +#endif + + x = env_get("ALLOW_INSECURE_AUTH"); + if(x) { scan_ulong(x,&u); if (u>0) allow_insecure_auth = 1; } + + x = env_get("REQUIRE_AUTH"); + if(x) { scan_ulong(x,&u); if (u>0) require_auth = 1; } + + auth_cdb_file = env_get("AUTH_CDB"); + if(auth_cdb_file) { + if ( ! access(auth_cdb_file,R_OK) ) { useauth_cdb = 1; } + else die_auth_cdb() ; + } + + if ( useauth_cl || useauth_cdb ) { useauth = 1 ; } + + if (require_auth && (!useauth)) die_cannot_auth() ; + + if (useauth) + { + x = env_get("ALLOW_CRAM"); + if(x) { scan_ulong(x,&u); usecram = (int) u; } + if(useauth_cdb && usecram) die_cannot_cram() ; + } + setup(); if (ipme_init() != 1) die_ipme(); + if (greetdelay||drop_pre_greet) { + x = timeoutread(greetdelay?greetdelay:1,0,ssinbuf,sizeof ssinbuf); + if(-1 == x) { + if(errno != error_timeout) strerr_die4sys(1, + title.s,"before greeting: [", remoteip, "] "); + } else if ( 0 == x ) { + strerr_die4x(1,title.s,"before greeting: [", remoteip, + "] client disconnected"); + } else if(drop_pre_greet) { + strerr_warn4(title.s,"before greeting: [", remoteip, + "] client sent data",0); + die_pre_greet(); + } + } smtp_greet("220 "); out(" ESMTP\r\n"); if (commands(&ssin,&smtpcommands) == 0) die_read(); die_nomem(); } + +int checkrcptcount() { + if (maxrcpt == -1) { return 0;} + else if (rcptcounter > maxrcpt ) { + strerr_warn4(title.s,"MAXRCPT fail [",remoteip,"]",0); + return 1; + } + return 0; +} diff -ruN qmail-1.03/qmail-start.c qmail-1.03.patched/qmail-start.c --- qmail-1.03/qmail-start.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/qmail-start.c 2007-09-27 03:28:05.000000000 +0200 @@ -8,6 +8,9 @@ char *(qcargs[]) = { "qmail-clean", 0 }; char *(qlargs[]) = { "qmail-lspawn", "./Mailbox", 0 }; char *(qrargs[]) = { "qmail-rspawn", 0 }; +#ifdef EXTERNAL_TODO +char *(qtargs[]) = { "qmail-todo", 0}; +#endif void die() { _exit(111); } @@ -18,13 +21,28 @@ int pi4[2]; int pi5[2]; int pi6[2]; - -void close23456() { close(2); close(3); close(4); close(5); close(6); } +#ifdef EXTERNAL_TODO +int pi7[2]; +int pi8[2]; +int pi9[2]; +int pi10[2]; +#endif + +void close23456() { + close(2); close(3); close(4); close(5); close(6); +#ifdef EXTERNAL_TODO + close(7); close(8); +#endif +} void closepipes() { close(pi1[0]); close(pi1[1]); close(pi2[0]); close(pi2[1]); close(pi3[0]); close(pi3[1]); close(pi4[0]); close(pi4[1]); close(pi5[0]); close(pi5[1]); close(pi6[0]); close(pi6[1]); +#ifdef EXTERNAL_TODO + close(pi7[0]); close(pi7[1]); close(pi8[0]); close(pi8[1]); + close(pi9[0]); close(pi9[1]); close(pi10[0]); close(pi10[1]); +#endif } void main(argc,argv) @@ -40,6 +58,10 @@ if (fd_copy(4,0) == -1) die(); if (fd_copy(5,0) == -1) die(); if (fd_copy(6,0) == -1) die(); +#ifdef EXTERNAL_TODO + if (fd_copy(7,0) == -1) die(); + if (fd_copy(8,0) == -1) die(); +#endif if (argv[1]) { qlargs[1] = argv[1]; @@ -70,6 +92,12 @@ if (pipe(pi4) == -1) die(); if (pipe(pi5) == -1) die(); if (pipe(pi6) == -1) die(); +#ifdef EXTERNAL_TODO + if (pipe(pi7) == -1) die(); + if (pipe(pi8) == -1) die(); + if (pipe(pi9) == -1) die(); + if (pipe(pi10) == -1) die(); +#endif switch(fork()) { case -1: die(); @@ -105,6 +133,34 @@ execvp(*qcargs,qcargs); die(); } + +#ifdef EXTERNAL_TODO + switch(fork()) { + case -1: die(); + case 0: + if (prot_uid(auto_uids) == -1) die(); + if (fd_copy(0,pi7[0]) == -1) die(); + if (fd_copy(1,pi8[1]) == -1) die(); + close23456(); + if (fd_copy(2,pi9[1]) == -1) die(); + if (fd_copy(3,pi10[0]) == -1) die(); + closepipes(); + execvp(*qtargs,qtargs); + die(); + } + + switch(fork()) { + case -1: die(); + case 0: + if (prot_uid(auto_uidq) == -1) die(); + if (fd_copy(0,pi9[0]) == -1) die(); + if (fd_copy(1,pi10[1]) == -1) die(); + close23456(); + closepipes(); + execvp(*qcargs,qcargs); + die(); + } +#endif if (prot_uid(auto_uids) == -1) die(); if (fd_copy(0,1) == -1) die(); @@ -114,6 +170,10 @@ if (fd_copy(4,pi4[0]) == -1) die(); if (fd_copy(5,pi5[1]) == -1) die(); if (fd_copy(6,pi6[0]) == -1) die(); +#ifdef EXTERNAL_TODO + if (fd_copy(7,pi7[1]) == -1) die(); + if (fd_copy(8,pi8[0]) == -1) die(); +#endif closepipes(); execvp(*qsargs,qsargs); die(); diff -ruN qmail-1.03/qmail-todo.8 qmail-1.03.patched/qmail-todo.8 --- qmail-1.03/qmail-todo.8 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/qmail-todo.8 2007-09-27 03:30:22.000000000 +0200 @@ -0,0 +1,60 @@ +.TH qmail-todo 8 +.SH NAME +qmail-todo \- forwarding signals from qmail-queue to qmail-send +.SH SYNOPSIS +.B qmail-todo +.SH DESCRIPTION +.B qmail-todo +catches the signals from +.BR qmail-queue +whenever new messages are added to the queue, and +runs a loop whose only job is the same classification +functions that +.BR qmail-send +normally does by itself. + +When a very large number of messages are added to the +queue in a short period of time, +.BR qmail-send +(i.e. the "todo" portion of the program) inspects every +newly-arrived message and decides, for each message, whether +it's "local" or "remote" - and while it's doing this, no new +deliveries are being started. + +This can also happen in reverse. If the system is doing a lot +of deliveries and they all "hang" (waiting for remote servers), +the delivery-scheduling side will be stuck and the classifying +side doesn't run, resulting in a delivery side sit there and do +nothing. + +.B qmail-todo +solves the problem by breaking the original qmail-send into two +processes. +.B qmail-todo +catches the signals from qmail-queue and each incoming message is +classified in "local" and "remote" ones. When it finishes each message, +it sends a message to +.B qmail-send +, telling it what the new message number is. +When it runs out of new messages to classify, +it goes to sleep until another signal arrives from qmail-queue. + +.SH AUTHOR + +Claudio Jeker and +Andre Oppermann stands for programming. + +Online information provided by +John M. Simpson + +This manpage was written by Alexander Hosfeld + +.SH COPYRIGHT +EXTTODO patches are under the BSD license. + +.SH "SEE ALSO" +qmail-queue(8), +qmail-inject(8), +qmail-qmqpc(8), +qmail-send(8), +qmail-smtpd(8) diff -ruN qmail-1.03/qmail-todo.c qmail-1.03.patched/qmail-todo.c --- qmail-1.03/qmail-todo.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/qmail-todo.c 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,688 @@ +#include +#include +#include "alloc.h" +#include "auto_qmail.h" +#include "byte.h" +#include "constmap.h" +#include "control.h" +#include "direntry.h" +#include "error.h" +#include "exit.h" +#include "fmt.h" +#include "fmtqfn.h" +#include "getln.h" +#include "open.h" +#include "ndelay.h" +#include "now.h" +#include "readsubdir.h" +#include "readwrite.h" +#include "scan.h" +#include "select.h" +#include "str.h" +#include "stralloc.h" +#include "substdio.h" +#include "trigger.h" + +/* critical timing feature #1: if not triggered, do not busy-loop */ +/* critical timing feature #2: if triggered, respond within fixed time */ +/* important timing feature: when triggered, respond instantly */ +#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */ +#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */ +#define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */ +#define SLEEP_SYSFAIL 123 + +stralloc percenthack = {0}; +struct constmap mappercenthack; +stralloc locals = {0}; +struct constmap maplocals; +stralloc vdoms = {0}; +struct constmap mapvdoms; +stralloc envnoathost = {0}; + +char strnum[FMT_ULONG]; + +/* XXX not good, if qmail-send.c changes this has to be updated */ +#define CHANNELS 2 +char *chanaddr[CHANNELS] = { "local/", "remote/" }; + +datetime_sec recent; + +void log1(char *x); +void log3(char* x, char* y, char* z); + +int flagstopasap = 0; +void sigterm(void) +{ + if (flagstopasap == 0) + log1("status: qmail-todo stop processing asap\n"); + flagstopasap = 1; +} + +int flagreadasap = 0; void sighup(void) { flagreadasap = 1; } +int flagsendalive = 1; void senddied(void) { flagsendalive = 0; } + +void nomem() { log1("alert: out of memory, sleeping...\n"); sleep(10); } +void pausedir(dir) char *dir; +{ log3("alert: unable to opendir ",dir,", sleeping...\n"); sleep(10); } + +void cleandied() +{ + log1("alert: qmail-todo: oh no! lost qmail-clean connection! dying...\n"); + flagstopasap = 1; +} + + +/* this file is not so long ------------------------------------- FILENAMES */ + +stralloc fn = {0}; + +void fnmake_init(void) +{ + while (!stralloc_ready(&fn,FMTQFN)) nomem(); +} + +void fnmake_info(unsigned long id) { fn.len = fmtqfn(fn.s,"info/",id,1); } +void fnmake_todo(unsigned long id) { fn.len = fmtqfn(fn.s,"todo/",id,0); } +void fnmake_mess(unsigned long id) { fn.len = fmtqfn(fn.s,"mess/",id,1); } +void fnmake_chanaddr(unsigned long id, int c) +{ fn.len = fmtqfn(fn.s,chanaddr[c],id,1); } + + +/* this file is not so long ------------------------------------- REWRITING */ + +stralloc rwline = {0}; + +/* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */ +/* may trash recip. must set up rwline, between a T and a \0. */ +int rewrite(char *recip) +{ + int i; + int j; + char *x; + static stralloc addr = {0}; + int at; + + if (!stralloc_copys(&rwline,"T")) return 0; + if (!stralloc_copys(&addr,recip)) return 0; + + i = byte_rchr(addr.s,addr.len,'@'); + if (i == addr.len) { + if (!stralloc_cats(&addr,"@")) return 0; + if (!stralloc_cat(&addr,&envnoathost)) return 0; + } + + while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) { + j = byte_rchr(addr.s,i,'%'); + if (j == i) break; + addr.len = i; + i = j; + addr.s[i] = '@'; + } + + at = byte_rchr(addr.s,addr.len,'@'); + + if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) { + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 1; + } + + for (i = 0;i <= addr.len;++i) + if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.'))) + if (x = constmap(&mapvdoms,addr.s + i,addr.len - i)) { + if (!*x) break; + if (!stralloc_cats(&rwline,x)) return 0; + if (!stralloc_cats(&rwline,"-")) return 0; + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 1; + } + + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 2; +} + +/* this file is not so long --------------------------------- COMMUNICATION */ + +substdio sstoqc; char sstoqcbuf[1024]; +substdio ssfromqc; char ssfromqcbuf[1024]; +stralloc comm_buf = {0}; +int comm_pos; +int fdout = -1; +int fdin = -1; + +void comm_init(void) +{ + substdio_fdbuf(&sstoqc,write,2,sstoqcbuf,sizeof(sstoqcbuf)); + substdio_fdbuf(&ssfromqc,read,3,ssfromqcbuf,sizeof(ssfromqcbuf)); + + fdout = 1; /* stdout */ + fdin = 0; /* stdin */ + if (ndelay_on(fdout) == -1) + /* this is so stupid: NDELAY semantics should be default on write */ + senddied(); /* drastic, but better than risking deadlock */ + + while (!stralloc_ready(&comm_buf,1024)) nomem(); +} + +int comm_canwrite(void) +{ + /* XXX: could allow a bigger buffer; say 10 recipients */ + /* XXX: returns true if there is something in the buffer */ + if (!flagsendalive) return 0; + if (comm_buf.s && comm_buf.len) return 1; + return 0; +} + +void log1(char* x) +{ + int pos; + + pos = comm_buf.len; + if (!stralloc_cats(&comm_buf,"L")) goto fail; + if (!stralloc_cats(&comm_buf,x)) goto fail; + if (!stralloc_0(&comm_buf)) goto fail; + return; + +fail: + /* either all or nothing */ + comm_buf.len = pos; +} + +void log3(char* x, char *y, char *z) +{ + int pos; + + pos = comm_buf.len; + if (!stralloc_cats(&comm_buf,"L")) goto fail; + if (!stralloc_cats(&comm_buf,x)) goto fail; + if (!stralloc_cats(&comm_buf,y)) goto fail; + if (!stralloc_cats(&comm_buf,z)) goto fail; + if (!stralloc_0(&comm_buf)) goto fail; + return; + +fail: + /* either all or nothing */ + comm_buf.len = pos; +} + +void comm_write(unsigned long id, int local, int remote) +{ + int pos; + char *s; + + if(local && remote) s="B"; + else if(local) s="L"; + else if(remote) s="R"; + else s="X"; + + pos = comm_buf.len; + strnum[fmt_ulong(strnum,id)] = 0; + if (!stralloc_cats(&comm_buf,"D")) goto fail; + if (!stralloc_cats(&comm_buf,s)) goto fail; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_0(&comm_buf)) goto fail; + return; + +fail: + /* either all or nothing */ + comm_buf.len = pos; +} + +static int issafe(char ch) +{ + if (ch == '%') return 0; /* general principle: allman's code is crap */ + if (ch < 33) return 0; + if (ch > 126) return 0; + return 1; +} + +void comm_info(unsigned long id, unsigned long size, char* from, unsigned long pid, unsigned long uid) +{ + int pos; + int i; + + pos = comm_buf.len; + if (!stralloc_cats(&comm_buf,"Linfo msg ")) goto fail; + strnum[fmt_ulong(strnum,id)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_cats(&comm_buf,": bytes ")) goto fail; + strnum[fmt_ulong(strnum,size)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_cats(&comm_buf," from <")) goto fail; + i = comm_buf.len; + if (!stralloc_cats(&comm_buf,from)) goto fail; + for (;i < comm_buf.len;++i) + if (comm_buf.s[i] == '\n') + comm_buf.s[i] = '/'; + else + if (!issafe(comm_buf.s[i])) + comm_buf.s[i] = '_'; + if (!stralloc_cats(&comm_buf,"> qp ")) goto fail; + strnum[fmt_ulong(strnum,pid)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_cats(&comm_buf," uid ")) goto fail; + strnum[fmt_ulong(strnum,uid)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_cats(&comm_buf,"\n")) goto fail; + if (!stralloc_0(&comm_buf)) goto fail; + return; + +fail: + /* either all or nothing */ + comm_buf.len = pos; +} + +void comm_exit(void) +{ + int w; + + /* if it fails exit, we have already stoped */ + if (!stralloc_cats(&comm_buf,"X")) _exit(1); + if (!stralloc_0(&comm_buf)) _exit(1); +} + +void comm_selprep(int *nfds, fd_set *wfds, fd_set *rfds) +{ + if (flagsendalive) { + if (flagstopasap && comm_canwrite() == 0) + comm_exit(); + if (comm_canwrite()) { + FD_SET(fdout,wfds); + if (*nfds <= fdout) + *nfds = fdout + 1; + } + FD_SET(fdin,rfds); + if (*nfds <= fdin) + *nfds = fdin + 1; + } +} + +void comm_do(fd_set *wfds, fd_set *rfds) +{ + /* first write then read */ + if (flagsendalive) + if (comm_canwrite()) + if (FD_ISSET(fdout,wfds)) { + int w; + int len; + len = comm_buf.len; + w = write(fdout,comm_buf.s + comm_pos,len - comm_pos); + if (w <= 0) { + if ((w == -1) && (errno == error_pipe)) + senddied(); + } else { + comm_pos += w; + if (comm_pos == len) { + comm_buf.len = 0; + comm_pos = 0; + } + } + } + if (flagsendalive) + if (FD_ISSET(fdin,rfds)) { + /* there are only two messages 'H' and 'X' */ + char c; + int r; + r = read(fdin, &c, 1); + if (r <= 0) { + if ((r == -1) && (errno != error_intr)) + senddied(); + } else { + switch(c) { + case 'H': + sighup(); + break; + case 'X': + sigterm(); + break; + default: + log1("warning: qmail-todo: qmail-send speaks an obscure dialect\n"); + break; + } + } + } +} + +/* this file is not so long ------------------------------------------ TODO */ + +datetime_sec nexttodorun; +DIR *tododir; /* if 0, have to opendir again */ +stralloc todoline = {0}; +char todobuf[SUBSTDIO_INSIZE]; +char todobufinfo[512]; +char todobufchan[CHANNELS][1024]; + +void todo_init(void) +{ + tododir = 0; + nexttodorun = now(); + trigger_set(); +} + +void todo_selprep(int *nfds, fd_set *rfds, datetime_sec *wakeup) +{ + if (flagstopasap) return; + trigger_selprep(nfds,rfds); + if (tododir) *wakeup = 0; + if (*wakeup > nexttodorun) *wakeup = nexttodorun; +} + +void todo_do(fd_set *rfds) +{ + struct stat st; + substdio ss; int fd; + substdio ssinfo; int fdinfo; + substdio sschan[CHANNELS]; + int fdchan[CHANNELS]; + int flagchan[CHANNELS]; + char ch; + int match; + unsigned long id; + unsigned int len; + direntry *d; + int c; + unsigned long uid; + unsigned long pid; + + fd = -1; + fdinfo = -1; + for (c = 0;c < CHANNELS;++c) fdchan[c] = -1; + + if (flagstopasap) return; + + if (!tododir) + { + if (!trigger_pulled(rfds)) + if (recent < nexttodorun) + return; + trigger_set(); + tododir = opendir("todo"); + if (!tododir) + { + pausedir("todo"); + return; + } + nexttodorun = recent + SLEEP_TODO; + } + + d = readdir(tododir); + if (!d) + { + closedir(tododir); + tododir = 0; + return; + } + if (str_equal(d->d_name,".")) return; + if (str_equal(d->d_name,"..")) return; + len = scan_ulong(d->d_name,&id); + if (!len || d->d_name[len]) return; + + fnmake_todo(id); + + fd = open_read(fn.s); + if (fd == -1) { log3("warning: qmail-todo: unable to open ",fn.s,"\n"); return; } + + fnmake_mess(id); + /* just for the statistics */ + if (stat(fn.s,&st) == -1) + { log3("warning: qmail-todo: unable to stat ",fn.s,"\n"); goto fail; } + + for (c = 0;c < CHANNELS;++c) + { + fnmake_chanaddr(id,c); + if (unlink(fn.s) == -1) if (errno != error_noent) + { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; } + } + + fnmake_info(id); + if (unlink(fn.s) == -1) if (errno != error_noent) + { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; } + + fdinfo = open_excl(fn.s); + if (fdinfo == -1) + { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; } + + strnum[fmt_ulong(strnum,id)] = 0; + log3("new msg ",strnum,"\n"); + + for (c = 0;c < CHANNELS;++c) flagchan[c] = 0; + + substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf)); + substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo)); + + uid = 0; + pid = 0; + + for (;;) + { + if (getln(&ss,&todoline,&match,'\0') == -1) + { + /* perhaps we're out of memory, perhaps an I/O error */ + fnmake_todo(id); + log3("warning: qmail-todo: trouble reading ",fn.s,"\n"); goto fail; + } + if (!match) break; + + switch(todoline.s[0]) + { + case 'u': + scan_ulong(todoline.s + 1,&uid); + break; + case 'p': + scan_ulong(todoline.s + 1,&pid); + break; + case 'F': + if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1) + { + fnmake_info(id); + log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; + } + comm_info(id, (unsigned long) st.st_size, todoline.s + 1, pid, uid); + break; + case 'T': + switch(rewrite(todoline.s + 1)) + { + case 0: nomem(); goto fail; + case 2: c = 1; break; + default: c = 0; break; + } + if (fdchan[c] == -1) + { + fnmake_chanaddr(id,c); + fdchan[c] = open_excl(fn.s); + if (fdchan[c] == -1) + { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; } + substdio_fdbuf(&sschan[c] + ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c])); + flagchan[c] = 1; + } + if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1) + { + fnmake_chanaddr(id,c); + log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; + } + break; + default: + fnmake_todo(id); + log3("warning: qmail-todo: unknown record type in ",fn.s,"\n"); goto fail; + } + } + + close(fd); fd = -1; + + fnmake_info(id); + if (substdio_flush(&ssinfo) == -1) + { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; } + if (fsync(fdinfo) == -1) + { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; } + close(fdinfo); fdinfo = -1; + + for (c = 0;c < CHANNELS;++c) + if (fdchan[c] != -1) + { + fnmake_chanaddr(id,c); + if (substdio_flush(&sschan[c]) == -1) + { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; } + if (fsync(fdchan[c]) == -1) + { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; } + close(fdchan[c]); fdchan[c] = -1; + } + + fnmake_todo(id); + if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; } + if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; } + if (ch != '+') + { + log3("warning: qmail-clean unable to clean up ",fn.s,"\n"); + return; + } + + comm_write(id, flagchan[0], flagchan[1]); + + return; + + fail: + if (fd != -1) close(fd); + if (fdinfo != -1) close(fdinfo); + for (c = 0;c < CHANNELS;++c) + if (fdchan[c] != -1) close(fdchan[c]); +} + +/* this file is too long ---------------------------------------------- MAIN */ + +int getcontrols(void) +{ + if (control_init() == -1) return 0; + if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0; + if (control_readfile(&locals,"control/locals",1) != 1) return 0; + if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0; + switch(control_readfile(&percenthack,"control/percenthack",0)) + { + case -1: return 0; + case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break; + case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break; + } + switch(control_readfile(&vdoms,"control/virtualdomains",0)) + { + case -1: return 0; + case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break; + case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break; + } + return 1; +} + +stralloc newlocals = {0}; +stralloc newvdoms = {0}; + +void regetcontrols(void) +{ + int r; + + if (control_readfile(&newlocals,"control/locals",1) != 1) + { log1("alert: qmail-todo: unable to reread control/locals\n"); return; } + r = control_readfile(&newvdoms,"control/virtualdomains",0); + if (r == -1) + { log1("alert: qmail-todo: unable to reread control/virtualdomains\n"); return; } + + constmap_free(&maplocals); + constmap_free(&mapvdoms); + + while (!stralloc_copy(&locals,&newlocals)) nomem(); + while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem(); + + if (r) + { + while (!stralloc_copy(&vdoms,&newvdoms)) nomem(); + while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem(); + } + else + while (!constmap_init(&mapvdoms,"",0,1)) nomem(); +} + +void reread(void) +{ + if (chdir(auto_qmail) == -1) + { + log1("alert: qmail-todo: unable to reread controls: unable to switch to home directory\n"); + return; + } + regetcontrols(); + while (chdir("queue") == -1) + { + log1("alert: qmail-todo: unable to switch back to queue directory; HELP! sleeping...\n"); + sleep(10); + } +} + +void main() +{ + datetime_sec wakeup; + fd_set rfds; + fd_set wfds; + int nfds; + struct timeval tv; + int r; + char c; + + if (chdir(auto_qmail) == -1) + { log1("alert: qmail-todo: cannot start: unable to switch to home directory\n"); _exit(111); } + if (!getcontrols()) + { log1("alert: qmail-todo: cannot start: unable to read controls\n"); _exit(111); } + if (chdir("queue") == -1) + { log1("alert: qmail-todo: cannot start: unable to switch to queue directory\n"); _exit(111); } + sig_pipeignore(); + umask(077); + + fnmake_init(); + + todo_init(); + comm_init(); + + do { + r = read(fdin, &c, 1); + if ((r == -1) && (errno != error_intr)) + _exit(100); /* read failed probably qmail-send died */ + } while (r =! 1); /* we assume it is a 'S' */ + + for (;;) + { + recent = now(); + + if (flagreadasap) { flagreadasap = 0; reread(); } + if (!flagsendalive) { + /* qmail-send finaly exited, so do the same. */ + if (flagstopasap) _exit(0); + /* qmail-send died. We can not log and we can not work therefor _exit(1). */ + _exit(1); + } + + wakeup = recent + SLEEP_FOREVER; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + nfds = 1; + + todo_selprep(&nfds,&rfds,&wakeup); + comm_selprep(&nfds,&wfds,&rfds); + + if (wakeup <= recent) tv.tv_sec = 0; + else tv.tv_sec = wakeup - recent + SLEEP_FUZZ; + tv.tv_usec = 0; + + if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1) + if (errno == error_intr) + ; + else + log1("warning: qmail-todo: trouble in select\n"); + else + { + recent = now(); + + todo_do(&rfds); + comm_do(&wfds, &rfds); + } + } + /* NOTREACHED */ +} + diff -ruN qmail-1.03/README.auth qmail-1.03.patched/README.auth --- qmail-1.03/README.auth 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/README.auth 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,175 @@ +*** Warning! Cuidado! Vorsicht! *** +=================================== +*** Version 0.30 of the patch changes the arguments which must be +*** passed to qmail-smtpd. If you are upgrading from a previous +*** version of the patch, take care to ensure your invocation of +*** qmail-smtpd uses the correct arguments. Otherwise, your server +*** may run as an open relay! +=================================== +*** Warning! Cuidado! Vorsicht! *** + + +This patch adds ESMTP AUTH authentication protocol support to +qmail-1.03. It's originally based on Mrs. Brisby's smtp-auth patch +with many enhancements from Krzysztof Dabrowski . + +Beginning with version 0.30, the patch was completely rewritten to +use only djb's string functions by Eric M. Johnston . + +You can always get the newest version from: +http://members.elysium.pl/brush/qmail-smtpd-auth/ + +To use all of it's functionality you will also have to obtain and +install Krzysztof's cmd5checkpw utility available at: +http://members.elysium.pl/brush/cmd5checkpw/ + +If you need more information about SMTP-AUTH itself and the +client/server support and configuration, visit: +http://members.elysium.pl/brush/smtp-auth/ + +--- + +Detailed patch information: + +This patch adds the ESMTP AUTH option to qmail-1.03, allowing the +LOGIN, PLAIN, and CRAM-MD5 AUTH types. An appropriate checkpassword +tool is necessary to support the authentication. See +http://cr.yp.to/checkpwd.html for more information on the interface. +Note that the checkpassword tool should support all of the AUTH types +advertised by qmail-smtpd. + +As reflected in the modified qmail-smtpd(8) man page, qmail-smtpd +must be invoked with three arguments: hostname, checkprogram, and +subprogram. If these arguments are missing, qmail-smtpd will still +advertise availability of AUTH, but will fail with a permanent error +when AUTH is used. + +hostname is simply used to form the CRAM-MD5 challenge. qmail-smtpd +invokes checkprogram, feeding it the username and password, in the +case of LOGIN or PLAIN, or the username, challenge, and response, in +the case of CRAM-MD5. If the user is permitted, checkprogram invokes +subprogram, which just has to exit with a status of 0 for the user to +be authenticated. Otherwise, checkprogram exits with a non-zero +status. subprogram can usually be /usr/bin/true (or /bin/true, +depending on your flavor of OS). + +If the user is successfully authenticated, the RELAYCLIENT +environment variable is effectively set for the SMTP session, and +the TCPREMOTEINFO environment variable is set to the authenticated +username, overriding any value that tcpserver may have set. The +value of TCPREMOTEINFO is reflected in a Received header. + + +How to install it: + +Simply patch your qmail-1.03 distribution with the included patch +file and recompile & install like usual. + +The steps to do this are as follows (assuming your virgin +qmail-1.03 install is in "../qmail-1.03"): + + cp README.auth base64.c base64.h ../qmail-1.03 + patch -d ../qmail-1.03 < auth.patch + +Install qmail normally, with the exception of the new arguments +to qmail-smtpd described elsewhere in this file. + +Also obtain, unpack, compile and install the cmd5checkpw utility +(or some other checkpassword utility) and add a sample account to +/etc/poppasswd file. This file must be readable by the qmail-smtpd +user, usually qmaild. + + +How to use it: + +*** Warning: In version 0.30 the arguments have changed from +*** previous versions of qmail-smtpd-auth. Take care to make sure +*** you update your startup scripts if updating! + +If you're running qmail-smtpd from inetd, you'll want to do the +following: + +smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env tcp-env \ +/var/qmail/bin/qmail-smtpd mail.acme.com /bin/cmd5checkpw /bin/true + +Replace mail.acme.com with your hostname. The second argument to +qmail-smtpd is your checkpassword utility (preferably cmd5checkpw +or some alternative that can handle CRAM-MD5). The third argument +is the executable that the checkpassword utility execs when +authentication is successful. (Note that the location of "true" +is OS dependent: you may need /usr/bin/true.) + +Invocations using tcpserver will require analagous changes. Give +your inetd a kill -HUP or restart tcpserver and away you go. + + +Caveats: + +Please note that as authentication needs vary wildly across +installations, no effort has been made to make this patch work ``out +of the box.'' You'll have to procure or develop your own +checkpassword program. Also note that CRAM-MD5 will require you to +keep plaintext passwords. You'll probably want to disable this AUTH +type if you're just using /etc/passwd (keeping in mind that PLAIN and +LOGIN aren't quite as safe over the wire) -- just undefine AUTHCRAM +in qmail-smtpd. + +Krzysztof Dabrowski's cmd5checkpw tool used as an example in this +document supports the three AUTH types included in this patch. +It's available at http://www.elysium.pl/members/brush/cmd5checkpw/. + +This patch has been generated against the stock qmail 1.03 +distribution. The results of combining this patch with others are +unknown. + + +Features: + +This patch supports the following auth methods: LOGIN, PLAIN and +CRAM-MD5. + + +Compatibility: + +The following MUA's are confirmed to work with this patch: + +Eudora 4.2.2 - CRAM-MD5 +Eudora 5.0.2 - CRAM-MD5 +The Bat 1.39 - LOGIN & CRAM-MD5 +Outlook Express 4 - LOGIN +Outlook Express 5 - LOGIN +Outlook 2000 - LOGIN +Netscape 4.x - LOGIN & PLAIN +Netscape 4.0x - LOGIN +Pegasus Mail 3.1x - CRAM-MD5 + + +Various compatibility issues: + +Testing with Pegasus Mail 3.1 revealed that it requires the new style +(RFC recommended) greeting message. Both styles are now enabled to +maintain the highest degree of compatibility with various clients. +This fix was suggested by David Harris , +the developer of Pegasus Mail. + + +Acknowledgments: + +This patch is based on work by Krzysztof Dabrowski at +http://members.elysium.pl/brush/qmail-smtpd-auth/ and ``Mrs. Brisby'' +at http://www.nimh.org/hacks/qmail-smtpd.c which has been further +developed by Eric M. Johnston . + +--- + +THIS SOFTWARE IS IN THE PUBLIC DOMAIN, IS PROVIDED BY THE AUTHOR +``AS IS,'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -ruN qmail-1.03/README.qregex qmail-1.03.patched/README.qregex --- qmail-1.03/README.qregex 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/README.qregex 2007-09-27 03:28:05.000000000 +0200 @@ -1,4 +1,4 @@ -QREGEX (v2) 20040725 - README July 25, 2004 +QREGEX (v2) 20060423 - README April 23, 2006 A Regular Expression matching patch for qmail 1.03 and netqmail @@ -22,16 +22,17 @@ Features of qregex include: 1. Performs pattern matching on envelope senders and envelope - recipients against REs in the badmailfrom and badmailto control + recipients against REs in the badmailfrom and badrcptto control files. Two additional control files, badmailfromnorelay and - badmailtonorelay, are used for pattern matching when the + badrcpttonorelay, are used for pattern matching when the RELAYCLIENT environment variable is not set. 2. Performs pattern matching on the helo/ehlo host name. Setting the NOBADHELO environment variable prevents the host name from being compared to the patterns in the badhelo control file. -3. Matches to patterns are logged +3. Matches to patterns are logged. Setting the LOGREGEX environment + variable causes the matched regex pattern to be included in the log. 4. Matching is case insensitive. @@ -47,7 +48,7 @@ a regex interface) but if you run into problems let me know. - OpenBSD 3.x - - FreeBSD 4.x + - FreeBSD 4.x, 5.x - Mandrake Linux 9.x - SuSE Linux 8.x @@ -99,8 +100,14 @@ badmailfrom and badmailfromnorelay qmail-smtpd: badmailfrom: at -badmailto and badmailtonorelay -qmail-smtpd: badmailto: at +badrcptto and badrcpttonorelay +qmail-smtpd: badrcptto: at + +When the LOGREGEX environment variable is set, the matched pattern will +be included in the log. Log messages will have the regex pattern appended +to them. For example, a badhelo log message will look like this: + +qmail-smtpd: badhelo: at matches pattern: CONTROL FILES: @@ -108,16 +115,16 @@ qregex provides you with five control files. None of these control files is mandatory and you can use them in any combination you choose in your setup. -The "control/badmailfrom" and "control/badmailto" files contain your REs for +The "control/badmailfrom" and "control/badrcptto" files contain your REs for matching against the 'mail from' (envelope sender) and 'rcpt to' (envelope recipient) smtp commands respectively. -The "control/badmailfromnorelay" and "control/badmailtonorelay" match against +The "control/badmailfromnorelay" and "control/badrcptonorelay" match against the same commands but are read only when the RELAYCLIENT environment variable is not set. The "control/badhelo" file matches against the 'helo/ehlo' smtp command. -If you prefer you can symlink the badmailfrom and badmailto control files -(ln -s badmailfrom badmailto) and maintain fewer sets of rules. Beware +If you prefer you can symlink the badmailfrom and badrcptto control files +(ln -s badmailfrom badrcptto) and maintain fewer sets of rules. Beware this might cause problems in certain setups. Here's an example "badhelo" file. @@ -136,7 +143,7 @@ !@ ----------------------------------- - And "badmailto" (a little more interesting) + And "badrcptto" (a little more interesting) ----------------------------------- # must not contain invalid characters, brakets or multiple @'s [!%#:*^(){}] @@ -147,7 +154,7 @@ negate the action. As used above in the badmailfrom file, by negating the '@' symbol qregex will signal qmail-smtpd to deny the 'mail from' command whenever the address doesn't contain an @ symbol. When used inside a bracket expression, -the '!' character looses this special meaning. This is shown in the badmailto +the '!' character looses this special meaning. This is shown in the badrcptto example. The norelay control files follow the same rules as the other control files but @@ -156,7 +163,7 @@ hosted on your mail server. It prevents a mail client that is not allowed to relay email through your server from using one of your hosted domains as its envelope sender. -The badmailtonorelay file can be used to create email addresses that cannot +The badrcpttonorelay file can be used to create email addresses that cannot receive mail from any source not allowed to relay email through your server. This is handy for creating email addresses for use only within your own domain(s) that can't receive spam from the world at large. diff -ruN qmail-1.03/README.tap qmail-1.03.patched/README.tap --- qmail-1.03/README.tap 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/README.tap 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,28 @@ +qmail provides the ability to make a copy of each email that flows through the system. +This is done using the QUEUE_EXTRA code. See qmail FAQ #8.2 + +The qmail tap patch adds additional functionality: +1) Specify which email addresses to tap using a regex style control file. With the + regex function, you can specify full domains or individual email addresses. + +2) Specify which email address to send the emails to. + +3) Qmail does not need to be restated to when the taps control file is changed. + +The regex match is applied to both the to and from email addresses. So email +sent to or from the addresses will be copied. Matching is case insensitive. +If there are multiple matches, the first match is used. + +The queue tap patch adds a new control file: + +/var/qmail/control/taps +Contains a regex style list of addresses to tap and the email +address of where you want the copy sent to. + +Examples: +a) To tap a whole domain add a line like: +.*@domain.com:joe@example.com + + +b) To tap an individual email address add a line like: +user@domain.com:other@example.com diff -ruN qmail-1.03/sendmail.c qmail-1.03.patched/sendmail.c --- qmail-1.03/sendmail.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/sendmail.c 2007-09-27 03:30:08.000000000 +0200 @@ -19,7 +19,7 @@ _exit(100); } -char *smtpdarg[] = { "/usr/sbin/qmail-smtpd", 0 }; +char *smtpdarg[] = { "/var/qmail/bin/qmail-smtpd", 0 }; void smtpd() { if (!env_get("PROTO")) { @@ -37,7 +37,7 @@ _exit(111); } -char *qreadarg[] = { "/usr/sbin/qmail-qread", 0 }; +char *qreadarg[] = { "/var/qmail/bin/qmail-qread", 0 }; void mailq() { execv(*qreadarg,qreadarg); @@ -45,6 +45,38 @@ _exit(111); } +void do_sender(s) +const char *s; +{ + char *x; + int n; + int a; + int i; + + env_unset("QMAILNAME"); + env_unset("MAILNAME"); + env_unset("NAME"); + env_unset("QMAILHOST"); + env_unset("MAILHOST"); + + n = str_len(s); + a = str_rchr(s, '@'); + if (a == n) + { + env_put2("QMAILUSER", s); + return; + } + env_put2("QMAILHOST", s + a + 1); + + x = (char *) alloc((a + 1) * sizeof(char)); + if (!x) nomem(); + for (i = 0; i < a; i++) + x[i] = s[i]; + x[i] = 0; + env_put2("QMAILUSER", x); + alloc_free(x); +} + int flagh; char *sender; @@ -113,11 +145,12 @@ if (!qiargv) nomem(); arg = qiargv; - *arg++ = "/usr/sbin/qmail-inject"; + *arg++ = "/var/qmail/bin/qmail-inject"; *arg++ = (flagh ? "-H" : "-a"); if (sender) { *arg++ = "-f"; *arg++ = sender; + do_sender(sender); } *arg++ = "--"; for (i = 0;i < argc;++i) *arg++ = argv[i]; diff -ruN qmail-1.03/spawn.c qmail-1.03.patched/spawn.c --- qmail-1.03/spawn.c 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/spawn.c 2007-09-27 03:28:05.000000000 +0200 @@ -5,7 +5,6 @@ #include "substdio.h" #include "byte.h" #include "str.h" -#include "alloc.h" #include "stralloc.h" #include "select.h" #include "exit.h" @@ -64,7 +63,7 @@ int flagreading = 1; char outbuf[1024]; substdio ssout; -int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */ +int stage = 0; /* reading 0:delnum 1:delnum2 2:messid 3:sender 4:recip */ int flagabort = 0; /* if 1, everything except delnum is garbage */ int delnum; stralloc messid = {0}; @@ -74,6 +73,7 @@ void err(s) char *s; { char ch; ch = delnum; substdio_put(&ssout,&ch,1); + ch = delnum >> 8; substdio_put(&ssout,&ch,1); substdio_puts(&ssout,s); substdio_putflush(&ssout,"",1); } @@ -156,16 +156,19 @@ { case 0: delnum = (unsigned int) (unsigned char) ch; - messid.len = 0; stage = 1; break; + stage = 1; break; case 1: + delnum += (unsigned int) ((unsigned int) ch) << 8; + messid.len = 0; stage = 2; break; + case 2: if (!stralloc_append(&messid,&ch)) flagabort = 1; if (ch) break; - sender.len = 0; stage = 2; break; - case 2: + sender.len = 0; stage = 3; break; + case 3: if (!stralloc_append(&sender,&ch)) flagabort = 1; if (ch) break; - recip.len = 0; stage = 3; break; - case 3: + recip.len = 0; stage = 4; break; + case 4: if (!stralloc_append(&recip,&ch)) flagabort = 1; if (ch) break; docmd(); @@ -202,7 +205,8 @@ initialize(argc,argv); - ch = auto_spawn; substdio_putflush(&ssout,&ch,1); + ch = auto_spawn; substdio_put(&ssout,&ch,1); + ch = auto_spawn >> 8; substdio_putflush(&ssout,&ch,1); for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; } @@ -237,7 +241,8 @@ continue; /* read error on a readable pipe? be serious */ if (r == 0) { - ch = i; substdio_put(&ssout,&ch,1); + char ch; ch = i; substdio_put(&ssout,&ch,1); + ch = i >> 8; substdio_put(&ssout,&ch,1); report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len); substdio_put(&ssout,"",1); substdio_flush(&ssout); diff -ruN qmail-1.03/spf.c qmail-1.03.patched/spf.c --- qmail-1.03/spf.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/spf.c 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,893 @@ +#include "stralloc.h" +#include "strsalloc.h" +#include "alloc.h" +#include "ip.h" +#include "ipalloc.h" +#include "ipme.h" +#include "str.h" +#include "fmt.h" +#include "scan.h" +#include "byte.h" +#include "now.h" +#include "dns.h" +#include "case.h" +#include "spf.h" +#include "env.h" +#include "qregex.h" + +#define SPF_EXT -1 +#define SPF_SYNTAX -2 + +#define WSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\r' || (x) == '\n') +#define NXTOK(b, p, a) do { (b) = (p); \ + while((p) < (a)->len && !WSPACE((a)->s[(p)])) ++(p); \ + while((p) < (a)->len && WSPACE((a)->s[(p)])) (a)->s[(p)++] = 0; \ + } while(0) + +/* this table and macro came from wget more or less */ +/* and was in turn stolen by me from libspf as is :) */ +const static unsigned char urlchr_table[256] = +{ + 1, 1, 1, 1, 1, 1, 1, 1, /* NUL SOH STX ETX EOT ENQ ACK BEL */ + 1, 1, 1, 1, 1, 1, 1, 1, /* BS HT LF VT FF CR SO SI */ + 1, 1, 1, 1, 1, 1, 1, 1, /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + 1, 1, 1, 1, 1, 1, 1, 1, /* CAN EM SUB ESC FS GS RS US */ + 1, 0, 1, 1, 0, 1, 1, 0, /* SP ! " # $ % & ' */ + 0, 0, 0, 1, 0, 0, 0, 1, /* ( ) * + , - . / */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */ + 0, 0, 1, 1, 1, 1, 1, 1, /* 8 9 : ; < = > ? */ + 1, 0, 0, 0, 0, 0, 0, 0, /* @ A B C D E F G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */ + 0, 0, 0, 1, 1, 1, 1, 0, /* X Y Z [ \ ] ^ _ */ + 1, 0, 0, 0, 0, 0, 0, 0, /* ` a b c d e f g */ + 0, 0, 0, 0, 0, 0, 0, 0, /* h i j k l m n o */ + 0, 0, 0, 0, 0, 0, 0, 0, /* p q r s t u v w */ + 0, 0, 0, 1, 1, 1, 1, 1, /* x y z { | } ~ DEL */ + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + + +extern stralloc addr; +extern stralloc helohost; +extern char *remoteip; +extern char *local; + +extern stralloc spflocal; +extern stralloc spfguess; +extern stralloc spfexp; + +static stralloc sender_fqdn = {0}; +static stralloc explanation = {0}; +static stralloc expdomain = {0}; +static stralloc errormsg = {0}; +static char *received; + +static int recursion; +static struct ip_address ip; + +static void hdr_pass() { received = "pass (%{xr}: %{xs} designates %{i} as permitted sender)"; }; +static void hdr_softfail() { received = "softfail (%{xr}: transitioning %{xs} does not designate %{i} as permitted sender)"; }; +static void hdr_fail() { received = "fail (%{xr}: %{xs} does not designate %{i} as permitted sender)"; }; +static void hdr_plusall() { received = "fail (%{xr}: %{xs} has an improper SPF record)"; }; +static void hdr_unknown() { received = "unknown (%{xr}: domain at %{d} does not designate permitted sender hosts)"; }; +static void hdr_neutral() { received = "neutral (%{xr}: %{i} is neither permitted nor denied by %{xs})"; }; +static void hdr_none() { received = "none (%{xr}: domain at %{d} does not designate permitted sender hosts)"; }; +static void hdr_unknown_msg(e) char *e; { stralloc_copys(&errormsg, e); received = "unknown (%{xr}: %{xe})"; }; +static void hdr_ext(e) char *e; { stralloc_copys(&errormsg, e); received = "unknown %{xe} (%{xr}: %{xs} uses mechanism not recognized by this client)"; }; +static void hdr_syntax() { received = "unknown (%{xr}: parse error in %{xs})"; }; +static void hdr_error(e) char *e; { stralloc_copys(&errormsg, e); received = "error (%{xr}: error in processing during lookup of %{d}: %{xe})"; }; +static void hdr_dns() { hdr_error("DNS problem"); } + + +static int matchip(struct ip_address *net, int mask, struct ip_address *ip) +{ + int j; + int bytemask; + + for (j = 0; j < 4 && mask > 0; ++j) { + if (mask > 8) bytemask = 8; else bytemask = mask; + mask -= bytemask; + + if ((net->d[j] ^ ip->d[j]) & (0x100 - (1 << (8 - bytemask)))) + return 0; + } + return 1; +} + +static int getipmask(char *mask, int ipv6) { + unsigned long r; + int pos; + + if (!mask) return 32; + + pos = scan_ulong(mask, &r); + if (!pos || (mask[pos] && !(mask[pos] == '/' && ipv6))) return -1; + if (r > 32) return -1; + + return r; +} + +int spfget(stralloc *spf, stralloc *domain) +{ + strsalloc ssa = {0}; + int j; + int begin, pos, i; + int r = SPF_NONE; + + spf->len = 0; + + switch(dns_txt(&ssa, domain)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: hdr_dns(); return SPF_ERROR; + case DNS_HARD: return SPF_NONE; + } + + for (j = 0;j < ssa.len;++j) { + pos = 0; + + NXTOK(begin, pos, &ssa.sa[j]); + if (str_len(ssa.sa[j].s + begin) < 6) continue; + if (!byte_equal(ssa.sa[j].s + begin,6,"v=spf1")) continue; + if (ssa.sa[j].s[begin + 6]) { + /* check for subversion */ + if (ssa.sa[j].s[begin + 6] != '.') continue; + for(i = begin + 7;;++i) + if (!(ssa.sa[j].s[i] >= '0' && ssa.sa[j].s[i] <= '9')) break; + if (i == (begin + 7)) continue; + if (ssa.sa[j].s[i]) continue; + } + + if (spf->len > 0) { + spf->len = 0; + hdr_unknown_msg("Multiple SPF records returned"); + r = SPF_UNKNOWN; + break; + } + if (!stralloc_0(&ssa.sa[j])) return SPF_NOMEM; + if (!stralloc_copys(spf,ssa.sa[j].s + pos)) return SPF_NOMEM; + r = SPF_OK; + } + + for (j = 0;j < ssa.len;++j) + alloc_free(ssa.sa[j].s); + alloc_free(ssa.sa); + return r; +} + +static int spf_ptr(char *spec, char *mask); + +int spfsubst(stralloc *expand, char *spec, char *domain) +{ + static char hexdigits[] = "0123456789abcdef"; + stralloc sa = {0}; + char ch; + int digits = -1; + int urlencode = 0; + int reverse = 0; + int start = expand->len; + int i, pos; + char *split = "."; + + if (!stralloc_readyplus(&sa,0)) return 0; + + if (*spec == 'x') { i = 1; ++spec; } else i = 0; + ch = *spec++; + if (!ch) { alloc_free(sa.s); return 1; } + if (ch >= 'A' && ch <= 'Z') { ch += 32; urlencode = 1; } + if (i) ch -= 32; + while(*spec >= '0' && *spec <= '9') { + if (digits < 0) digits = 0; + if (digits >= 1000000) { digits = 10000000; continue; } + digits = (digits * 10) + (*spec - '0'); + spec++; + } + + while((*spec >= 'a' && *spec <= 'z') || (*spec >= 'A' && *spec <= 'Z')) { + if (*spec == 'r') reverse = 1; + spec++; + } + + if (*spec) split = spec; + + switch(ch) { + case 'l': + pos = byte_rchr(addr.s, addr.len, '@'); + if (pos < addr.len) { + if (!stralloc_copyb(&sa, addr.s, pos)) return 0; + } else + if (!stralloc_copys(&sa, "postmaster")) return 0; + break; + case 's': + if (!stralloc_copys(&sa, addr.s)) return 0; + break; + case 'o': + pos = byte_rchr(addr.s, addr.len, '@') + 1; + if (pos > addr.len) break; + if (!stralloc_copys(&sa, addr.s + pos)) return 0; + break; + case 'd': + if (!stralloc_copys(&sa, domain)) return 0; + break; + case 'i': + if (!stralloc_ready(&sa, IPFMT)) return 0; + sa.len = ip_fmt(sa.s, &ip); + break; + case 't': + if (!stralloc_ready(&sa, FMT_ULONG)) return 0; + sa.len = fmt_ulong(sa.s, (unsigned long)now()); + break; + case 'p': + if (!sender_fqdn.len) + spf_ptr(domain, 0); + if (sender_fqdn.len) { + if (!stralloc_copy(&sa, &sender_fqdn)) return 0; + } else + if (!stralloc_copys(&sa, "unknown")) return 0; + break; + case 'v': + if (!stralloc_copys(&sa, "in-addr")) return 0; + break; + case 'h': + if (!stralloc_copys(&sa, helohost.s)) return 0; /* FIXME: FQDN? */ + break; + case 'E': + if (errormsg.len && !stralloc_copy(&sa, &errormsg)) return 0; + break; + case 'R': + if (!stralloc_copys(&sa, local)) return 0; + break; + case 'S': + if (expdomain.len > 0) { + if (!stralloc_copys(&sa, "SPF record at ")) return 0; + if (!stralloc_cats(&sa, expdomain.s)) return 0; + } else { + if (!stralloc_copys(&sa, "local policy")) return 0; + } + break; + } + + if (reverse) { + for(pos = 0; digits; ++pos) { + pos += byte_cspn(sa.s + pos, sa.len - pos, split); + if (pos >= sa.len) break; + if (!--digits) break; + } + + for(; pos > 0; pos = i - 1) { + i = byte_rcspn(sa.s, pos, split) + 1; + if (i > pos) i = 0; + if (!stralloc_catb(expand, sa.s + i, pos - i)) return 0; + if (i > 0 && !stralloc_append(expand, ".")) return 0; + } + } else { + for(pos = sa.len; digits; --pos) { + i = byte_rcspn(sa.s, pos, split) + 1; + if (i > pos) { pos = 0; break; } + pos = i; + if (!--digits) break; + } + + if (!stralloc_catb(expand, sa.s + pos, sa.len - pos)) return 0; + if (split[0] != '.' || split[1]) + for(pos = 0; pos < expand->len; pos++) { + pos += byte_cspn(expand->s + pos, expand->len - pos, split); + if (pos < expand->len) + expand->s[pos] = '.'; + } + } + + if (urlencode) { + stralloc_copyb(&sa, expand->s + start, expand->len - start); + expand->len = start; + + for(pos = 0; pos < sa.len; ++pos) { + ch = sa.s[pos]; + if (urlchr_table[(unsigned char)ch]) { + if (!stralloc_readyplus(expand, 3)) return 0; + expand->s[expand->len++] = '%'; + expand->s[expand->len++] = hexdigits[(unsigned char)ch >> 4]; + expand->s[expand->len++] = hexdigits[(unsigned char)ch & 15]; + } else + if (!stralloc_append(expand, &ch)) return 0; + } + } + + alloc_free(sa.s); + return 1; +} + +int spfexpand(stralloc *sa, char *spec, char *domain) +{ + char *p; + char append; + int pos; + + if (!stralloc_readyplus(sa, 0)) return 0; + sa->len = 0; + + for(p = spec; *p; p++) { + append = *p; + if (*p == '%') { + p++; + switch(*p) { + case '%': break; + case '_': append = ' '; break; + case '-': if (!stralloc_cats(sa, "%20")) return 0; continue; + case '{': + pos = str_chr(p, '}'); + if (p[pos] != '}') { p--; break; } + p[pos] = 0; + if (!spfsubst(sa, p + 1, domain)) return 0; + p += pos; + continue; + default: p--; + } + } + if (!stralloc_append(sa, &append)) return 0; + } + + return 1; +} + +static int spflookup(stralloc *domain); + +static int spf_include(char *spec, char *mask) +{ + stralloc sa = {0}; + int r; + + if (!stralloc_copys(&sa, spec)) return SPF_NOMEM; + r = spflookup(&sa); + alloc_free(sa.s); + + switch(r) { + case SPF_NONE: + hdr_unknown(); + r = SPF_UNKNOWN; + break; + case SPF_SYNTAX: + r = SPF_UNKNOWN; + break; + case SPF_NEUTRAL: + case SPF_SOFTFAIL: + case SPF_FAIL: + r = SPF_NONE; + break; + } + + return r; +} + +static int spf_a(char *spec, char *mask) +{ + stralloc sa = {0}; + ipalloc ia = {0}; + int ipmask = getipmask(mask, 1); + int r; + int j; + + if (ipmask < 0) return SPF_SYNTAX; + + if (!stralloc_copys(&sa, spec)) return SPF_NOMEM; + if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM; + + switch(dns_ip(&ia, &sa)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break; + case DNS_HARD: r = SPF_NONE; break; + default: + r = SPF_NONE; + for(j = 0; j < ia.len; ++j) + if (matchip(&ia.ix[j].ip, ipmask, &ip)) { + r = SPF_OK; + break; + } + } + + alloc_free(sa.s); + alloc_free(ia.ix); + return r; +} + +static int spf_mx(char *spec, char *mask) +{ + stralloc sa = {0}; + ipalloc ia = {0}; + int ipmask = getipmask(mask, 1); + int random = now() + (getpid() << 16); + int r; + int j; + + if (ipmask < 0) return SPF_SYNTAX; + + if (!stralloc_copys(&sa, spec)) return SPF_NOMEM; + if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM; + + switch(dns_mxip(&ia, &sa, random)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break; + case DNS_HARD: r = SPF_NONE; break; + default: + r = SPF_NONE; + for(j = 0; j < ia.len; ++j) + if (matchip(&ia.ix[j].ip, ipmask, &ip)) { + r = SPF_OK; + break; + } + } + + alloc_free(sa.s); + alloc_free(ia.ix); + return r; +} + +static int spf_ptr(char *spec, char *mask) +{ + strsalloc ssa = {0}; + ipalloc ia = {0}; + int len = str_len(spec); + int r; + int j, k; + int pos; + + /* we didn't find host with the matching ip before */ + if (sender_fqdn.len == 7 && str_equal(sender_fqdn.s, "unknown")) + return SPF_NONE; + + /* the hostname found will probably be the same as before */ + while (sender_fqdn.len) { + pos = sender_fqdn.len - len; + if (pos < 0) break; + if (pos > 0 && sender_fqdn.s[pos - 1] != '.') break; + if (case_diffb(sender_fqdn.s + pos, len, spec)) break; + + return SPF_OK; + } + + /* ok, either it's the first test or it's a very weird setup */ + + if (!stralloc_readyplus(&ssa, 0)) return SPF_NOMEM; + if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM; + + switch(dns_ptr(&ssa, &ip)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break; + case DNS_HARD: r = SPF_NONE; break; + default: + r = SPF_NONE; + for(j = 0; j < ssa.len; ++j) { + switch(dns_ip(&ia, &ssa.sa[j])) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break; + case DNS_HARD: break; + default: + for(k = 0; k < ia.len; ++k) + if (matchip(&ia.ix[k].ip, 32, &ip)) { + if (!sender_fqdn.len) + if (!stralloc_copy(&sender_fqdn, &ssa.sa[j])) return SPF_NOMEM; + + pos = ssa.sa[j].len - len; + if (pos < 0) continue; + if (pos > 0 && ssa.sa[j].s[pos - 1] != '.') continue; + if (case_diffb(ssa.sa[j].s + pos, len, spec)) continue; + + stralloc_copy(&sender_fqdn, &ssa.sa[j]); + r = SPF_OK; + break; + } + } + + if (r == SPF_ERROR) break; + } + } + + for(j = 0;j < ssa.len;++j) + alloc_free(ssa.sa[j].s); + + alloc_free(ssa.sa); + alloc_free(ia.ix); + + if (!sender_fqdn.len) + if (!stralloc_copys(&sender_fqdn, "unknown")) return SPF_NOMEM; + + return r; +} + +static int spf_ip(char *spec, char *mask) +{ + struct ip_address net; + int ipmask = getipmask(mask, 0); + + if (ipmask < 0) return SPF_SYNTAX; + if (!ip_scan(spec, &net)) return SPF_SYNTAX; + + if (matchip(&net, ipmask, &ip)) return SPF_OK; + + return SPF_NONE; +} + +static int spf_exists(char *spec, char *mask) +{ + stralloc sa = {0}; + ipalloc ia = {0}; + int r; + + if (!stralloc_copys(&sa, spec)) return SPF_NOMEM; + if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM; + + switch(dns_ip(&ia, &sa)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break; + case DNS_HARD: r = SPF_NONE; break; + default: r = SPF_OK; + } + + alloc_free(sa.s); + alloc_free(ia.ix); + return r; +} + +static struct mechanisms { + char *mechanism; + int (*func)(char *spec, char *mask); + unsigned int takes_spec : 1; + unsigned int takes_mask : 1; + unsigned int expands : 1; + unsigned int filldomain : 1; + int defresult : 4; +} mechanisms[] = { + { "all", 0, 0,0,0,0,SPF_OK } +, { "include", spf_include,1,0,1,0,0 } +, { "a", spf_a, 1,1,1,1,0 } +, { "mx", spf_mx, 1,1,1,1,0 } +, { "ptr", spf_ptr, 1,0,1,1,0 } +, { "ip4", spf_ip, 1,1,0,0,0 } +, { "ip6", 0, 1,1,0,0,SPF_NONE } +, { "exists", spf_exists, 1,0,1,0,0 } +, { "extension",0, 1,1,0,0,SPF_EXT } +, { 0, 0, 1,1,0,0,SPF_EXT } +}; + +static int spfmech(char *mechanism, char *spec, char *mask, char *domain) +{ + struct mechanisms *mech; + stralloc sa = {0}; + int r; + int pos; + + for(mech = mechanisms; mech->mechanism; mech++) + if (str_equal(mech->mechanism, mechanism)) break; + + if (mech->takes_spec && !spec && mech->filldomain) spec = domain; + if (!mech->takes_spec != !spec) return SPF_SYNTAX; + if (!mech->takes_mask && mask) return SPF_SYNTAX; + if (!mech->func) return mech->defresult; + + if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM; + if (mech->expands && spec != domain) { + if (!spfexpand(&sa, spec, domain)) return SPF_NOMEM; + for (pos = 0; (sa.len - pos) > 255;) { + pos += byte_chr(sa.s + pos, sa.len - pos, '.'); + if (pos < sa.len) pos++; + } + sa.len -= pos; + if (pos > 0) byte_copy(sa.s, sa.len, sa.s + pos); + stralloc_0(&sa); + spec = sa.s; + } + + r = mech->func(spec, mask); + + alloc_free(sa.s); + return r; +} + +static struct default_aliases { + char *alias; + int defret; +} default_aliases[] = { + { "allow", SPF_OK } +, { "pass", SPF_OK } +, { "deny", SPF_FAIL } +, { "softdeny",SPF_SOFTFAIL } +, { "fail", SPF_FAIL } +, { "softfail",SPF_SOFTFAIL } +, { "unknown", SPF_NEUTRAL } +, { 0, SPF_UNKNOWN } +}; + +static int spflookup(stralloc *domain) +{ + stralloc spf = {0}; + stralloc sa = {0}; + struct default_aliases *da; + int main = !recursion; + int local_pos = -1; + int r, q; + int begin, pos; + int i; + int prefix; + int done; + int guessing = 0; + char *p; + char *x; + + if (!stralloc_readyplus(&spf, 0)) return SPF_NOMEM; + if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM; + + /* fallthrough result */ + if (main) hdr_none(); + +redirect: + if (++recursion > 20) { + alloc_free(spf.s); + alloc_free(sa.s); + hdr_unknown_msg("Maximum nesting level exceeded, possible loop"); + return SPF_SYNTAX; + } + + if (!stralloc_0(domain)) return SPF_NOMEM; + if (!stralloc_copy(&expdomain, domain)) return SPF_NOMEM; + + r = spfget(&spf, domain); + if (r == SPF_NONE) { + if (!main) { alloc_free(spf.s); return r; } + + if (spfguess.len) { + /* try to guess */ + guessing = 1; + if (!stralloc_copys(&spf, spfguess.s)) return SPF_NOMEM; + if (!stralloc_append(&spf, " ")) return SPF_NOMEM; + } else + spf.len = 0; + + /* append local rulest */ + if (spflocal.len) { + local_pos = spf.len; + if (!stralloc_cats(&spf, spflocal.s)) return SPF_NOMEM; + } + if (!stralloc_0(&spf)) return SPF_NOMEM; + + expdomain.len = 0; + } else if (r == SPF_OK) { + if (!stralloc_0(&spf)) return SPF_NOMEM; + if (main) hdr_neutral(); + r = SPF_NEUTRAL; + + /* try to add local rules before fail all mechs */ + if (main && spflocal.len) { + pos = 0; + p = (char *) 0; + while(pos < spf.len) { + NXTOK(begin, pos, &spf); + if (!spf.s[begin]) continue; + + if (p && spf.s[begin] != *p) p = (char *) 0; + if (!p && (spf.s[begin] == '-' || spf.s[begin] == '~' || + spf.s[begin] == '?')) p = &spf.s[begin]; + + if (p && p > spf.s && str_equal(spf.s + begin + 1, "all")) { + /* ok, we can insert the local rules at p */ + local_pos = p - spf.s; + + stralloc_readyplus(&spf, spflocal.len); + p = spf.s + local_pos; + byte_copyr(p + spflocal.len, spf.len - local_pos, p); + byte_copy(p, spflocal.len, spflocal.s); + spf.len += spflocal.len; + + pos += spflocal.len; + break; + } + } + + if (pos >= spf.len) pos = spf.len - 1; + for(i = 0; i < pos; i++) + if (!spf.s[i]) spf.s[i] = ' '; + } + } else { + alloc_free(spf.s); + return r; + } + + /* if "+all" is seen, reject the message */ + x = env_get("SPF_BLOCK_PLUS_ALL"); + if (x) + if (!str_equal(x,"0")) + if (matchregex(spf.s,"\\+all")) { + alloc_free(spf.s); + alloc_free(sa.s); + hdr_plusall(); + return SPF_FAIL; + } + + pos = 0; + done = 0; + while(pos < spf.len) { + NXTOK(begin, pos, &spf); + if (!spf.s[begin]) continue; + + /* in local ruleset? */ + if (!done && local_pos >= 0 && begin >= local_pos) { + if (begin < (local_pos + spflocal.len)) + expdomain.len = 0; + else + if (!stralloc_copy(&expdomain, domain)) + return SPF_NOMEM; + } + + for (p = spf.s + begin;*p;++p) + if (*p == ':' || *p == '/' || *p == '=') break; + + if (*p == '=') { + *p++ = 0; + + /* modifiers are simply handled here */ + if (str_equal(spf.s + begin, "redirect")) { + if (done) continue; + + if (!spfexpand(&sa, p, domain->s)) return SPF_NOMEM; + stralloc_copy(domain, &sa); + + hdr_unknown(); + r = SPF_UNKNOWN; + + goto redirect; + } else if (str_equal(spf.s + begin, "default")) { + if (done) continue; + + for(da = default_aliases; da->alias; ++da) + if (str_equal(da->alias, p)) break; + + r = da->defret; + } else if (str_equal(spf.s + begin, "exp")) { + strsalloc ssa = {0}; + + if (!main) continue; + + if (!stralloc_copys(&sa, p)) return SPF_NOMEM; + switch(dns_txt(&ssa, &sa)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: continue; /* FIXME... */ + case DNS_HARD: continue; + } + + explanation.len = 0; + for(i = 0; i < ssa.len; i++) { + if (!stralloc_cat(&explanation, &ssa.sa[i])) return SPF_NOMEM; + if (i < (ssa.len - 1)) + if (!stralloc_append(&explanation, "\n")) return SPF_NOMEM; + + alloc_free(ssa.sa[i].s); + } + if (!stralloc_0(&explanation)) return SPF_NOMEM; + } /* and unknown modifiers are ignored */ + } else if (!done) { + if (!stralloc_copys(&sa, spf.s + begin)) return SPF_NOMEM; + if (!stralloc_0(&sa)) return SPF_NOMEM; + + switch(spf.s[begin]) { + case '-': begin++; prefix = SPF_FAIL; break; + case '~': begin++; prefix = SPF_SOFTFAIL; break; + case '+': begin++; prefix = SPF_OK; break; + case '?': begin++; prefix = SPF_NEUTRAL; break; + default: prefix = SPF_OK; + } + + if (*p == '/') { + *p++ = 0; + q = spfmech(spf.s + begin, 0, p, domain->s); + } else { + if (*p) *p++ = 0; + i = str_chr(p, '/'); + if (p[i] == '/') { + p[i++] = 0; + q = spfmech(spf.s + begin, p, p + i, domain->s); + } else if (i > 0) + q = spfmech(spf.s + begin, p, 0, domain->s); + else + q = spfmech(spf.s + begin, 0, 0, domain->s); + } + + if (q == SPF_OK) q = prefix; + + switch(q) { + case SPF_OK: hdr_pass(); break; + case SPF_NEUTRAL: hdr_neutral(); break; + case SPF_SYNTAX: hdr_syntax(); break; + case SPF_SOFTFAIL: hdr_softfail(); break; + case SPF_FAIL: hdr_fail(); break; + case SPF_EXT: hdr_ext(sa.s); break; + case SPF_ERROR: + if (!guessing) + break; + if (local_pos >= 0 && begin >= local_pos) + break; + hdr_none(); + q = SPF_NONE; + break; + case SPF_NONE: continue; + } + + r = q; + done = 1; /* we're done, no more mechanisms */ + } + } + + /* we fell through, no local rule applied */ + if (!done && !stralloc_copy(&expdomain, domain)) return SPF_NOMEM; + + alloc_free(spf.s); + alloc_free(sa.s); + return r; +} + +int spfcheck() +{ + stralloc domain = {0}; + int pos; + int r; + + pos = byte_rchr(addr.s, addr.len, '@') + 1; + if (pos < addr.len) { + if (!stralloc_copys(&domain, addr.s + pos)) return SPF_NOMEM; + } else { + pos = str_rchr(helohost.s, '@'); + if (helohost.s[pos]) { + if (!stralloc_copys(&domain, helohost.s + pos + 1)) return SPF_NOMEM; + } else + if (!stralloc_copys(&domain, helohost.s)) return SPF_NOMEM; + } + if (!stralloc_copys(&explanation, spfexp.s)) return SPF_NOMEM; + if (!stralloc_0(&explanation)) return SPF_NOMEM; + recursion = 0; + + if (!remoteip || !ip_scan(remoteip, &ip)) { + hdr_unknown_msg("No IP address in conversation"); + return SPF_UNKNOWN; + } + + if (!stralloc_readyplus(&expdomain, 0)) return SPF_NOMEM; + if (!stralloc_readyplus(&errormsg, 0)) return SPF_NOMEM; + expdomain.len = 0; + errormsg.len = 0; + sender_fqdn.len = 0; + received = (char *) 0; + + if ((ip.d[0] == 127 && ip.d[1] == 0 && ip.d[2] == 0 && ip.d[3] == 1) || ipme_is(&ip)) + { hdr_pass(); r = SPF_OK; } + else + r = spflookup(&domain); + + if (r < 0) r = SPF_UNKNOWN; + + alloc_free(domain.s); + return r; +} + +int spfexplanation(sa) +stralloc *sa; +{ + return spfexpand(sa, explanation.s, expdomain.s); +} + +int spfinfo(sa) +stralloc *sa; +{ + stralloc tmp = {0}; + if (!stralloc_copys(&tmp, received)) return 0; + if (!stralloc_0(&tmp)) return 0; + if (!spfexpand(sa, tmp.s, expdomain.s)) return 0; + alloc_free(tmp.s); + return 1; +} diff -ruN qmail-1.03/spf.h qmail-1.03.patched/spf.h --- qmail-1.03/spf.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/spf.h 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,20 @@ +#ifndef SPF_H +#define SPF_H + +#define SPF_OK 0 +#define SPF_NONE 1 +#define SPF_UNKNOWN 2 +#define SPF_NEUTRAL 3 +#define SPF_SOFTFAIL 4 +#define SPF_FAIL 5 +#define SPF_ERROR 6 +#define SPF_NOMEM 7 + +#define SPF_DEFEXP "See http://spf.pobox.com/" \ + "why.html?sender=%{S}&ip=%{I}&receiver=%{xR}" + +extern int spfcheck(); +extern int spfexplanation(); +extern int spfinfo(); + +#endif diff -ruN qmail-1.03/spfquery.c qmail-1.03.patched/spfquery.c --- qmail-1.03/spfquery.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/spfquery.c 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,84 @@ +#include "substdio.h" +#include "subfd.h" +#include "stralloc.h" +#include "alloc.h" +#include "spf.h" +#include "exit.h" + +void die(e,s) int e; char *s; { substdio_putsflush(subfderr,s); _exit(e); } +void die_usage() { die(100,"fatal: invalid usage\nusage: spfquery [] []\n"); } +void die_nomem() { die(111,"fatal: out of memory\n"); } + +stralloc addr = {0}; +stralloc helohost = {0}; +char *remoteip; +char *local; + +stralloc spflocal = {0}; +stralloc spfguess = {0}; +stralloc spfexp = {0}; + +void main(argc,argv) +int argc; +char **argv; +{ + stralloc sa = {0}; + int r; + + if (argc < 4) die_usage(); + + remoteip = (char *)strdup(argv[1]); + local = "localhost"; + + if (!stralloc_copys(&helohost, argv[2])) die_nomem(); + if (!stralloc_0(&helohost)) die_nomem(); + + if (!stralloc_copys(&addr, argv[3])) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + + if (argc > 4) { + if (!stralloc_copys(&spflocal, argv[4])) die_nomem(); + if (spflocal.len && !stralloc_0(&spflocal)) die_nomem(); + } + + if (argc > 5) { + if (!stralloc_copys(&spfguess, argv[5])) die_nomem(); + if (spfguess.len && !stralloc_0(&spfguess)) die_nomem(); + } + + if (argc > 6) { + if (!stralloc_copys(&spfexp, argv[6])) die_nomem(); + } else + if (!stralloc_copys(&spfexp, SPF_DEFEXP)) die_nomem(); + if (spfexp.len && !stralloc_0(&spfexp)) die_nomem(); + + dns_init(0); + r = spfcheck(); + if (r == SPF_NOMEM) die_nomem(); + + substdio_puts(subfdout,"result="); + switch(r) { + case SPF_OK: substdio_puts(subfdout,"pass"); break; + case SPF_NONE: substdio_puts(subfdout,"none"); break; + case SPF_UNKNOWN: substdio_puts(subfdout,"unknown"); break; + case SPF_NEUTRAL: substdio_puts(subfdout,"neutral"); break; + case SPF_SOFTFAIL: substdio_puts(subfdout,"softfail"); break; + case SPF_FAIL: substdio_puts(subfdout,"fail"); break; + case SPF_ERROR: substdio_puts(subfdout,"error"); break; + } + + if (r == SPF_FAIL) { + substdio_puts(subfdout,": "); + if (!spfexplanation(&sa)) die_nomem(); + substdio_put(subfdout,sa.s,sa.len); + } + + substdio_putsflush(subfdout,"\n"); + + substdio_puts(subfdout,"Received-SPF: "); + if (!spfinfo(&sa)) die_nomem(); + substdio_put(subfdout,sa.s,sa.len); + substdio_putsflush(subfdout,"\n"); + + _exit(0); +} diff -ruN qmail-1.03/str_cpyb.c qmail-1.03.patched/str_cpyb.c --- qmail-1.03/str_cpyb.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/str_cpyb.c 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,18 @@ +#include "str.h" + +unsigned int str_copyb(s,t,max) +register char *s; +register char *t; +unsigned int max; +{ + register int len; + + len = 0; + while (max-- > 0) { + if (!(*s = *t)) return len; ++s; ++t; ++len; + if (!(*s = *t)) return len; ++s; ++t; ++len; + if (!(*s = *t)) return len; ++s; ++t; ++len; + if (!(*s = *t)) return len; ++s; ++t; ++len; + } + return len; +} diff -ruN qmail-1.03/strerr_die.c qmail-1.03.patched/strerr_die.c --- qmail-1.03/strerr_die.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/strerr_die.c 2007-09-27 03:28:05.000000000 +0200 @@ -3,8 +3,8 @@ #include "exit.h" #include "strerr.h" -void strerr_warn(x1,x2,x3,x4,x5,x6,se) -char *x1; char *x2; char *x3; char *x4; char *x5; char *x6; +void strerr_warn(x1,x2,x3,x4,x5,x6,x7,x8,x9,se) +char *x1; char *x2; char *x3; char *x4; char *x5; char *x6; char *x7; char *x8; char *x9; struct strerr *se; { strerr_sysinit(); @@ -15,6 +15,9 @@ if (x4) substdio_puts(subfderr,x4); if (x5) substdio_puts(subfderr,x5); if (x6) substdio_puts(subfderr,x6); + if (x7) substdio_puts(subfderr,x7); + if (x8) substdio_puts(subfderr,x8); + if (x9) substdio_puts(subfderr,x9); while(se) { if (se->x) substdio_puts(subfderr,se->x); @@ -27,11 +30,11 @@ substdio_flush(subfderr); } -void strerr_die(e,x1,x2,x3,x4,x5,x6,se) +void strerr_die(e,x1,x2,x3,x4,x5,x6,x7,x8,x9,se) int e; -char *x1; char *x2; char *x3; char *x4; char *x5; char *x6; +char *x1; char *x2; char *x3; char *x4; char *x5; char *x6; char *x7; char *x8; char *x9; struct strerr *se; { - strerr_warn(x1,x2,x3,x4,x5,x6,se); + strerr_warn(x1,x2,x3,x4,x5,x6,x7,x8,x9,se); _exit(e); } diff -ruN qmail-1.03/strerr.h qmail-1.03.patched/strerr.h --- qmail-1.03/strerr.h 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/strerr.h 2007-09-27 03:28:05.000000000 +0200 @@ -25,56 +25,80 @@ #define STRERR_SYS3(r,se,a,b,c) \ { se.who = &strerr_sys; se.x = a; se.y = b; se.z = c; return r; } +#define strerr_warn9(x1,x2,x3,x4,x5,x6,x7,x8,x9,se) \ +strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),(struct strerr *) (se)) +#define strerr_warn8(x1,x2,x3,x4,x5,x6,x7,x8,se) \ +strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(char *) 0,(struct strerr *) (se)) +#define strerr_warn7(x1,x2,x3,x4,x5,x6,x7,se) \ +strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(x7),(char *) 0,(char *) 0,(struct strerr *) (se)) #define strerr_warn6(x1,x2,x3,x4,x5,x6,se) \ -strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se)) +strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) #define strerr_warn5(x1,x2,x3,x4,x5,se) \ -strerr_warn((x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se)) +strerr_warn((x1),(x2),(x3),(x4),(x5),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) #define strerr_warn4(x1,x2,x3,x4,se) \ -strerr_warn((x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se)) +strerr_warn((x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) #define strerr_warn3(x1,x2,x3,se) \ -strerr_warn((x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) +strerr_warn((x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) #define strerr_warn2(x1,x2,se) \ -strerr_warn((x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) +strerr_warn((x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) #define strerr_warn1(x1,se) \ -strerr_warn((x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) +strerr_warn((x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) +#define strerr_die9(e,x1,x2,x3,x4,x5,x6,x7,x8,x9,se) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),(struct strerr *) (se)) +#define strerr_die8(e,x1,x2,x3,x4,x5,x6,x7,x8,se) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(char *) 0,(struct strerr *) (se)) +#define strerr_die7(e,x1,x2,x3,x4,x5,x6,x7,se) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(char *) 0,(char *) 0,(struct strerr *) (se)) #define strerr_die6(e,x1,x2,x3,x4,x5,x6,se) \ -strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se)) +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) #define strerr_die5(e,x1,x2,x3,x4,x5,se) \ -strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se)) +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) #define strerr_die4(e,x1,x2,x3,x4,se) \ -strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se)) +strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) #define strerr_die3(e,x1,x2,x3,se) \ -strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) +strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) #define strerr_die2(e,x1,x2,se) \ -strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) +strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) #define strerr_die1(e,x1,se) \ -strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) +strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se)) +#define strerr_die9sys(e,x1,x2,x3,x4,x5,x6,x7,x8,x9,se) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),&strerr_sys) +#define strerr_die8sys(e,x1,x2,x3,x4,x5,x6,x7,x8,se) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(char *) 0,&strerr_sys) +#define strerr_die7sys(e,x1,x2,x3,x4,x5,x6,x7,se) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(char *) 0,(char *) 0,&strerr_sys) #define strerr_die6sys(e,x1,x2,x3,x4,x5,x6) \ -strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),&strerr_sys) +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(char *) 0,(char *) 0,(char *) 0,&strerr_sys) #define strerr_die5sys(e,x1,x2,x3,x4,x5) \ -strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,&strerr_sys) +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys) #define strerr_die4sys(e,x1,x2,x3,x4) \ -strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,&strerr_sys) +strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys) #define strerr_die3sys(e,x1,x2,x3) \ -strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,&strerr_sys) +strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys) #define strerr_die2sys(e,x1,x2) \ -strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys) +strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys) #define strerr_die1sys(e,x1) \ -strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys) +strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys) +#define strerr_die9x(e,x1,x2,x3,x4,x5,x6,x7,x8,x9,se) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),(struct strerr *) 0) +#define strerr_die8x(e,x1,x2,x3,x4,x5,x6,x7,x8,se) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(char *) 0,(struct strerr *) 0) +#define strerr_die7x(e,x1,x2,x3,x4,x5,x6,x7,se) \ +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(char *) 0,(char *) 0,(struct strerr *) 0) #define strerr_die6x(e,x1,x2,x3,x4,x5,x6) \ -strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) 0) +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0) #define strerr_die5x(e,x1,x2,x3,x4,x5) \ -strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) 0) +strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0) #define strerr_die4x(e,x1,x2,x3,x4) \ -strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) 0) +strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0) #define strerr_die3x(e,x1,x2,x3) \ -strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0) +strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0) #define strerr_die2x(e,x1,x2) \ -strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0) +strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0) #define strerr_die1x(e,x1) \ -strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0) +strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0) #endif diff -ruN qmail-1.03/str.h qmail-1.03.patched/str.h --- qmail-1.03/str.h 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/str.h 2007-09-27 03:28:05.000000000 +0200 @@ -2,6 +2,7 @@ #define STR_H extern unsigned int str_copy(); +extern unsigned int str_copyb(); extern int str_diff(); extern int str_diffn(); extern unsigned int str_len(); diff -ruN qmail-1.03/strsalloc.c qmail-1.03.patched/strsalloc.c --- qmail-1.03/strsalloc.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/strsalloc.c 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,7 @@ +#include "alloc.h" +#include "gen_allocdefs.h" +#include "stralloc.h" +#include "strsalloc.h" + +GEN_ALLOC_readyplus(strsalloc,stralloc,sa,len,a,i,n,x,10,strsalloc_readyplus) +GEN_ALLOC_append(strsalloc,stralloc,sa,len,a,i,n,x,10,strsalloc_readyplus,strsalloc_append) diff -ruN qmail-1.03/strsalloc.h qmail-1.03.patched/strsalloc.h --- qmail-1.03/strsalloc.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03.patched/strsalloc.h 2007-09-27 03:28:05.000000000 +0200 @@ -0,0 +1,12 @@ +#ifndef STRSALLOC_H +#define STRSALLOC_H + +#include "stralloc.h" + +#include "gen_alloc.h" + +GEN_ALLOC_typedef(strsalloc,stralloc,sa,len,a) +extern int strsalloc_readyplus(); +extern int strsalloc_append(); + +#endif diff -ruN qmail-1.03/TARGETS qmail-1.03.patched/TARGETS --- qmail-1.03/TARGETS 2007-09-27 04:20:18.000000000 +0200 +++ qmail-1.03.patched/TARGETS 2007-09-27 03:28:05.000000000 +0200 @@ -100,11 +100,14 @@ str_diff.o str_diffn.o str_cpy.o +str_cpyb.o str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o +byte_cspn.o +byte_rcspn.o byte_diff.o byte_copy.o byte_cr.o @@ -171,8 +174,10 @@ timeoutconn.o tcpto.o dns.o +spf.o ip.o ipalloc.o +strsalloc.o hassalen.h ipme.o ndelay.o @@ -212,6 +217,9 @@ headerbody.o hfield.o token822.o +spf.o +spfquery.o +spfquery qmail-inject predate.o predate @@ -250,6 +258,7 @@ qmail-qmtpd.o rcpthosts.o qmail-qmtpd +base64.o qmail-smtpd.o qmail-smtpd qregex.o @@ -271,6 +280,8 @@ dnsip dnsmxip.o dnsmxip +dnstxt.o +dnstxt dnsfq.o dnsfq hostname.o @@ -386,3 +397,5 @@ man setup check +qmail-todo.o +qmail-todo diff -ruN qmail-1.03/tcp-env.c qmail-1.03.patched/tcp-env.c --- qmail-1.03/tcp-env.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/tcp-env.c 2007-09-27 03:28:05.000000000 +0200 @@ -10,6 +10,7 @@ #include "scan.h" #include "subgetopt.h" #include "ip.h" +#include "strsalloc.h" #include "dns.h" #include "byte.h" #include "remoteinfo.h" @@ -34,6 +35,7 @@ int argc; char *argv[]; { + strsalloc ssa = {0}; int dummy; char *proto; int opt; @@ -74,12 +76,13 @@ temp[ip_fmt(temp,&iplocal)] = 0; if (!env_put2("TCPLOCALIP",temp)) die(); - switch(dns_ptr(&localname,&iplocal)) + switch(dns_ptr(&ssa,&iplocal)) { case DNS_MEM: die(); case DNS_SOFT: if (!stralloc_copys(&localname,"softdnserror")) die(); case 0: + if (!stralloc_copy(&localname,&ssa.sa[0])) die(); if (!stralloc_0(&localname)) die(); case_lowers(localname.s); if (!env_put2("TCPLOCALHOST",localname.s)) die(); @@ -99,12 +102,13 @@ temp[ip_fmt(temp,&ipremote)] = 0; if (!env_put2("TCPREMOTEIP",temp)) die(); - switch(dns_ptr(&remotename,&ipremote)) + switch(dns_ptr(&ssa,&ipremote)) { case DNS_MEM: die(); case DNS_SOFT: if (!stralloc_copys(&remotename,"softdnserror")) die(); case 0: + if (!stralloc_copy(&remotename,&ssa.sa[0])) die(); if (!stralloc_0(&remotename)) die(); case_lowers(remotename.s); if (!env_put2("TCPREMOTEHOST",remotename.s)) die(); diff -ruN qmail-1.03/timeoutconn.c qmail-1.03.patched/timeoutconn.c --- qmail-1.03/timeoutconn.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03.patched/timeoutconn.c 2007-09-27 03:28:05.000000000 +0200 @@ -10,6 +10,54 @@ #include "byte.h" #include "timeoutconn.h" +#define BIND_SOCKET 1 /* 0 to ignore bind fail, 1 to tempfail and requeue */ +#ifdef BIND_SOCKET +#include "control.h" +#include "constmap.h" +#include "stralloc.h" + +int bind_socket(s,ip) +int s; +struct ip_address *ip; +{ + struct sockaddr_in salocal; + struct ip_address iplocal; + char *ipstr, ipstring[IPFMT+1]; + int iplen; + stralloc routes = {0}; + struct constmap bindroutes; + char *bindroute = (char *)0; + + /* Right, do we actually have any bind routes? */ + switch(control_readfile(&routes,"control/bindroutes",0)) + { + case 0: return 0; /* no file, no bind to worry about */ + case -1: return -2; /* buggered up somewhere, urgh! */ + case 1: if (!constmap_init(&bindroutes,routes.s,routes.len,1)) return -3; + } + + ipstring[0] = '.'; /* "cheating", but makes the loop check easier below! */ + ipstr = ipstring+1; + iplen = ip_fmt(ipstr,ip); /* Well, Dan seems to trust its output! */ + + /* check d.d.d.d, d.d.d., d.d., d., none */ + bindroute = constmap(&bindroutes,ipstr,iplen); + if (!bindroute) while (iplen--) /* no worries - the lost char must be 0-9 */ + if (ipstring[iplen] == '.') + if (bindroute = constmap(&bindroutes,ipstr,iplen)) break; + if (!bindroute || !*bindroute) return 0; /* no bind required */ + if (!ip_scan(bindroute,&iplocal)) return -4; /* wasn't an ip returned */ + + byte_zero(&salocal,sizeof(salocal)); + salocal.sin_family = AF_INET; + byte_copy(&salocal.sin_addr,4,&iplocal); + + if (bind(s, (struct sockaddr *)&salocal,sizeof(salocal))) return BIND_SOCKET; + return 0; +} + +#endif + int timeoutconn(s,ip,port,timeout) int s; struct ip_address *ip; @@ -31,6 +79,9 @@ if (ndelay_on(s) == -1) return -1; /* XXX: could bind s */ +#ifdef BIND_SOCKET + if (ch = bind_socket(s,ip)) return ch; +#endif if (connect(s,(struct sockaddr *) &sin,sizeof(sin)) == 0) { ndelay_off(s);