qmail-1.03_outgoingips.patch by Alberto Brealey-Guzmaˇn version 1.0 With this patch qmail-remote is modified to bind to different local ip addresses depending on the domain part in the sender argument. The mapping between ip addresses and domains is specified in a new control file: control/outgoingips. The HELO string will be set to the domain part if there is a match. Based on outgoingip.patch (http://qmail.org./outgoingip.patch). Let me know if you find it useful. diff -Nru qmail-1.03.orig/qmail-control.9 qmail-1.03/qmail-control.9 --- qmail-1.03.orig/qmail-control.9 1998-06-15 04:53:16.000000000 -0600 +++ qmail-1.03/qmail-control.9 2004-09-15 13:44:28.000000000 -0600 @@ -56,6 +56,7 @@ .I localiphost \fIme \fRqmail-smtpd .I locals \fIme \fRqmail-send .I morercpthosts \fR(none) \fRqmail-smtpd +.I outgoingips \fR(none) \fRqmail-remote .I percenthack \fR(none) \fRqmail-send .I plusdomain \fIme \fRqmail-inject .I qmqpservers \fR(none) \fRqmail-qmqpc diff -Nru qmail-1.03.orig/qmail-remote.8 qmail-1.03/qmail-remote.8 --- qmail-1.03.orig/qmail-remote.8 1998-06-15 04:53:16.000000000 -0600 +++ qmail-1.03/qmail-remote.8 2004-09-15 13:44:28.000000000 -0600 @@ -124,6 +124,23 @@ .B qmail-remote refuses to run. .TP 5 +.I outgoingips +IP addresses to be used on outgoing connections. +Each line has the form +.IR fromdomain\fB:\fIlocalip , +without any extra spaces. +If +.I domain +matches the domain part in +.IR sender , +.B qmail-remote +will bind to +.IR localip +when connecting to +.IR host . +If it matches, it will also set the HELO string to the domain part of +.IR sender . +.TP 5 .I smtproutes Artificial SMTP routes. Each route has the form diff -Nru qmail-1.03.orig/qmail-remote.c qmail-1.03/qmail-remote.c --- qmail-1.03.orig/qmail-remote.c 1998-06-15 04:53:16.000000000 -0600 +++ qmail-1.03/qmail-remote.c 2004-09-15 13:44:28.000000000 -0600 @@ -39,6 +39,9 @@ static stralloc sauninit = {0}; stralloc helohost = {0}; +stralloc outdomain = {0}; +stralloc outgoingips = {0}; +struct constmap mapoutgoingips; stralloc routes = {0}; struct constmap maproutes; stralloc host = {0}; @@ -47,6 +50,7 @@ saa reciplist = {0}; struct ip_address partner; +struct ip_address outgoingip; void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); } void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); } @@ -56,6 +60,7 @@ ch = sa->s[i]; if (ch < 33) ch = '?'; if (ch > 126) ch = '?'; if (substdio_put(subfdoutsmall,&ch,1) == -1) _exit(0); } } +void temp_noip() { out("Zinvalid ipaddr in control/outgoingips (#4.3.0)\n"); zerodie(); } void temp_nomem() { out("ZOut of memory. (#4.3.0)\n"); zerodie(); } void temp_oserr() { out("Z\ System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); } @@ -324,6 +329,14 @@ case 1: if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; } + switch(control_readfile(&outgoingips,"control/outgoingips",0)) { + case -1: + temp_control(); + case 0: + if (!constmap_init(&mapoutgoingips,"",0,1)) temp_nomem(); break; + case 1: + if (!constmap_init(&mapoutgoingips,outgoingips.s,outgoingips.len,1)) temp_nomem(); break; + } } void main(argc,argv) @@ -338,6 +351,7 @@ int flagallaliases; int flagalias; char *relayhost; + char *localip; sig_pipeignore(); if (argc < 4) perm_usage(); @@ -366,6 +380,22 @@ addrmangle(&sender,argv[2],&flagalias,0); + if (!stralloc_copy(&outdomain,&canonhost)) temp_nomem(); /* is there a better way? */ + localip = 0; + for (i = 0;i <= outdomain.len;++i) + if ((i == 0) || (i == outdomain.len) || (outdomain.s[i] == '.')) + if (localip = constmap(&mapoutgoingips,outdomain.s + i,outdomain.len - i)) + break; + if (localip && !*localip) localip = 0; + + if (localip) { + if (!ip_scan(localip,&outgoingip)) temp_noip(); + if (!stralloc_copy(&helohost,&outdomain)) temp_nomem(); /* could be in control file */ + } + else + outgoingip.d[0] = outgoingip.d[1] = outgoingip.d[2] = outgoingip.d[3] = (unsigned long) 0; + + if (!saa_readyplus(&reciplist,0)) temp_nomem(); if (ipme_init() != 1) temp_oserr(); @@ -414,7 +444,7 @@ smtpfd = socket(AF_INET,SOCK_STREAM,0); if (smtpfd == -1) temp_oserr(); - if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { + if (timeoutconn(smtpfd,&ip.ix[i].ip,&outgoingip,(unsigned int) port,timeoutconnect) == 0) { tcpto_err(&ip.ix[i].ip,0); partner = ip.ix[i].ip; smtp(); /* does not return */ diff -Nru qmail-1.03.orig/qmail-showctl.c qmail-1.03/qmail-showctl.c --- qmail-1.03.orig/qmail-showctl.c 1998-06-15 04:53:16.000000000 -0600 +++ qmail-1.03/qmail-showctl.c 2004-09-15 13:44:28.000000000 -0600 @@ -230,6 +230,7 @@ do_str("localiphost",1,"localiphost","Local IP address becomes "); do_lst("locals","Messages for me are delivered locally.","Messages for "," are delivered locally."); do_str("me",0,"undefined! Uh-oh","My name is "); + do_lst("outgoingips","No outgoing ip mappings defined.","Map from sender domain part to local ip: ",""); do_lst("percenthack","The percent hack is not allowed.","The percent hack is allowed for user%host@","."); do_str("plusdomain",1,"plusdomain","Plus domain name is "); do_lst("qmqpservers","No QMQP servers.","QMQP server: ","."); @@ -296,6 +297,7 @@ if (str_equal(d->d_name,"timeoutremote")) continue; if (str_equal(d->d_name,"timeoutsmtpd")) continue; if (str_equal(d->d_name,"virtualdomains")) continue; + if (str_equal(d->d_name,"outgoingips")) continue; substdio_puts(subfdout,"\n"); substdio_puts(subfdout,d->d_name); substdio_puts(subfdout,": I have no idea what this file does.\n"); diff -Nru qmail-1.03.orig/remoteinfo.c qmail-1.03/remoteinfo.c --- qmail-1.03.orig/remoteinfo.c 1998-06-15 04:53:16.000000000 -0600 +++ qmail-1.03/remoteinfo.c 2004-09-15 13:44:28.000000000 -0600 @@ -44,12 +44,12 @@ s = socket(AF_INET,SOCK_STREAM,0); if (s == -1) return 0; - byte_zero(&sin,sizeof(sin)); +/* byte_zero(&sin,sizeof(sin)); sin.sin_family = AF_INET; byte_copy(&sin.sin_addr,4,ipl); sin.sin_port = 0; - if (bind(s,(struct sockaddr *) &sin,sizeof(sin)) == -1) { close(s); return 0; } - if (timeoutconn(s,ipr,113,timeout) == -1) { close(s); return 0; } + if (bind(s,(struct sockaddr *) &sin,sizeof(sin)) == -1) { close(s); return 0; } */ + if (timeoutconn(s,ipr,ipl,113,timeout) == -1) { close(s); return 0; } fcntl(s,F_SETFL,fcntl(s,F_GETFL,0) & ~O_NDELAY); len = 0; diff -Nru qmail-1.03.orig/timeoutconn.c qmail-1.03/timeoutconn.c --- qmail-1.03.orig/timeoutconn.c 1998-06-15 04:53:16.000000000 -0600 +++ qmail-1.03/timeoutconn.c 2004-09-15 13:44:28.000000000 -0600 @@ -10,14 +10,16 @@ #include "byte.h" #include "timeoutconn.h" -int timeoutconn(s,ip,port,timeout) +int timeoutconn(s,ip,localip,port,timeout) int s; struct ip_address *ip; +struct ip_address *localip; unsigned int port; int timeout; { char ch; struct sockaddr_in sin; + struct sockaddr_in sin_local; char *x; fd_set wfds; struct timeval tv; @@ -30,7 +32,11 @@ if (ndelay_on(s) == -1) return -1; - /* XXX: could bind s */ + byte_zero(&sin_local,sizeof(sin_local)); + byte_copy(&sin_local.sin_addr,4,localip); + sin_local.sin_family = AF_INET; + + if (bind(s,(struct sockaddr *) &sin_local,sizeof(sin_local)) == -1) return -1; if (connect(s,(struct sockaddr *) &sin,sizeof(sin)) == 0) { ndelay_off(s);