# HG changeset patch # User Carl Byington # Date 1333910579 25200 # Node ID 15bf4f68a0b24c210a81aca6bd3b0e85bbe3d282 # Parent b0738685bf51826bbbb6c4105a1421f95bb8c387 Add dnswl support diff -r b0738685bf51 -r 15bf4f68a0b2 ChangeLog --- a/ChangeLog Fri Jan 06 22:07:45 2012 -0800 +++ b/ChangeLog Sun Apr 08 11:42:59 2012 -0700 @@ -1,3 +1,6 @@ +6.29 2012-04-08 + Add dnswl support. + 6.28 2011-09-30 Add prvs decoding to envelope addresses. diff -r b0738685bf51 -r 15bf4f68a0b2 NEWS --- a/NEWS Fri Jan 06 22:07:45 2012 -0800 +++ b/NEWS Sun Apr 08 11:42:59 2012 -0700 @@ -1,3 +1,4 @@ +6.29 2012-04-08 Add dnswl support. 6.28 2011-09-30 Add prvs decoding to envelope addresses. 6.27 2011-08-15 const correctness fixes from new gcc 6.26 2010-11-19 64 bit fixes for libresolv.a diff -r b0738685bf51 -r 15bf4f68a0b2 configure.in --- a/configure.in Fri Jan 06 22:07:45 2012 -0800 +++ b/configure.in Sun Apr 08 11:42:59 2012 -0700 @@ -1,6 +1,6 @@ AC_PREREQ(2.59) -AC_INIT(dnsbl,6.28,carl@five-ten-sg.com) +AC_INIT(dnsbl,6.29,carl@five-ten-sg.com) AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADER([config.h]) diff -r b0738685bf51 -r 15bf4f68a0b2 dnsbl.conf --- a/dnsbl.conf Fri Jan 06 22:07:45 2012 -0800 +++ b/dnsbl.conf Sun Apr 08 11:42:59 2012 -0700 @@ -40,7 +40,9 @@ dnsbl local blackholes.five-ten-sg.com "Mail from %s rejected - local; see http://www.five-ten-sg.com/blackhole.php?%s"; dnsbl sbl zen.spamhaus.org "Mail from %s rejected - sbl; see http://www.spamhaus.org/query/bl?ip=%s"; dnsbl xbl xbl.spamhaus.org "Mail from %s rejected - xbl; see http://www.spamhaus.org/query/bl?ip=%s"; + dnswl dnswl.org list.dnswl.org 2; dnsbl_list local sbl; + dnswl_list dnswl.org; content on { filter sbl-xbl.spamhaus.org "Mail containing %s rejected - sbl; see http://www.spamhaus.org/query/bl?ip=%s"; diff -r b0738685bf51 -r 15bf4f68a0b2 dnsbl.spec.in --- a/dnsbl.spec.in Fri Jan 06 22:07:45 2012 -0800 +++ b/dnsbl.spec.in Sun Apr 08 11:42:59 2012 -0700 @@ -103,6 +103,9 @@ %changelog +* Fri Apr 08 2012 Carl Byington - 6.29-1 +- Add dnswl support. + * Fri Sep 30 2011 Carl Byington - 6.28-1 - Add prvs decoding to envelope addresses. diff -r b0738685bf51 -r 15bf4f68a0b2 src/context.cpp --- a/src/context.cpp Fri Jan 06 22:07:45 2012 -0800 +++ b/src/context.cpp Sun Apr 08 11:42:59 2012 -0700 @@ -32,6 +32,8 @@ const char *token_default; const char *token_dnsbl; const char *token_dnsbll; +const char *token_dnswl; +const char *token_dnswll; const char *token_envfrom; const char *token_envto; const char *token_filter; @@ -597,6 +599,20 @@ } +DNSWL::DNSWL(const char *n, const char *s, const int l) { + name = n; + suffix = s; + level = l; +} + + +bool DNSWL::operator==(const DNSWL &rhs) { + return (strcmp(name, rhs.name) == 0) && + (strcmp(suffix, rhs.suffix) == 0) && + (level == rhs.level); +} + + CONFIG::CONFIG() { reference_count = 0; generation = 0; @@ -919,6 +935,14 @@ } +DNSWLP CONTEXT::find_dnswl(const char *name) { + dnswlp_map::iterator i = dnswl_names.find(name); + if (i != dnswl_names.end()) return (*i).second; + if (parent) return parent->find_dnswl(name); + return NULL; +} + + const char* CONTEXT::get_content_suffix() { if (!content_suffix && parent) return parent->get_content_suffix(); return content_suffix; @@ -972,6 +996,12 @@ } +dnswlp_list& CONTEXT::get_dnswl_list() { + if (dnswl_list.empty() && parent) return parent->get_dnswl_list(); + return dnswl_list; +} + + bool CONTEXT::acceptable_content(recorder &memory, int score, int bulk, string& msg) { if (spamassassin_limit && (score > spamassassin_limit)) { char buf[maxlen]; @@ -1012,6 +1042,13 @@ printf("%s dnsbl %s %s \"%s\"; \n", indent, n, d.suffix, d.message); } + for (dnswlp_map::iterator i=dnswl_names.begin(); i!=dnswl_names.end(); i++) { + const char *n = (*i).first; + DNSWL &d = *(*i).second; + printf("%s dnswl %s %s %d; \n", indent, n, d.suffix, d.level); + } + + { dnsblp_list dl = get_dnsbl_list(); if (!dl.empty()) { printf("%s dnsbl_list", indent); @@ -1020,7 +1057,18 @@ printf(" %s", d.name); } printf("; \n"); + }} + + { + dnswlp_list dl = get_dnswl_list(); + if (!dl.empty()) { + printf("%s dnswl_list", indent); + for (dnswlp_list::iterator i=dl.begin(); i!=dl.end(); i++) { + DNSWL &d = *(*i); + printf(" %s", d.name); } + printf("; \n"); + }} if (content_filtering) { printf("%s content on { \n", indent); @@ -1222,6 +1270,26 @@ //////////////////////////////////////////////// // +bool parse_dnswl(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_dnswl(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + const char *name = tok.next(); + const char *suf = tok.next(); + const int lev = tok.nextint(); + if (!tsa(tok, token_semi)) return false; + DNSWLP dnsnew = new DNSWL(name, suf, lev); + DNSWLP dnsold = me.find_dnswl(name); + if (dnsold && (*dnsold == *dnsnew)) { + // duplicate redefinition, ignore it + delete dnsnew; + return true; + } + me.add_dnswl(name, dnsnew); + return true; +} + + +//////////////////////////////////////////////// +// bool parse_dnsbll(TOKEN &tok, CONFIG &dc, CONTEXT &me); bool parse_dnsbll(TOKEN &tok, CONFIG &dc, CONTEXT &me) { while (true) { @@ -1243,6 +1311,27 @@ //////////////////////////////////////////////// // +bool parse_dnswll(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_dnswll(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + while (true) { + const char *have = tok.next(); + if (!have) break; + if (have == token_semi) break; + DNSWLP dns = me.find_dnswl(have); + if (dns) { + me.add_dnswl(dns); + } + else { + tok.token_error("dnswl name", have); + return false; + } + } + return true; +} + + +//////////////////////////////////////////////// +// bool parse_content(TOKEN &tok, CONFIG &dc, CONTEXT &me); bool parse_content(TOKEN &tok, CONFIG &dc, CONTEXT &me) { const char *setting = tok.next(); @@ -1650,6 +1739,12 @@ else if (have == token_dnsbll) { if (!parse_dnsbll(tok, dc, *con)) return false; } + else if (have == token_dnswl) { + if (!parse_dnswl(tok, dc, *con)) return false; + } + else if (have == token_dnswll) { + if (!parse_dnswll(tok, dc, *con)) return false; + } else if (have == token_content) { if (!parse_content(tok, dc, *con)) return false; } @@ -1736,6 +1831,8 @@ token_default = register_string("default"); token_dnsbl = register_string("dnsbl"); token_dnsbll = register_string("dnsbl_list"); + token_dnswl = register_string("dnswl"); + token_dnswll = register_string("dnswl_list"); token_envfrom = register_string("env_from"); token_envto = register_string("env_to"); token_filter = register_string("filter"); diff -r b0738685bf51 -r 15bf4f68a0b2 src/context.h --- a/src/context.h Fri Jan 06 22:07:45 2012 -0800 +++ b/src/context.h Sun Apr 08 11:42:59 2012 -0700 @@ -15,6 +15,7 @@ reject}; // rejected by a dns list class DNSBL; +class DNSWL; class CONTEXT; class VERIFY; class SMTP; @@ -27,11 +28,14 @@ typedef set int32_t_set; typedef list smtp_list; typedef DNSBL * DNSBLP; +typedef DNSWL * DNSWLP; typedef VERIFY * VERIFYP; typedef WHITELISTER * WHITELISTERP; typedef DELAYWHITE * DELAYWHITEP; typedef list dnsblp_list; typedef map dnsblp_map; +typedef list dnswlp_list; +typedef map dnswlp_map; typedef CONTEXT * CONTEXTP; typedef list context_list; typedef map context_map; @@ -126,6 +130,14 @@ bool operator==(const DNSBL &rhs); }; +struct DNSWL { + const char *name; // nickname for this dns based list + const char *suffix; // whitelist suffix like list.dnswl.org + int level; // matches 127.0.x.y where y >= level + DNSWL(const char *n, const char *s, const int l); + bool operator==(const DNSWL &rhs); +}; + class CONTEXT { CONTEXTP parent; const char * name; @@ -163,6 +175,8 @@ int dcc_bulk_threshold; // off = 0, many = 1000 dnsblp_map dnsbl_names; // name to dnsbl mapping for lists that are available in this context and children dnsblp_list dnsbl_list; // list of dnsbls to be used in this context + dnswlp_map dnswl_names; // name to dnswl mapping for lists that are available in this context and children + dnswlp_list dnswl_list; // list of dnswls to be used in this context int default_rcpt_rate; // if not specified per user rcpt_rates rcpt_per_hour; // per user limits on number of recipients per hour @@ -221,6 +235,10 @@ void add_dnsbl(DNSBLP dns) {dnsbl_list.push_back(dns);}; DNSBLP find_dnsbl(const char *name); + void add_dnswl(const char *name, DNSWLP dns) {dnswl_names[name] = dns; }; + void add_dnswl(DNSWLP dns) {dnswl_list.push_back(dns);}; + DNSWLP find_dnswl(const char *name); + bool set_white(const char *regx); bool white_match(const char *from); @@ -247,6 +265,7 @@ string_set& get_content_cctlds(); string_set& get_html_tags(); dnsblp_list& get_dnsbl_list(); + dnswlp_list& get_dnswl_list(); bool acceptable_content(recorder &memory, int score, int bulk, string& msg); bool ignore_host(const char *host); @@ -288,6 +307,8 @@ extern const char *token_default; extern const char *token_dnsbl; extern const char *token_dnsbll; +extern const char *token_dnswl; +extern const char *token_dnswll; extern const char *token_envfrom; extern const char *token_envto; extern const char *token_filter; diff -r b0738685bf51 -r 15bf4f68a0b2 src/dnsbl.cpp --- a/src/dnsbl.cpp Fri Jan 06 22:07:45 2012 -0800 +++ b/src/dnsbl.cpp Sun Apr 08 11:42:59 2012 -0700 @@ -819,14 +819,14 @@ //////////////////////////////////////////////// // check a single dnsbl // -bool check_single(mlfiPriv &priv, int32_t ip, const char *suffix); -bool check_single(mlfiPriv &priv, int32_t ip, const char *suffix) { +int32_t check_single(mlfiPriv &priv, int32_t ip, const char *suffix); +int32_t check_single(mlfiPriv &priv, int32_t ip, const char *suffix) { // make a dns question const u_char *src = (const u_char *)&ip; - if (src[0] == 127) return false; // don't do dns lookups on localhost - if (src[0] == 10) return false; // don't do dns lookups on rfc1918 space - if ((src[0] == 192) && (src[1] == 168)) return false; - if ((src[0] == 172) && (16 <= src[1]) && (src[1] <= 31)) return false; + if (src[0] == 127) return 0; // don't do dns lookups on localhost + if (src[0] == 10) return 0; // don't do dns lookups on rfc1918 space + if ((src[0] == 192) && (src[1] == 168)) return 0; + if ((src[0] == 172) && (16 <= src[1]) && (src[1] <= 31)) return 0; #ifdef NS_MAXDNAME char question[NS_MAXDNAME]; #else @@ -848,6 +848,23 @@ //////////////////////////////////////////////// +// check a single dnswl +// +bool check_single(mlfiPriv &priv, int32_t ip, DNSWL &wl); +bool check_single(mlfiPriv &priv, int32_t ip, DNSWL &wl) { + int32_t r = check_single(priv, ip, wl.suffix); + int32_t v = (int32_t)0x7f000000; + int32_t m = (int32_t)0xffff0000; + int32_t m2 = (int32_t)0x000000ff; + if ((r & m) == v) { + int32_t l = r & m2; + if (l >= wl.level) return true; + } + return false; +} + + +//////////////////////////////////////////////// // check the dnsbls specified for this recipient // bool check_dnsbl(mlfiPriv &priv, dnsblp_list &dnsbll, DNSBLP &rejectlist); @@ -855,12 +872,12 @@ for (dnsblp_list::iterator i=dnsbll.begin(); i!=dnsbll.end(); i++) { DNSBLP dp = *i; // non null by construction bool st; - map::iterator f = priv.checked.find(dp); - if (f == priv.checked.end()) { + map::iterator f = priv.checked_black.find(dp); + if (f == priv.checked_black.end()) { // have not checked this list yet st = check_single(priv, priv.ip, *dp); rejectlist = dp; - priv.checked[dp] = st; + priv.checked_black[dp] = st; } else { st = (*f).second; @@ -873,6 +890,31 @@ //////////////////////////////////////////////// +// check the dnswls specified for this recipient +// +bool check_dnswl(mlfiPriv &priv, dnswlp_list &dnswll, DNSWLP &acceptlist); +bool check_dnswl(mlfiPriv &priv, dnswlp_list &dnswll, DNSWLP &acceptlist) { + for (dnswlp_list::iterator i=dnswll.begin(); i!=dnswll.end(); i++) { + DNSWLP dp = *i; // non null by construction + bool st; + map::iterator f = priv.checked_white.find(dp); + if (f == priv.checked_white.end()) { + // have not checked this list yet + st = check_single(priv, priv.ip, *dp); + acceptlist = dp; + priv.checked_white[dp] = st; + } + else { + st = (*f).second; + acceptlist = (*f).first; + } + if (st) return st; + } + return false; +} + + +//////////////////////////////////////////////// // check the hosts from the body against the content filter and uribl dnsbls // // @@ -1117,8 +1159,9 @@ const char *replyvalue = con2.find_from(loto); if (debug_syslog > 1) { char buf[maxlen]; + char buf2[maxlen]; char msg[maxlen]; - snprintf(msg, sizeof(msg), "from <%s> to <%s> using context %s state %s reply state %s", priv.mailaddr, loto, con.get_full_name(buf,maxlen), fromvalue, replyvalue); + snprintf(msg, sizeof(msg), "from <%s> to <%s> using context %s state %s reply context %s state %s", priv.mailaddr, loto, con.get_full_name(buf,maxlen), fromvalue, con2.get_full_name(buf2,maxlen), replyvalue); my_syslog(&priv, msg); } free((void*)loto); @@ -1148,8 +1191,22 @@ st = white; } else { - // check the dns based lists - st = (check_dnsbl(priv, con.get_dnsbl_list(), rejectlist)) ? reject : oksofar; + // check the dns based lists, whitelist first + DNSWLP acceptlist = NULL; // list that caused the whitelisting + if (check_dnswl(priv, con.get_dnswl_list(), acceptlist)) { + st = white; + if (debug_syslog > 1) { + char msg[maxlen]; + snprintf(msg, sizeof(msg), "whitelisted by %s", acceptlist->name); + my_syslog(&priv, msg); + } + } + else if (check_dnsbl(priv, con.get_dnsbl_list(), rejectlist)) { + st = reject; + } + else { + st = oksofar; + } } if (st == reject) { // reject the recipient based on some dnsbl diff -r b0738685bf51 -r 15bf4f68a0b2 src/dnsbl.h --- a/src/dnsbl.h Fri Jan 06 22:07:45 2012 -0800 +++ b/src/dnsbl.h Sun Apr 08 11:42:59 2012 -0700 @@ -33,7 +33,8 @@ bool err; // did we get any errors on the resolver socket? int32_t ip; // ip4 address of the smtp client const char *helo; // helo from client - map checked; // map of dnsblp to result of (ip listed on that dnsbl) + map checked_black; // map of dnsblp to result of (ip listed on that dnsbl) + map checked_white; // map of dnswlp to result of (ip listed on that dnswl) // message specific data const char *mailaddr; // envelope from value const char *queueid; // sendmail queue id diff -r b0738685bf51 -r 15bf4f68a0b2 xml/dnsbl.in --- a/xml/dnsbl.in Fri Jan 06 22:07:45 2012 -0800 +++ b/xml/dnsbl.in Sun Apr 08 11:42:59 2012 -0700 @@ -298,6 +298,17 @@ DNSBL-LIST - a named list of DNSBLs that will be used for specific recipients or recipient domains. + + DNSWL - a named DNS based white list is defined by a dns suffix (e.g. + list.dnswl.org) and an integer level. If the level is greater than or + equal to x in the 127.0.z.x return code from the white list, then the + ip address is considered to match, and the message will be whitelisted. + The names of these DNSWLs will be used to define the DNSWL-LISTs. + + + DNSWL-LIST - a named list of DNSWLs that will be used for specific + recipients or recipient domains. + @@ -371,7 +382,14 @@ expression. - If the mail has not been accepted or rejected yet, the dns lists + If the mail has not been accepted or rejected yet, the dns white lists + specified in the filtering context are checked and the mail is accepted + if any list has an A record for the standard dns based lookup scheme + (reversed octets of the client followed by the dns suffix) with a final + octet greater than or equal to the level specified for that dnswl. + + + If the mail has not been accepted or rejected yet, the dns black lists specified in the filtering context are checked and the mail is rejected if any list has an A record for the standard dns based lookup scheme (reversed octets of the client followed by the dns suffix). @@ -424,7 +442,9 @@ If the content uribl DNSBL is defined, and any of those host names are on that DNSBL, and the host name is not on the <configurable> - ignore list, the mail is rejected. + ignore list, the mail is rejected. Note that the Spamhaus DBL is not (yet) + suitable here, since we currently pass ip addresses to the uribl checker, + and the DBL lists all such bare ip addresses. If any non-whitelisted recipient has a filtering context with a non-zero @@ -564,6 +584,14 @@ http:// protocol header. Such references are still clickable in common mail software. + + Add the ability to use the DBL for content filtering. We need to avoid + checking bare ip addresses against that list. + + + Add daily recipient limits based on some fixed multiple (perhaps 3?) + of the hourly limit. + @@ -628,12 +656,15 @@