QMAIL SRS PATCH Marcelo Coelho marcelo at tpn dot com dot br 1) Download and install libsrs2 from http://www.libsrs2.org/download.html 2) Apply the patch tar -xzf /path/to/qmail-1.03.tar.gz cd qmail-1.03 patch -p1 < qmail-srs-0.1.patch And follow your qmail instalation (config, make, make setup check, ...) 3) Configure some variables in /var/qmail/control echo srs.YOURDOMAIN > /var/qmail/control/srs_domain echo SECRET > /var/qmail/control/srs_secrets echo 21 > /var/qmail/control/srs_maxage echo 4 > /var/qmail/control/srs_hashlength echo 4> /var/qmail/control/srs_hashmin This document describes what each control file does: http://www.libsrs2.org/docs/mta-users.html echo srs.YOURDOMAIN >> /var/qmail/control/rcpthosts echo "srs.YOURDOMAIN:srs" >> /var/qmail/control/virtualdomains echo "| /var/qmail/bin/srsfilter" >> /var/qmail/alias/.qmail-srs-default * YOURDOMAIN: Replace with your domain name, e.g. srs.foo-bar.com * SECRET: Replace with a random string diff -Nu qmail-1.03.orig/Makefile qmail-1.03/Makefile --- qmail-1.03.orig/Makefile Mon Jun 15 07:53:16 1998 +++ qmail-1.03/Makefile Fri Dec 15 15:08:59 2006 @@ -808,7 +808,7 @@ 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 srsfilter load: \ make-load warn-auto.sh systype @@ -1136,15 +1136,16 @@ nroff -man qmail-header.5 > qmail-header.0 qmail-inject: \ -load qmail-inject.o headerbody.o hfield.o newfield.o quote.o now.o \ +load qmail-inject.o rcpthosts.o cdb.a srs.o headerbody.o hfield.o newfield.o quote.o now.o \ control.o date822fmt.o constmap.o qmail.o case.a fd.a wait.a open.a \ getln.a sig.a getopt.a datetime.a token822.o env.a stralloc.a alloc.a \ substdio.a error.a str.a fs.a auto_qmail.o - ./load qmail-inject headerbody.o hfield.o newfield.o \ + ./load qmail-inject rcpthosts.o cdb.a srs.o headerbody.o hfield.o newfield.o \ quote.o now.o control.o date822fmt.o constmap.o qmail.o \ case.a fd.a wait.a open.a getln.a sig.a getopt.a datetime.a \ token822.o env.a stralloc.a alloc.a substdio.a error.a \ - str.a fs.a auto_qmail.o + str.a fs.a auto_qmail.o \ + -I/usr/local/include -L/usr/local/lib -lsrs2 qmail-inject.0: \ qmail-inject.8 @@ -1171,15 +1172,16 @@ > qmail-limits.7 qmail-local: \ -load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \ +load qmail-local.o constmap.o control.o rcpthosts.o srs.o cdb.a qmail.o quote.o now.o gfrom.o myctime.o \ slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \ wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \ fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib - ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \ + ./load qmail-local constmap.o control.o rcpthosts.o srs.o cdb.a qmail.o quote.o now.o gfrom.o myctime.o \ slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \ lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \ substdio.a error.a str.a fs.a datetime.a auto_qmail.o \ - auto_patrn.o `cat socket.lib` + auto_patrn.o `cat socket.lib` \ + -I/usr/local/include -L/usr/local/lib -lsrs2 qmail-local.0: \ qmail-local.8 @@ -1479,16 +1481,17 @@ ./compile qmail-rspawn.c qmail-send: \ -load qmail-send.o qsutil.o control.o constmap.o newfield.o prioq.o \ +load qmail-send.o rcpthosts.o cdb.a srs.o 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 - ./load qmail-send qsutil.o control.o constmap.o newfield.o \ + ./load qmail-send rcpthosts.o cdb.a srs.o 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 \ + -I/usr/local/include -L/usr/local/lib -lsrs2 qmail-send.0: \ qmail-send.8 @@ -1910,6 +1913,23 @@ compile splogger.c error.h substdio.h subfd.h substdio.h exit.h str.h \ scan.h fmt.h ./compile splogger.c + +srs.o: \ +compile srs.c srs.h auto_qmail.h stralloc.h + ./compile srs.c + + +srsfilter: \ +load srsfilter.o srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o cdb.a case.a open.a stralloc.a alloc.a getln.a \ + fd.a wait.a sig.a env.a substdio.a error.a str.a fs.a auto_qmail.o + ./load srsfilter srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o cdb.a case.a open.a stralloc.a alloc.a getln.a \ + fd.a wait.a sig.a env.a substdio.a error.a str.a fs.a auto_qmail.o \ + -I/usr/local/include -L/usr/local/lib -lsrs2 + +srsfilter.o: \ +compile srsfilter.c sig.h readwrite.h exit.h env.h qmail.h substdio.h strerr.h substdio.h fmt.h stralloc.h srs.h + ./compile srsfilter.c + str.a: \ makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \ diff -Nu qmail-1.03.orig/TARGETS qmail-1.03/TARGETS --- qmail-1.03.orig/TARGETS Mon Jun 15 07:53:16 1998 +++ qmail-1.03/TARGETS Fri Dec 15 14:58:33 2006 @@ -171,6 +171,10 @@ timeoutconn.o tcpto.o dns.o +srsfilter +srsfilter.o +srs +srs.o ip.o ipalloc.o hassalen.h diff -Nu qmail-1.03.orig/hier.c qmail-1.03/hier.c --- qmail-1.03.orig/hier.c Mon Jun 15 07:53:16 1998 +++ qmail-1.03/hier.c Fri Dec 15 14:59:07 2006 @@ -127,6 +127,7 @@ c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","srsfilter",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755); diff -Nu qmail-1.03.orig/install-big.c qmail-1.03/install-big.c --- qmail-1.03.orig/install-big.c Mon Jun 15 07:53:16 1998 +++ qmail-1.03/install-big.c Fri Dec 15 14:58:39 2006 @@ -133,6 +133,7 @@ c(auto_qmail,"bin","qsmhook",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","srsfilter",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); c(auto_qmail,"bin","bouncesaying",auto_uido,auto_gidq,0755); diff -Nu qmail-1.03.orig/qmail-inject.c qmail-1.03/qmail-inject.c --- qmail-1.03.orig/qmail-inject.c Mon Jun 15 07:53:16 1998 +++ qmail-1.03/qmail-inject.c Fri Dec 15 14:55:20 2006 @@ -22,6 +22,7 @@ #include "auto_qmail.h" #include "newfield.h" #include "constmap.h" +#include "srs.h" #define LINELEN 80 @@ -61,6 +62,8 @@ void temp() { _exit(111); } void die_nomem() { substdio_putsflush(subfderr,"qmail-inject: fatal: out of memory\n"); temp(); } +void die_srs() { + substdio_putsflush(subfderr,"qmail-inject: fatal: unable to rewrite envelope\n"); perm(); } void die_invalid(sa) stralloc *sa; { substdio_putsflush(subfderr,"qmail-inject: fatal: invalid header field: "); substdio_putflush(subfderr,sa->s,sa->len); perm(); } @@ -99,6 +102,15 @@ int i; if (!stralloc_0(&sender)) die_nomem(); + + switch(srsforward(sender.s)) { + case -3: die_srs(); break; + case -2: die_nomem(); break; + case -1: die_read(); break; + case 0: break; + case 1: if (!stralloc_copy(&sender,&srs_result)) die_nomem(); break; + } + qmail_from(&qqt,sender.s); for (i = 0;i < reciplist.len;++i) diff -Nu qmail-1.03.orig/qmail-local.c qmail-1.03/qmail-local.c --- qmail-1.03.orig/qmail-local.c Mon Jun 15 07:53:16 1998 +++ qmail-1.03/qmail-local.c Fri Dec 15 14:54:30 2006 @@ -28,6 +28,7 @@ #include "myctime.h" #include "gfrom.h" #include "auto_patrn.h" +#include "srs.h" void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); } @@ -67,6 +68,14 @@ char buf[1024]; char outbuf[1024]; +void die_control() { strerr_die1x(111,"Unable to read controls (#4.3.0)"); } +void die_srs() { + if (!stralloc_copys(&foo,srs_error.s)) temp_nomem(); + if (!stralloc_cats(&foo," (#4.3.0)")) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + strerr_die1x(111,foo.s); +} + /* child process */ char fntmptph[80 + FMT_ULONG * 2]; @@ -282,6 +291,15 @@ qmail_put(&qqt,messline.s,messline.len); } while (match); + + switch(srsforward(ueo.s)) { + case -3: die_srs(); break; + case -2: temp_nomem(); break; + case -1: die_control(); break; + case 0: break; + case 1: if (!stralloc_copy(&ueo,&srs_result)) temp_nomem(); break; + } + qmail_from(&qqt,ueo.s); while (*recips) qmail_to(&qqt,*recips++); qqx = qmail_close(&qqt); diff -Nu qmail-1.03.orig/qmail-send.c qmail-1.03/qmail-send.c --- qmail-1.03.orig/qmail-send.c Mon Jun 15 07:53:16 1998 +++ qmail-1.03/qmail-send.c Fri Dec 15 14:56:34 2006 @@ -31,6 +31,7 @@ #include "constmap.h" #include "fmtqfn.h" #include "readsubdir.h" +#include "srs.h" /* critical timing feature #1: if not triggered, do not busy-loop */ /* critical timing feature #2: if triggered, respond within fixed time */ @@ -55,6 +56,7 @@ stralloc bouncehost = {0}; stralloc doublebounceto = {0}; stralloc doublebouncehost = {0}; +stralloc srs_domain = {0}; char strnum2[FMT_ULONG]; char strnum3[FMT_ULONG]; @@ -689,9 +691,28 @@ { log1("warning: unable to start qmail-queue, will try later\n"); return 0; } qp = qmail_qp(&qqt); - if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; } - else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; } - + if (*sender.s) { + int j = 0; + j = byte_rchr(sender.s, sender.len, '@'); + if (j < sender.len) { + if (srs_domain.len == sender.len - j - 1 && stralloc_starts(&srs_domain, sender.s + j + 1)) { + switch(srsreverse(sender.s)) { + case -3: log1(srs_error.s); log1("\n"); _exit(111); break; + case -2: nomem(); break; + case -1: log1("alert: unable to read controls\n"); _exit(111); break; + case 0: break; + case 1: if (!stralloc_copy(&sender,&srs_result)) nomem(); break; + } + if (chdir("queue") == -1) { log1("alert: unable to switch to queue directory\n"); _exit(111); } + } + } + bouncesender = ""; + bouncerecip = sender.s; + } else { + bouncesender = "#@[]"; + bouncerecip = doublebounceto.s; + } + while (!newfield_datemake(now())) nomem(); qmail_put(&qqt,newfield_date.s,newfield_date.len); qmail_puts(&qqt,"From: "); @@ -1449,6 +1470,8 @@ if (control_rldef(&bouncefrom,"control/bouncefrom",0,"MAILER-DAEMON") != 1) return 0; if (control_rldef(&bouncehost,"control/bouncehost",1,"bouncehost") != 1) return 0; if (control_rldef(&doublebouncehost,"control/doublebouncehost",1,"doublebouncehost") != 1) return 0; + if (control_readline(&srs_domain,"control/srs_domain") == -1) return 0; + if (srs_domain.len && !stralloc_0(&srs_domain)) return 0; if (control_rldef(&doublebounceto,"control/doublebounceto",0,"postmaster") != 1) return 0; if (!stralloc_cats(&doublebounceto,"@")) return 0; if (!stralloc_cat(&doublebounceto,&doublebouncehost)) return 0; diff -Nu qmail-1.03.orig/qmail-showctl.c qmail-1.03/qmail-showctl.c --- qmail-1.03.orig/qmail-showctl.c Mon Jun 15 07:53:16 1998 +++ qmail-1.03/qmail-showctl.c Fri Dec 15 14:57:33 2006 @@ -257,6 +257,11 @@ do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 "); do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ",""); + do_str("srs_domain",0,"","SRS domain name is "); + do_lst("srs_secrets","No secrets","",""); + do_int("srs_maxage","21","SRS maxage is ",""); + do_int("srs_hashlength","4","SRS hashlength is ",""); + do_int("srs_hashmin","4","SRS hashmin is ",""); 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,11 @@ 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,"srs_domain")) continue; + if (str_equal(d->d_name,"srs_secrets")) continue; + if (str_equal(d->d_name,"srs_maxage")) continue; + if (str_equal(d->d_name,"srs_hashlength")) continue; + if (str_equal(d->d_name,"srs_hashmin")) 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 -Nu qmail-1.03.orig/srs.c qmail-1.03/srs.c --- qmail-1.03.orig/srs.c Wed Dec 31 21:00:00 1969 +++ qmail-1.03/srs.c Fri Dec 15 14:53:25 2006 @@ -0,0 +1,149 @@ +#include +#include +#include +#include "auto_qmail.h" +#include "stralloc.h" +#include "srs.h" + + +static stralloc srs_domain = {0}; +static stralloc srs_secrets = {0}; +static unsigned int srs_maxage = 21; +static unsigned int srs_hashlength = 4; +static unsigned int srs_hashmin = 4; +stralloc srs_result = {0}; +stralloc srs_error = {0}; + +static int setup_ok = 0; +static int srs_secrets_ok = 0; + +static int setup(int with_rcpthosts) { + + if (setup_ok == 1) return 1; + + if (chdir(auto_qmail) == -1) return -1; + + if (control_init() == -1) return -1; + + if (control_readline(&srs_domain,"control/srs_domain") == -1) return -1; + if (srs_domain.len) { + if (!stralloc_0(&srs_domain)) return -2; + } else { + return 0; + } + + srs_secrets_ok = control_readfile(&srs_secrets,"control/srs_secrets",0); + if (srs_secrets_ok == -1) return -1; + + if (control_readint(&srs_maxage,"control/srs_maxage") == -1) return 0; + if (control_readint(&srs_maxage,"control/srs_hashlength") == -1) return 0; + if (control_readint(&srs_maxage,"control/srs_hashmin") == -1) return 0; + if (srs_hashmin > srs_hashlength) srs_hashmin = srs_hashlength; + + if (with_rcpthosts && rcpthosts_init() == -1) return -1; + + setup_ok = 1; + return 1; + +} + +int srsforward(char *address) { + + int x = 0; + + x = setup(1); + + /* Return if setup was unsucessfull */ + if (x < 1) return(x); + + x = str_len(address); + + /* Return zero if null-sender */ + if (x <= 1) return 0; + + /* Return zero if local address */ + if (rcpthosts(address,x) == 1) return 0; + + /* Now it's time to rewrite the envelope */ + char srsaddress[1000]; + + srs_t *srs; + srs = srs_new(); + srs->maxage = srs_maxage; + srs->hashlength = srs_hashlength; + srs->hashmin = srs_hashmin; + + int i = 0; + int j = 0; + for (j = 0;j < srs_secrets.len;++j) + if (!srs_secrets.s[j]) { + x = srs_add_secret(srs, srs_secrets.s + i); + if (x != SRS_SUCCESS) return srs_error_str(x); + i = j + 1; + } + + x = srs_forward(srs, srsaddress, 1000, address, srs_domain.s); + if (x != SRS_SUCCESS) return srs_error_str(x); + + if (!stralloc_copys(&srs_result,srsaddress)) return -2; + if (!stralloc_0(&srs_result)) return -2; + + srs_free(srs); + + return 1; + +} + + +int srsreverse(char *srsaddress) { + + int x = 0; + + x = setup(0); + + /* Return if setup was unsucessfull */ + if (x < 1) return(x); + + x = str_len(srsaddress); + + /* Return error if null-sender */ + if (x <= 1) return -3; + + /* Now it's time to rewrite the envelope */ + char address[1000]; + + srs_t *srs; + srs = srs_new(); + srs->maxage = srs_maxage; + srs->hashlength = srs_hashlength; + srs->hashmin = srs_hashmin; + + int i = 0; + int j = 0; + for (j = 0;j < srs_secrets.len;++j) + if (!srs_secrets.s[j]) { + x = srs_add_secret(srs, srs_secrets.s + i); + if (x != SRS_SUCCESS) return srs_error_str(x); + i = j + 1; + } + + x = srs_reverse(srs, address, 1000, srsaddress); + if (x != SRS_SUCCESS) return srs_error_str(x); + + if (!stralloc_copys(&srs_result,address)) return -2; + if (!stralloc_0(&srs_result)) return -2; + + srs_free(srs); + + return 1; + +} + +int srs_error_str(int code) { + if (!stralloc_copys(&srs_error,"SRS: ")) return -2; + if (!stralloc_cats(&srs_error,srs_strerror(code))) return -2; + if (!stralloc_0(&srs_error)) return -2; + return -3; +} + + diff -Nu qmail-1.03.orig/srs.h qmail-1.03/srs.h --- qmail-1.03.orig/srs.h Wed Dec 31 21:00:00 1969 +++ qmail-1.03/srs.h Fri Dec 15 14:53:30 2006 @@ -0,0 +1,10 @@ +#ifndef SRS_H +#define SRS_H + +extern stralloc srs_result; +extern stralloc srs_error; +extern int srsforward(char *); +extern int srsreverse(char *); + +#endif + diff -Nu qmail-1.03.orig/srsfilter.c qmail-1.03/srsfilter.c --- qmail-1.03.orig/srsfilter.c Wed Dec 31 21:00:00 1969 +++ qmail-1.03/srsfilter.c Fri Dec 15 14:53:12 2006 @@ -0,0 +1,74 @@ +#include "sig.h" +#include "readwrite.h" +#include "exit.h" +#include "env.h" +#include "qmail.h" +#include "strerr.h" +#include "substdio.h" +#include "fmt.h" +#include "stralloc.h" +#include "srs.h" + +#define FATAL "srsfilter: fatal: " + +void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); } + +struct qmail qqt; + +int mywrite(fd,buf,len) int fd; char *buf; int len; +{ + qmail_put(&qqt,buf,len); + return len; +} + +char inbuf[SUBSTDIO_INSIZE]; +char outbuf[1]; +substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf); +substdio ssout = SUBSTDIO_FDBUF(mywrite,-1,outbuf,sizeof outbuf); + +char num[FMT_ULONG]; + +void main(argc,argv) +int argc; +char **argv; +{ + char *ext2; + char *host; + char *qqx; + + sig_pipeignore(); + + ext2 = env_get("EXT2"); + if (!ext2) + strerr_die2x(100,FATAL,"EXT2 not set"); + host = env_get("HOST"); + if (!host) + strerr_die2x(100,FATAL,"HOST not set"); + + switch(srsreverse(ext2)) { + case -3: strerr_die2x(111,FATAL,srs_error.s); break; + case -2: die_nomem(); break; + case -1: strerr_die2x(111,FATAL,"unable to read controls"); break; + case 0: strerr_die2x(100,FATAL,"unable to rewrite envelope"); break; + } + + if (qmail_open(&qqt) == -1) + strerr_die2x(111,FATAL,"unable to fork"); + if (substdio_copy(&ssout,&ssin) != 0) + strerr_die2x(111,FATAL,"unable to read message"); + substdio_flush(&ssout); + + num[fmt_ulong(num,qmail_qp(&qqt))] = 0; + + /* Always from nullsender */ + qmail_from(&qqt,""); + + qmail_to(&qqt,srs_result.s); + + qqx = qmail_close(&qqt); + if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1); + strerr_die2x(0,"srsfilter: qp ",num); + +} + +