After applying the patch please look at the control.9 and qmail-smtpd.8 man page. diff -Nur qmail-1.03.orig/byte_cspn.c qmail-1.03/byte_cspn.c --- qmail-1.03.orig/byte_cspn.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/byte_cspn.c 2004-01-29 13:48:03.000000000 +0100 @@ -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 -Nur qmail-1.03.orig/byte.h qmail-1.03/byte.h --- qmail-1.03.orig/byte.h 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/byte.h 2004-01-29 13:46:57.000000000 +0100 @@ -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 -Nur qmail-1.03.orig/byte_rcspn.c qmail-1.03/byte_rcspn.c --- qmail-1.03.orig/byte_rcspn.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/byte_rcspn.c 2004-01-29 13:47:52.000000000 +0100 @@ -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 -Nur qmail-1.03.orig/dns.c qmail-1.03/dns.c --- qmail-1.03.orig/dns.c 2004-01-29 03:48:43.000000000 +0100 +++ qmail-1.03/dns.c 2004-01-31 22:45:53.000000000 +0100 @@ -9,6 +9,7 @@ extern int res_search(); #include "ip.h" #include "ipalloc.h" +#include "strsalloc.h" #include "fmt.h" #include "alloc.h" #include "str.h" @@ -16,6 +17,8 @@ #include "dns.h" #include "case.h" +#define MAXTXT 256 + static unsigned short getshort(c) unsigned char *c; { unsigned short u; u = c[0]; return (u << 8) + c[1]; } @@ -29,6 +32,7 @@ static int numanswers; static char name[MAXDNAME]; static struct ip_address ip; +static char txt[MAXTXT]; unsigned short pref; static stralloc glue = {0}; @@ -194,6 +198,38 @@ 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) { + str_copyb(txt, responsepos, rrdlen); + responsepos += rrdlen; + return 1; + } + + responsepos += rrdlen; + return 0; +} + void dns_init(flagsearch) int flagsearch; { @@ -252,15 +288,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 +310,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; @@ -413,3 +474,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_copyb(&sa,txt+1,(unsigned char)txt[0])) 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 -Nur qmail-1.03.orig/dnsfq.c qmail-1.03/dnsfq.c --- qmail-1.03.orig/dnsfq.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/dnsfq.c 2004-01-29 02:36:34.000000000 +0100 @@ -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 -Nur qmail-1.03.orig/dns.h qmail-1.03/dns.h --- qmail-1.03.orig/dns.h 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/dns.h 2004-01-28 17:24:01.000000000 +0100 @@ -10,5 +10,6 @@ int dns_mxip(); int dns_ip(); int dns_ptr(); +int dns_txt(); #endif diff -Nur qmail-1.03.orig/dnsptr.c qmail-1.03/dnsptr.c --- qmail-1.03.orig/dnsptr.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/dnsptr.c 2004-01-29 02:39:09.000000000 +0100 @@ -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 -Nur qmail-1.03.orig/dnstxt.c qmail-1.03/dnstxt.c --- qmail-1.03.orig/dnstxt.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/dnstxt.c 2004-01-29 02:25:23.000000000 +0100 @@ -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 -Nur qmail-1.03.orig/FILES qmail-1.03/FILES --- qmail-1.03.orig/FILES 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/FILES 2004-02-02 15:12:35.926140408 +0100 @@ -135,6 +135,8 @@ dnsip.c dnsmxip.c dnsptr.c +dnstxt.c +spfquery.c hostname.c ipmeprint.c tcp-env.c @@ -335,13 +337,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 +406,8 @@ date822fmt.c dns.h dns.c +spf.h +spf.c trylsock.c tryrsolv.c ip.h diff -Nur qmail-1.03.orig/Makefile qmail-1.03/Makefile --- qmail-1.03.orig/Makefile 2004-01-29 03:48:26.000000000 +0100 +++ qmail-1.03/Makefile 2004-02-02 15:16:22.329721832 +0100 @@ -203,6 +203,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 +215,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 @@ -393,23 +401,23 @@ 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 ip.h gen_alloc.h fmt.h alloc.h \ +str.h stralloc.h gen_alloc.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` @@ -423,46 +431,46 @@ ./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 +dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.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 +dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.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 \ +gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.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` @@ -471,6 +479,18 @@ 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 substdio.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 @@ -777,24 +797,27 @@ ./compile ip.c ipalloc.o: \ -compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h ip.h \ -gen_alloc.h +compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h gen_alloc.h ./compile ipalloc.c +strsalloc.o: \ +compile ipalloc.c alloc.h gen_allocdefs.h strsalloc.h gen_alloc.h + ./compile strsalloc.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 +827,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 load: \ make-load warn-auto.sh systype @@ -1439,12 +1462,12 @@ 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 ./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` @@ -1455,7 +1478,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 @@ -1528,21 +1551,28 @@ 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 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 substdio.a error.a str.a \ -fs.a auto_qmail.o 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 substdio.a \ +error.a str.a fs.a auto_qmail.o socket.lib dns.lib ./load qmail-smtpd 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 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 substdio.a error.a fs.a auto_qmail.o \ + str.a `cat socket.lib` `cat dns.lib` + +spfquery: \ +load spfquery.o spf.o ip.o ipme.o ipalloc.o strsalloc.o now.o dns.o \ +datetime.a stralloc.a alloc.a str.a substdio.a error.a fs.a case.a dns.lib + ./load spfquery spf.o ip.o ipme.o ipalloc.o strsalloc.o \ + now.o dns.o datetime.a stralloc.a alloc.a str.a substdio.a \ + case.a error.a fs.a `cat dns.lib` qmail-smtpd.0: \ qmail-smtpd.8 @@ -1551,11 +1581,20 @@ 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 ./compile qmail-smtpd.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 + ./compile spf.c + +spfquery.o: \ +compile spfquery.c substdio.h subfd.h stralloc.h gen_alloc.h alloc.h spf.h exit.h + ./compile spfquery.c + qmail-start: \ load qmail-start.o prot.o fd.a auto_uids.o ./load qmail-start prot.o fd.a auto_uids.o @@ -1779,7 +1818,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 \ @@ -1813,8 +1852,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 \ @@ -1824,7 +1864,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 @@ -1912,12 +1952,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 @@ -1927,6 +1967,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 @@ -2066,11 +2110,11 @@ 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 +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 \ + 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: \ diff -Nur qmail-1.03.orig/qmail-control.9 qmail-1.03/qmail-control.9 --- qmail-1.03.orig/qmail-control.9 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/qmail-control.9 2004-02-02 14:58:56.583699392 +0100 @@ -63,6 +63,10 @@ .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 timeoutconnect \fR60 \fRqmail-remote .I timeoutremote \fR1200 \fRqmail-remote .I timeoutsmtpd \fR1200 \fRqmail-smtpd diff -Nur qmail-1.03.orig/qmail-showctl.c qmail-1.03/qmail-showctl.c --- qmail-1.03.orig/qmail-showctl.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/qmail-showctl.c 2004-02-02 15:06:42.506868304 +0100 @@ -15,6 +15,7 @@ #include "auto_patrn.h" #include "auto_spawn.h" #include "auto_split.h" +#include "spf.h" stralloc me = {0}; int meok; @@ -257,6 +258,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("spfrules",0,"","The local SPF rules are: "); + do_str("spfguess",0,"","The guess SPF rules are: "); + do_str("spfexp",0,SPF_DEFEXP,"The SPF default explanation is: 550 "); 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"); @@ -292,6 +297,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 -Nur qmail-1.03.orig/qmail-smtpd.8 qmail-1.03/qmail-smtpd.8 --- qmail-1.03.orig/qmail-smtpd.8 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/qmail-smtpd.8 2004-02-02 13:42:02.253184256 +0100 @@ -169,6 +169,41 @@ .B qmail-smtpd will wait for each new buffer of data from the remote SMTP client. Default: 1200. +.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. +.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 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 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. .SH "SEE ALSO" tcp-env(1), tcp-environ(5), diff -Nur qmail-1.03.orig/qmail-smtpd.c qmail-1.03/qmail-smtpd.c --- qmail-1.03.orig/qmail-smtpd.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/qmail-smtpd.c 2004-02-02 14:52:55.825542968 +0100 @@ -23,10 +23,12 @@ #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" +#include "spf.h" #define MAXHOPS 100 unsigned int databytes = 0; int timeout = 1200; +unsigned int spfbehavior = 0; int safewrite(fd,buf,len) int fd; char *buf; int len; { @@ -61,6 +63,9 @@ stralloc greeting = {0}; +stralloc spflocal = {0}; +stralloc spfguess = {0}; +stralloc spfexp = {0}; void smtp_greet(code) char *code; { @@ -122,6 +127,19 @@ if (x) { scan_ulong(x,&u); databytes = u; } if (!(databytes + 1)) --databytes; + if (control_readint(&spfbehavior,"control/spfbehavior") == -1) + die_control(); + x = env_get("SPFBEHAVIOR"); + if (x) { scan_ulong(x,&u); spfbehavior = u; } + + 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(); + remoteip = env_get("TCPREMOTEIP"); if (!remoteip) remoteip = "unknown"; local = env_get("TCPLOCALHOST"); @@ -219,6 +237,8 @@ int seenmail = 0; int flagbarf; /* defined if seenmail */ +int flagbarfspf; +stralloc spfbarfmsg = {0}; stralloc mailfrom = {0}; stralloc rcptto = {0}; @@ -237,20 +257,66 @@ seenmail = 0; out("250 flushed\r\n"); } + void smtp_mail(arg) char *arg; { if (!addrparse(arg)) { err_syntax(); return; } flagbarf = bmfcheck(); + flagbarfspf = 0; + if (spfbehavior && !relayclient) + { + switch (spfcheck()) { + case SPF_NOMEM: + die_nomem(); + case SPF_ERROR: + if (spfbehavior < 1) 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; + } + } seenmail = 1; if (!stralloc_copys(&rcptto,"")) die_nomem(); if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); if (!stralloc_0(&mailfrom)) die_nomem(); 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'; + out("\r\n"); + } else { + out("550 "); + out(spfbarfmsg.s); + out(" (#5.7.1)\r\n"); + } + } +} + void smtp_rcpt(arg) char *arg; { if (!seenmail) { err_wantmail(); return; } if (!addrparse(arg)) { err_syntax(); return; } if (flagbarf) { err_bmf(); return; } + if (flagbarfspf) { err_spf(); return; } if (relayclient) { --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); @@ -351,6 +417,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; { @@ -379,6 +464,7 @@ out("354 go ahead\r\n"); received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); + spfreceived(); blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt); diff -Nur qmail-1.03.orig/spf.c qmail-1.03/spf.c --- qmail-1.03.orig/spf.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/spf.c 2004-02-02 14:44:58.999031616 +0100 @@ -0,0 +1,858 @@ +#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 "spf.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_unknown() { received = "unknown (%{xr}: %{i} is neither permitted nor denied by %{xs})"; }; +static void hdr_neutral() { received = "neutral (%{xr}: %{i} is neither permitted nor denied by %{xs})"; }; +static void hdr_none() { received = "none (%{xr}: %{xs} 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; 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; + char *p; + + 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 */ + 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; + } + + 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); + + 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_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}; + char temp[IPFMT]; + 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 -Nur qmail-1.03.orig/spf.h qmail-1.03/spf.h --- qmail-1.03.orig/spf.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/spf.h 2004-02-02 15:14:59.376332664 +0100 @@ -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 -Nur qmail-1.03.orig/spfquery.c qmail-1.03/spfquery.c --- qmail-1.03.orig/spfquery.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/spfquery.c 2004-02-02 15:16:43.361524512 +0100 @@ -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,"spfquery: fatal: invalid usage\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 -Nur qmail-1.03.orig/str_cpyb.c qmail-1.03/str_cpyb.c --- qmail-1.03.orig/str_cpyb.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/str_cpyb.c 2004-01-28 17:24:01.000000000 +0100 @@ -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 -Nur qmail-1.03.orig/str.h qmail-1.03/str.h --- qmail-1.03.orig/str.h 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/str.h 2004-01-28 22:41:57.000000000 +0100 @@ -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 -Nur qmail-1.03.orig/strsalloc.c qmail-1.03/strsalloc.c --- qmail-1.03.orig/strsalloc.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/strsalloc.c 2004-01-29 02:24:19.000000000 +0100 @@ -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 -Nur qmail-1.03.orig/strsalloc.h qmail-1.03/strsalloc.h --- qmail-1.03.orig/strsalloc.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/strsalloc.h 2004-01-29 02:23:54.000000000 +0100 @@ -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 -Nur qmail-1.03.orig/TARGETS qmail-1.03/TARGETS --- qmail-1.03.orig/TARGETS 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/TARGETS 2004-01-29 19:28:12.000000000 +0100 @@ -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 @@ -270,6 +278,8 @@ dnsip dnsmxip.o dnsmxip +dnstxt.o +dnstxt dnsfq.o dnsfq hostname.o diff -Nur qmail-1.03.orig/tcp-env.c qmail-1.03/tcp-env.c --- qmail-1.03.orig/tcp-env.c 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/tcp-env.c 2004-01-29 02:44:17.000000000 +0100 @@ -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();