diff -ruN qmail-1.03-factory/FILES qmail-1.03-6b/FILES --- qmail-1.03-factory/FILES 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/FILES 2005-05-23 15:13:57.000000000 -0400 @@ -135,6 +135,8 @@ dnsip.c dnsmxip.c dnsptr.c +dnstxt.c +spfquery.c hostname.c ipmeprint.c tcp-env.c @@ -335,13 +337,16 @@ byte.h byte_chr.c byte_copy.c +byte_cspn.c byte_cr.c byte_diff.c byte_rchr.c +byte_rcspn.c byte_zero.c str.h str_chr.c str_cpy.c +str_cpyb.c str_diff.c str_diffn.c str_len.c @@ -401,6 +406,8 @@ date822fmt.c dns.h dns.c +spf.h +spf.c trylsock.c tryrsolv.c ip.h diff -ruN qmail-1.03-factory/Makefile qmail-1.03-6b/Makefile --- qmail-1.03-factory/Makefile 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/Makefile 2005-05-23 15:13:57.000000000 -0400 @@ -136,6 +136,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 +207,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 +219,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 +405,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 @@ -777,24 +801,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 +828,11 @@ qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \ qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \ qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \ -dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \ +dnsptr dnsip dnsmxip dnsfq dnstxt hostname ipmeprint qreceipt qsmhook qbiff \ forward preline condredirect bouncesaying except maildirmake \ maildir2mbox maildirwatch qail elq pinq idedit install-big install \ instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ -binm3 binm3+df +binm3 binm3+df spfquery load: \ make-load warn-auto.sh systype @@ -1333,10 +1357,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` @@ -1439,14 +1463,15 @@ qmail-remote: \ load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \ -timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o ipme.o quote.o \ +timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o strsalloc.o ipme.o quote.o \ ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \ substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib ./load qmail-remote control.o constmap.o timeoutread.o \ timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \ - ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ + ipalloc.o strsalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \ - str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` + str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` \ + -lssl -lcrypto qmail-remote.0: \ qmail-remote.8 @@ -1455,7 +1480,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 +1508,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 @@ -1528,21 +1553,21 @@ compile qmail-showctl.c substdio.h subfd.h substdio.h exit.h fmt.h \ str.h control.h constmap.h stralloc.h gen_alloc.h direntry.h \ auto_uids.h auto_qmail.h auto_break.h auto_patrn.h auto_spawn.h \ -auto_split.h +auto_split.h spf.h ./compile qmail-showctl.c qmail-smtpd: \ load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ -timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ -date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ -open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ -fs.a auto_qmail.o socket.lib +timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o constmap.o \ +received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a fd.a wait.a \ +datetime.a getln.a open.a sig.a case.a env.a stralloc.a alloc.a strerr.a \ +substdio.a error.a str.a fs.a auto_qmail.o base64.o socket.lib dns.lib ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ - timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ - received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ - datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ - alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ - socket.lib` + timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o \ + constmap.o received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a \ + fd.a wait.a datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ + alloc.a strerr.a substdio.a error.a str.a fs.a auto_qmail.o \ + base64.o `cat socket.lib` `cat dns.lib` -lssl -lcrypto qmail-smtpd.0: \ qmail-smtpd.8 @@ -1551,9 +1576,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 ./compile qmail-smtpd.c qmail-start: \ @@ -1779,7 +1805,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 +1839,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 +1851,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 +1924,23 @@ ./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 + ./compile spf.c + +spfquery: \ +load spfquery.o spf.o ip.o ipme.o ipalloc.o strsalloc.o now.o dns.o \ +datetime.a stralloc.a alloc.a str.a substdio.a error.a fs.a case.a dns.lib + ./load spfquery spf.o ip.o ipme.o ipalloc.o strsalloc.o \ + now.o dns.o datetime.a stralloc.a alloc.a str.a substdio.a \ + case.a error.a fs.a `cat dns.lib` + +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 +1956,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 +1971,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 +2054,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 +2119,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 +2194,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-6b/README.auth --- qmail-1.03-factory/README.auth 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-6b/README.auth 2005-05-23 15:13:57.000000000 -0400 @@ -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/TARGETS qmail-1.03-6b/TARGETS --- qmail-1.03-factory/TARGETS 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/TARGETS 2005-05-23 15:13:57.000000000 -0400 @@ -100,11 +100,14 @@ str_diff.o str_diffn.o str_cpy.o +str_cpyb.o str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o +byte_cspn.o +byte_rcspn.o byte_diff.o byte_copy.o byte_cr.o @@ -171,8 +174,10 @@ timeoutconn.o tcpto.o dns.o +spf.o ip.o ipalloc.o +strsalloc.o hassalen.h ipme.o ndelay.o @@ -212,6 +217,9 @@ headerbody.o hfield.o token822.o +spf.o +spfquery.o +spfquery qmail-inject predate.o predate @@ -250,6 +258,7 @@ qmail-qmtpd.o rcpthosts.o qmail-qmtpd +base64.o qmail-smtpd.o qmail-smtpd sendmail.o @@ -270,6 +279,8 @@ dnsip dnsmxip.o dnsmxip +dnstxt.o +dnstxt dnsfq.o dnsfq hostname.o diff -ruN qmail-1.03-factory/base64.c qmail-1.03-6b/base64.c --- qmail-1.03-factory/base64.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-6b/base64.c 2005-05-23 15:13:57.000000000 -0400 @@ -0,0 +1,90 @@ +#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 i, j; + unsigned char a[4]; + unsigned char b[3]; + char *s; + + if (l == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + if (!stralloc_ready(out,l + 2)) return -1; /* XXX generous */ + s = out->s; + + for (i = 0;i < l;i += 4) { + for (j = 0;j < 4;j++) + if ((i + j) < l && in[i + j] != B64PAD) + { + a[j] = str_chr(b64alpha,in[i + j]); + if (a[j] > 63) return 1; + } + else a[j] = 0; + + b[0] = (a[0] << 2) | (a[1] >> 4); + b[1] = (a[1] << 4) | (a[2] >> 2); + b[2] = (a[2] << 6) | (a[3]); + + *s++ = b[0]; + + if (in[i + 1] == B64PAD) break; + *s++ = b[1]; + + if (in[i + 2] == B64PAD) break; + *s++ = b[2]; + } + out->len = s - out->s; + while (out->len && !out->s[out->len - 1]) --out->len; /* XXX avoid? */ + 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-6b/base64.h --- qmail-1.03-factory/base64.h 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-6b/base64.h 2005-05-23 15:13:57.000000000 -0400 @@ -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-6b/byte.h --- qmail-1.03-factory/byte.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/byte.h 2005-05-23 15:13:57.000000000 -0400 @@ -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-6b/byte_cspn.c --- qmail-1.03-factory/byte_cspn.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-6b/byte_cspn.c 2005-05-23 15:13:57.000000000 -0400 @@ -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-6b/byte_rcspn.c --- qmail-1.03-factory/byte_rcspn.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-6b/byte_rcspn.c 2005-05-23 15:13:57.000000000 -0400 @@ -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/conf-cc qmail-1.03-6b/conf-cc --- qmail-1.03-factory/conf-cc 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/conf-cc 2005-05-23 15:13:57.000000000 -0400 @@ -1,3 +1,3 @@ -cc -O2 +cc -O2 -DTLS -I/usr/local/ssl/include -I/usr/kerberos/include This will be used to compile .c files. diff -ruN qmail-1.03-factory/date822fmt.c qmail-1.03-6b/date822fmt.c --- qmail-1.03-factory/date822fmt.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/date822fmt.c 2005-05-23 15:13:57.000000000 -0400 @@ -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-6b/dns.c --- qmail-1.03-factory/dns.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/dns.c 2005-05-23 15:13:57.000000000 -0400 @@ -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" @@ -18,6 +19,8 @@ #include "dns.h" #include "case.h" +#define MAXTXT 256 + static unsigned short getshort(c) unsigned char *c; { unsigned short u; u = c[0]; return (u << 8) + c[1]; } @@ -29,6 +32,7 @@ static int numanswers; static char name[MAXDNAME]; static struct ip_address ip; +static char txt[MAXTXT]; unsigned short pref; static stralloc glue = {0}; @@ -179,6 +183,38 @@ return 0; } +static int findtxt(wanttype) +int wanttype; +{ + unsigned short rrtype; + unsigned short rrdlen; + int i; + + if (numanswers <= 0) return 2; + --numanswers; + if (responsepos == responseend) return DNS_SOFT; + + i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); + if (i < 0) return DNS_SOFT; + responsepos += i; + + i = responseend - responsepos; + if (i < 4 + 3 * 2) return DNS_SOFT; + + rrtype = getshort(responsepos); + rrdlen = getshort(responsepos + 8); + responsepos += 10; + + if (rrtype == wanttype) { + str_copyb(txt, responsepos, rrdlen); + responsepos += rrdlen; + return 1; + } + + responsepos += rrdlen; + return 0; +} + void dns_init(flagsearch) int flagsearch; { @@ -237,15 +273,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 +295,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 +331,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 +399,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 +470,49 @@ alloc_free(mx); return flagsoft; } + + +static int dns_txtplus(ssa,sa) +strsalloc *ssa; +stralloc *sa; +{ + int r; + + switch(resolve(sa,T_TXT)) + { + case DNS_MEM: return DNS_MEM; + case DNS_SOFT: return DNS_SOFT; + case DNS_HARD: return DNS_HARD; + } + while ((r = findtxt(T_TXT)) != 2) + { + if (r == DNS_SOFT) return DNS_SOFT; + if (r == 1) + { + stralloc sa = {0}; + if (!stralloc_copyb(&sa,txt+1,(unsigned char)txt[0])) return DNS_MEM; + if (!strsalloc_append(ssa,&sa)) return DNS_MEM; + } + } + if (ssa->len) return 0; + return DNS_HARD; +} + +int dns_txt(ssa,sa) +strsalloc *ssa; +stralloc *sa; +{ + int r; + int j; + + if (!strsalloc_readyplus(ssa,0)) return DNS_MEM; + ssa->len = 0; + r = dns_txtplus(ssa,sa); + if (r < 0) + { + for (j = 0;j < ssa->len;++j) + alloc_free(ssa->sa[j].s); + ssa->len = 0; + } + return r; +} diff -ruN qmail-1.03-factory/dns.h qmail-1.03-6b/dns.h --- qmail-1.03-factory/dns.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/dns.h 2005-05-23 15:13:57.000000000 -0400 @@ -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-6b/dnsfq.c --- qmail-1.03-factory/dnsfq.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/dnsfq.c 2005-05-23 15:13:57.000000000 -0400 @@ -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-6b/dnsptr.c --- qmail-1.03-factory/dnsptr.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/dnsptr.c 2005-05-23 15:13:57.000000000 -0400 @@ -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-6b/dnstxt.c --- qmail-1.03-factory/dnstxt.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-6b/dnstxt.c 2005-05-23 15:13:57.000000000 -0400 @@ -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-6b/error.c --- qmail-1.03-factory/error.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/error.c 2005-05-23 15:13:57.000000000 -0400 @@ -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-6b/error.h --- qmail-1.03-factory/error.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/error.h 2005-05-23 15:13:57.000000000 -0400 @@ -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/ipalloc.h qmail-1.03-6b/ipalloc.h --- qmail-1.03-factory/ipalloc.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/ipalloc.h 2005-05-23 15:13:57.000000000 -0400 @@ -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-6b/ipme.c --- qmail-1.03-factory/ipme.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/ipme.c 2005-05-23 15:13:58.000000000 -0400 @@ -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/qmail-control.9 qmail-1.03-6b/qmail-control.9 --- qmail-1.03-factory/qmail-control.9 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/qmail-control.9 2005-05-23 15:13:58.000000000 -0400 @@ -55,6 +55,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 +64,10 @@ .I rcpthosts \fR(none) \fRqmail-smtpd .I smtpgreeting \fIme \fRqmail-smtpd .I smtproutes \fR(none) \fRqmail-remote +.I spfbehavior \fR0 \fRqmail-smtpd +.I spfexp \fR(default) \fRqmail-smtpd +.I spfguess \fR(none) \fRqmail-smtpd +.I spfrules \fR(none) \fRqmail-smtpd .I timeoutconnect \fR60 \fRqmail-remote .I timeoutremote \fR1200 \fRqmail-remote .I timeoutsmtpd \fR1200 \fRqmail-smtpd diff -ruN qmail-1.03-factory/qmail-local.c qmail-1.03-6b/qmail-local.c --- qmail-1.03-factory/qmail-local.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/qmail-local.c 2005-05-23 15:13:58.000000000 -0400 @@ -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-remote.c qmail-1.03-6b/qmail-remote.c --- qmail-1.03-factory/qmail-remote.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/qmail-remote.c 2005-05-23 15:13:58.000000000 -0400 @@ -26,8 +26,18 @@ #include "tcpto.h" #include "readwrite.h" #include "timeoutconn.h" +#ifndef TLS #include "timeoutread.h" #include "timeoutwrite.h" +#endif + +#ifdef TLS +#include +#include +SSL *ssl = NULL; + +stralloc tlsclientciphers = {0}; +#endif #define HUGESMTPTEXT 5000 @@ -107,17 +117,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 +273,34 @@ out(append); out(".\n"); outsmtptext(); + +/* TAG */ +#if defined(TLS) && defined(DEBUG) +#define ONELINE_NAME(X) X509_NAME_oneline(X,NULL,0) + + if(ssl){ + X509 *peer; + + out("STARTTLS proto="); out(SSL_get_version(ssl)); + out("; cipher="); out(SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); + + /* we want certificate details */ + peer=SSL_get_peer_certificate(ssl); + if (peer != NULL) { + char *str; + + str=ONELINE_NAME(X509_get_subject_name(peer)); + out("; subject="); out(str); + OPENSSL_free(str); + str=ONELINE_NAME(X509_get_issuer_name(peer)); + out("; issuer="); out(str); + OPENSSL_free(str); + X509_free(peer); + } + out(";\n"); + } +#endif + zerodie(); } @@ -216,20 +331,158 @@ stralloc recip = {0}; +#ifdef TLS +void smtp(fqdn) +char *fqdn; +#else void smtp() +#endif { unsigned long code; int flagbother; int i; - +#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 + if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); +#ifdef TLS + substdio_puts(&smtpto,"EHLO "); +#else substdio_puts(&smtpto,"HELO "); +#endif substdio_put(&smtpto,helohost.s,helohost.len); substdio_puts(&smtpto,"\r\n"); substdio_flush(&smtpto); +#ifdef TLS + 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"); + } +#else if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); - +#endif + +#ifdef 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();} +#endif + substdio_puts(&smtpto,"MAIL FROM:<"); substdio_put(&smtpto,sender.s,sender.len); substdio_puts(&smtpto,">\r\n"); @@ -324,6 +577,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) @@ -338,7 +596,10 @@ int flagallaliases; int flagalias; char *relayhost; - + +#ifdef TLS + sig_alarmcatch(sigalrm); +#endif sig_pipeignore(); if (argc < 4) perm_usage(); if (chdir(auto_qmail) == -1) temp_chdir(); @@ -417,7 +678,11 @@ if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { tcpto_err(&ip.ix[i].ip,0); partner = ip.ix[i].ip; +#ifdef TLS + smtp(ip.ix[i].fqdn); /* does not return */ +#else smtp(); /* does not return */ +#endif } tcpto_err(&ip.ix[i].ip,errno == error_timeout); close(smtpfd); diff -ruN qmail-1.03-factory/qmail-send.c qmail-1.03-6b/qmail-send.c --- qmail-1.03-factory/qmail-send.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/qmail-send.c 2005-05-23 15:13:58.000000000 -0400 @@ -44,6 +44,8 @@ int lifetime = 604800; +int bouncemaxbytes = 0; + stralloc percenthack = {0}; struct constmap mappercenthack; stralloc locals = {0}; @@ -740,9 +742,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); @@ -1442,6 +1461,7 @@ /* 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; diff -ruN qmail-1.03-factory/qmail-showctl.c qmail-1.03-6b/qmail-showctl.c --- qmail-1.03-factory/qmail-showctl.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/qmail-showctl.c 2005-05-23 15:13:58.000000000 -0400 @@ -15,6 +15,7 @@ #include "auto_patrn.h" #include "auto_spawn.h" #include "auto_split.h" +#include "spf.h" stralloc me = {0}; int meok; @@ -257,6 +258,10 @@ do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 "); do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ",""); + do_int("spfbehavior","0","The SPF behavior is ",""); + do_str("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"); @@ -292,6 +297,10 @@ if (str_equal(d->d_name,"rcpthosts")) continue; if (str_equal(d->d_name,"smtpgreeting")) continue; if (str_equal(d->d_name,"smtproutes")) continue; + if (str_equal(d->d_name,"spfbehavior")) continue; + if (str_equal(d->d_name,"spfexp")) continue; + if (str_equal(d->d_name,"spfguess")) continue; + if (str_equal(d->d_name,"spfrules")) continue; if (str_equal(d->d_name,"timeoutconnect")) continue; if (str_equal(d->d_name,"timeoutremote")) continue; if (str_equal(d->d_name,"timeoutsmtpd")) continue; diff -ruN qmail-1.03-factory/qmail-smtpd.8 qmail-1.03-6b/qmail-smtpd.8 --- qmail-1.03-factory/qmail-smtpd.8 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/qmail-smtpd.8 2005-05-23 15:13:58.000000000 -0400 @@ -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,29 @@ header fields. .B qmail-smtpd -supports ESMTP, including the 8BITMIME and PIPELINING options. +supports ESMTP, including the 8BITMIME, PIPELINING, and AUTH options. + +.B qmail-smtpd +can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types. It invokes +.IR checkprogram , +which reads on file descriptor 3 the username, a 0 byte, the password +or challenge derived from +.IR hostname , +another 0 byte, a CRAM-MD5 response (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 @@ -97,6 +124,12 @@ This is done before .IR rcpthosts . .TP 5 +.I mfcheck +If set, +.B qmail-smtpd +tries to resolve the domain of the envelope from address. It can be +handy when you want to filter out spamhosts. +.TP 5 .I morercpthosts Extra allowed RCPT domains. If @@ -169,6 +202,41 @@ .B qmail-smtpd will wait for each new buffer of data from the remote SMTP client. Default: 1200. +.TP 5 +.I spfbehavior +Set to a value between 1 and 6 to enable SPF checks; 0 to disable. +1 selects 'annotate-only' mode, where +.B qmail-smtpd +will annotate incoming email with +.B Received-SPF +fields, but will not reject any messages. 2 will produce temporary +failures on DNS lookup problems so you can make sure you always have +meaningful Received-SPF headers. 3 selects 'reject' mode, +where incoming mail will be rejected if the SPF record says 'fail'. 4 +selects a more stricter rejection mode, which is like 'reject' mode, +except that incoming mail will also be rejected when the SPF record +says 'softfail'. 5 will also reject when the SPF record says 'neutral', +and 6 if no SPF records are available at all (or a syntax error was +encountered). The contents of this file are overridden by the value of +the +.B SPFBEHAVIOR +environment variable, if set. +Default: 0. +.TP 5 +.I 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. .SH "SEE ALSO" tcp-env(1), tcp-environ(5), @@ -177,3 +245,6 @@ qmail-newmrh(8), qmail-queue(8), qmail-remote(8) +.SH "HISTORY" +The patch enabling the ESMTP AUTH option is not part of the standard +qmail-1.03 distribution. diff -ruN qmail-1.03-factory/qmail-smtpd.c qmail-1.03-6b/qmail-smtpd.c --- qmail-1.03-factory/qmail-smtpd.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/qmail-smtpd.c 2005-05-23 15:14:01.000000000 -0400 @@ -20,18 +20,101 @@ #include "now.h" #include "exit.h" #include "rcpthosts.h" +#ifndef TLS #include "timeoutread.h" #include "timeoutwrite.h" +#endif #include "commands.h" +#include "strerr.h" +#include "wait.h" +#include "fd.h" +#include "dns.h" +#include "spf.h" +#include "cdb.h" + +#ifdef TLS +#include +#include +SSL *ssl = NULL; + +stralloc clientcert = {0}; +stralloc tlsserverciphers = {0}; +#endif +/* #define AUTHCRAM */ #define MAXHOPS 100 unsigned int databytes = 0; +unsigned int mfchk = 0; int timeout = 1200; +int rcptcounter = 0; +int maxrcpt = -1; +unsigned int spfbehavior = 0; +int useauth = 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 resp = {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; + +#ifdef TLS +unsigned int force_tls = 0; +unsigned int deny_tls = 0; +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)); + }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; } @@ -50,7 +133,14 @@ 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 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_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } @@ -59,8 +149,24 @@ void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } +int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; } +int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; } +int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; } +int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; } +void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); } +void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); } +int err_noauth() { out("504 auth type unimplemented (#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"); } + +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}; void smtp_greet(code) char *code; { @@ -81,6 +187,9 @@ char *remoteinfo; char *local; char *relayclient; +#ifdef TLS +char *tlsciphers; +#endif stralloc helohost = {0}; char *fakehelo; /* pointer into helohost, or 0 */ @@ -97,10 +206,18 @@ stralloc bmf = {0}; struct constmap mapbmf; +int vrtfd = -1; +int vrtcount = 0; +int vrtlimit = 10; +int vrtlog_do = 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) @@ -109,19 +226,40 @@ 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(); + x = env_get("MFCHECK"); + if (x) { scan_ulong(x,&u); mfchk = u; } + 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(); + + vrtfd = open_read("control/validrcptto.cdb"); + if (-1 == vrtfd) if (errno != error_noent) 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(); + x = env_get("SPFBEHAVIOR"); + if (x) { scan_ulong(x,&u); spfbehavior = u; } + + if (control_readline(&spflocal,"control/spfrules") == -1) die_control(); + if (spflocal.len && !stralloc_0(&spflocal)) die_nomem(); + if (control_readline(&spfguess,"control/spfguess") == -1) die_control(); + if (spfguess.len && !stralloc_0(&spfguess)) die_nomem(); + if (control_rldef(&spfexp,"control/spfexp",0,SPF_DEFEXP) == -1) + die_control(); + if (!stralloc_0(&spfexp)) die_nomem(); + remoteip = env_get("TCPREMOTEIP"); if (!remoteip) remoteip = "unknown"; local = env_get("TCPLOCALHOST"); @@ -131,6 +269,17 @@ 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(); +#endif + dohelo(remotehost); } @@ -208,6 +357,90 @@ return 0; } +int mfcheck() +{ + stralloc sa = {0}; + ipalloc ia = {0}; + unsigned int random; + int j; + + if (!mfchk) return 0; + random = now() + (getpid() << 16); + j = byte_rchr(addr.s,addr.len,'@') + 1; + if (j < addr.len) { + stralloc_copys(&sa, addr.s + j); + dns_init(0); + j = dns_mxip(&ia,&sa,random); + if (j < 0) return j; + } + return 0; +} + +void vrtlog(a,b) +const char *a; +const char *b; +{ + if (vrtlog_do) strerr_warn2(a,b,0); +} + +int vrtcheck() +{ + static char *rcptto = "qmail-smtpd: validrcptto RCPT TO: "; + static char *found = "qmail-smtpd: validrcptto found: "; + int j,k,r; + uint32 dlen; + stralloc laddr = {0}; + + stralloc user = {0}; + stralloc adom = {0}; + stralloc utry = {0}; + + if (-1 == vrtfd) return 1; + + /* lowercase whatever we were sent */ + if (!stralloc_copy(&laddr,&addr)) die_nomem() ; + case_lowerb(laddr.s,laddr.len); + + vrtlog(rcptto,laddr.s,0); + + /* exact match? */ + r = cdb_seek(vrtfd,laddr.s,laddr.len-1,&dlen) ; + if (r>0) { vrtlog(found,laddr.s,0); return r; } + + 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,'-'); + if (k >= user.len) break ; + + user.len = k+1; + stralloc_cats(&user,"default"); + + stralloc_copy(&utry,&user); + stralloc_cat (&utry,&adom); + stralloc_0(&utry); + + r = cdb_seek(vrtfd,utry.s,utry.len-1,&dlen); + if (r>0) { vrtlog(found,utry.s,0); return r; } + + user.len = k ; + } + + /* try "@domain" */ + + r = cdb_seek(vrtfd,laddr.s+j,laddr.len-j-1,&dlen) ; + if (r>0) { vrtlog(found,laddr.s+j,0); return r; } + } + + return 0; +} + int addrallowed() { int r; @@ -219,6 +452,8 @@ int seenmail = 0; int flagbarf; /* defined if seenmail */ +int flagbarfspf; +stralloc spfbarfmsg = {0}; stralloc mailfrom = {0}; stralloc rcptto = {0}; @@ -229,7 +464,25 @@ } void smtp_ehlo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + smtp_greet("250-"); +#ifdef TLS + if ( useauth && ( ssl || essl || allow_insecure_auth ) ) +#else + if ( useauth && ( essl || allow_insecure_auth ) ) +#endif + { +#ifdef AUTHCRAM + 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"); +#endif + } +#ifdef TLS + if (!(ssl||essl||deny_tls)) out("\r\n250-STARTTLS"); +#endif + out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg); } void smtp_rset() @@ -239,25 +492,164 @@ } void smtp_mail(arg) char *arg; { + int r; + rcptcounter = 0 ; +#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(); + switch(mfcheck()) { + 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; + } + 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(); 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 (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_warn4("qmail-smtpd: not in validrcptto: ",addr.s, + " at ",remoteip,0); + if(vrtlimit && (++vrtcount >= vrtlimit)) { + strerr_warn2("qmail-smtpd: excessive validrcptto violations," + " hanging up on ",remoteip,0); + die_vrt(); + } + err_vrt(); + return; + } + if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); @@ -269,7 +661,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; @@ -351,6 +747,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,20 +780,81 @@ out("\r\n"); } +void auth_fixenv() +{ + int i,f; + char *envi,*eq; + stralloc work = {0}; + + do + { + f=0; + for (i=0;envi=environ[i];++i) + { + if (!str_diffn("AUTH_SET_",envi,9)) + { + stralloc_copys(&work,envi); + stralloc_0(&work); + eq = env_findeq(work.s); + *eq=0; + env_unset(work.s); + if(authd) + { + env_unset(work.s+9); + *eq='='; + env_put(work.s+9); + } + f=1; + break; + } + if (!str_diffn("AUTH_UNSET_",envi,11)) + { + stralloc_copys(&work,envi); + stralloc_0(&work); + eq = env_findeq(work.s); + *eq=0; + env_unset(work.s); + if(authd) env_unset(work.s+11); + f=1; + break; + } + } + } while (f); +} + void smtp_data() { int hops; unsigned long qp; char *qqx; +#ifdef TLS + stralloc protocolinfo = {0}; +#endif if (!seenmail) { err_wantmail(); return; } if (!rcptto.len) { err_wantrcpt(); return; } seenmail = 0; if (databytes) bytestooverflow = databytes + 1; + auth_fixenv(); 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); @@ -389,29 +865,346 @@ 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 (*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, "control/servercert.pem", 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, "control/servercert.pem")) + {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(); + + if(!(ssl=SSL_new(ctx))) die_read(); + SSL_set_fd(ssl,0); + if(SSL_accept(ssl)<=0) die_read(); + substdio_fdbuf(&ssout,SSL_write,ssl,ssoutbuf,sizeof(ssoutbuf)); + + remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) remotehost = "unknown"; + dohelo(remotehost); +} +#endif + + +int authgetl(void) { + int i; + + if (!stralloc_copys(&authin, "")) die_nomem(); + + for (;;) { + if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ + i = substdio_get(&ssin,authin.s + authin.len,1); + if (i != 1) die_read(); + if (authin.s[authin.len] == '\n') break; + ++authin.len; + } + + if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len; + authin.s[authin.len] = 0; + + if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); } + if (authin.len == 0) { return err_input(); } + return authin.len; +} + +int authenticate(void) +{ + int child; + int wstat; + int pi[2]; + + if (!stralloc_0(&user)) die_nomem(); + if (!stralloc_0(&pass)) die_nomem(); + if (!stralloc_0(&resp)) die_nomem(); + + if (fd_copy(2,1) == -1) return err_pipe(); + close(3); + if (pipe(pi) == -1) return err_pipe(); + if (pi[0] != 3) return err_pipe(); + switch(child = fork()) { + case -1: + return err_fork(); + case 0: + close(pi[1]); + 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,resp.s,resp.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)) { sleep(5); return 1; } /* no */ + return 0; /* yes */ +} + +int auth_login(arg) char *arg; +{ + int r; + + 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 (*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(); +} + +#ifdef AUTHCRAM +int auth_cram() +{ + int i, r; + char *s; + + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + + if (!stralloc_copys(&pass,"<")) die_nomem(); + if (!stralloc_cats(&pass,unique)) die_nomem(); + if (!stralloc_cats(&pass,hostname)) die_nomem(); + if (!stralloc_cats(&pass,">")) die_nomem(); + if (b64encode(&pass,&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(&resp,s)) die_nomem(); + + if (!user.len || !resp.len) return err_input(); + return authenticate(); +} +#endif + +struct authcmd { + char *text; + int (*fun)(); +} authcmds[] = { + { "login", auth_login } +, { "plain", auth_plain } +#ifdef AUTHCRAM +, { "cram-md5", auth_cram } +#endif +, { 0, err_noauth } +}; + +void smtp_auth(arg) +char *arg; +{ + int i; + char *cmd = arg; + +#ifdef TLS + if (!( useauth && ( ssl || essl || allow_insecure_auth ) )) +#else + if (!( useauth && ( essl || allow_insecure_auth ) )) +#endif + { + out("503 auth not available (#5.3.3)\r\n"); + 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(&resp,"")) die_nomem(); + + i = str_chr(cmd,' '); + arg = cmd + i; + while (*arg == ' ') ++arg; + cmd[i] = 0; + + for (i = 0;authcmds[i].text;++i) + if (case_equals(authcmds[i].text,cmd)) break; + + switch (authcmds[i].fun(arg)) { + case 0: + authd = 1; + relayclient = ""; + 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 = 1; + } + +#ifdef TLS + sig_alarmcatch(sigalrm); +#endif sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); +#ifdef TLS + if(access("control/servercert.pem",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; } + + if(useauth) { + x = env_get("REQUIRE_AUTH"); + if(x) { scan_ulong(x,&u); if (u>0) require_auth = 1; } + } + + 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; } + setup(); if (ipme_init() != 1) die_ipme(); smtp_greet("220 "); @@ -419,3 +1212,9 @@ if (commands(&ssin,&smtpcommands) == 0) die_read(); die_nomem(); } + +int checkrcptcount() { + if (maxrcpt == -1) { return 0;} + else if (rcptcounter > maxrcpt ) { return 1;} + return 0; +} diff -ruN qmail-1.03-factory/qmail.c qmail-1.03-6b/qmail.c --- qmail-1.03-factory/qmail.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/qmail.c 2005-05-23 15:13:58.000000000 -0400 @@ -6,8 +6,17 @@ #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; @@ -15,6 +24,8 @@ int pim[2]; int pie[2]; + setup_qqargs(); + if (pipe(pim) == -1) return -1; if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; } @@ -95,8 +106,10 @@ switch(exitcode) { case 115: /* compatibility */ + case 1: return "IYour SPAM has been ignored."; 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)"; diff -ruN qmail-1.03-factory/sendmail.c qmail-1.03-6b/sendmail.c --- qmail-1.03-factory/sendmail.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/sendmail.c 2005-05-23 15:13:58.000000000 -0400 @@ -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/spf.c qmail-1.03-6b/spf.c --- qmail-1.03-factory/spf.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-6b/spf.c 2005-05-23 15:13:58.000000000 -0400 @@ -0,0 +1,868 @@ +#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" + +#define SPF_EXT -1 +#define SPF_SYNTAX -2 + +#define WSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\r' || (x) == '\n') +#define NXTOK(b, p, a) do { (b) = (p); \ + while((p) < (a)->len && !WSPACE((a)->s[(p)])) ++(p); \ + while((p) < (a)->len && WSPACE((a)->s[(p)])) (a)->s[(p)++] = 0; \ + } while(0) + +/* this table and macro came from wget more or less */ +/* and was in turn stolen by me from libspf as is :) */ +const static unsigned char urlchr_table[256] = +{ + 1, 1, 1, 1, 1, 1, 1, 1, /* NUL SOH STX ETX EOT ENQ ACK BEL */ + 1, 1, 1, 1, 1, 1, 1, 1, /* BS HT LF VT FF CR SO SI */ + 1, 1, 1, 1, 1, 1, 1, 1, /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + 1, 1, 1, 1, 1, 1, 1, 1, /* CAN EM SUB ESC FS GS RS US */ + 1, 0, 1, 1, 0, 1, 1, 0, /* SP ! " # $ % & ' */ + 0, 0, 0, 1, 0, 0, 0, 1, /* ( ) * + , - . / */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */ + 0, 0, 1, 1, 1, 1, 1, 1, /* 8 9 : ; < = > ? */ + 1, 0, 0, 0, 0, 0, 0, 0, /* @ A B C D E F G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */ + 0, 0, 0, 1, 1, 1, 1, 0, /* X Y Z [ \ ] ^ _ */ + 1, 0, 0, 0, 0, 0, 0, 0, /* ` a b c d e f g */ + 0, 0, 0, 0, 0, 0, 0, 0, /* h i j k l m n o */ + 0, 0, 0, 0, 0, 0, 0, 0, /* p q r s t u v w */ + 0, 0, 0, 1, 1, 1, 1, 1, /* x y z { | } ~ DEL */ + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + + +extern stralloc addr; +extern stralloc helohost; +extern char *remoteip; +extern char *local; + +extern stralloc spflocal; +extern stralloc spfguess; +extern stralloc spfexp; + +static stralloc sender_fqdn = {0}; +static stralloc explanation = {0}; +static stralloc expdomain = {0}; +static stralloc errormsg = {0}; +static char *received; + +static int recursion; +static struct ip_address ip; + +static void hdr_pass() { received = "pass (%{xr}: %{xs} designates %{i} as permitted sender)"; }; +static void hdr_softfail() { received = "softfail (%{xr}: transitioning %{xs} does not designate %{i} as permitted sender)"; }; +static void hdr_fail() { received = "fail (%{xr}: %{xs} does not designate %{i} as permitted sender)"; }; +static void hdr_unknown() { received = "unknown (%{xr}: 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; + char *p; + + if (!stralloc_readyplus(&spf, 0)) return SPF_NOMEM; + if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM; + + /* fallthrough result */ + if (main) hdr_none(); + +redirect: + if (++recursion > 20) { + alloc_free(spf.s); + alloc_free(sa.s); + hdr_unknown_msg("Maximum nesting level exceeded, possible loop"); + return SPF_SYNTAX; + } + + if (!stralloc_0(domain)) return SPF_NOMEM; + if (!stralloc_copy(&expdomain, domain)) return SPF_NOMEM; + + r = spfget(&spf, domain); + if (r == SPF_NONE) { + if (!main) { alloc_free(spf.s); return r; } + + if (spfguess.len) { + /* try to guess */ + if (!stralloc_copys(&spf, spfguess.s)) return SPF_NOMEM; + if (!stralloc_append(&spf, " ")) return SPF_NOMEM; + } else + spf.len = 0; + + /* append local rulest */ + if (spflocal.len) { + local_pos = spf.len; + if (!stralloc_cats(&spf, spflocal.s)) return SPF_NOMEM; + } + if (!stralloc_0(&spf)) return SPF_NOMEM; + + expdomain.len = 0; + } else if (r == SPF_OK) { + if (!stralloc_0(&spf)) return SPF_NOMEM; + if (main) hdr_neutral(); + r = SPF_NEUTRAL; + + /* try to add local rules before fail all mechs */ + if (main && spflocal.len) { + pos = 0; + p = (char *) 0; + while(pos < spf.len) { + NXTOK(begin, pos, &spf); + if (!spf.s[begin]) continue; + + if (p && spf.s[begin] != *p) p = (char *) 0; + if (!p && (spf.s[begin] == '-' || spf.s[begin] == '~' || + spf.s[begin] == '?')) p = &spf.s[begin]; + + if (p && p > spf.s && str_equal(spf.s + begin + 1, "all")) { + /* ok, we can insert the local rules at p */ + local_pos = p - spf.s; + + stralloc_readyplus(&spf, spflocal.len); + p = spf.s + local_pos; + byte_copyr(p + spflocal.len, spf.len - local_pos, p); + byte_copy(p, spflocal.len, spflocal.s); + spf.len += spflocal.len; + + pos += spflocal.len; + break; + } + } + + if (pos >= spf.len) pos = spf.len - 1; + for(i = 0; i < pos; i++) + if (!spf.s[i]) spf.s[i] = ' '; + } + } else { + alloc_free(spf.s); + return r; + } + + pos = 0; + done = 0; + while(pos < spf.len) { + NXTOK(begin, pos, &spf); + if (!spf.s[begin]) continue; + + /* in local ruleset? */ + if (!done && local_pos >= 0 && begin >= local_pos) { + if (begin < (local_pos + spflocal.len)) + expdomain.len = 0; + else + if (!stralloc_copy(&expdomain, domain)) + return SPF_NOMEM; + } + + for (p = spf.s + begin;*p;++p) + if (*p == ':' || *p == '/' || *p == '=') break; + + if (*p == '=') { + *p++ = 0; + + /* modifiers are simply handled here */ + if (str_equal(spf.s + begin, "redirect")) { + if (done) continue; + + if (!spfexpand(&sa, p, domain->s)) return SPF_NOMEM; + stralloc_copy(domain, &sa); + + 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_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-6b/spf.h --- qmail-1.03-factory/spf.h 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-6b/spf.h 2005-05-23 15:13:58.000000000 -0400 @@ -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-6b/spfquery.c --- qmail-1.03-factory/spfquery.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-6b/spfquery.c 2005-05-23 15:13:58.000000000 -0400 @@ -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-6b/str.h --- qmail-1.03-factory/str.h 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/str.h 2005-05-23 15:13:58.000000000 -0400 @@ -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-6b/str_cpyb.c --- qmail-1.03-factory/str_cpyb.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-6b/str_cpyb.c 2005-05-23 15:13:58.000000000 -0400 @@ -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/strsalloc.c qmail-1.03-6b/strsalloc.c --- qmail-1.03-factory/strsalloc.c 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-6b/strsalloc.c 2005-05-23 15:13:58.000000000 -0400 @@ -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-6b/strsalloc.h --- qmail-1.03-factory/strsalloc.h 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03-6b/strsalloc.h 2005-05-23 15:13:58.000000000 -0400 @@ -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-6b/tcp-env.c --- qmail-1.03-factory/tcp-env.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/tcp-env.c 2005-05-23 15:13:58.000000000 -0400 @@ -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-6b/timeoutconn.c --- qmail-1.03-factory/timeoutconn.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03-6b/timeoutconn.c 2005-05-23 15:13:58.000000000 -0400 @@ -10,6 +10,54 @@ #include "byte.h" #include "timeoutconn.h" +#define BIND_SOCKET 1 /* 0 to ignore bind fail, 1 to tempfail and requeue */ +#ifdef BIND_SOCKET +#include "control.h" +#include "constmap.h" +#include "stralloc.h" + +int bind_socket(s,ip) +int s; +struct ip_address *ip; +{ + struct sockaddr_in salocal; + struct ip_address iplocal; + char *ipstr, ipstring[IPFMT+1]; + int iplen; + stralloc routes = {0}; + struct constmap bindroutes; + char *bindroute = (char *)0; + + /* Right, do we actually have any bind routes? */ + switch(control_readfile(&routes,"control/bindroutes",0)) + { + case 0: return 0; /* no file, no bind to worry about */ + case -1: return -2; /* buggered up somewhere, urgh! */ + case 1: if (!constmap_init(&bindroutes,routes.s,routes.len,1)) return -3; + } + + ipstring[0] = '.'; /* "cheating", but makes the loop check easier below! */ + ipstr = ipstring+1; + iplen = ip_fmt(ipstr,ip); /* Well, Dan seems to trust its output! */ + + /* check d.d.d.d, d.d.d., d.d., d., none */ + bindroute = constmap(&bindroutes,ipstr,iplen); + if (!bindroute) while (iplen--) /* no worries - the lost char must be 0-9 */ + if (ipstring[iplen] == '.') + if (bindroute = constmap(&bindroutes,ipstr,iplen)) break; + if (!bindroute || !*bindroute) return 0; /* no bind required */ + if (!ip_scan(bindroute,&iplocal)) return -4; /* wasn't an ip returned */ + + byte_zero(&salocal,sizeof(salocal)); + salocal.sin_family = AF_INET; + byte_copy(&salocal.sin_addr,4,&iplocal); + + if (bind(s, (struct sockaddr *)&salocal,sizeof(salocal))) return BIND_SOCKET; + return 0; +} + +#endif + int timeoutconn(s,ip,port,timeout) int s; struct ip_address *ip; @@ -31,6 +79,9 @@ if (ndelay_on(s) == -1) return -1; /* XXX: could bind s */ +#ifdef BIND_SOCKET + if (ch = bind_socket(s,ip)) return ch; +#endif if (connect(s,(struct sockaddr *) &sin,sizeof(sin)) == 0) { ndelay_off(s);