diff -ruN qmail-1.03-factory/EXTTODO qmail-1.03-7.10/EXTTODO --- qmail-1.03-factory/EXTTODO 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/EXTTODO 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/EXTTODO-INFO qmail-1.03-7.10/EXTTODO-INFO --- qmail-1.03-factory/EXTTODO-INFO 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/EXTTODO-INFO 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/FILES qmail-1.03-7.10/FILES --- qmail-1.03-factory/FILES 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/FILES 2010-02-14 22:53:32.000000000 -0500 @@ -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 @@ -431,3 +438,4 @@ tcp-environ.5 constmap.h constmap.c +qmail-todo.c diff -ruN qmail-1.03-factory/Makefile qmail-1.03-7.10/Makefile --- qmail-1.03-factory/Makefile 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/Makefile 2010-02-14 22:53:32.000000000 -0500 @@ -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 @@ -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 @@ -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 @@ -1483,12 +1512,12 @@ trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \ datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \ lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ -auto_split.o +auto_split.o env.a ./load qmail-send qsutil.o control.o constmap.o newfield.o \ prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \ qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \ wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \ - substdio.a error.a str.a fs.a auto_qmail.o auto_split.o + substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a qmail-send.0: \ qmail-send.8 @@ -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 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 - ./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` +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 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,20 @@ 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.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 \ @@ -1681,6 +1727,10 @@ constmap.h stralloc.h gen_alloc.h rcpthosts.h ./compile rcpthosts.c +qregex.o: \ +compile qregex.c qregex.h + ./compile qregex.c + readsubdir.o: \ compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \ auto_split.h @@ -1779,7 +1829,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 +1863,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 +1875,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 @@ -1897,6 +1948,24 @@ ./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 \ @@ -1912,12 +1981,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 +1996,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 @@ -2006,6 +2079,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 @@ -2066,11 +2144,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: \ @@ -2139,3 +2219,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-factory/README.auth qmail-1.03-7.10/README.auth --- qmail-1.03-factory/README.auth 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/README.auth 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/README.qregex qmail-1.03-7.10/README.qregex --- qmail-1.03-factory/README.qregex 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/README.qregex 2010-02-14 22:53:32.000000000 -0500 @@ -0,0 +1,203 @@ +QREGEX (v2) 20060423 - README April 23, 2006 +A Regular Expression matching patch for qmail 1.03 and netqmail + + +OVERVIEW: + +qregex adds the ability to match address evelopes via Regular Expressions (REs) +in the qmail-smtpd process. It has the abiltiy to match `helo/ehlo` (host name), +`mail from` (envelope sender), and `rcpt to` (envelope recipient) commands. +It follows all the base rules that are set out with qmail (ie using control +files) so it makes for easy integretion into an existing setup (see the +install instructions for more info). The v2 is specified because qregex was +re-written to better conform to the security guarantee set forth by the author +of qmail. The original version used stdio.h and stdlib.h for reading the +control files whereas v2 now uses all stralloc functions which are much more +regulated against buffer overruns and the like. +See: http://cr.yp.to/qmail/guarantee.html + + +FEATURES: + +Features of qregex include: + +1. Performs pattern matching on envelope senders and envelope + recipients against REs in the badmailfrom and badrcptto control + files. Two additional control files, badmailfromnorelay and + 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. Setting the LOGREGEX environment + variable causes the matched regex pattern to be included in the log. + +4. Matching is case insensitive. + +5. qregex ignores empty envelope senders. An empty envelope sender is not + compared to the patterns in the badmailfrom and badmailfromnorelay + control files and is always accepted. + + +PLATFORMS: + +qregex has been built and tested on the following platforms. I'm sure it won't +have any problems on any platform that qmail will run on (providing they have +a regex interface) but if you run into problems let me know. + + - OpenBSD 3.x + - FreeBSD 4.x, 5.x + - Mandrake Linux 9.x + - SuSE Linux 8.x + + + +INSTALLATION INSTRUCTIONS: + +Installation is very simple, there is only one requirement. You need to use the +GNU version of the patch utility (http://www.gnu.org/software/patch/patch.html). +(For Solaris 8 users it is installed as 'gpatch') + +- If this is a new setup. +Unpack the qmail archive, cd into the qmail-1.03 directory and run +"patch < /path/to/qregex-.patch". Follow the instructions as per the +included qmail INSTALL file. Once you are done come back to this file and read +the section on the control files. + +If you are using netqmail, then unpack the netqmail archive. Run the collate.sh +script and cd into the resulting netqmail- directory. From there, run +"patch < /path/to/qregex-.patch". Complete the netqmail installation +normally. Once you are done, come back to this file and read the section on the +control files. + +- If this is an existing setup. +FIRST: create your control files (see below). +cd into your existing qmail or netqmail source directory. Run +"patch < /path/to/qregex-.patch" then "make qmail-smtpd". Now run +./qmail-smtpd and test your new rules to make sure they work as expected. + +Install the new binary by cd'ing to /var/qmail/bin and as root (in one command) +copy the existing binary to 'qmail-smtpd.old' and copy the new binary from the +source directory to 'qmail-smtpd'. +(ex. cp qmail-smtpd qmail-smtpd.old && cp ~/qmail-1.03/qmail-smtpd qmail-smtpd) + +You can also optionally just run "make setup check" as it will install the +updated documentation and man pages provided with this patch. Stopping qmail +before doing the "make setup check" is always a good idea. + + +LOGGING: + +qregex will log matches to the patterns in the various control files. Log +messages will take these three forms depending on which control file was +matched: + +badhelo +qmail-smtpd: badhelo: at + +badmailfrom and badmailfromnorelay +qmail-smtpd: badmailfrom: 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: + +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/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/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 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. + ----------------------------------- + # block host strings with no dot (not a FQDN) + !\. + ----------------------------------- + + An example "badmailfrom" file. + ----------------------------------- + # this will drop everything containing the string + # bad.domain.com or Bad.Domain.Com or BAD.domain.COM + bad\.domain\.com + # force users to fully qualify themselves + # (i.e. deny "user", accept "user@domain") + !@ + ----------------------------------- + + And "badrcptto" (a little more interesting) + ----------------------------------- + # must not contain invalid characters, brakets or multiple @'s + [!%#:*^(){}] + @.*@ + ----------------------------------- + +You can use the non-RE character '!' to start an RE as a signal to qregex to +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 badrcptto +example. + +The norelay control files follow the same rules as the other control files but +are intended to address two specific scenarios. +The badmailfromnorelay file can be used to block mail trying to spoof a domain +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 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. + + +INTERNALS: + +qregex (or regexmatch as the function is called) will be called during the +`helo/ehlo`, `rcpt to` and `mail from` handling routines in "qmail-smtpd.c". +When called, it will read the proper control file then one by one compile and +execute the regex on the string passed into qmail-smtpd. If the regex matches +it returns TRUE (1) and the qmail-smtpd process will deny the user the ability +to continue. If you change anything and think it betters this patch please +send me a new diff file so I can take a peek. + + +CONTACT: +qregex is maintained by: + Andrew St. Jean + andrew@arda.homeunix.net + www.arda.homeunix.net/store/qmail/ + +Contributers to qregex: + Jeremy Kitchen + kitchen at scriptkitchen dot com + http://www.scriptkitchen.com/qmail + + Alex Pleiner + alex@zeitform.de + zeitform Internet Dienste + http://www.zeitform.de/ + + Thanos Massias + +Original qregex patch written by: + Evan Borgstrom + evan at unixpimps dot org diff -ruN qmail-1.03-factory/README.tap qmail-1.03-7.10/README.tap --- qmail-1.03-factory/README.tap 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/README.tap 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/TARGETS qmail-1.03-7.10/TARGETS --- qmail-1.03-factory/TARGETS 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/TARGETS 2010-02-14 22:53:32.000000000 -0500 @@ -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,8 +258,10 @@ qmail-qmtpd.o rcpthosts.o qmail-qmtpd +base64.o qmail-smtpd.o qmail-smtpd +qregex.o sendmail.o sendmail tcp-env.o @@ -270,6 +280,8 @@ dnsip dnsmxip.o dnsmxip +dnstxt.o +dnstxt dnsfq.o dnsfq hostname.o @@ -385,3 +397,5 @@ man setup check +qmail-todo.o +qmail-todo diff -ruN qmail-1.03-factory/base64.c qmail-1.03-7.10/base64.c --- qmail-1.03-factory/base64.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/base64.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/base64.h qmail-1.03-7.10/base64.h --- qmail-1.03-factory/base64.h 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/base64.h 2010-02-14 22:53:32.000000000 -0500 @@ -0,0 +1,7 @@ +#ifndef BASE64_H +#define BASE64_H + +extern int b64decode(); +extern int b64encode(); + +#endif diff -ruN qmail-1.03-factory/byte.h qmail-1.03-7.10/byte.h --- qmail-1.03-factory/byte.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/byte.h 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/byte_cspn.c qmail-1.03-7.10/byte_cspn.c --- qmail-1.03-factory/byte_cspn.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/byte_cspn.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/byte_rcspn.c qmail-1.03-7.10/byte_rcspn.c --- qmail-1.03-factory/byte_rcspn.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/byte_rcspn.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/chkspawn.c qmail-1.03-7.10/chkspawn.c --- qmail-1.03-factory/chkspawn.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/chkspawn.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/conf-cc qmail-1.03-7.10/conf-cc --- qmail-1.03-factory/conf-cc 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/conf-cc 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/conf-spawn qmail-1.03-7.10/conf-spawn --- qmail-1.03-factory/conf-spawn 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/conf-spawn 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/date822fmt.c qmail-1.03-7.10/date822fmt.c --- qmail-1.03-factory/date822fmt.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/date822fmt.c 2010-02-14 22:53:32.000000000 -0500 @@ -1,3 +1,4 @@ +#include #include "datetime.h" #include "fmt.h" #include "date822fmt.h" @@ -12,18 +13,51 @@ { unsigned int i; unsigned int len; + time_t now; + datetime_sec utc; + datetime_sec local; + struct tm *tm; + struct datetime new_dt; + int minutes; + + utc = datetime_untai(dt); + now = (time_t)utc; + tm = localtime(&now); + new_dt.year = tm->tm_year; + new_dt.mon = tm->tm_mon; + new_dt.mday = tm->tm_mday; + new_dt.hour = tm->tm_hour; + new_dt.min = tm->tm_min; + new_dt.sec = tm->tm_sec; + local = datetime_untai(&new_dt); + len = 0; - i = fmt_uint(s,dt->mday); len += i; if (s) s += i; + i = fmt_uint(s,new_dt.mday); len += i; if (s) s += i; i = fmt_str(s," "); len += i; if (s) s += i; - i = fmt_str(s,montab[dt->mon]); len += i; if (s) s += i; + i = fmt_str(s,montab[new_dt.mon]); len += i; if (s) s += i; i = fmt_str(s," "); len += i; if (s) s += i; - i = fmt_uint(s,dt->year + 1900); len += i; if (s) s += i; + i = fmt_uint(s,new_dt.year + 1900); len += i; if (s) s += i; i = fmt_str(s," "); len += i; if (s) s += i; - i = fmt_uint0(s,dt->hour,2); len += i; if (s) s += i; + i = fmt_uint0(s,new_dt.hour,2); len += i; if (s) s += i; i = fmt_str(s,":"); len += i; if (s) s += i; - i = fmt_uint0(s,dt->min,2); len += i; if (s) s += i; + i = fmt_uint0(s,new_dt.min,2); len += i; if (s) s += i; i = fmt_str(s,":"); len += i; if (s) s += i; - i = fmt_uint0(s,dt->sec,2); len += i; if (s) s += i; - i = fmt_str(s," -0000\n"); len += i; if (s) s += i; + i = fmt_uint0(s,new_dt.sec,2); len += i; if (s) s += i; + + if (local < utc) { + minutes = (utc - local + 30) / 60; + i = fmt_str(s," -"); len += i; if (s) s += i; + i = fmt_uint0(s,minutes / 60,2); len += i; if (s) s += i; + i = fmt_uint0(s,minutes % 60,2); len += i; if (s) s += i; + } + else { + minutes = (local - utc + 30) / 60; + i = fmt_str(s," +"); len += i; if (s) s += i; + i = fmt_uint0(s,minutes / 60,2); len += i; if (s) s += i; + i = fmt_uint0(s,minutes % 60,2); len += i; if (s) s += i; + } + + i = fmt_str(s,"\n"); len += i; if (s) s += i; + return len; } diff -ruN qmail-1.03-factory/dns.c qmail-1.03-7.10/dns.c --- qmail-1.03-factory/dns.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/dns.c 2010-02-14 22:53:32.000000000 -0500 @@ -11,6 +11,7 @@ extern int h_errno; #include "ip.h" #include "ipalloc.h" +#include "strsalloc.h" #include "fmt.h" #include "alloc.h" #include "str.h" @@ -21,14 +22,17 @@ static unsigned short getshort(c) unsigned char *c; { unsigned short u; u = c[0]; return (u << 8) + c[1]; } -static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response; +static struct { unsigned char *buf; } response; +static int responsebuflen = 0; static int responselen; static unsigned char *responseend; static unsigned char *responsepos; +static u_long saveresoptions; static int numanswers; static char name[MAXDNAME]; static struct ip_address ip; +static stralloc txt = {0}; unsigned short pref; static stralloc glue = {0}; @@ -45,18 +49,33 @@ errno = 0; if (!stralloc_copy(&glue,domain)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; - responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response)); + if (!responsebuflen) + if (response.buf = (unsigned char *)alloc(PACKETSZ+1)) + responsebuflen = PACKETSZ+1; + else return DNS_MEM; + + responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen); + if ((responselen >= responsebuflen) || + (responselen > 0 && (((HEADER *)response.buf)->tc))) + { + if (responsebuflen < 65536) + if (alloc_re(&response.buf, responsebuflen, 65536)) + responsebuflen = 65536; + else return DNS_MEM; + saveresoptions = _res.options; + _res.options |= RES_USEVC; + responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen); + _res.options = saveresoptions; + } if (responselen <= 0) { if (errno == ECONNREFUSED) return DNS_SOFT; if (h_errno == TRY_AGAIN) return DNS_SOFT; return DNS_HARD; } - if (responselen >= sizeof(response)) - responselen = sizeof(response); responseend = response.buf + responselen; responsepos = response.buf + sizeof(HEADER); - n = ntohs(response.hdr.qdcount); + n = ntohs(((HEADER *)response.buf)->qdcount); while (n-- > 0) { i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); @@ -66,7 +85,7 @@ if (i < QFIXEDSZ) return DNS_SOFT; responsepos += QFIXEDSZ; } - numanswers = ntohs(response.hdr.ancount); + numanswers = ntohs(((HEADER *)response.buf)->ancount); return 0; } @@ -179,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; { @@ -196,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; @@ -237,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; @@ -256,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; @@ -270,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; @@ -330,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; } @@ -398,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-factory/dns.h qmail-1.03-7.10/dns.h --- qmail-1.03-factory/dns.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/dns.h 2010-02-14 22:53:32.000000000 -0500 @@ -10,5 +10,6 @@ int dns_mxip(); int dns_ip(); int dns_ptr(); +int dns_txt(); #endif diff -ruN qmail-1.03-factory/dnsfq.c qmail-1.03-7.10/dnsfq.c --- qmail-1.03-factory/dnsfq.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/dnsfq.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/dnsptr.c qmail-1.03-7.10/dnsptr.c --- qmail-1.03-factory/dnsptr.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/dnsptr.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/dnstxt.c qmail-1.03-7.10/dnstxt.c --- qmail-1.03-factory/dnstxt.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/dnstxt.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/error.c qmail-1.03-7.10/error.c --- qmail-1.03-factory/error.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/error.c 2010-02-14 22:53:32.000000000 -0500 @@ -93,3 +93,10 @@ #else -13; #endif + +int error_dquot = +#ifdef EDQUOT +EDQUOT; +#else +-14; +#endif diff -ruN qmail-1.03-factory/error.h qmail-1.03-7.10/error.h --- qmail-1.03-factory/error.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/error.h 2010-02-14 22:53:32.000000000 -0500 @@ -1,7 +1,8 @@ #ifndef ERROR_H #define ERROR_H -extern int errno; +/* 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-factory/hier.c qmail-1.03-7.10/hier.c --- qmail-1.03-factory/hier.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/hier.c 2010-02-14 22:53:32.000000000 -0500 @@ -76,6 +76,7 @@ c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755); c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644); @@ -108,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-factory/install-big.c qmail-1.03-7.10/install-big.c --- qmail-1.03-factory/install-big.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/install-big.c 2010-02-14 22:53:32.000000000 -0500 @@ -76,6 +76,7 @@ c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755); c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644); @@ -108,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-factory/ipalloc.h qmail-1.03-7.10/ipalloc.h --- qmail-1.03-factory/ipalloc.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/ipalloc.h 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/ipme.c qmail-1.03-7.10/ipme.c --- qmail-1.03-factory/ipme.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/ipme.c 2010-02-14 22:53:32.000000000 -0500 @@ -46,9 +46,14 @@ ipme.len = 0; ix.pref = 0; + /* 0.0.0.0 is a special address which always refers to + * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a. + */ + byte_copy(&ix.ip,4,"\0\0\0\0"); + 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; @@ -60,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-factory/make-makelib.sh qmail-1.03-7.10/make-makelib.sh --- qmail-1.03-factory/make-makelib.sh 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/make-makelib.sh 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/qmail-control.9 qmail-1.03-7.10/qmail-control.9 --- qmail-1.03-factory/qmail-control.9 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail-control.9 2010-02-14 22:53:32.000000000 -0500 @@ -20,7 +20,11 @@ Comments are allowed in +.IR badhelo , .IR badmailfrom , +.IR badmailfromnorelay , +.IR badrcpto , +.IR badrcptonorelay , .IR locals , .IR percenthack , .IR qmqpservers , @@ -40,7 +44,11 @@ .ta 5c 10c control default used by +.I badhelo \fR(none) \fRqmail-smtpd .I badmailfrom \fR(none) \fRqmail-smtpd +.I badmailfromnorelay \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 @@ -55,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 @@ -63,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 @@ -72,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-factory/qmail-local.c qmail-1.03-7.10/qmail-local.c --- qmail-1.03-factory/qmail-local.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail-local.c 2010-02-14 22:53:32.000000000 -0500 @@ -41,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; @@ -110,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)); @@ -131,7 +139,12 @@ /* 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 */ @@ -162,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)"); } } @@ -221,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); @@ -645,7 +664,7 @@ { cmds.s[j] = 0; k = j; - while ((k > i) && (cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t')) + while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))) cmds.s[--k] = 0; switch(cmds.s[i]) { diff -ruN qmail-1.03-factory/qmail-qmtpd.c qmail-1.03-7.10/qmail-qmtpd.c --- qmail-1.03-factory/qmail-qmtpd.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail-qmtpd.c 2010-02-14 22:53:32.000000000 -0500 @@ -45,6 +45,8 @@ for (;;) { substdio_get(&ssin,&ch,1); if (ch == ':') return len; + /* trap non-numeric input in netstring: */ + if ((ch < '0') || (ch > '9')) badproto(); if (len > 200000000) resources(); len = 10 * len + (ch - '0'); } @@ -193,6 +195,8 @@ substdio_get(&ssin,&ch,1); --biglen; if (ch == ':') break; + /* trap non-numeric input in netstring: */ + if ((ch < '0') || (ch > '9')) badproto(); if (len > 200000000) resources(); len = 10 * len + (ch - '0'); } diff -ruN qmail-1.03-factory/qmail-queue.8 qmail-1.03-7.10/qmail-queue.8 --- qmail-1.03-factory/qmail-queue.8 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail-queue.8 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/qmail-queue.c qmail-1.03-7.10/qmail-queue.c --- qmail-1.03-factory/qmail-queue.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail-queue.c 2010-02-14 22:53:32.000000000 -0500 @@ -16,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 @@ -25,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; @@ -159,6 +168,11 @@ 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(); @@ -219,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 (;;) @@ -237,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); } @@ -252,3 +294,42 @@ 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-factory/qmail-remote.8 qmail-1.03-7.10/qmail-remote.8 --- qmail-1.03-factory/qmail-remote.8 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail-remote.8 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/qmail-remote.c qmail-1.03-7.10/qmail-remote.c --- qmail-1.03-factory/qmail-remote.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail-remote.c 2010-02-14 22:54:43.000000000 -0500 @@ -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(); } @@ -215,25 +338,236 @@ } stralloc recip = {0}; +stralloc xuser = {0} ; + +int xtext(sa,s,len) +stralloc *sa; +char *s; +int len; +{ + int i; + + if(!stralloc_copys(sa,"")) temp_nomem(); + + for (i = 0; i < len; i++) { + if (s[i] == '=') { + if (!stralloc_cats(sa,"+3D")) temp_nomem(); + } else if (s[i] == '+') { + if (!stralloc_cats(sa,"+2B")) temp_nomem(); + } else if ((int) s[i] < 33 || (int) s[i] > 126) { + if (!stralloc_cats(sa,"+3F")) temp_nomem(); /* ok. not correct */ + } else if (!stralloc_catb(sa,s+i,1)) { + temp_nomem(); + } + } + + return sa->len; +} + +#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) { + if (!xtext(&xuser,auth_smtp_user.s,auth_smtp_user.len)) temp_nomem(); + substdio_puts(&smtpto,"MAIL FROM:<"); + substdio_put(&smtpto,sender.s,sender.len); + substdio_puts(&smtpto,"> AUTH="); + substdio_put(&smtpto,xuser.s,xuser.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 +658,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 +670,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 +706,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); @@ -365,7 +731,9 @@ addrmangle(&sender,argv[2],&flagalias,0); - + stralloc_0(&sender); + sender.len --; + if (!saa_readyplus(&reciplist,0)) temp_nomem(); if (ipme_init() != 1) temp_oserr(); @@ -413,11 +781,18 @@ smtpfd = socket(AF_INET,SOCK_STREAM,0); if (smtpfd == -1) temp_oserr(); - + + bind_by_sender ( smtpfd , sender.s , 1 ) ; + bind_by_remoteip ( smtpfd , &ip.ix[i].ip , 0 ) ; + 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-factory/qmail-send.c qmail-1.03-7.10/qmail-send.c --- qmail-1.03-factory/qmail-send.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail-send.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/qmail-showctl.c qmail-1.03-7.10/qmail-showctl.c --- qmail-1.03-factory/qmail-showctl.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail-showctl.c 2010-02-14 22:53:32.000000000 -0500 @@ -15,6 +15,7 @@ #include "auto_patrn.h" #include "auto_spawn.h" #include "auto_split.h" +#include "spf.h" stralloc me = {0}; int meok; @@ -214,7 +215,12 @@ _exit(111); } - do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM."); + 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("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 ",""); @@ -257,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"); @@ -267,7 +277,11 @@ if (str_equal(d->d_name,"..")) continue; if (str_equal(d->d_name,"bouncefrom")) continue; if (str_equal(d->d_name,"bouncehost")) continue; + 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,"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; @@ -292,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-factory/qmail-smtpd.8 qmail-1.03-7.10/qmail-smtpd.8 --- qmail-1.03-factory/qmail-smtpd.8 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail-smtpd.8 2010-02-14 23:12:05.000000000 -0500 @@ -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 @@ -37,11 +71,30 @@ even though such messages violate the SMTP protocol. .SH "CONTROL FILES" .TP 5 +.I badhelo +Unacceptable HELO/EHLO host names. +.B qmail-smtpd +will reject every recipient address for a message if +the host name is listed in, +or matches a POSIX regular expression pattern listed in, +.IR badhelo . +If the +.B NOBADHELO +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 Unacceptable envelope sender addresses. .B qmail-smtpd will reject every recipient address for a message -if the envelope sender address is listed in +if the envelope sender address is listed in, or matches a POSIX regular expression +pattern listed in, .IR badmailfrom . A line in .I badmailfrom @@ -49,6 +102,41 @@ .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 +Functions the same as the +.IR badmailfrom +control file but is read only if the +.B RELAYCLIENT +environment variable is not set. +For more information, please have a look at doc/README.qregex. +.TP 5 +.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 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 badrcpttonorelay +Functions the same as the +.IR badrcptto +control file but is read only if the +.B RELAYCLIENT +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 Maximum number of bytes allowed in a message, @@ -76,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. @@ -97,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 @@ -163,12 +280,584 @@ 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 PASSWORD_EXPIRES +If this variable is set, it is expected to contain a unix time() value (the +number of seconds since 1970-01-01 00:00:00 GMT.) If the current time is +equal to or greater than this value, any AUTH command will fail with an +error message saying that the password is expired. + +Logically, this variable should only be used on a per-user basis. The +AUTH_CDB patch allows you to create environment variables which are +created when a specific user sends a successful AUTH command. Otherwise, +there is currently no other way to set this on a per-user basis. +.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 RCPTCHECK +If this variable exists, it should contain the full pathname to a +program which +.B qmail-smtpd +will run after receiving each +.B RCPT +command. + +No arguments are passed on the command line; before running the program, +.B qmail-smtpd +will store the recipient address sent by the client in the +.B RECIPIENT +environment variable, and the sender address (from the +.B MAIL +command) in the +.B SENDER +variable. Any other environment variables which were present when +.B qmail-smtpd +started, as possibly modified by the +.B AUTH_SET +mechanism, will be present as well. Note that this includes the +.B TCPREMOTEIP +variable, set by tcpserver, which will contain the client's IP address. + +The exit code of the program determines whether or not +.B qmail-smtpd +will accept the command. The possible values are: +.RS 5 +.TP 5 +.B 100 +The recipient is not valid. The client receives a 553 error. +.TP 5 +.B 111 +Temporary problem verifying the recipient. The client receives a 421 +error code and the connection is closed. +.TP 5 +.B 120 +The program could not execute correctly. The client receives a 421 error +code and the connection is closed. +.TP 5 +.B anything else +The recipient is valid. + +.RE +.RS 5 +Anything the program may send to its standard out or standard error +channel will be sent to the SMTP service's logs, just as if +.B qmail-smtpd +had printed it. This allows your program to log what it's doing. + +This variable may be modified using the +.B AUTH_SET +mechanism if you want to bypass recipient checking, or use a different +program, for clients who have sent a valid AUTH command. However, I +recommend you write your program to check the +.B SMTP_AUTH_USER +variable instead- if a valid AUTH command has been sent, this variable +will contain the userid from that command. + +This functionality (calling an external program in response to an AUTH +command) comes from Jay Soffian's RCPTCHECK patch, and is more fully +documented on his web site: +.IP +http://www.soffian.org/downloads/qmail/qmail-smtpd-doc.html + +.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), @@ -177,3 +866,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.10 +version of the combined patch file. diff -ruN qmail-1.03-factory/qmail-smtpd.c qmail-1.03-7.10/qmail-smtpd.c --- qmail-1.03-factory/qmail-smtpd.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail-smtpd.c 2010-02-14 22:54:53.000000000 -0500 @@ -1,5 +1,7 @@ #include "sig.h" +#ifndef TLS #include "readwrite.h" +#endif #include "stralloc.h" #include "substdio.h" #include "alloc.h" @@ -20,18 +22,124 @@ #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_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; +static stralloc authcdb_vars = {0}; +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; +unsigned long pw_expire = 0; +char rcptcheck_err[1024]; + +#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; } @@ -47,20 +155,59 @@ void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); } 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 die_fork() { out("421 unable to fork (#4.3.0)\r\n"); flush(); _exit(1); } +void die_rcpt() { out("421 unable to verify recipient (#4.3.0)\r\n"); flush(); _exit(1); } +void die_rcpt2() { out("421 unable to execute recipient check (#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 err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } +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_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"); } +#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() { 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"); } +void err_badrcpt() { out("553 sorry, no mailbox here by that name. (#5.1.1)\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; { @@ -69,7 +216,11 @@ } 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.10 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() { @@ -81,6 +232,15 @@ char *remoteinfo; char *local; char *relayclient; +static char *rcptcheck[2] = { 0, 0 }; +#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 */ @@ -93,35 +253,159 @@ int liphostok = 0; stralloc liphost = {0}; + int bmfok = 0; stralloc bmf = {0}; -struct constmap mapbmf; + +int bmfnrok = 0; +stralloc bmfnr = {0}; + +int brtok = 0; +stralloc brt = {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; } + + rcptcheck[0] = env_get("RCPTCHECK"); +} + +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(); - if (bmfok) - if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); + + bmfnrok = control_readfile(&bmfnr,"control/badmailfromnorelay",0); + if (bmfnrok == -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(); + + bhelook = control_readfile(&bhelo,"control/badhelo",0); + if(bhelook == -1) die_control() ; + if(env_get("NOBADHELO")) bhelook = 0; + + if(env_get("LOGREGEX")) logregex = 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_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"); @@ -131,6 +415,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); } @@ -197,14 +495,213 @@ return 1; } -int bmfcheck() +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; + int j = 0; + int x = 0; + int negate = 0; + static stralloc bmb = {0}; + static stralloc curregex = {0}; + + if (which == BMCHECK_BMF) { + if (!stralloc_copy(&bmb,&bmf)) die_nomem(); + } else if (which == BMCHECK_BMFNR) { + if (!stralloc_copy(&bmb,&bmfnr)) 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 { + die_control(); + } + + while (j < bmb.len) { + i = j; + while ((bmb.s[i] != '\0') && (i < bmb.len)) i++; + if (bmb.s[j] == '!') { + negate = 1; + j++; + } + if (!stralloc_copyb(&curregex,bmb.s + j,(i - j))) die_nomem(); + if (!stralloc_0(&curregex)) die_nomem(); + if (which == BMCHECK_BHELO) { + x = matchregex(helohost.s, curregex.s); + } else { + x = matchregex(addr.s, curregex.s); + } + 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 (!bmfok) return 0; - if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; - j = byte_rchr(addr.s,addr.len,'@'); - if (j < addr.len) - if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; + + if (str_equal(addr.s,"#@[]")) return 0; + if (!mfchk) return 0; + random = now() + (getpid() << 16); + j = byte_rchr(addr.s,addr.len,'@') + 1; + if (addr.len == 1) return 0; + 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>1) strerr_warn5(title.s,"MFCHECK invalid [",remoteip,"] ",addr.s,0); + return DNS_HARD; +} + +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; } @@ -216,21 +713,143 @@ 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 flagbarfbrt; +int flagbarfbhelo; +int flagbarfspf; +stralloc spfbarfmsg = {0}; stralloc mailfrom = {0}; stralloc rcptto = {0}; +int addrvalid() +{ + int pid; + int wstat; + int pierr[2] ; + substdio ss; + char ssbuf[sizeof(rcptcheck_err)]; + int len = 0 ; + char ch; + + if (!rcptcheck[0]) return 1; + if (pipe(pierr) == -1) die_rcpt2(); + + switch(pid = fork()) { + case -1: + close(pierr[0]); + close(pierr[1]); + die_fork(); + case 0: + if (!env_put2("SENDER",mailfrom.s)) die_nomem(); + if (!env_put2("RECIPIENT",addr.s)) die_nomem(); + if (!env_put2("HELO",helohost.s)) die_nomem(); + if (!env_put2("USE_FD4","1")) die_nomem(); + close(1); + dup2(2,1); + close(pierr[0]); + if (fd_move(4,pierr[1]) == -1) die_rcpt2(); + execv(*rcptcheck,rcptcheck); + _exit(120); + } + + close(pierr[1]); + if (wait_pid(&wstat,pid) == -1) die_rcpt2(); + if (wait_crashed(wstat)) die_rcpt2(); + + substdio_fdbuf(&ss,read,pierr[0],ssbuf,sizeof(ssbuf)); + while ( substdio_bget(&ss,&ch,1) && len < (sizeof(ssbuf)-3) ) + rcptcheck_err[len++] = ch; + close(pierr[0]); + + while (len&&((rcptcheck_err[len-1]=='\n')||(rcptcheck_err[len-1]=='\r'))) + len -- ; + if (len) { + rcptcheck_err[len] = '\0'; + strerr_warn3(title.s,"RCPTCHECK error: ",rcptcheck_err,0); + rcptcheck_err[len++] = '\r'; + rcptcheck_err[len++] = '\n'; + } + rcptcheck_err[len] = '\0'; + + switch(wait_exitcode(wstat)) { + case 100: + if (!len) { + len = str_copy(rcptcheck_err,"553 sorry, no mailbox here by that name. (#5.1.1)\r\n"); + rcptcheck_err[len] = '\0' ; + } + case 111: + if (!len) { + len = str_copy(rcptcheck_err,"421 unable to verify recipient (#4.3.0)\r\n"); + rcptcheck_err[len] = '\0' ; + } + return 0; + case 120: die_rcpt2(); + } + return 1; +} + void smtp_helo(arg) char *arg; { smtp_greet("250 "); out("\r\n"); 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() { @@ -239,25 +858,219 @@ } 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; } - flagbarf = bmfcheck(); + 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) && (!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 (flagbarf) { err_bmf(); return; } + if (addrrelay()) { err_relay(); return; } + if (log_rcpt) { strerr_warn4(title.s,"RCPT TO:<",addr.s,">",0); } + if (flagbarfbhelo) { + 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) { + 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 (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 (!addrvalid()) { + if (rcptcheck_err[0]) + out (rcptcheck_err); + else + err_badrcpt(); + return; + } + if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); @@ -269,7 +1082,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; @@ -316,8 +1133,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) { @@ -351,6 +1168,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; { @@ -365,10 +1201,91 @@ out("\r\n"); } +/* addvars() - find and add environment variables after encrypted password + in cdb data. if doit=0, only PASSWORD_EXPIRES is searched for. this way + expired passwords don't get their extra vars if password is expired. */ +void addvars(s,doit) +char *s; +int doit; +{ + char *n; + char *v; + int x; + int y; + + n = s; + + while (*n) + { + if (','==*n) n++ ; + x = str_chr(n,'='); + if (!n[x]) return ; + if (n[x+1]!='\"') return ; + v = n+x+2 ; + y = str_chr(v,'\"'); + if (!v[y]) return ; + if(!str_diff(n,"PASSWORD_EXPIRES")) scan_ulong(v,&pw_expire); + if(doit) { + n[x]=0; + v[y]=0; + env_put2(n,v); + } + n = v+y+1; + } +} + +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); + + if(authcdb_vars.s) + addvars(authcdb_vars.s,1); +} + 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; } @@ -377,8 +1294,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); @@ -388,34 +1320,443 @@ 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_rfd(ssl,0); + SSL_set_wfd(ssl,1); + 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 */ +} + +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 ) { + stralloc_copyb(&authcdb_vars,epw.s+x+1,dlen-x-1); + stralloc_0(&authcdb_vars); + addvars(authcdb_vars.s,0); + if(pw_expire && pw_expire <= time(0)) { + strerr_warn5(title.s,"AUTH failed (password expired) [", + remoteip,"] ",user.s,0); + return 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(); + if (!stralloc_copys(&authcdb_vars,"")) die_nomem(); + pw_expire = 0 ; + + 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(); + setup(); + +#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; } + + 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() ; + } + 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-factory/qmail-start.c qmail-1.03-7.10/qmail-start.c --- qmail-1.03-factory/qmail-start.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail-start.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/qmail-todo.c qmail-1.03-7.10/qmail-todo.c --- qmail-1.03-factory/qmail-todo.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/qmail-todo.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/qmail.c qmail-1.03-7.10/qmail.c --- qmail-1.03-factory/qmail.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail.c 2010-02-14 22:53:32.000000000 -0500 @@ -6,28 +6,49 @@ #include "fd.h" #include "qmail.h" #include "auto_qmail.h" +#include "env.h" -static char *binqqargs[2] = { "bin/qmail-queue", 0 } ; +static char *binqqargs[2] = { 0, 0 } ; + +static void setup_qqargs() +{ + if(!binqqargs[0]) + binqqargs[0] = env_get("QMAILQUEUE"); + if(!binqqargs[0]) + binqqargs[0] = "bin/qmail-queue"; +} int qmail_open(qq) struct qmail *qq; { 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); @@ -35,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; @@ -82,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)"; @@ -94,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)"; @@ -118,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-factory/qmail.h qmail-1.03-7.10/qmail.h --- qmail-1.03-factory/qmail.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/qmail.h 2010-02-14 22:53:32.000000000 -0500 @@ -8,6 +8,7 @@ unsigned long pid; int fdm; int fde; + int fderr; substdio ss; char buf[1024]; } ; diff -ruN qmail-1.03-factory/qregex.c qmail-1.03-7.10/qregex.c --- qmail-1.03-factory/qregex.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/qregex.c 2010-02-14 22:53:32.000000000 -0500 @@ -0,0 +1,57 @@ +/* + * qregex (v2) + * $Id: qregex.c,v 2.1 2001/12/28 07:05:21 evan Exp $ + * + * Author : Evan Borgstrom (evan at unixpimps dot org) + * Created : 2001/12/14 23:08:16 + * Modified: $Date: 2001/12/28 07:05:21 $ + * Revision: $Revision: 2.1 $ + * + * Do POSIX regex matching on addresses for anti-relay / spam control. + * It logs to the maillog + * See the qregex-readme file included with this tarball. + * If you didn't get this file in a tarball please see the following URL: + * http://www.unixpimps.org/software/qregex + * + * qregex.c is released under a BSD style copyright. + * See http://www.unixpimps.org/software/qregex/copyright.html + * + * Note: this revision follows the coding guidelines set forth by the rest of + * the qmail code and that described at the following URL. + * http://cr.yp.to/qmail/guarantee.html + * + */ + +#include +#include +#include "qregex.h" + +#define REGCOMP(X,Y) regcomp(&X, Y, REG_EXTENDED|REG_ICASE) +#define REGEXEC(X,Y) regexec(&X, Y, (size_t)0, (regmatch_t *)0, (int)0) + +int matchregex(char *text, char *regex) { + regex_t qreg; + int retval = 0; + + + /* build the regex */ + if ((retval = REGCOMP(qreg, regex)) != 0) { + regfree(&qreg); + return(-retval); + } + + /* execute the regex */ + if ((retval = REGEXEC(qreg, text)) != 0) { + /* did we just not match anything? */ + if (retval == REG_NOMATCH) { + regfree(&qreg); + return(0); + } + regfree(&qreg); + return(-retval); + } + + /* signal the match */ + regfree(&qreg); + return(1); +} diff -ruN qmail-1.03-factory/qregex.h qmail-1.03-7.10/qregex.h --- qmail-1.03-factory/qregex.h 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/qregex.h 2010-02-14 22:53:32.000000000 -0500 @@ -0,0 +1,5 @@ +/* simple header file for the matchregex prototype */ +#ifndef _QREGEX_H_ +#define _QREGEX_H_ +int matchregex(char *text, char *regex); +#endif diff -ruN qmail-1.03-factory/sendmail.c qmail-1.03-7.10/sendmail.c --- qmail-1.03-factory/sendmail.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/sendmail.c 2010-02-14 22:53:32.000000000 -0500 @@ -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; @@ -118,6 +150,7 @@ if (sender) { *arg++ = "-f"; *arg++ = sender; + do_sender(sender); } *arg++ = "--"; for (i = 0;i < argc;++i) *arg++ = argv[i]; diff -ruN qmail-1.03-factory/spawn.c qmail-1.03-7.10/spawn.c --- qmail-1.03-factory/spawn.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/spawn.c 2010-02-14 22:53:32.000000000 -0500 @@ -63,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}; @@ -73,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); } @@ -155,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(); @@ -201,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; } @@ -236,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-factory/spf.c qmail-1.03-7.10/spf.c --- qmail-1.03-factory/spf.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/spf.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/spf.h qmail-1.03-7.10/spf.h --- qmail-1.03-factory/spf.h 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/spf.h 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/spfquery.c qmail-1.03-7.10/spfquery.c --- qmail-1.03-factory/spfquery.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/spfquery.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/str.h qmail-1.03-7.10/str.h --- qmail-1.03-factory/str.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/str.h 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/str_cpyb.c qmail-1.03-7.10/str_cpyb.c --- qmail-1.03-factory/str_cpyb.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/str_cpyb.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/strerr.h qmail-1.03-7.10/strerr.h --- qmail-1.03-factory/strerr.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/strerr.h 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/strerr_die.c qmail-1.03-7.10/strerr_die.c --- qmail-1.03-factory/strerr_die.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/strerr_die.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/strsalloc.c qmail-1.03-7.10/strsalloc.c --- qmail-1.03-factory/strsalloc.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/strsalloc.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/strsalloc.h qmail-1.03-7.10/strsalloc.h --- qmail-1.03-factory/strsalloc.h 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-7.10/strsalloc.h 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/tcp-env.c qmail-1.03-7.10/tcp-env.c --- qmail-1.03-factory/tcp-env.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/tcp-env.c 2010-02-14 22:53:32.000000000 -0500 @@ -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-factory/timeoutconn.c qmail-1.03-7.10/timeoutconn.c --- qmail-1.03-factory/timeoutconn.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/timeoutconn.c 2010-02-14 22:53:32.000000000 -0500 @@ -9,6 +9,87 @@ #include "ip.h" #include "byte.h" #include "timeoutconn.h" +#include "control.h" +#include "constmap.h" +#include "stralloc.h" + +/* if 1, bind() failing will be ignored */ +#define IGNORE_BIND_ERROR 0 + +struct ip_address iplocal; +int bindlocal = 0 ; + +int bind_by_sender(s,addr,force) +int s; +char *addr; +int force; +{ + int j; + stralloc stext = {0} ; + struct constmap senderip ; + stralloc domain = {0} ; + char *chosenip = (char *) 0 ; + + if (!force) if(bindlocal) return 0; /* already bound, no bind */ + + switch ( control_readfile ( &stext , "control/senderip" , 0 ) ) + { + case 0: return 0 ; /* no file, no bind */ + case -1: return -2 ; /* error */ + case 1: + if ( ! constmap_init ( &senderip , stext.s , stext.len , 1 ) ) + return -3 ; + } + + j = str_chr(addr,'@') ; + stralloc_copys ( &domain , addr[j] ? &(addr[j+1]) : addr ) ; + stralloc_0 ( &domain ) ; + domain.len -- ; + + chosenip = constmap ( &senderip , domain.s , domain.len ) ; + if ( !chosenip || !*chosenip ) return 0 ; /* no match, no bind */ + if ( ! ip_scan ( chosenip , &iplocal ) ) return -4 ; /* invalid IP */ + bindlocal = 1 ; + return 0 ; +} + +int bind_by_remoteip(s,ip,force) +int s; +struct ip_address *ip; +int force; +{ + struct sockaddr_in salocal; + char *ipstr, ipstring[IPFMT+1]; + int iplen; + stralloc routes = {0}; + struct constmap bindroutes; + char *bindroute = (char *)0; + + if (!force) if(bindlocal) return 0; /* already bound, no bind */ + + /* make sure we have a control/bindroutes file */ + 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; + } + + /* search for d.d.d.d, d.d.d., d.d., d., none */ + 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! */ + + 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 */ + bindlocal = 1 ; + return 0; +} + int timeoutconn(s,ip,port,timeout) int s; @@ -18,6 +99,7 @@ { char ch; struct sockaddr_in sin; + struct sockaddr_in salocal; char *x; fd_set wfds; struct timeval tv; @@ -30,8 +112,15 @@ if (ndelay_on(s) == -1) return -1; - /* XXX: could bind s */ - + /* bind s, if we've been given a local IP */ + if ( bindlocal ) { + 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) ) ) + if ( ! IGNORE_BIND_ERROR ) return errno ; + } + if (connect(s,(struct sockaddr *) &sin,sizeof(sin)) == 0) { ndelay_off(s); return 0; diff -ruN qmail-1.03-factory/timeoutconn.h qmail-1.03-7.10/timeoutconn.h --- qmail-1.03-factory/timeoutconn.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-7.10/timeoutconn.h 2010-02-14 22:53:32.000000000 -0500 @@ -2,5 +2,7 @@ #define TIMEOUTCONN_H extern int timeoutconn(); +extern int bind_by_sender(); +extern int bind_by_remoteip(); #endif