diff --git a/FILES b/FILES
index 0f70971..2c463e9 100644
--- a/FILES
+++ b/FILES
@@ -3,6 +3,7 @@ BLURB2
BLURB3
BLURB4
README
+README.auth
FAQ
INSTALL
INSTALL.alias
@@ -40,6 +41,7 @@ TARGETS
Makefile
BIN.README
BIN.Makefile
+LICENSE.authentication
idedit.c
conf-break
auto_break.h
@@ -136,6 +138,8 @@ dnsfq.c
dnsip.c
dnsmxip.c
dnsptr.c
+dnstxt.c
+spfquery.c
hostname.c
ipmeprint.c
tcp-env.c
@@ -336,13 +340,16 @@ envread.c
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
@@ -402,6 +409,8 @@ date822fmt.h
date822fmt.c
dns.h
dns.c
+spf.h
+spf.c
trylsock.c
tryrsolv.c
ip.h
@@ -432,3 +441,12 @@ maildir.c
tcp-environ.5
constmap.h
constmap.c
+base64.c
+base64.h
+case_startb.c
+global.h
+hmac_md5.h
+hmac_md5.c
+install_authentication.sh
+md5.h
+md5c.c
diff --git a/FILES.auth b/FILES.auth
deleted file mode 100644
index e9c4b44..0000000
--- a/FILES.auth
+++ /dev/null
@@ -1,27 +0,0 @@
-The qmail-smtpd Auth patch modifies the following QMAIL 1.03 files:
-
-= TARGETS
-= Makefile
-= qmail-smtpd.c
-= qmail-smtpd.8
-= qmail-remote.c
-= qmail-remote.8
-= qmail-showctl.c
-= qmail-control.9
-
-Added files:
-
-+ base64.c
-+ base64.h
-+ case_startb.c
-+ hmac_md5.h
-+ hmac_md5.c
-+ global.h
-+ md5.h
-+ md5c.c
-
-Informational files:
-
-% install_auth.sh (Installation shell script)
-% LICENSE.authentication (some sort of ...)
-% README.auth
diff --git a/LICENSE.authentication b/LICENSE.authentication
index 707a870..ee95da6 100644
--- a/LICENSE.authentication
+++ b/LICENSE.authentication
@@ -3,9 +3,9 @@ AUTHOR
Author:
Dr. Erwin Hoffmann - FEHCom Germany
-Web-Site:
+Web-Site:
http://www.fehcom.de/qmail.html
-E-Mail:
+E-Mail:
feh@fehcom.de
diff --git a/Makefile b/Makefile
index ca58373..a9b6dcb 100644
--- a/Makefile
+++ b/Makefile
@@ -140,14 +140,6 @@ base64.o: \
compile base64.c base64.h stralloc.h substdio.h str.h
./compile base64.c
-md5c.o : \
-compile md5c.c md5.h
- ./compile md5c.c
-
-hmac_md5.o : \
-compile hmac_md5.c hmac_md5.h global.h
- ./compile hmac_md5.c
-
binm1: \
binm1.sh conf-qmail
cat binm1.sh \
@@ -215,6 +207,10 @@ byte_cr.o: \
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
@@ -223,6 +219,10 @@ byte_rchr.o: \
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
@@ -405,84 +405,96 @@ compile trydrent.c direntry.h1 direntry.h2
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
@@ -717,6 +729,10 @@ hier.o: \
compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h
./compile hier.c
+hmac_md5.o : \
+compile hmac_md5.c hmac_md5.h global.h
+ ./compile hmac_md5.c
+
home: \
home.sh conf-qmail
cat home.sh \
@@ -789,24 +805,24 @@ compile ip.c fmt.h scan.h ip.h
./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: \
@@ -816,11 +832,11 @@ predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \
qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \
qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \
qmail-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 \
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
@@ -953,6 +969,10 @@ mbox.0: \
mbox.5
nroff -man mbox.5 > mbox.0
+md5c.o : \
+compile md5c.c md5.h
+ ./compile md5c.c
+
myctime.o: \
compile myctime.c datetime.h fmt.h myctime.h
./compile myctime.c
@@ -1451,14 +1471,14 @@ auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h
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 \
base64.o md5c.o hmac_md5.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 \
base64.o md5c.o hmac_md5.o \
str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib`
@@ -1470,7 +1490,7 @@ qmail-remote.8
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
@@ -1543,21 +1563,21 @@ qmail-showctl.o: \
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 base64.o socket.lib
+timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o constmap.o \
+received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a fd.a wait.a \
+datetime.a getln.a open.a sig.a case.a env.a stralloc.a alloc.a substdio.a \
+error.a str.a fs.a auto_qmail.o 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 base64.o `cat \
- socket.lib`
+ timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o \
+ constmap.o received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a \
+ fd.a wait.a datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
+ alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o \
+ str.a `cat socket.lib` `cat dns.lib`
qmail-smtpd.0: \
qmail-smtpd.8
@@ -1566,9 +1586,9 @@ qmail-smtpd.8
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 base64.h
+exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h spf.h base64.h
./compile qmail-smtpd.c
qmail-start: \
@@ -1760,6 +1780,12 @@ substdio.h alloc.h auto_qmail.h exit.h env.h str.h
setup: \
it man
./install
+ mv "`head -1 conf-qmail`/bin/qmail-remote" "`head -1 conf-qmail`/bin/qmail-remote-orig"
+ cp ./qmail-remote-dkim "`head -1 conf-qmail`/bin/qmail-remote"
+ ## It seems with qmail-remote being a Perl program, it cannot
+ ## have the same permissions as qmail-remote-orig. I could
+ ## get it work with 0755 and did not investigate further.
+ chmod 0755 "`head -1 conf-qmail`/bin/qmail-remote"
sgetopt.o: \
compile sgetopt.c substdio.h subfd.h substdio.h sgetopt.h subgetopt.h \
@@ -1794,7 +1820,7 @@ qmail-popup.c qmail-pw2u.c qmail-qmqpc.c qmail-qmqpd.c qmail-qmtpd.c \
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 \
@@ -1828,8 +1854,9 @@ uint32.h2 tryulong32.c wait.3 wait.h wait_pid.c wait_nohang.c \
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 \
@@ -1839,7 +1866,7 @@ slurpclose.h slurpclose.c quote.h quote.c hfield.h hfield.c \
headerbody.h headerbody.c token822.h token822.c control.h control.c \
datetime.3 datetime.h datetime.c datetime_un.c prioq.h prioq.c \
date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \
-ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
+ipalloc.h strsalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \
prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \
maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c
@@ -1912,6 +1939,23 @@ auto_qmail.h auto_uids.h auto_spawn.h
./chkspawn
./compile spawn.c
+spf.o: \
+compile spf.c stralloc.h gen_alloc.h alloc.h ipme.h ip.h ipalloc.h \
+strsalloc.h str.h fmt.h scan.h byte.h now.h case.h
+ ./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` `cat socket.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 \
@@ -1927,12 +1971,12 @@ scan.h fmt.h
./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
@@ -1942,6 +1986,10 @@ str_cpy.o: \
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
@@ -2021,6 +2069,11 @@ strerr_sys.o: \
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
@@ -2081,11 +2134,11 @@ find-systype trycpp.c
tcp-env: \
load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \
-timeoutconn.o ip.o ipalloc.o case.a ndelay.a sig.a env.a getopt.a \
-stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
+timeoutconn.o ip.o ipalloc.o strsalloc.o case.a ndelay.a sig.a env.a \
+getopt.a stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
./load tcp-env dns.o remoteinfo.o timeoutread.o \
- timeoutwrite.o timeoutconn.o ip.o ipalloc.o case.a ndelay.a \
- sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \
+ timeoutwrite.o timeoutconn.o ip.o ipalloc.o strsalloc.o case.a \
+ ndelay.a sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \
str.a fs.a `cat dns.lib` `cat socket.lib`
tcp-env.0: \
diff --git a/README.auth b/README.auth
index 0a014b5..551d336 100644
--- a/README.auth
+++ b/README.auth
@@ -4,8 +4,8 @@ README qmail SMTP Authentication
Scope:
------
-This patch supports RFC 2554 "SMTP Service Extension for Authentication" and
-RFC 4409 "Message Submission for Mail" for
+This patch supports RFC 2554 "SMTP Service Extension for Authentication" and
+RFC 4409 "Message Submission for Mail" for
* qmail-smtpd and
* qmail-remote
@@ -48,7 +48,7 @@ In order activate SMTP authentication, you need to provide the environment
variable 'SMTPAUTH' to qmail-smtpd.
Possible choices:
-
+
a) SMTPAUTH=""; qmail-smtpd supports auth of type PLAIN and/or LOGIN.
b) SMTPAUTH="+cram"; qmail-smtpd will additionally annonce CRAM-MD5,
this requires a CRAM-MD5 supporting PAM.
@@ -74,7 +74,7 @@ but with additional authentication information (username + password):
Note: The choice of the AUTH method depends on the capabilities of the server.
-b) SMTP Authentication by recipient domain:
+b) SMTP Authentication by recipient domain:
The control file 'smtproutes' is enhanced with the authentication information:
@@ -125,7 +125,7 @@ Version: Notes: Date:
0.7.1 cosmetics for qmail-remote 5.8.2010
0.7.2 authorization-id = authentication-id
for qmail-remote;
- added SMTPAUTH environment variable
+ added SMTPAUTH environment variable
for qmail-smtpd; backport from SC 2.7 29.4.2012
0.7.3 Fixed missing AUTH for qmai-smtpd announcement.
Improved SUBMISSION port handling. 2.5.2012
@@ -134,7 +134,7 @@ Version: Notes: Date:
0.7.5 Fixed bug in qmail-remote not respecting
announced AUTH types. Tx. Callum Gibson.
Added '432' server code evaluation for
- AUTH password expired in qmail-remote. 23.10.2012
+ AUTH password expired in qmail-remote. 23.10.2012
0.7.6 Fixed order of SMTP commands (tx roberto).
02.02.2013
0.8.0 Added authentication by recipient domain
diff --git a/TARGETS b/TARGETS
index 4cba994..2b64490 100644
--- a/TARGETS
+++ b/TARGETS
@@ -101,11 +101,14 @@ 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
@@ -172,8 +175,10 @@ timeoutwrite.o
timeoutconn.o
tcpto.o
dns.o
+spf.o
ip.o
ipalloc.o
+strsalloc.o
hassalen.h
ipme.o
ndelay.o
@@ -215,6 +220,9 @@ qmail-inject.o
headerbody.o
hfield.o
token822.o
+spf.o
+spfquery.o
+spfquery
qmail-inject
predate.o
predate
@@ -273,6 +281,8 @@ dnsip.o
dnsip
dnsmxip.o
dnsmxip
+dnstxt.o
+dnstxt
dnsfq.o
dnsfq
hostname.o
diff --git a/base64.c b/base64.c
index 9784755..53da4ea 100644
--- a/base64.c
+++ b/base64.c
@@ -101,7 +101,7 @@ stralloc *out; /* not null terminated */
return 0;
}
- i = in->len / 3 * 4 + 4;
+ i = in->len / 3 * 4 + 4;
if (!stralloc_ready(out,i)) return -1;
s = out->s;
diff --git a/byte.h b/byte.h
index de06c69..927a5de 100644
--- a/byte.h
+++ b/byte.h
@@ -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 --git a/byte_cspn.c b/byte_cspn.c
new file mode 100644
index 0000000..ed40ebe
--- /dev/null
+++ b/byte_cspn.c
@@ -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 --git a/byte_rcspn.c b/byte_rcspn.c
new file mode 100644
index 0000000..e2bb0ec
--- /dev/null
+++ b/byte_rcspn.c
@@ -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 --git a/dns.c b/dns.c
index e9faad7..f70ce29 100644
--- a/dns.c
+++ b/dns.c
@@ -9,6 +9,7 @@ extern int res_query();
extern int res_search();
#include "ip.h"
#include "ipalloc.h"
+#include "strsalloc.h"
#include "fmt.h"
#include "alloc.h"
#include "str.h"
@@ -27,6 +28,7 @@ static unsigned char *responsepos;
static int numanswers;
static char name[MAXDNAME];
static struct ip_address ip;
+static stralloc txt = {0};
unsigned short pref;
static stralloc glue = {0};
@@ -177,6 +179,49 @@ int wanttype;
return 0;
}
+static int findtxt(wanttype)
+int wanttype;
+{
+ unsigned short rrtype;
+ unsigned short rrdlen;
+ int i;
+
+ if (numanswers <= 0) return 2;
+ --numanswers;
+ if (responsepos == responseend) return DNS_SOFT;
+
+ i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
+ if (i < 0) return DNS_SOFT;
+ responsepos += i;
+
+ i = responseend - responsepos;
+ if (i < 4 + 3 * 2) return DNS_SOFT;
+
+ rrtype = getshort(responsepos);
+ rrdlen = getshort(responsepos + 8);
+ responsepos += 10;
+
+ if (rrtype == wanttype)
+ {
+ unsigned short txtpos;
+ unsigned char txtlen;
+
+ txt.len = 0;
+ for (txtpos = 0;txtpos < rrdlen;txtpos += txtlen)
+ {
+ txtlen = responsepos[txtpos++];
+ if (txtlen > rrdlen-txtpos) txtlen = rrdlen-txtpos;
+ if (!stralloc_catb(&txt,&responsepos[txtpos],txtlen)) return DNS_MEM;
+ }
+
+ responsepos += rrdlen;
+ return 1;
+ }
+
+ responsepos += rrdlen;
+ return 0;
+}
+
void dns_init(flagsearch)
int flagsearch;
{
@@ -194,7 +239,7 @@ stralloc *sa;
if (!sa->len) return loop;
if (sa->s[sa->len - 1] == ']') return loop;
if (sa->s[sa->len - 1] == '.') { --sa->len; continue; }
- switch(resolve(sa,T_ANY))
+ switch(resolve(sa,T_CNAME))
{
case DNS_MEM: return DNS_MEM;
case DNS_SOFT: return DNS_SOFT;
@@ -235,15 +280,18 @@ struct ip_address *ip;
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;
@@ -254,13 +302,35 @@ struct ip_address *ip;
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;
@@ -396,3 +466,49 @@ unsigned long random;
alloc_free(mx);
return flagsoft;
}
+
+
+static int dns_txtplus(ssa,sa)
+strsalloc *ssa;
+stralloc *sa;
+{
+ int r;
+
+ switch(resolve(sa,T_TXT))
+ {
+ case DNS_MEM: return DNS_MEM;
+ case DNS_SOFT: return DNS_SOFT;
+ case DNS_HARD: return DNS_HARD;
+ }
+ while ((r = findtxt(T_TXT)) != 2)
+ {
+ if (r == DNS_SOFT) return DNS_SOFT;
+ if (r == 1)
+ {
+ stralloc sa = {0};
+ if (!stralloc_copy(&sa,&txt)) return DNS_MEM;
+ if (!strsalloc_append(ssa,&sa)) return DNS_MEM;
+ }
+ }
+ if (ssa->len) return 0;
+ return DNS_HARD;
+}
+
+int dns_txt(ssa,sa)
+strsalloc *ssa;
+stralloc *sa;
+{
+ int r;
+ int j;
+
+ if (!strsalloc_readyplus(ssa,0)) return DNS_MEM;
+ ssa->len = 0;
+ r = dns_txtplus(ssa,sa);
+ if (r < 0)
+ {
+ for (j = 0;j < ssa->len;++j)
+ alloc_free(ssa->sa[j].s);
+ ssa->len = 0;
+ }
+ return r;
+}
diff --git a/dns.h b/dns.h
index bca9490..b5992be 100644
--- a/dns.h
+++ b/dns.h
@@ -10,5 +10,6 @@ int dns_cname();
int dns_mxip();
int dns_ip();
int dns_ptr();
+int dns_txt();
#endif
diff --git a/dnsfq.c b/dnsfq.c
index b7619b9..a0ae90c 100644
--- a/dnsfq.c
+++ b/dnsfq.c
@@ -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 @@ char **argv;
{
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 --git a/dnsptr.c b/dnsptr.c
index 6a92fe0..270a33f 100644
--- a/dnsptr.c
+++ b/dnsptr.c
@@ -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 --git a/dnstxt.c b/dnstxt.c
new file mode 100644
index 0000000..247527f
--- /dev/null
+++ b/dnstxt.c
@@ -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 --git a/global.h b/global.h
index e0cf2dc..fa084b7 100644
--- a/global.h
+++ b/global.h
@@ -40,7 +40,7 @@ typedef unsigned short int UINT2;
/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
- returns an empty list.
+ returns an empty list.
*/
#if PROTOTYPES
#define PROTO_LIST(list) list
diff --git a/hier.c b/hier.c
index 28e568d..a1af469 100644
--- a/hier.c
+++ b/hier.c
@@ -104,7 +104,7 @@ void hier()
c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700);
c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711);
c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711);
- c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
@@ -131,7 +131,7 @@ void hier()
c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qsmhook",auto_uido,auto_gidq,0755);
- c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755);
+ /* c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755); */
c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755);
diff --git a/install_authentication.sh b/install_authentication.sh
index 49f3786..4d2fce3 100755
--- a/install_authentication.sh
+++ b/install_authentication.sh
@@ -15,7 +15,7 @@
#
# Return Codes: 0 - Patches applied successfully
# 1 - Original QMAIL files not found (Patch not extracted in QMAIL source directory)
-# 2 - Patch files not found
+# 2 - Patch files not found
#
# Output: install_auth.log
#
@@ -40,7 +40,7 @@ BUILD=20150823180830
if [ $# -eq 0 ] ; then
- echo "Installing qmail AUTH $REL (Build $BUILD) at $DATE <<<" | tee -a $LOGFILE 2>&1
+ echo "Installing qmail AUTH $REL (Build $BUILD) at $DATE <<<" | tee -a $LOGFILE 2>&1
for FILE in $(grep "^= " ${TARGETS} | awk '{print $2}'); do
echo "Targeting file $FILE ..." | tee -a $LOGFILE 2>&1
@@ -63,27 +63,27 @@ if [ $# -eq 0 ] ; then
echo "!! ${FILE}.patch not found !"
exit 2
fi
- done
+ done
- echo "Copying documentation and samples to ${QMAILHOME}/doc/ ..." | tee -a $LOGFILE 2>&1
+ echo "Copying documentation and samples to ${QMAILHOME}/doc/ ..." | tee -a $LOGFILE 2>&1
cp README.auth* ${QMAILHOME}/doc/ | tee -a $LOGFILE 2>&1
echo ""
echo "If you dont wont CRAM-MD5 suport disable '#define CRAM_MD5' in qmail-smtpd !"
- echo "Installation of qmail authentication $REL (Build $BUILD) finished at $DATE <<<" | tee -a $LOGFILE 2>&1
+ echo "Installation of qmail authentication $REL (Build $BUILD) finished at $DATE <<<" | tee -a $LOGFILE 2>&1
# Now go for the uninstallation....
elif [ "$1" = "-u" ] ; then
-# Get the Version Number from INPUT
+# Get the Version Number from INPUT
if [ $# -eq 2 ] ; then
REL=$2
fi
- echo "De-installing qmail authentication $REL (Build $BUILD) at $DATE <<<" | tee -a $LOGFILE 2>&1
+ echo "De-installing qmail authentication $REL (Build $BUILD) at $DATE <<<" | tee -a $LOGFILE 2>&1
for FILE in $(grep "^= " ${TARGETS} | awk '{print $2}'); do
echo "Targeting file $FILE ..." | tee -a $LOGFILE 2>&1
@@ -95,7 +95,7 @@ elif [ "$1" = "-u" ] ; then
echo "!! ${FILE}.$REL not found !"
fi
done
- echo "De-installation of qmail authentication $REL (Build $BUILD) finished at $DATE <<<" | tee -a $LOGFILE 2>&1
+ echo "De-installation of qmail authentication $REL (Build $BUILD) finished at $DATE <<<" | tee -a $LOGFILE 2>&1
fi
exit 0
diff --git a/md5.h b/md5.h
index 94774ba..9826d62 100644
--- a/md5.h
+++ b/md5.h
@@ -12,15 +12,15 @@
License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
- mentioning or referencing the derived work.
-
+ mentioning or referencing the derived work.
+
RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
- without express or implied warranty of any kind.
-
+ without express or implied warranty of any kind.
+
These notices must be retained in any copies of any part of this
- documentation and/or software.
+ documentation and/or software.
*/
#ifndef _MD5_H_
diff --git a/md5c.c b/md5c.c
index 8b0c856..c07ce23 100644
--- a/md5c.c
+++ b/md5c.c
@@ -12,15 +12,15 @@
License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
- mentioning or referencing the derived work.
-
+ mentioning or referencing the derived work.
+
RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
- without express or implied warranty of any kind.
-
+ without express or implied warranty of any kind.
+
These notices must be retained in any copies of any part of this
- documentation and/or software.
+ documentation and/or software.
*/
#include "global.h"
@@ -128,26 +128,26 @@ unsigned int inputLen; /* length of input block */
< ((UINT4)inputLen << 3))
context->count[1]++;
context->count[1] += ((UINT4)inputLen >> 29);
-
+
partLen = 64 - index;
-
+
/* Transform as many times as possible.
*/
if (inputLen >= partLen) {
MD5_memcpy
((POINTER)&context->buffer[index], (POINTER)input, partLen);
MD5Transform (context->state, context->buffer);
-
+
for (i = partLen; i + 63 < inputLen; i += 64)
MD5Transform (context->state, &input[i]);
-
+
index = 0;
}
else
i = 0;
-
+
/* Buffer remaining input */
- MD5_memcpy
+ MD5_memcpy
((POINTER)&context->buffer[index], (POINTER)&input[i],
inputLen-i);
}
@@ -170,13 +170,13 @@ MD5_CTX *context; /* context */
index = (unsigned int)((context->count[0] >> 3) & 0x3f);
padLen = (index < 56) ? (56 - index) : (120 - index);
MD5Update (context, PADDING, padLen);
-
+
/* Append length (before padding) */
MD5Update (context, bits, 8);
/* Store state in digest */
Encode (digest, context->state, 16);
-
+
/* Zeroize sensitive information.
*/
MD5_memset ((POINTER)context, 0, sizeof (*context));
@@ -189,7 +189,7 @@ UINT4 state[4];
unsigned char block[64];
{
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
-
+
Decode (x, block, 64);
/* Round 1 */
@@ -268,7 +268,7 @@ unsigned char block[64];
state[1] += b;
state[2] += c;
state[3] += d;
-
+
/* Zeroize sensitive information.
*/
MD5_memset ((POINTER)x, 0, sizeof (x));
@@ -315,7 +315,7 @@ POINTER input;
unsigned int len;
{
unsigned int i;
-
+
for (i = 0; i < len; i++)
output[i] = input[i];
}
@@ -328,7 +328,7 @@ int value;
unsigned int len;
{
unsigned int i;
-
+
for (i = 0; i < len; i++)
((char *)output)[i] = (char)value;
}
diff --git a/qmail-control.9 b/qmail-control.9
index 2857813..ac4f832 100644
--- a/qmail-control.9
+++ b/qmail-control.9
@@ -64,6 +64,10 @@ control default used by
.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 --git a/qmail-remote-dkim b/qmail-remote-dkim
new file mode 100755
index 0000000..63b053f
--- /dev/null
+++ b/qmail-remote-dkim
@@ -0,0 +1,308 @@
+#!/usr/local/bin/perl
+#
+# Copyright (C) 2007 Manuel Mausz (manuel@mausz.at)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+use strict;
+use warnings;
+our $VERSION = '0.3';
+
+use Mail::DKIM 0.29;
+use Mail::DKIM::Signer;
+
+# enable support for "pretty" signatures, if available
+eval 'require Mail::DKIM::TextWrap';
+
+=head
+config file structure
+ - missing settings will be merged from the global-node
+ - domain-entry will also match its subdomains
+ - create empty domain-node to omit signing (or specify "none" as id)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+=cut
+
+my $configfile = undef;
+$configfile = '/var/qmail/control/dkim/signconf.xml';
+my $debugfile = undef;
+#$debugfile = '/tmp/dkim.debug';
+my $qremote = '/var/qmail/bin/qmail-remote-orig';
+my $rcpthosts = '/var/qmail/control/rcpthosts';
+my $binary = 0;
+our $config;
+$config->{'global'} = {
+ types => { dkim => {} },
+ keyfile => '/var/qmail/control/dkim/global.key',
+ algorithm => 'rsa-sha256',
+ method => 'simple',
+ selector => 'beta',
+ # either undefined (=sender), string or file (first line of file will be used)
+ #domain => '/var/qmail/control/me'
+};
+
+#-------------------------------------------------------------------------------
+
+# read config file. safely
+if (defined($configfile) && -r $configfile)
+{
+ eval 'use XML::Simple';
+ if (!$@)
+ {
+ my $xmlconf;
+ eval { $xmlconf = XMLin($configfile, ForceArray => ['types'], KeyAttr => ['id']); };
+ qexit_deferral('Unable to read config file: ', $@)
+ if ($@);
+ ConfigMerge::merge($config, $xmlconf);
+ }
+}
+
+# open debug file
+my $debugfh = undef;
+if (defined($debugfile))
+{
+ open($debugfh, '>', $debugfile)
+ or qexit_deferral('Unable to open ', $debugfile, ' to writing: ', $!);
+}
+
+# generate signatures
+my $dkim;
+my $mailbuf = '';
+eval
+{
+ my $conf = $config->{'global'};
+ $dkim = Mail::DKIM::Signer->new(
+ Policy => 'MySignerPolicy',
+ Debug_Canonicalization => $debugfh
+ );
+
+ if ($binary)
+ {
+ binmode STDIN;
+ }
+
+ while ()
+ {
+ $mailbuf .= $_;
+ unless ($binary)
+ {
+ chomp $_;
+ s/\015?$/\015\012/s;
+ }
+ $dkim->PRINT($_);
+ }
+ $dkim->CLOSE();
+};
+qexit_deferral('Error while signing: ', $@)
+ if ($@);
+
+# close debug file
+close($debugfh)
+ if (defined($debugfh));
+
+# execute qmail-remote
+unshift(@ARGV, $qremote);
+open(QR, '|-') || exec { $ARGV[0] } @ARGV
+ or qexit_deferral('Unable to run qmail-remote: ', $!);
+foreach my $dkim_signature ($dkim->signatures)
+{
+ my $sig = $dkim_signature->as_string;
+ $sig =~ s/\015\012\t/\012\t/g;
+ print QR $sig."\012";
+}
+print QR $mailbuf;
+close(QR);
+
+#-------------------------------------------------------------------------------
+
+sub qexit
+{
+ print @_, "\0";
+ exit(0);
+}
+
+sub qexit_deferral
+{
+ return qexit('Z', @_);
+}
+
+sub qexit_failure
+{
+ return qexit('D', @_);
+}
+
+sub qexit_success
+{
+ return qexit('K', @_);
+}
+
+#-------------------------------------------------------------------------------
+
+package ConfigMerge;
+
+# merge config hashes. arrays and scalars will be copied.
+sub merge
+{
+ my ($left, $right) = @_;
+ foreach my $rkey (keys(%$right))
+ {
+ my $rtype = ref($right->{$rkey}) eq 'HASH' ? 'HASH'
+ : ref($right->{$rkey}) eq 'ARRAY' ? 'ARRAY'
+ : defined($right->{$rkey}) ? 'SCALAR'
+ : '';
+ my $ltype = ref($left->{$rkey}) eq 'HASH' ? 'HASH'
+ : ref($left->{$rkey}) eq 'ARRAY' ? 'ARRAY'
+ : defined($left->{$rkey}) ? 'SCALAR'
+ : '';
+ if ($rtype ne 'HASH' || $ltype ne 'HASH')
+ {
+ $left->{$rkey} = $right->{$rkey};
+ }
+ else
+ {
+ merge($left->{$rkey}, $right->{$rkey});
+ }
+ }
+ return;
+}
+
+#-------------------------------------------------------------------------------
+
+package MySignerPolicy;
+use Mail::DKIM::SignerPolicy;
+use base 'Mail::DKIM::SignerPolicy';
+use Mail::DKIM::Signature;
+use Mail::DKIM::DkSignature;
+use Carp;
+use strict;
+use warnings;
+
+sub apply
+{
+ my ($self, $signer) = @_;
+ my $domain = undef;
+ $domain = lc($signer->message_sender->host)
+ if (defined($signer) && defined($signer->message_sender)
+ && defined($signer->message_sender->host));
+ my $sender = $domain;
+
+ # merge configs
+ while($domain)
+ {
+ if (defined($config->{$domain}))
+ {
+ $config->{'global'}->{'types'} = undef;
+ ConfigMerge::merge($config->{'global'}, $config->{$domain});
+ last;
+ }
+ (undef, $domain) = split(/\./, $domain, 2);
+ }
+
+ my $conf = $config->{'global'};
+ return 0
+ if (!defined($conf->{'types'}) || defined($conf->{'types'}->{'none'}));
+
+ # set key file
+ $signer->key_file($conf->{'keyfile'});
+
+ # parse (signature) domain
+ if (!defined($conf->{'domain'}) || $conf->{'domain'} eq 'sender')
+ {
+ return 0
+ if (!defined($sender));
+
+ $conf->{'domain'} = undef;
+ open(FH, '<', $rcpthosts)
+ or croak('Unable to open rcpthosts: '.$!);
+ while (my $row = )
+ {
+ chomp($row);
+ if ($row eq $sender)
+ {
+ $conf->{'domain'} = $sender;
+ last;
+ }
+ }
+ close(FH);
+ return 0
+ if (!defined($conf->{'domain'}));
+ }
+ elsif (substr($conf->{'domain'}, 0, 1) eq '/')
+ {
+ open(FH, '<', $conf->{'domain'})
+ or croak('Unable to open domain-file: '.$!);
+ my $newdom = (split(/ /, ))[0];
+ close(FH);
+ croak("Unable to read domain-file. Maybe empty file.")
+ if (!$newdom);
+ chomp($newdom);
+ $conf->{'domain'} = $newdom;
+ }
+
+ # generate signatures
+ my $sigdone = 0;
+ foreach my $type (keys(%{$conf->{'types'}}))
+ {
+ my $sigconf = $conf->{'types'}->{$type};
+
+ if ($type eq 'dkim')
+ {
+ $signer->add_signature(
+ new Mail::DKIM::Signature(
+ Algorithm => $sigconf->{'algorithm'} || $conf->{'algorithm'} || $signer->algorithm,
+ Method => $sigconf->{'method'} || $conf->{'method'} || $signer->method,
+ Headers => $sigconf->{'headers'} || $conf->{'headers'} || $signer->headers,
+ Domain => $sigconf->{'domain'} || $conf->{'domain'} || $signer->domain,
+ Selector => $sigconf->{'selector'} || $conf->{'selector'} || $signer->selector,
+ Query => $sigconf->{'query'} || $conf->{'query'},
+ Identity => $sigconf->{'identity'} || $conf->{'identity'},
+ Expiration => $sigconf->{'expiration'} || $conf->{'expiration'}
+ )
+ );
+ $sigdone = 1;
+ }
+ elsif ($type eq 'domainkey')
+ {
+ $signer->add_signature(
+ new Mail::DKIM::DkSignature(
+ Algorithm => 'rsa-sha1', # only rsa-sha1 supported
+ Method => $sigconf->{'method'} || $conf->{'method'} || $signer->method,
+ Headers => $sigconf->{'selector'} || $conf->{'headers'} || $signer->headers,
+ Domain => $sigconf->{'domain'} || $conf->{'domain'} || $signer->domain,
+ Selector => $sigconf->{'selector'} || $conf->{'selector'} || $signer->selector,
+ Query => $sigconf->{'query'} || $conf->{'query'}
+ )
+ );
+ $sigdone = 1;
+ }
+ }
+
+ return $sigdone;
+}
diff --git a/qmail-remote.8 b/qmail-remote.8
index 8ab9b93..9a714a6 100644
--- a/qmail-remote.8
+++ b/qmail-remote.8
@@ -53,7 +53,6 @@ does not take options
and does not follow the
.B getopt
standard.
-
.SH TRANSPARENCY
End-of-file in SMTP is encoded as dot CR LF.
A dot at the beginning of a line is encoded as dot dot.
@@ -110,17 +109,17 @@ K
no supported AUTH method found, continuing without authentication.
.TP 5
Z
-Connected to
+Connected to
.I host
but authentication was rejected (AUTH PLAIN).
.TP 5
Z
-Connected to
-.I host
+Connected to
+.I host
but unable to base64encode (plain).
.TP 5
Z
-Connected to
+Connected to
.I host
but authentication was rejected (plain)."
.TP 5
@@ -130,18 +129,18 @@ Connected to
but authentication was rejected (AUTH LOGIN).
.TP 5
Z
-Connected to
+Connected to
.I host
but unable to base64encode user.
.TP 5
Z
-Connected to
-.I host
+Connected to
+.I host
but authentication was rejected (username).
.TP 5
Z
-Connected to
-.I host
+Connected to
+.I host
but unable to base64encode pass.
.TP 5
Z
@@ -149,23 +148,23 @@ Connected to
.I host
but authentication was rejected (AUTH CRAM-MD5).
Z
-Connected to
+Connected to
.I host
but unable to base64decode challenge.
.TP 5
Z
-Connected to
+Connected to
.I host
but unable to base64encode username+digest.
.TP 5
Z
-Connected to
-.I host
+Connected to
+.I host
but password expired.
.TP 5
Z
-Connected to
-.I host
+Connected to
+.I host
but authentication was rejected (username+digest).
.PP
The recipient reports will always be printed in the same order as
@@ -185,36 +184,36 @@ always exits zero.
.I authsenders
Authenticated sender.
For each
-.I sender
-included in
+.I sender
+included in
.IR authsenders :
-.I sender\fB:\fIrelay\fB:\fIport\fB|\fIuser\fB|\fIpassword
+.I sender\fB:\fIrelay\fB:\fIport\fB|\fIuser\fB|\fIpassword
.B qmail-remote
-will try SMTP Authentication
-of type CRAM-MD5, LOGIN, or PLAIN
+will try SMTP Authentication
+of type CRAM-MD5, LOGIN, or PLAIN
with the provided user name
-.I user
+.I user
and password
-.I password
-(the authentication information)
-and eventually relay the
+.I password
+(the authentication information)
+and eventually relay the
mail through
.I relay
on port
.IR port .
-The use of
+The use of
.I relay
-and
-.I port
+and
+.I port
follows the same rules as for
-.IR smtproutes
+.IR smtproutes
Note: In case
.I sender
-is empty,
+is empty,
.B qmail-remote
-will try to deliver each outgoing mail
+will try to deliver each outgoing mail
SMTP authenticated. If the authentication
-information is missing, the mail is
+information is missing, the mail is
delivered none-authenticated.
.I authsenders
can be constructed as follows:
@@ -241,7 +240,7 @@ refuses to run.
Artificial SMTP routes.
Each route has the form
.IR domain\fB:\fIrelay ,
-or
+or
.IR domain\fB:\fIrelay\fB|\fIuser\fB|\fIpassword
in case of authenticated routes without any extra spaces.
If
@@ -293,13 +292,13 @@ any other address is artificially routed to
The
.B qmail
system does not protect you if you create an artificial
-mail loop between machines.
+mail loop between machines.
However,
you are always safe using
.I smtproutes
if you do not accept mail from the network.
-Note:
-.I authsender
+Note:
+.I authsender
routes have precedence over
.IR smtproutes .
.TP 5
diff --git a/qmail-remote.c b/qmail-remote.c
index ddc7317..b89e728 100644
--- a/qmail-remote.c
+++ b/qmail-remote.c
@@ -251,12 +251,12 @@ int len;
int i;
if(!stralloc_copys(sa,"")) temp_nomem();
-
+
for (i = 0; i < len; i++) {
if (s[i] == '=') {
if (!stralloc_cats(sa,"+3D")) temp_nomem();
- } else if (s[i] == '+') {
- if (!stralloc_cats(sa,"+2B")) temp_nomem();
+ } else if (s[i] == '+') {
+ if (!stralloc_cats(sa,"+2B")) temp_nomem();
} else if ((int) s[i] < 33 || (int) s[i] > 126) {
if (!stralloc_cats(sa,"+3F")) temp_nomem(); /* ok. not correct */
} else if (!stralloc_catb(sa,s+i,1)) {
@@ -296,7 +296,7 @@ int mailfrom_plain()
if (smtpcode() == 235) { mailfrom_xtext(); return 0; }
else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; }
else { quit("ZConnected to "," but authentication was rejected (plain)."); return 1; }
-
+
return 0;
}
@@ -340,13 +340,13 @@ int mailfrom_cram()
if (!stralloc_copyb(&slop,smtptext.s+4,smtptext.len-5)) temp_nomem();
if (b64decode(slop.s,slop.len,&chal)) quit("ZConnected to "," but unable to base64decode challenge.");
}
-
+
hmac_md5(chal.s,chal.len,pass.s,pass.len,digest);
for (j = 0;j < 16;j++) /* HEX => ASCII */
{
- digascii[2*j] = hextab[digest[j] >> 4];
- digascii[2*j+1] = hextab[digest[j] & 0xf];
+ digascii[2*j] = hextab[digest[j] >> 4];
+ digascii[2*j+1] = hextab[digest[j] & 0xf];
}
digascii[32]=0;
@@ -354,7 +354,7 @@ int mailfrom_cram()
if (!stralloc_copys(&slop,"")) temp_nomem();
if (!stralloc_cat(&slop,&user)) temp_nomem(); /* user-id */
if (!stralloc_cats(&slop," ")) temp_nomem();
- if (!stralloc_catb(&slop,digascii,32)) temp_nomem(); /* digest */
+ if (!stralloc_catb(&slop,digascii,32)) temp_nomem(); /* digest */
if (!stralloc_copys(&auth,"")) temp_nomem();
if (b64encode(&slop,&auth)) quit("ZConnected to "," but unable to base64encode username+digest.");
@@ -363,25 +363,25 @@ int mailfrom_cram()
substdio_flush(&smtpto);
if (smtpcode() == 235) { mailfrom_xtext(); return 0; }
else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; }
- else { quit("ZConnected to "," but authentication was rejected (username+digest)."); return 1; }
+ else { quit("ZConnected to "," but authentication was rejected (username+digest)."); return 1; }
}
void smtp_auth()
{
- int i, j;
+ int i, j;
for (i = 0; i + 8 < smtptext.len; i += str_chr(smtptext.s+i,'\n')+1)
- if (!str_diffn(smtptext.s+i+4,"AUTH",4)) {
+ if (!str_diffn(smtptext.s+i+4,"AUTH",4)) {
if (j = str_chr(smtptext.s+i+8,'C') > 0)
if (case_starts(smtptext.s+i+8+j,"CRAM"))
if (mailfrom_cram() >= 0) return;
if (j = str_chr(smtptext.s+i+8,'P') > 0)
- if (case_starts(smtptext.s+i+8+j,"PLAIN"))
+ if (case_starts(smtptext.s+i+8+j,"PLAIN"))
if (mailfrom_plain() >= 0) return;
if (j = str_chr(smtptext.s+i+8,'L') > 0)
- if (case_starts(smtptext.s+i+8+j,"LOGIN"))
+ if (case_starts(smtptext.s+i+8+j,"LOGIN"))
if (mailfrom_login() >= 0) return;
err_authprot();
@@ -416,7 +416,7 @@ void smtp()
if (user.len && pass.len)
smtp_auth();
- else
+ else
mailfrom();
code = smtpcode();
@@ -509,7 +509,7 @@ void getcontrols()
case 1:
if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break;
}
-
+
switch(control_readfile(&authsenders,"control/authsenders",0)) {
case -1:
temp_control();
@@ -532,7 +532,7 @@ char **argv;
int flagallaliases;
int flagalias;
char *relayhost;
-
+
sig_pipeignore();
if (argc < 4) perm_usage();
if (chdir(auto_qmail) == -1) temp_chdir();
diff --git a/qmail-showctl.c b/qmail-showctl.c
index dbd93fd..e25475b 100644
--- a/qmail-showctl.c
+++ b/qmail-showctl.c
@@ -15,6 +15,7 @@
#include "auto_patrn.h"
#include "auto_spawn.h"
#include "auto_split.h"
+#include "spf.h"
stralloc me = {0};
int meok;
@@ -258,6 +259,10 @@ void main()
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 @@ void main()
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 --git a/qmail-smtpd.8 b/qmail-smtpd.8
index 785d5ba..acc9840 100644
--- a/qmail-smtpd.8
+++ b/qmail-smtpd.8
@@ -229,15 +229,15 @@ In case
is defined,
.B qmail-smtpd
honors LOGIN, PLAIN, and additionally CRAM-MD5 authentication.
-Simply
+Simply
.TP 5
.IR SMTPAUTH='cram'
restricts authentication just to CRAM-MD5.
If however
.TP 5
.IR SMTPAUTH='!'
-starts with an exclamation mark, AUTH is required.
-You can enforce 'Submission' using this option
+starts with an exclamation mark, AUTH is required.
+You can enforce 'Submission' using this option
and binding
.B qmail-smtpd
to the SUBMISSION port \'587'\.
@@ -253,6 +253,42 @@ connections.
Note: The use of 'cram' requires a CRAM-MD5 enabled PAM.
+.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),
diff --git a/qmail-smtpd.c b/qmail-smtpd.c
index 4ca7293..1283dbd 100644
--- a/qmail-smtpd.c
+++ b/qmail-smtpd.c
@@ -24,12 +24,13 @@
#include "timeoutwrite.h"
#include "commands.h"
#include "wait.h"
+#include "spf.h"
#define AUTHSLEEP 5
-
#define MAXHOPS 100
unsigned int databytes = 0;
int timeout = 1200;
+unsigned int spfbehavior = 0;
int safewrite(fd,buf,len) int fd; char *buf; int len;
{
@@ -50,7 +51,7 @@ void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
-void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
+void straynewline() { out("451 See https://cr.yp.to/docs/smtplf.html.\r\n"); flush(); _exit(1); }
void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); }
void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
@@ -76,6 +77,9 @@ void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); }
void err_submission() { out("530 Authorization required (#5.7.1) \r\n"); }
stralloc greeting = {0};
+stralloc spflocal = {0};
+stralloc spfguess = {0};
+stralloc spfexp = {0};
void smtp_greet(code) char *code;
{
@@ -84,7 +88,7 @@ void smtp_greet(code) char *code;
}
void smtp_help(arg) char *arg;
{
- out("214 netqmail home page: http://qmail.org/netqmail\r\n");
+ out("214 Omg, you didn't take ``SMTP 101'' in college.\r\n");
}
void smtp_quit(arg) char *arg;
{
@@ -128,6 +132,7 @@ void setup()
if (liphostok == -1) die_control();
if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
if (timeout <= 0) timeout = 1;
+
if (rcpthosts_init() == -1) die_control();
bmfok = control_readfile(&bmf,"control/badmailfrom",0);
@@ -141,6 +146,19 @@ void setup()
if (!(databytes + 1)) --databytes;
protocol = "SMTP";
+ 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");
@@ -166,6 +184,7 @@ void setup()
dohelo(remotehost);
}
+
stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
int addrparse(arg)
@@ -247,11 +266,13 @@ int addrallowed()
return r;
}
-char *auth;
+
int seenauth = 0;
int seenmail = 0;
int flagbarf; /* defined if seenmail */
int flagsize;
+int flagbarfspf;
+stralloc spfbarfmsg = {0};
stralloc mailfrom = {0};
stralloc rcptto = {0};
stralloc fuser = {0};
@@ -268,13 +289,13 @@ int mailfrom_size(arg) char *arg;
return 0;
}
-void mailfrom_auth(arg,len)
-char *arg;
+void mailfrom_auth(arg,len)
+char *arg;
int len;
{
if (!stralloc_copys(&fuser,"")) die_nomem();
if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); }
- else
+ else
while (len) {
if (*arg == '+') {
if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); }
@@ -302,14 +323,14 @@ void mailfrom_parms(arg) char *arg;
i = byte_chr(arg,len,'>');
if (i > 4 && i < len) {
while (len) {
- arg++; len--;
+ arg++; len--;
if (*arg == ' ' || *arg == '\0' ) {
if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; }
- if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5);
+ if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5);
if (!stralloc_copys(&mfparms,"")) die_nomem();
}
else
- if (!stralloc_catb(&mfparms,arg,1)) die_nomem();
+ if (!stralloc_catb(&mfparms,arg,1)) die_nomem();
}
}
}
@@ -323,8 +344,7 @@ void smtp_ehlo(arg) char *arg;
{
char size[FMT_ULONG];
size[fmt_ulong(size,(unsigned int) databytes)] = 0;
- smtp_greet("250-");
- out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n");
+ smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n");
if (smtpauth == 1 || smtpauth == 11) out("250-AUTH LOGIN PLAIN\r\n");
if (smtpauth == 2 || smtpauth == 12) out("250-AUTH CRAM-MD5\r\n");
if (smtpauth == 3 || smtpauth == 13) out("250-AUTH LOGIN PLAIN CRAM-MD5\r\n");
@@ -333,29 +353,88 @@ void smtp_ehlo(arg) char *arg;
}
void smtp_rset(arg) char *arg;
{
- seenmail = 0; seenauth = 0;
+ seenmail = 0; seenauth = 0;
mailfrom.len = 0; rcptto.len = 0;
out("250 flushed\r\n");
}
+
void smtp_mail(arg) char *arg;
{
+ int r;
if (smtpauth)
if (smtpauth > 10 && !seenauth) { err_submission(); return; }
+
if (!addrparse(arg)) { err_syntax(); return; }
flagsize = 0;
mailfrom_parms(arg);
if (flagsize) { err_size(); return; }
flagbarf = bmfcheck();
+ 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';
+ out("\r\n");
+ } else {
+ out("550 ");
+ out(spfbarfmsg.s);
+ out(" (#5.7.1)\r\n");
+ }
+ }
+}
+
void smtp_rcpt(arg) char *arg; {
if (!seenmail) { err_wantmail(); return; }
if (!addrparse(arg)) { err_syntax(); return; }
if (flagbarf) { err_bmf(); return; }
+ if (flagbarfspf) { err_spf(); return; }
if (relayclient) {
--addr.len;
if (!stralloc_cats(&addr,relayclient)) die_nomem();
@@ -456,6 +535,25 @@ int *hops;
}
}
+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;
{
@@ -484,6 +582,7 @@ void smtp_data(arg) char *arg; {
out("354 go ahead\r\n");
received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo);
+ spfreceived();
blast(&hops);
hops = (hops >= MAXHOPS);
if (hops) qmail_fail(&qqt);
@@ -558,7 +657,7 @@ int authenticate(void)
substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf);
if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write();
if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write();
- if (smtpauth == 2 || smtpauth == 3 || smtpauth == 12 || smtpauth == 13)
+ if (smtpauth == 2 || smtpauth == 3 || smtpauth == 12 || smtpauth == 13)
if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write();
if (substdio_flush(&ssauth) == -1) return err_write();
diff --git a/spf.c b/spf.c
new file mode 100644
index 0000000..d2a5f27
--- /dev/null
+++ b/spf.c
@@ -0,0 +1,878 @@
+#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;
+ int guessing = 0;
+ 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 */
+ guessing = 1;
+ if (!stralloc_copys(&spf, spfguess.s)) return SPF_NOMEM;
+ if (!stralloc_append(&spf, " ")) return SPF_NOMEM;
+ } else
+ spf.len = 0;
+
+ /* append local rulest */
+ if (spflocal.len) {
+ local_pos = spf.len;
+ if (!stralloc_cats(&spf, spflocal.s)) return SPF_NOMEM;
+ }
+ if (!stralloc_0(&spf)) return SPF_NOMEM;
+
+ expdomain.len = 0;
+ } else if (r == SPF_OK) {
+ if (!stralloc_0(&spf)) return SPF_NOMEM;
+ if (main) hdr_neutral();
+ r = SPF_NEUTRAL;
+
+ /* try to add local rules before fail all mechs */
+ if (main && spflocal.len) {
+ pos = 0;
+ p = (char *) 0;
+ while(pos < spf.len) {
+ NXTOK(begin, pos, &spf);
+ if (!spf.s[begin]) continue;
+
+ if (p && spf.s[begin] != *p) p = (char *) 0;
+ if (!p && (spf.s[begin] == '-' || spf.s[begin] == '~' ||
+ spf.s[begin] == '?')) p = &spf.s[begin];
+
+ if (p && p > spf.s && str_equal(spf.s + begin + 1, "all")) {
+ /* ok, we can insert the local rules at p */
+ local_pos = p - spf.s;
+
+ stralloc_readyplus(&spf, spflocal.len);
+ p = spf.s + local_pos;
+ byte_copyr(p + spflocal.len, spf.len - local_pos, p);
+ byte_copy(p, spflocal.len, spflocal.s);
+ spf.len += spflocal.len;
+
+ pos += spflocal.len;
+ break;
+ }
+ }
+
+ if (pos >= spf.len) pos = spf.len - 1;
+ for(i = 0; i < pos; i++)
+ if (!spf.s[i]) spf.s[i] = ' ';
+ }
+ } else {
+ alloc_free(spf.s);
+ return r;
+ }
+
+ pos = 0;
+ done = 0;
+ while(pos < spf.len) {
+ NXTOK(begin, pos, &spf);
+ if (!spf.s[begin]) continue;
+
+ /* in local ruleset? */
+ if (!done && local_pos >= 0 && begin >= local_pos) {
+ if (begin < (local_pos + spflocal.len))
+ expdomain.len = 0;
+ else
+ if (!stralloc_copy(&expdomain, domain))
+ return SPF_NOMEM;
+ }
+
+ for (p = spf.s + begin;*p;++p)
+ if (*p == ':' || *p == '/' || *p == '=') break;
+
+ if (*p == '=') {
+ *p++ = 0;
+
+ /* modifiers are simply handled here */
+ if (str_equal(spf.s + begin, "redirect")) {
+ if (done) continue;
+
+ if (!spfexpand(&sa, p, domain->s)) return SPF_NOMEM;
+ stralloc_copy(domain, &sa);
+
+ hdr_unknown();
+ r = SPF_UNKNOWN;
+
+ goto redirect;
+ } else if (str_equal(spf.s + begin, "default")) {
+ if (done) continue;
+
+ for(da = default_aliases; da->alias; ++da)
+ if (str_equal(da->alias, p)) break;
+
+ r = da->defret;
+ } else if (str_equal(spf.s + begin, "exp")) {
+ strsalloc ssa = {0};
+
+ if (!main) continue;
+
+ if (!stralloc_copys(&sa, p)) return SPF_NOMEM;
+ switch(dns_txt(&ssa, &sa)) {
+ case DNS_MEM: return SPF_NOMEM;
+ case DNS_SOFT: continue; /* FIXME... */
+ case DNS_HARD: continue;
+ }
+
+ explanation.len = 0;
+ for(i = 0; i < ssa.len; i++) {
+ if (!stralloc_cat(&explanation, &ssa.sa[i])) return SPF_NOMEM;
+ if (i < (ssa.len - 1))
+ if (!stralloc_append(&explanation, "\n")) return SPF_NOMEM;
+
+ alloc_free(ssa.sa[i].s);
+ }
+ if (!stralloc_0(&explanation)) return SPF_NOMEM;
+ } /* and unknown modifiers are ignored */
+ } else if (!done) {
+ if (!stralloc_copys(&sa, spf.s + begin)) return SPF_NOMEM;
+ if (!stralloc_0(&sa)) return SPF_NOMEM;
+
+ switch(spf.s[begin]) {
+ case '-': begin++; prefix = SPF_FAIL; break;
+ case '~': begin++; prefix = SPF_SOFTFAIL; break;
+ case '+': begin++; prefix = SPF_OK; break;
+ case '?': begin++; prefix = SPF_NEUTRAL; break;
+ default: prefix = SPF_OK;
+ }
+
+ if (*p == '/') {
+ *p++ = 0;
+ q = spfmech(spf.s + begin, 0, p, domain->s);
+ } else {
+ if (*p) *p++ = 0;
+ i = str_chr(p, '/');
+ if (p[i] == '/') {
+ p[i++] = 0;
+ q = spfmech(spf.s + begin, p, p + i, domain->s);
+ } else if (i > 0)
+ q = spfmech(spf.s + begin, p, 0, domain->s);
+ else
+ q = spfmech(spf.s + begin, 0, 0, domain->s);
+ }
+
+ if (q == SPF_OK) q = prefix;
+
+ switch(q) {
+ case SPF_OK: hdr_pass(); break;
+ case SPF_NEUTRAL: hdr_neutral(); break;
+ case SPF_SYNTAX: hdr_syntax(); break;
+ case SPF_SOFTFAIL: hdr_softfail(); break;
+ case SPF_FAIL: hdr_fail(); break;
+ case SPF_EXT: hdr_ext(sa.s); break;
+ case SPF_ERROR:
+ if (!guessing)
+ break;
+ if (local_pos >= 0 && begin >= local_pos)
+ break;
+ hdr_none();
+ q = SPF_NONE;
+ break;
+ case SPF_NONE: continue;
+ }
+
+ r = q;
+ done = 1; /* we're done, no more mechanisms */
+ }
+ }
+
+ /* we fell through, no local rule applied */
+ if (!done && !stralloc_copy(&expdomain, domain)) return SPF_NOMEM;
+
+ alloc_free(spf.s);
+ alloc_free(sa.s);
+ return r;
+}
+
+int spfcheck()
+{
+ stralloc domain = {0};
+ int pos;
+ int r;
+
+ pos = byte_rchr(addr.s, addr.len, '@') + 1;
+ if (pos < addr.len) {
+ if (!stralloc_copys(&domain, addr.s + pos)) return SPF_NOMEM;
+ } else {
+ pos = str_rchr(helohost.s, '@');
+ if (helohost.s[pos]) {
+ if (!stralloc_copys(&domain, helohost.s + pos + 1)) return SPF_NOMEM;
+ } else
+ if (!stralloc_copys(&domain, helohost.s)) return SPF_NOMEM;
+ }
+ if (!stralloc_copys(&explanation, spfexp.s)) return SPF_NOMEM;
+ if (!stralloc_0(&explanation)) return SPF_NOMEM;
+ recursion = 0;
+
+ if (!remoteip || !ip_scan(remoteip, &ip)) {
+ hdr_unknown_msg("No IP address in conversation");
+ return SPF_UNKNOWN;
+ }
+
+ if (!stralloc_readyplus(&expdomain, 0)) return SPF_NOMEM;
+ if (!stralloc_readyplus(&errormsg, 0)) return SPF_NOMEM;
+ expdomain.len = 0;
+ errormsg.len = 0;
+ sender_fqdn.len = 0;
+ received = (char *) 0;
+
+ if ((ip.d[0] == 127 && ip.d[1] == 0 && ip.d[2] == 0 && ip.d[3] == 1) || ipme_is(&ip))
+ { hdr_pass(); r = SPF_OK; }
+ else
+ r = spflookup(&domain);
+
+ if (r < 0) r = SPF_UNKNOWN;
+
+ alloc_free(domain.s);
+ return r;
+}
+
+int spfexplanation(sa)
+stralloc *sa;
+{
+ return spfexpand(sa, explanation.s, expdomain.s);
+}
+
+int spfinfo(sa)
+stralloc *sa;
+{
+ stralloc tmp = {0};
+ if (!stralloc_copys(&tmp, received)) return 0;
+ if (!stralloc_0(&tmp)) return 0;
+ if (!spfexpand(sa, tmp.s, expdomain.s)) return 0;
+ alloc_free(tmp.s);
+ return 1;
+}
diff --git a/spf.h b/spf.h
new file mode 100644
index 0000000..e1cfd1b
--- /dev/null
+++ b/spf.h
@@ -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 --git a/spfquery.c b/spfquery.c
new file mode 100644
index 0000000..b5dfd5e
--- /dev/null
+++ b/spfquery.c
@@ -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 --git a/str.h b/str.h
index e00773c..00307f4 100644
--- a/str.h
+++ b/str.h
@@ -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 --git a/str_cpyb.c b/str_cpyb.c
new file mode 100644
index 0000000..9afcfc4
--- /dev/null
+++ b/str_cpyb.c
@@ -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 --git a/strsalloc.c b/strsalloc.c
new file mode 100644
index 0000000..482e489
--- /dev/null
+++ b/strsalloc.c
@@ -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 --git a/strsalloc.h b/strsalloc.h
new file mode 100644
index 0000000..c42804e
--- /dev/null
+++ b/strsalloc.h
@@ -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 --git a/tcp-env.c b/tcp-env.c
index feb85cc..db64808 100644
--- a/tcp-env.c
+++ b/tcp-env.c
@@ -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 @@ void main(argc,argv)
int argc;
char *argv[];
{
+ strsalloc ssa = {0};
int dummy;
char *proto;
int opt;
@@ -74,12 +76,13 @@ char *argv[];
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 @@ char *argv[];
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();