Index: Makefile =================================================================== RCS file: /home/source/qmail/Makefile,v retrieving revision 1.1.1.3 retrieving revision 1.2 diff -u -r1.1.1.3 -r1.2 --- Makefile 1998/06/29 17:47:58 1.1.1.3 +++ Makefile 1998/08/23 01:50:52 1.2 @@ -1536,13 +1536,13 @@ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ -fs.a auto_qmail.o socket.lib +fs.a auto_qmail.o dns.o dns.lib socket.lib syslog.lib ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ - alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ - socket.lib` + alloc.a substdio.a error.a str.a fs.a auto_qmail.o dns.o \ + `cat dns.lib` `cat socket.lib` `cat syslog.lib` qmail-smtpd.0: \ qmail-smtpd.8 Index: control.c =================================================================== RCS file: /home/source/qmail/control.c,v retrieving revision 1.1.1.2 retrieving revision 1.2 diff -u -r1.1.1.2 -r1.2 --- control.c 1998/05/15 17:02:19 1.1.1.2 +++ control.c 1998/07/28 06:08:34 1.2 @@ -13,7 +13,7 @@ static stralloc me = {0}; static int meok = 0; -static void striptrailingwhitespace(sa) +void striptrailingwhitespace(sa) stralloc *sa; { while (sa->len > 0) Index: dns.c =================================================================== RCS file: /home/source/qmail/dns.c,v retrieving revision 1.1.1.2 retrieving revision 1.6 diff -u -r1.1.1.2 -r1.6 --- dns.c 1998/06/29 17:49:20 1.1.1.2 +++ dns.c 1999/03/11 00:22:17 1.6 @@ -104,6 +104,43 @@ return 0; } +static int findstring(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) + { + i = *responsepos; + if (i > MAXDNAME - 1) return DNS_SOFT; + if (responsepos + i > responseend) return DNS_SOFT; + byte_copy(name, i, responsepos+1); + name[i] = '\0'; + responsepos += rrdlen; + return 1; + } + + responsepos += rrdlen; + return 0; +} + static int findip(wanttype) int wanttype; { @@ -219,9 +256,10 @@ #define FMT_IAA 40 -static int iaafmt(s,ip) +static int iaafmt(s,ip,dom) char *s; struct ip_address *ip; +const char *dom; { unsigned int i; unsigned int len; @@ -233,7 +271,7 @@ i = fmt_ulong(s,(unsigned long) ip->d[1]); len += i; if (s) s += i; i = fmt_str(s,"."); len += i; if (s) s += i; i = fmt_ulong(s,(unsigned long) ip->d[0]); len += i; if (s) s += i; - i = fmt_str(s,".in-addr.arpa."); len += i; if (s) s += i; + i = fmt_str(s,dom); len += i; if (s) s += i; return len; } @@ -243,8 +281,8 @@ { int r; - if (!stralloc_ready(sa,iaafmt((char *) 0,ip))) return DNS_MEM; - sa->len = iaafmt(sa->s,ip); + if (!stralloc_ready(sa,iaafmt((char *) 0,ip,".in-addr.arpa."))) return DNS_MEM; + sa->len = iaafmt(sa->s,ip,".in-addr.arpa."); switch(resolve(sa,T_PTR)) { case DNS_MEM: return DNS_MEM; @@ -260,6 +298,63 @@ return 0; } } + return DNS_HARD; +} + +int dns_maps(sa,ip,suffix) +stralloc *sa; +struct ip_address *ip; +char *suffix; +{ + int r; + + /* + * First, look for a TXT type. If it is found, we will skip looking + * for an A record. + */ + if (!stralloc_ready(sa, iaafmt(NULL, ip, suffix))) + return DNS_MEM; + sa->len = iaafmt(sa->s, ip, suffix); + 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 = findstring(T_TXT)) != 2) { + if (r == DNS_SOFT) + return DNS_SOFT; + if (r == 1) { + if (!stralloc_copys(sa,name)) + return DNS_MEM; + return 0; + } + } + + /* + * ok, look for an CNAME or A record, and return a more generic message. + * Yick. + */ + switch(resolve(sa, T_A)) { + case DNS_MEM: return DNS_MEM; + case DNS_SOFT: return DNS_SOFT; + case DNS_HARD: return DNS_HARD; + } + while ((r = findip(T_A)) != 2) { + if (r == DNS_SOFT) + return DNS_SOFT; + if (r == 1) { + if (!stralloc_copys(sa, "This host is blackholed. No further information is known. ")) + return DNS_MEM; + if (!stralloc_cats(sa, "[")) + return DNS_MEM; + if (!stralloc_cats(sa, suffix)) + return DNS_MEM; + if (!stralloc_cats(sa, "]")) + return DNS_MEM; + return 0; + } + } + return DNS_HARD; } Index: qmail-smtpd.c =================================================================== RCS file: /home/source/qmail/qmail-smtpd.c,v retrieving revision 1.1.1.3 retrieving revision 1.17 diff -u -r1.1.1.3 -r1.17 --- qmail-smtpd.c 1998/06/29 17:48:22 1.1.1.3 +++ qmail-smtpd.c 1999/03/11 00:51:28 1.17 @@ -23,7 +23,24 @@ #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" +#include "dns.h" +#include + +#include +#include +#include + +#ifdef __amigaos__ /* Work around syslog.h bug. */ +#include +#endif +#include + +/* + * This is the version of the anti-spam patch. + */ +#define FLAME_ORG_VERSION "1.6.2" + #define MAXHOPS 100 unsigned int databytes = 0; int timeout = 1200; @@ -50,7 +67,10 @@ void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } +void err_dnssoft() { out("451 Temporary DNS failure. See http://www.flame.org/qmail/dnssoft.html (#4.3.0)\r\n"); } +void err_dnsbad() { out("553 Envelope not returnable. See http://www.flame.org/qmail/dnsbad.html (#5.7.1)\r\n"); } void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } +void err_badheader() { out("553 Spam or junk mail threshold exceeded. See http://www.flame.org/qmail/spamjunk.html (#5.7.1)\r\n"); } void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); } void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } @@ -58,6 +78,7 @@ void err_noop() { out("250 ok\r\n"); } void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } +void err_maps(char *m) { out(m) ; out("\r\n"); } stralloc greeting = {0}; @@ -81,6 +102,7 @@ char *remoteinfo; char *local; char *relayclient; +char *bouncemail; stralloc helohost = {0}; char *fakehelo; /* pointer into helohost, or 0 */ @@ -97,10 +119,152 @@ stralloc bmf = {0}; struct constmap mapbmf; +/* + * handle badheaders and badheadthresh + */ +typedef struct _badmailheader badmailheader_t; +struct _badmailheader { + int rating; + int always; +#define ALWAYS_RATE 'N' +#define ALWAYS_REJECT 'R' +#define ALWAYS_ACCEPT 'A' + char where; + regex_t regex; + badmailheader_t *next; +}; + +badmailheader_t *badmailheaders = NULL; + +int badheadersok = 0; +long badheaderthresh = 0; /* disable by default */ +long headerthresh = 0; +int headeralways = ALWAYS_RATE; + +void +badheader_add(char *s) +{ + char *glob; + badmailheader_t *nbmh; + int rank; + int pos; + int flags; + int always; + char where; + + where = 'h'; /* default to headers, case insensitive. */ + + /* + * new format: [hHbB] score regexp + * + * h == case insensitive, H == case sensitive matching in headers, + * b == case insensitive, B == case sensitive matching in body. + */ + if (*s == 'h' || *s == 'H' || *s == 'b' || *s == 'B') { + where = *s++; + if (isspace(*s)) + s++; + } + + /* + * If the next character is "A" or "R", always accept, or always reject. + */ + switch (*s) { + case 'A': + s++; + always = ALWAYS_ACCEPT; + break; + case 'R': + s++; + always = ALWAYS_REJECT; + break; + default: + always = ALWAYS_RATE; + pos = scan_int(s, &rank); + s += pos; + } + if (isspace(*s)) + s++; + glob = s; + + nbmh = malloc(sizeof(badmailheader_t)); + if (nbmh == NULL) + return; + nbmh->rating = rank; + nbmh->where = tolower(where); + nbmh->always = always; + + if (where == 'h' || where == 'b') + flags = REG_EXTENDED | REG_ICASE | REG_NOSUB; + else + flags = REG_EXTENDED | REG_NOSUB; + + if (regcomp(&nbmh->regex, glob, flags) != 0) { + free(nbmh); + return; + } + + nbmh->next = badmailheaders; + badmailheaders = nbmh; +} + +/* + * read lines in the form + * [where ][+-]rating string... + * "rating" is added if string matches an input header, and + * "where" is one of hHbB, where h = headers, b = body, and uppercase means + * to do case sensitive matches. + * + * Note that "rating" can be negative, which means that line is less + * likely to be in spam. + */ +int +read_badheaders(void) +{ + int fd; + substdio ss; + int match; + char inbuf[64]; + stralloc line = { 0 }; + + fd = open_read("control/badheaders"); + if (fd < 0) + return 0; + + substdio_fdbuf(&ss, read, fd, inbuf, sizeof(inbuf)); + + for (;;) { + if (getln(&ss, &line, &match, '\n') == -1) + break; + + if (!match && (line.len == 0)) { + close(fd); + return (1); + } + + striptrailingwhitespace(&line); + if (!stralloc_0(&line)) + break; + + if (line.s[0] && line.s[0] != '#') + badheader_add(line.s); + + if (!match) { + close(fd); + return (1); + } + } + + close(fd); + + return (-1); +} + void setup() { char *x; unsigned long u; + int i; if (control_init() == -1) die_control(); if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) @@ -112,6 +276,21 @@ if (rcpthosts_init() == -1) die_control(); + /* + * read the control files for bad mail header filtering. + */ + if (control_readint(&badheaderthresh, "control/badheaderthresh") == -1) + die_control(); + x = env_get("BADHEADERTHRESH"); + if (x) { + scan_ulong(x, &u); + badheaderthresh = u; + } + + badheadersok = read_badheaders(); + if (badheadersok == -1) + die_control(); + bmfok = control_readfile(&bmf,"control/badmailfrom",0); if (bmfok == -1) die_control(); if (bmfok) @@ -131,6 +310,7 @@ if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); + bouncemail = env_get("BOUNCEMAIL"); dohelo(remotehost); } @@ -196,18 +376,177 @@ if (addr.len > 900) return 0; return 1; } + +/* + * check the "mail from: " envelope line + */ +int +bmfcheck() +{ + int j; + + /* + * check the badmailfrom map for the full name provided + */ + if (bmfok && constmap(&mapbmf, addr.s, addr.len - 1)) + return 1; + + /* + * let "#@[]" through. Ick. + */ + if (!addr.s[0] || str_diff("#@[]", addr.s) == 0) + return 0; + + /* + * find an @ sign, and check just the domain portion. + * no @ sign indicates a syntax error. + */ + j = byte_rchr(addr.s, addr.len, '@'); + if (j == 0) + return 1; + + if (j < addr.len) { + if (bmfok && constmap(&mapbmf, addr.s + j, addr.len - j - 1)) + return 1; + + /* + * do a DNS call here too, and if it _fails_ bomb out. + * if it is a soft error, set dns_softerror and let our + * caller deal with that case, but don't reject the mail + * as bad. + */ + switch (badmxcheck(&addr.s[j + 1])) { + case DNS_SOFT: + return 2; + break; + case DNS_HARD: + return 3; + break; + } + } + + return 0; +} + +static void +log_junkthresh(char *mailfrom, char *rcptto, long headerthresh, + long badheaderthresh, int headeralways) +{ + syslog(LOG_INFO, "REJECT JUNK_THRESHOLD <%s> <%s> [%s, %s] (HELO %s) %d/%d/%c", + mailfrom, rcptto, remoteip, remotehost, helohost.s, + headerthresh, badheaderthresh, headeralways); +} + +static void +log_deny(char *m, char *f, char *t, char *msg) +{ + if (msg != NULL) + syslog(LOG_INFO, "REJECT %s <%s> <%s> [%s, %s] (HELO %s) %s", + m, f, t, remoteip, remotehost, helohost.s, msg); + else + syslog(LOG_INFO, "REJECT %s <%s> <%s> [%s, %s] (HELO %s)", + m, f, t, remoteip, remotehost, helohost.s); +} + +static void +log_bmf(char *s, char *r) +{ + log_deny("BAD_MAIL_FROM", s, r, NULL); +} -int bmfcheck() +static void +log_dnssoft(char *s, char *r) { - int j; - if (!bmfok) return 0; - if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; - j = byte_rchr(addr.s,addr.len,'@'); - if (j < addr.len) - if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; - return 0; + log_deny("SOFTDNS_MAIL_FROM", s, r, NULL); } +static void +log_dnshard(char *s, char *r) +{ + log_deny("HARDDNS_MAIL_FROM", s, r, NULL); +} + +static void +log_maps(char *s, char *r, char *msg) +{ + log_deny("BLACKHOLED", s, r, msg); +} + +static void +log_nogateway(char *s, char *r) +{ + log_deny("GATEWAY", s, r, NULL); +} + +static void +log_helo() +{ + syslog(LOG_INFO, "Received: from %s (HELO %s)", + remotehost, helohost.s); +} + +int +badmxcheck(char *dom) +{ + ipalloc checkip = {0}; + stralloc checkhost = {0}; + int ret; + + if (!*dom) + return (DNS_HARD); + if (!stralloc_copys(&checkhost, dom)) + return (DNS_SOFT); + + ret = 0; + + switch (dns_mxip(&checkip, &checkhost, 1)) { + case DNS_MEM: + case DNS_SOFT: + ret = DNS_SOFT; + break; + + case DNS_HARD: + ret = DNS_HARD; + break; + + case 1: + if (checkip.len <= 0) + ret = DNS_HARD; + break; + } + + return update_dnssoft(dom, ret); +} + +/* + * On input, dom is a valid domain name, and ret is the status of the + * lookup, either DNS_SOFT, DNS_HARD, or 0. + */ +int +update_dnssoft(char *dom, int ret) +{ + /* + * open the file in exclusive mode, so noone else can touch it while + * we have our grimy fingers on it. + */ + + /* + * If "ret" is other than DNS_SOFT, we will want to remove this host + * from our file if it is found. Scan through the file, and if the + * name is found, remove it. + */ + + /* + * If the error is DNS_SOFT, check to see if the domain is already + * in the file. If not, add it and return soft. If so, update its + * information, and if the first time checked is greater than 24 + * hours ago, return DNS_HARD instead of DNS_SOFT. Else, return DNS_SOFT. + */ + + return (ret); +} + + int addrallowed() { int r; @@ -250,14 +589,37 @@ void smtp_rcpt(arg) char *arg; { if (!seenmail) { err_wantmail(); return; } if (!addrparse(arg)) { err_syntax(); return; } - if (flagbarf) { err_bmf(); return; } + if (flagbarf == 1) { + log_bmf(mailfrom.s, addr.s); + err_bmf(); + return; + } + if (flagbarf == 2) { + log_dnssoft(mailfrom.s, addr.s); + err_dnssoft(); + return; + } + if (flagbarf == 3) { + log_dnshard(mailfrom.s, addr.s); + err_dnsbad(); + return; + } + if (bouncemail) { + log_maps(mailfrom.s, addr.s, bouncemail); + err_maps(bouncemail); + return; + } if (relayclient) { --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); if (!stralloc_0(&addr)) die_nomem(); } else - if (!addrallowed()) { err_nogateway(); return; } + if (!addrallowed()) { + log_nogateway(mailfrom.s, addr.s); + err_nogateway(); + return; + } if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); @@ -290,6 +652,91 @@ qmail_put(&qqt,ch,1); } +void +send_warn_headers(void) +{ + int i; + char *msg; + char envar[10]; + char msgbuf[1024]; + + for (i = 0 ; i < 10 ; i++) { + strcpy(envar, "WARNMAILx"); + envar[8] = '0' + i; + msg = env_get(envar); + if (msg == NULL) + return; + snprintf(msgbuf, sizeof(msgbuf), "X-Spam-Warning: %s %s %s\n", + local, FLAME_ORG_VERSION, msg); + qmail_puts(&qqt, msgbuf); + } +} + +/* + * Yes, build a state machine. I'm sorry. + */ +void +badmailheader(char ch) +{ + badmailheader_t *bh; + static int pos; + static char line[2048]; /* truncate at 2047 chars (+ nul) */ + + /* + * short circuit if we have no rules to match against. + */ + if (badmailheaders == NULL) + return; + + switch (ch) { + case 0: + break; + + case '\n': + return; + + case '\r': + ch = ' '; + /* FALLTHROUGH */ + default: + if (pos < sizeof(line) - 1) { + line[pos++] = ch; + line[pos] = 0; + } + return; + } + + /* + * another shortcut + */ + if (pos == 0) + return; + + /* + * if we exited the above switch statement to get here, we have a full + * line, or at least the first characters of the line + */ + for (bh = badmailheaders ; bh != NULL ; bh = bh->next) { + if (bh->where == 'h') + if (regexec(&bh->regex, line, 0, NULL, 0) == 0) { + switch (bh->always) { + case ALWAYS_RATE: + headerthresh += bh->rating; + break; + case ALWAYS_ACCEPT: + headeralways = ALWAYS_ACCEPT; + break; + case ALWAYS_REJECT: + if (headeralways != ALWAYS_ACCEPT) + headeralways = ALWAYS_REJECT; + break; + } + } + } + pos = 0; + line[pos] = 0; +} + void blast(hops) int *hops; { @@ -300,6 +747,7 @@ int flagmaybex; /* 1 if this line might match RECEIVED, if fih */ int flagmaybey; /* 1 if this line might match \r\n, if fih */ int flagmaybez; /* 1 if this line might match DELIVERED, if fih */ + char spam_thresh[1024]; state = 1; *hops = 0; @@ -308,6 +756,7 @@ for (;;) { substdio_get(&ssin,&ch,1); if (flaginheader) { + badmailheader(ch); if (pos < 9) { if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0; if (flagmaybez) if (pos == 8) ++*hops; @@ -315,10 +764,22 @@ if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0; if (flagmaybex) if (pos == 7) ++*hops; if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; - if (flagmaybey) if (pos == 1) flaginheader = 0; + if (flagmaybey) if (pos == 1) { + flaginheader = 0; + snprintf(spam_thresh, sizeof(spam_thresh), + "X-Spam-Rating: %s %s %d/%d/%c\n", + local, FLAME_ORG_VERSION, headerthresh, badheaderthresh, + headeralways); + qmail_puts(&qqt, spam_thresh); + send_warn_headers(); + } } ++pos; - if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } + if (ch == '\n') { + pos = 0; + flagmaybex = flagmaybey = flagmaybez = 1; + badmailheader(0); /* reset things */ + } } switch(state) { case 0: @@ -369,7 +830,7 @@ int hops; unsigned long qp; char *qqx; - + if (!seenmail) { err_wantmail(); return; } if (!rcptto.len) { err_wantrcpt(); return; } seenmail = 0; @@ -379,14 +840,30 @@ out("354 go ahead\r\n"); received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); + if (remotehost) + log_helo(); + headerthresh = 0; blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt); + if ((headeralways == ALWAYS_REJECT) + || (headeralways == ALWAYS_RATE + && badheaderthresh != 0 && headerthresh >= badheaderthresh)) { + qmail_fail(&qqt); + log_junkthresh(mailfrom.s, rcptto.s, headerthresh, badheaderthresh, + headeralways); + } qmail_from(&qqt,mailfrom.s); qmail_put(&qqt,rcptto.s,rcptto.len); qqx = qmail_close(&qqt); if (!*qqx) { acceptmessage(qp); return; } + if ((headeralways == ALWAYS_REJECT) + || (headeralways == ALWAYS_RATE + && badheaderthresh != 0 && headerthresh >= badheaderthresh)) { + err_badheader(); + return; + } if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } if (*qqx == 'D') out("554 "); else out("451 "); @@ -413,6 +890,7 @@ sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); + openlog("qmail-smtpd", LOG_PID, LOG_MAIL); if (ipme_init() != 1) die_ipme(); smtp_greet("220 "); out(" ESMTP\r\n"); Index: qmail.h =================================================================== RCS file: /home/source/qmail/qmail.h,v retrieving revision 1.1.1.2 retrieving revision 1.2 diff -u -r1.1.1.2 -r1.2 --- qmail.h 1998/05/15 17:01:45 1.1.1.2 +++ qmail.h 1999/03/11 00:51:29 1.2 @@ -1,6 +1,8 @@ #ifndef QMAIL_H #define QMAIL_H +#include + #include "substdio.h" struct qmail { Index: scan.h =================================================================== RCS file: /home/source/qmail/scan.h,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- scan.h 1998/05/15 16:52:21 1.1.1.1 +++ scan.h 1998/07/28 06:08:35 1.2 @@ -3,6 +3,7 @@ extern unsigned int scan_uint(); extern unsigned int scan_xint(); +extern unsigned int scan_int(); extern unsigned int scan_nbbint(); extern unsigned int scan_ushort(); extern unsigned int scan_xshort(); Index: scan_ulong.c =================================================================== RCS file: /home/source/qmail/scan_ulong.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- scan_ulong.c 1998/05/15 16:52:21 1.1.1.1 +++ scan_ulong.c 1998/07/28 06:08:35 1.2 @@ -9,3 +9,48 @@ { result = result * 10 + c; ++pos; } *u = result; return pos; } + +unsigned int +scan_int(s, i) + char *s; + int *i; +{ + unsigned int pos; + int result; + unsigned char c; + int sign; + + pos = 0; + result = 0; + sign = 1; + + /* + * determine sign of the number + */ + switch (s[0]) { + case '\0': + return 0; + + case '-': + ++pos; + sign = -1; + break; + + case '+': + ++pos; + sign = 1; + break; + + default: + break; + } + + while ((c = (unsigned char)(s[pos] - '0')) < 10) { + result = result * 10 + c; + ++pos; + } + + *i = result * sign; + + return pos; +} Index: tcp-env.c =================================================================== RCS file: /home/source/qmail/tcp-env.c,v retrieving revision 1.1.1.1 retrieving revision 1.4 diff -u -r1.1.1.1 -r1.4 --- tcp-env.c 1998/05/15 16:52:15 1.1.1.1 +++ tcp-env.c 1999/03/11 00:22:18 1.4 @@ -16,6 +16,9 @@ #include "exit.h" #include "case.h" +void bounce_mail(stralloc *, char *, char *); +void warn_mail(stralloc *, char *); + void die() { _exit(111); } struct sockaddr_in salocal; @@ -28,6 +31,9 @@ struct ip_address ipremote; stralloc remotename = {0}; +stralloc bouncemail = {0}; +stralloc bouncetmp = {0}; + char temp[IPFMT + FMT_ULONG]; void main(argc,argv) @@ -39,6 +45,7 @@ int opt; int flagremoteinfo; unsigned long timeout; + char *msg; sig_pipeignore(); @@ -58,8 +65,6 @@ if (argc < 1) die(); if (!env_init()) die(); - proto = env_get("PROTO"); - if (!proto || str_diff(proto,"TCP")) { if (!env_put("PROTO=TCP")) die(); @@ -123,7 +128,115 @@ } } + /* + * If BOUNCEMAIL is already set, we bounce this host. If it is not set, + * do the MAPS lookup. + */ + msg = env_get("BOUNCEMAIL"); + if (msg != NULL) { + stralloc_copys(&bouncemail, msg); + bounce_mail(&bouncemail, NULL, NULL); + goto done; + } + msg = env_get("WARNMAIL"); + if (msg != NULL) { + stralloc_copys(&bouncemail, msg); + warn_mail(&bouncemail, NULL); + goto done; + } + + /* + * look in the MAPS RBL, and trust it to contain hosts we really don't + * want to hear from. + */ + switch(dns_maps(&bouncemail, &ipremote, ".rbl.maps.vix.com.")) { + case DNS_MEM: + die(); + case 0: + bounce_mail(&bouncemail, "553 ", "http://maps.vix.com/rbl/"); + goto done; + break; + } + + /* + * Next, look in the MAPS DUL, and only add a warning header. + */ + switch(dns_maps(&bouncemail, &ipremote, ".dul.maps.vix.com.")) { + case DNS_MEM: + die(); + case 0: + warn_mail(&bouncemail, "http://maps.vix.com/dul/"); + break; + } + +#if 0 + /* + * Next, look in our list, and bounce. + */ + switch(dns_maps(&bouncemail, &ipremote, ".nosmtp.flame.org.")) { + case DNS_MEM: + die(); + case 0: + bounce_mail(&bouncemail, NULL, NULL); + break; + } + + /* + * Next, look in our list, and bounce. + */ + switch(dns_maps(&bouncemail, &ipremote, ".warnsmtp.flame.org.")) { + case DNS_MEM: + die(); + case 0: + warn_mail(&bouncemail, NULL); + break; + } +#endif + +done: sig_pipedefault(); execvp(*argv,argv); die(); +} + +/* + * return code "code" and message "msg". If "code" is NULL, the msg + * portion needs to contain the code. + */ +void +bounce_mail(stralloc *msg, char *code, char *source) +{ + if (!stralloc_copys(&bouncetmp, "")) die(); + + if (code != NULL) + if (!stralloc_cats(&bouncetmp, code)) die(); + + if (source != NULL) { + if (!stralloc_cats(&bouncetmp, source)) die(); + if (!stralloc_cats(&bouncetmp, " ")) die(); + } + if (!stralloc_cat(&bouncetmp, msg)) die(); + if (!stralloc_0(&bouncetmp)) die(); + + if (!env_put2("BOUNCEMAIL", bouncetmp.s)) die(); +} + +void +warn_mail(stralloc *msg, char *source) +{ + static int count = 0; + char envar[10]; + + if (count > 9) + return; + strcpy(envar, "WARNMAILx"); + if (source != NULL) { + if (!stralloc_copys(&bouncetmp, source)) die(); + if (!stralloc_cats(&bouncetmp, " ")) die(); + } + if (!stralloc_cat(&bouncetmp, msg)) die(); + if (!stralloc_0(&bouncetmp)) die(); + envar[8] = '0' + count; + if (!env_put2(envar, bouncetmp.s)) die(); + count++; }