Mercurial > dnsbl
diff src/dnsbl.cpp @ 72:e6a2d0be7c5e
start coding on new config syntax
author | carl |
---|---|
date | Sun, 10 Jul 2005 13:28:33 -0700 |
parents | 1ab70970c8c8 |
children | 2b369f7db7bf |
line wrap: on
line diff
--- a/src/dnsbl.cpp Sat Jul 09 19:24:41 2005 -0700 +++ b/src/dnsbl.cpp Sun Jul 10 13:28:33 2005 -0700 @@ -67,28 +67,15 @@ // misc stuff needed here #include <ctype.h> -#include <fstream> #include <syslog.h> #include <pwd.h> #include <sys/wait.h> /* header for waitpid() and various macros */ #include <signal.h> /* header for signal functions */ -static char* dnsbl_version="$Id$"; - -#define DEFAULT "default" -#define WHITE "white" -#define BLACK "black" -#define OK "ok" -#define MANY "many" +#include "includes.h" -enum status {oksofar, // not rejected yet - white, // whitelisted by envelope from - black, // blacklisted by envelope from or to - reject, // rejected by a dns list - reject_tag, // too many bad html tags - reject_host}; // too many hosts/urls in body +static char* dnsbl_version="$Id:"; -using namespace std; extern "C" { #include "libmilter/mfapi.h" @@ -102,115 +89,57 @@ void sig_chld(int signo); } -struct ltstr { - bool operator()(char* s1, char* s2) const { - return strcmp(s1, s2) < 0; - } -}; +bool debug_syslog = false; +bool syslog_opened = false; +bool loader_run = true; // used to stop the config loader thread +CONFIG * config = NULL; // protected by the config_mutex +int generation = 0; // protected by the config_mutex -struct DNSBL { - char *suffix; // blacklist suffix like blackholes.five-ten-sg.com - char *message; // error message with one or two %s operators for the ip address replacement - DNSBL(char *s, char *m); -}; -DNSBL::DNSBL(char *s, char *m) { - suffix = s; - message = m; -} +pthread_mutex_t config_mutex; +pthread_mutex_t syslog_mutex; +pthread_mutex_t resolve_mutex; +pthread_mutex_t fd_pool_mutex; -typedef DNSBL * DNSBLP; -typedef list<DNSBLP> DNSBLL; -typedef DNSBLL * DNSBLLP; -typedef map<char *, char *, ltstr> string_map; -typedef map<char *, string_map *, ltstr> from_map; -typedef map<char *, DNSBLP, ltstr> dnsblp_map; -typedef map<char *, DNSBLLP, ltstr> dnsbllp_map; -typedef set<char *, ltstr> string_set; -typedef set<int> int_set; -typedef list<char *> string_list; -typedef map<char *, int, ltstr> ns_mapper; +std::set<int> fd_pool; +int NULL_SOCKET = -1; +char *resolver_port = NULL; // unix domain socket to talk to the dns resolver process +int resolver_socket = NULL_SOCKET; // socket used to listen for resolver requests +time_t ERROR_SOCKET_TIME = 60; // number of seconds between attempts to open the spam filter socket +time_t last_error_time; +int resolver_sock_count = 0; // protected with fd_pool_mutex +int resolver_pool_size = 0; // protected with fd_pool_mutex + struct ns_map { // all the strings are owned by the keys/values in the ns_host string map string_map ns_host; // nameserver name -> host name that uses this name server ns_mapper ns_ip; // nameserver name -> ip address of the name server + ~ns_map(); + void add(char *name, char *refer); }; -struct CONFIG { - // the only mutable stuff once it has been loaded from the config file - int reference_count; // protected by the global config_mutex - // all the rest is constant after loading from the config file - int generation; - time_t load_time; - string_list config_files; - dnsblp_map dnsbls; - dnsbllp_map dnsblls; - from_map env_from; - string_map env_to_dnsbll; // map recipient to a named dnsbll - string_map env_to_chkfrom; // map recipient to a named from map - char * content_suffix; // for sbl url body filtering - char * content_message; // "" - string_set content_host_ignore;// hosts to ignore for content sbl checking - char * host_limit_message; // error message for excessive host names - int host_limit; // limit on host names - bool host_random; // pick a random selection of host names rather than error for excessive hosts - char * tag_limit_message; // error message for excessive bad html tags - int tag_limit; // limit on bad html tags - string_set html_tags; // set of valid html tags - string_set tlds; // set of valid tld components - CONFIG(); - ~CONFIG(); -}; -CONFIG::CONFIG() { - reference_count = 0; - generation = 0; - load_time = 0; - content_suffix = NULL; - content_message = NULL; - host_limit_message = NULL; - host_limit = 0; - host_random = false; - tag_limit_message = NULL; - tag_limit = 0; + +void ns_map::~ns_map() { + for (string_map::iterator i=ns_host.begin(); i!=ns_host.end(); i++) { + char *x = (*i).first; + char *y = (*i).second; + free(x); + free(y); } -CONFIG::~CONFIG() { - for (dnsblp_map::iterator i=dnsbls.begin(); i!=dnsbls.end(); i++) { - DNSBLP d = (*i).second; - // delete the underlying DNSBL objects. - delete d; - } - for (dnsbllp_map::iterator i=dnsblls.begin(); i!=dnsblls.end(); i++) { - DNSBLLP d = (*i).second; - // *d is a list of pointers to DNSBL objects, but - // the underlying objects have already been deleted above. - delete d; - } - for (from_map::iterator i=env_from.begin(); i!=env_from.end(); i++) { - string_map *d = (*i).second; - delete d; - } + ns_ip.clear(); + ns_host.clear(); } -static bool debug_syslog = false; -static bool loader_run = true; // used to stop the config loader thread -static string_set all_strings; // owns all the strings, only modified by the config loader thread -static CONFIG * config = NULL; // protected by the config_mutex -static int generation = 0; // protected by the config_mutex -static pthread_mutex_t config_mutex; -static pthread_mutex_t syslog_mutex; -static pthread_mutex_t resolve_mutex; -static pthread_mutex_t fd_pool_mutex; +void ns_map::add(char *name, char *refer) { + string_map::iterator i = ns_host.find(name); + if (i != ns_host.end()) return; + char *x = strdup(name); + char *y = strdup(refer); + ns_ip[x] = 0; + ns_host[x] = y; -static std::set<int> fd_pool; -static int NULL_SOCKET = -1; -static char *resolver_port = NULL; // unix domain socket to talk to the dns resolver process -static int resolver_socket = NULL_SOCKET; // socket used to listen for resolver requests -static time_t ERROR_SOCKET_TIME = 60; // number of seconds between attempts to open the spam filter socket -static time_t last_error_time; -static int resolver_sock_count = 0; // protected with fd_pool_mutex -static int resolver_pool_size = 0; // protected with fd_pool_mutex - +} // packed structure to allow a single socket write to dump the // length and the following answer. The packing attribute is gcc specific. @@ -223,77 +152,36 @@ #endif } __attribute__ ((packed)); -struct mlfiPriv; + +//////////////////////////////////////////////// +// helper to discard the strings held by a context_map +// +void discard(context_map &cm); +void discard(context_map &cm) { + for (context_map::iterator i=cm.begin(); i!=cm.end(); i++) { + char *x = (*i).first; + free(x); + } +} //////////////////////////////////////////////// -// helper to discard the strings and objects held by an ns_map +// helper to register a string in a context_map // -static void discard(ns_map &s); -static void discard(ns_map &s) { - for (string_map::iterator i=s.ns_host.begin(); i!=s.ns_host.end(); i++) { - char *x = (*i).first; - char *y = (*i).second; - free(x); - free(y); - } - s.ns_ip.clear(); - s.ns_host.clear(); -} - -//////////////////////////////////////////////// -// helper to register a string in an ns_map -// -static void register_string(ns_map &s, char *name, char *refer); -static void register_string(ns_map &s, char *name, char *refer) { - string_map::iterator i = s.ns_host.find(name); - if (i != s.ns_host.end()) return; +void register_string(context_map &cm, char *name, CONTEXT *con); +void register_string(context_map &cm, char *name, CONTEXT *con) { + context_map::iterator i = cm.find(name); + if (i != cm.end()) return; char *x = strdup(name); - char *y = strdup(refer); - s.ns_ip[x] = 0; - s.ns_host[x] = y; - + cm[x] = con; } -//////////////////////////////////////////////// -// helper to discard the strings held by a string_set -// -static void discard(string_set &s); -static void discard(string_set &s) { - for (string_set::iterator i=s.begin(); i!=s.end(); i++) { - free(*i); - } - s.clear(); -} - -//////////////////////////////////////////////// -// helper to register a string in a string set -// -static char* register_string(string_set &s, char *name); -static char* register_string(string_set &s, char *name) { - string_set::iterator i = s.find(name); - if (i != s.end()) return *i; - char *x = strdup(name); - s.insert(x); - return x; -} - -//////////////////////////////////////////////// -// syslog a message -// -static void my_syslog(mlfiPriv *priv, char *text); - - -// include the content scanner -#include "scanner.cpp" - //////////////////////////////////////////////// // disconnect the fd from the dns resolver process // void my_disconnect(int sock, bool decrement = true); -void my_disconnect(int sock, bool decrement) -{ +void my_disconnect(int sock, bool decrement) { if (sock != NULL_SOCKET) { if (decrement) { pthread_mutex_lock(&fd_pool_mutex); @@ -310,8 +198,7 @@ // return fd connected to the dns resolver process // int my_connect(); -int my_connect() -{ +int my_connect() { // if we have had recent errors, don't even try to open the socket time_t now = time(NULL); if ((now - last_error_time) < ERROR_SOCKET_TIME) return NULL_SOCKET; @@ -341,35 +228,6 @@ } -//////////////////////////////////////////////// -// mail filter private data, held for us by sendmail -// -struct mlfiPriv -{ - // connection specific data - CONFIG *pc; // global context with our maps - int fd; // to talk to dns resolvers process - bool err; // did we get any errors on the resolver socket? - int ip; // ip4 address of the smtp client - map<DNSBLP, status> checked; // status from those lists - // message specific data - char *mailaddr; // envelope from value - char *queueid; // sendmail queue id - bool authenticated; // client authenticated? if so, suppress all dnsbl checks - bool have_whites; // have at least one whitelisted recipient? need to accept content and remove all non-whitelisted recipients if it fails - bool only_whites; // every recipient is whitelisted? - string_set non_whites; // remember the non-whitelisted recipients so we can remove them if need be - recorder *memory; // memory for the content scanner - url_scanner *scanner; // object to handle body scanning - mlfiPriv(); - ~mlfiPriv(); - void reset(bool final = false); // for a new message - void get_fd(); - void return_fd(); - int my_read(char *buf, int len); - int my_write(char *buf, int len); -}; - mlfiPriv::mlfiPriv() { pthread_mutex_lock(&config_mutex); pc = config; @@ -382,7 +240,7 @@ authenticated = false; have_whites = false; only_whites = true; - memory = new recorder(this, &pc->html_tags, &pc->tlds); + memory = new recorder(this, pc->get_html_tags(), pc->get_content_tlds()); scanner = new url_scanner(memory); } @@ -397,7 +255,7 @@ void mlfiPriv::reset(bool final) { if (mailaddr) free(mailaddr); if (queueid) free(queueid); - discard(non_whites); + discard(env_to); delete memory; delete scanner; if (!final) { @@ -406,13 +264,12 @@ authenticated = false; have_whites = false; only_whites = true; - memory = new recorder(this, &pc->html_tags, &pc->tlds); + memory = new recorder(this, pc->get_html_tags(), pc->get_content_tlds()); scanner = new url_scanner(memory); } } -void mlfiPriv::get_fd() -{ +void mlfiPriv::get_fd() { err = true; fd = NULL_SOCKET; int result = pthread_mutex_lock(&fd_pool_mutex); @@ -441,8 +298,7 @@ } } -void mlfiPriv::return_fd() -{ +void mlfiPriv::return_fd() { if (err) { // this fd got a socket error, so close it, rather than returning it to the pool my_disconnect(fd); @@ -470,8 +326,7 @@ } } -int mlfiPriv::my_write(char *buf, int len) -{ +int mlfiPriv::my_write(char *buf, int len) { if (err) return 0; int rs = 0; while (len) { @@ -491,8 +346,7 @@ return rs; } -int mlfiPriv::my_read(char *buf, int len) -{ +int mlfiPriv::my_read(char *buf, int len) { if (err) return 0; int rs = 0; while (len > 1) { @@ -512,133 +366,42 @@ return rs; } +void mlfiPriv::need_content_filter(char *rcpt, CONTEXT &con) { + register_string(env_to, rcpt, &con); +} + #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) //////////////////////////////////////////////// // syslog a message // -static void my_syslog(mlfiPriv *priv, char *text) { +void my_syslog(mlfiPriv *priv, char *text) { char buf[1000]; if (priv) { snprintf(buf, sizeof(buf), "%s: %s", priv->queueid, text); text = buf; } pthread_mutex_lock(&syslog_mutex); + if (!syslog_opened) { openlog("dnsbl", LOG_PID, LOG_MAIL); + syslog_opened = true; + } syslog(LOG_NOTICE, "%s", text); - closelog(); pthread_mutex_unlock(&syslog_mutex); } -static void my_syslog(char *text); -static void my_syslog(char *text) { +void my_syslog(char *text) { my_syslog(NULL, text); } -//////////////////////////////////////////////// -// register a global string -// -static char* register_string(char *name); -static char* register_string(char *name) { - return register_string(all_strings, name); -} - - -static char* next_token(char *delim); -static char* next_token(char *delim) { - char *name = strtok(NULL, delim); - if (!name) return name; - return register_string(name); -} - - -//////////////////////////////////////////////// -// lookup an email address in the env_from or env_to maps -// -static char* lookup1(char *email, string_map map); -static char* lookup1(char *email, string_map map) { - string_map::iterator i = map.find(email); - if (i != map.end()) return (*i).second; - char *x = strchr(email, '@'); - if (!x) return DEFAULT; - x++; - i = map.find(x); - if (i != map.end()) return (*i).second; - return DEFAULT; -} - - -//////////////////////////////////////////////// -// lookup an email address in the env_from or env_to maps -// this email address is passed in from sendmail, and will -// always be enclosed in <>. It may have mixed case, just -// as the mail client sent it. -// -static char* lookup(char* email, string_map map); -static char* lookup(char* email, string_map map) { - int n = strlen(email)-2; - if (n < 1) return DEFAULT; // malformed - char *key = strdup(email+1); - key[n] = '\0'; - for (int i=0; i<n; i++) key[i] = tolower(key[i]); - char *rc = lookup1(key, map); - free(key); - return rc; -} - - -//////////////////////////////////////////////// -// find the dnsbl with a specific name -// -static DNSBLP find_dnsbl(CONFIG &dc, char *name); -static DNSBLP find_dnsbl(CONFIG &dc, char *name) { - dnsblp_map::iterator i = dc.dnsbls.find(name); - if (i == dc.dnsbls.end()) return NULL; - return (*i).second; -} - - -//////////////////////////////////////////////// -// find the dnsbll with a specific name -// -static DNSBLLP find_dnsbll(CONFIG &dc, char *name); -static DNSBLLP find_dnsbll(CONFIG &dc, char *name) { - dnsbllp_map::iterator i = dc.dnsblls.find(name); - if (i == dc.dnsblls.end()) return NULL; - return (*i).second; -} - - -//////////////////////////////////////////////// -// find the envfrom map with a specific name -// -static string_map* find_from_map(CONFIG &dc, char *name); -static string_map* find_from_map(CONFIG &dc, char *name) { - from_map::iterator i = dc.env_from.find(name); - if (i == dc.env_from.end()) return NULL; - return (*i).second; -} - - -static string_map& really_find_from_map(CONFIG &dc, char *name); -static string_map& really_find_from_map(CONFIG &dc, char *name) { - string_map *sm = find_from_map(dc, name); - if (!sm) { - sm = new string_map; - dc.env_from[name] = sm; - } - return *sm; -} - - //////////////////////////////////////////////// // read a resolver request from the socket, process it, and // write the result back to the socket. -static void process_resolver_requests(int socket); -static void process_resolver_requests(int socket) { +void process_resolver_requests(int socket); +void process_resolver_requests(int socket) { #ifdef NS_MAXDNAME char question[NS_MAXDNAME]; #else @@ -713,8 +476,8 @@ // If we cannot get an answer, we just accept the mail. // // -static int dns_interface(mlfiPriv &priv, char *question, bool maybe_ip, ns_map *nameservers); -static int dns_interface(mlfiPriv &priv, char *question, bool maybe_ip, ns_map *nameservers) { +int dns_interface(mlfiPriv &priv, char *question, bool maybe_ip, ns_map *nameservers); +int dns_interface(mlfiPriv &priv, char *question, bool maybe_ip, ns_map *nameservers) { // this part can be done without locking the resolver mutex. Each // milter thread is talking over its own socket to a separate resolver // process, which does the actual dns resolution. @@ -772,7 +535,7 @@ } if (n-nam) n--; // remove trailing . *n = '\0'; // null terminate it - register_string(ns, nam, question); // ns host to lookup later + ns.add(nam, question); // ns host to lookup later } } rrnum = 0; @@ -817,8 +580,8 @@ //////////////////////////////////////////////// // check a single dnsbl // -static status check_single(mlfiPriv &priv, int ip, char *suffix); -static status check_single(mlfiPriv &priv, int ip, char *suffix) { +bool check_single(mlfiPriv &priv, int ip, char *suffix); +bool check_single(mlfiPriv &priv, int ip, char *suffix) { // make a dns question const u_char *src = (const u_char *)&ip; if (src[0] == 127) return oksofar; // don't do dns lookups on localhost @@ -829,15 +592,15 @@ #endif snprintf(question, sizeof(question), "%u.%u.%u.%u.%s.", src[3], src[2], src[1], src[0], suffix); // ask the question, if we get an A record it implies a blacklisted ip address - return (dns_interface(priv, question, false, NULL)) ? reject : oksofar; + return dns_interface(priv, question, false, NULL); } //////////////////////////////////////////////// // check a single dnsbl // -static status check_single(mlfiPriv &priv, int ip, DNSBL &bl); -static status check_single(mlfiPriv &priv, int ip, DNSBL &bl) { +bool check_single(mlfiPriv &priv, int ip, DNSBL &bl); +bool check_single(mlfiPriv &priv, int ip, DNSBL &bl) { return check_single(priv, ip, bl.suffix); } @@ -845,15 +608,13 @@ //////////////////////////////////////////////// // check the dnsbls specified for this recipient // -static status check_dnsbl(mlfiPriv &priv, DNSBLLP dnsbllp, DNSBLP &rejectlist); -static status check_dnsbl(mlfiPriv &priv, DNSBLLP dnsbllp, DNSBLP &rejectlist) { +bool check_dnsbl(mlfiPriv &priv, dnsblp_list &dnsbll, DNSBLP &rejectlist); +bool check_dnsbl(mlfiPriv &priv, dnsblp_list &dnsbll, DNSBLP &rejectlist) { if (priv.authenticated) return oksofar; - if (!dnsbllp) return oksofar; - DNSBLL &dnsbll = *dnsbllp; - for (DNSBLL::iterator i=dnsbll.begin(); i!=dnsbll.end(); i++) { + for (dnsblp_list::iterator i=dnsbll.begin(); i!=dnsbll.end(); i++) { DNSBLP dp = *i; // non null by construction - status st; - map<DNSBLP, status>::iterator f = priv.checked.find(dp); + bool st; + map<DNSBLP, bool>::iterator f = priv.checked.find(dp); if (f == priv.checked.end()) { // have not checked this list yet st = check_single(priv, priv.ip, *dp); @@ -864,35 +625,37 @@ st = (*f).second; rejectlist = (*f).first; } - if (st == reject) return st; + if (st) return st; } - return oksofar; + return false; } //////////////////////////////////////////////// // check the hosts from the body against the content dnsbl // -static status check_hosts(mlfiPriv &priv, char *&host, int &ip); -static status check_hosts(mlfiPriv &priv, char *&host, int &ip) { +bool check_hosts(mlfiPriv &priv, bool random, int limit, char *&host, int ip); +bool check_hosts(mlfiPriv &priv) { + static buf[2000]; CONFIG &dc = *priv.pc; + string_set &hosts = priv.memory->hosts; + string_set &ignore = dc.get_content_host_ignore(); + int count = 0; + int cnt = hosts.size(); // number of hosts we could look at + int_set ips; ns_map nameservers; - bool ran = priv.pc->host_random; - int lim = priv.pc->host_limit; // we should not look at more than this many hosts - int cnt = priv.memory->hosts.size(); // number of hosts we could look at - int_set ips; // remove duplicate ip addresses - for (string_set::iterator i=priv.memory->hosts.begin(); i!=priv.memory->hosts.end(); i++) { - host = *i; // a reference into priv.memory->hosts, which will live until this smtp transaction is closed + for (string_set::iterator i=hosts.begin(); i!=hosts.end(); i++) { + host = *i; // a reference into hosts, which will live until this smtp transaction is closed // don't bother looking up hosts on the ignore list - string_set::iterator j = priv.pc->content_host_ignore.find(host); - if (j != priv.pc->content_host_ignore.end()) continue; + string_set::iterator j = ignore.find(host); + if (j != ignore.end()) continue; - // try to only look at lim/cnt fraction of the available cnt host names in random mode - if ((cnt > lim) && (lim > 0) && ran) { + // try to only look at limit/cnt fraction of the available cnt host names in random mode + if ((cnt > limit) && (limit > 0) && random) { int r = rand() % cnt; - if (r >= lim) { + if (r >= limit) { char buf[1000]; snprintf(buf, sizeof(buf), "host %s skipped", host); my_syslog(&priv, buf); @@ -900,10 +663,6 @@ } } count++; - if ((count > lim) && (lim > 0) && (!ran)) { - discard(nameservers); - return reject_host; - } ip = dns_interface(priv, host, true, &nameservers); if (debug_syslog) { char buf[1000]; @@ -922,21 +681,18 @@ int_set::iterator i = ips.find(ip); if (i == ips.end()) { ips.insert(ip); - status st = check_single(priv, ip, dc.content_suffix); - if (st == reject) { - discard(nameservers); - return st; + if (check_single(priv, ip, dc.content_suffix)) { + return true; } } } } - lim *= 4; // allow average of 3 ns per host name + limit *= 4; // allow average of 3 ns per host name for (ns_mapper::iterator i=nameservers.ns_ip.begin(); i!=nameservers.ns_ip.end(); i++) { count++; - if ((count > lim) && (lim > 0)) { - if (ran) continue; // don't complain - discard(nameservers); - return reject_host; + if ((count > limit) && (limit > 0)) { + if (random) continue; // don't complain + return true; } host = (*i).first; // a transient reference that needs to be replaced before we return it ip = (*i).second; @@ -958,32 +714,23 @@ int_set::iterator i = ips.find(ip); if (i == ips.end()) { ips.insert(ip); - status st = check_single(priv, ip, dc.content_suffix); - if (st == reject) { + if (check_single(priv, ip, dc.content_suffix)) { string_map::iterator j = nameservers.ns_host.find(host); if (j != nameservers.ns_host.end()) { char *refer = (*j).second; char buf[1000]; snprintf(buf, sizeof(buf), "%s with nameserver %s", refer, host); - host = register_string(priv.memory->hosts, buf); // put a copy into priv.memory->hosts, and return that reference + host = register_string(hosts, buf); // put a copy into hosts, and return that reference } else { - host = register_string(priv.memory->hosts, host); // put a copy into priv.memory->hosts, and return that reference + host = register_string(hosts, host); // put a copy into hosts, and return that reference } - discard(nameservers); - return st; + return true; } } } } - discard(nameservers); - host = NULL; - int bin = priv.memory->binary_tags; - int bad = priv.memory->bad_html_tags; - lim = priv.pc->tag_limit; - if (3*bin > bad) return oksofar; // probably .zip or .tar.gz with random content - if ((bad > lim) && (lim > 0)) return reject_tag; - return oksofar; + return false; } @@ -1016,38 +763,23 @@ sfsistat mlfi_envrcpt(SMFICTX *ctx, char **rcpt) { DNSBLP rejectlist = NULL; // list that caused the reject - status st = oksofar; mlfiPriv &priv = *MLFIPRIV; CONFIG &dc = *priv.pc; if (!priv.queueid) priv.queueid = strdup(smfi_getsymval(ctx, "i")); char *rcptaddr = rcpt[0]; - char *dnsname = lookup(rcptaddr, dc.env_to_dnsbll); - char *fromname = lookup(rcptaddr, dc.env_to_chkfrom); - if ((strcmp(dnsname, BLACK) == 0) || - (strcmp(fromname, BLACK) == 0)) { - st = black; // two options to blacklist this recipient + CONTEXT &con = *(dc.find_context(rcptaddr, priv.mailaddr)); + char *fromvalue = con.find_from(priv.mailaddr); + status st; + if (fromvalue == token_black) { + st = black; } - else if (strcmp(fromname, WHITE) == 0) { + else if (fromvalue == token_white) { st = white; } else { - // check an env_from map - string_map *sm = find_from_map(dc, fromname); - if (sm != NULL) { - fromname = lookup(priv.mailaddr, *sm); // returns default if name not in map - if (strcmp(fromname, BLACK) == 0) { - st = black; // blacklist this envelope from value + // check the dns based lists + st = check_dnsbl(priv, con.get_dnsbl_list(), rejectlist); } - if (strcmp(fromname, WHITE) == 0) { - st = white; // blacklist this envelope from value - } - } - } - if ((st == oksofar) && (strcmp(dnsname, WHITE) != 0)) { - // check dns lists - st = check_dnsbl(priv, find_dnsbll(dc, dnsname), rejectlist); - } - if (st == reject) { // reject the recipient based on some dnsbl char adr[sizeof "255.255.255.255"]; @@ -1065,9 +797,10 @@ } else { // accept the recipient + if (!con.get_content_filtering()) st = white; if (st == oksofar) { // but remember the non-whites - register_string(priv.non_whites, rcptaddr); + priv.need_content_filter(rcptaddr, con); priv.only_whites = false; } if (st == white) { @@ -1082,7 +815,6 @@ mlfiPriv &priv = *MLFIPRIV; if (priv.authenticated) return SMFIS_CONTINUE; if (priv.only_whites) return SMFIS_CONTINUE; - if (!priv.pc->content_suffix) return SMFIS_CONTINUE; priv.scanner->scan(data, len); return SMFIS_CONTINUE; } @@ -1091,38 +823,53 @@ { sfsistat rc; mlfiPriv &priv = *MLFIPRIV; + CONFIG &dc = *priv.pc; char *host = NULL; int ip; status st; // process end of message - if (priv.authenticated || - priv.only_whites || - (!priv.pc->content_suffix) || - ((st=check_hosts(priv, host, ip)) == oksofar)) rc = SMFIS_CONTINUE; + if (priv.authenticated || priv.only_whites) rc = SMFIS_CONTINUE; else { - if (!priv.have_whites) { - // can reject the entire message - char buf[2000]; - if (st == reject_tag) { - // rejected due to excessive bad html tags - snprintf(buf, sizeof(buf), priv.pc->tag_limit_message); - } - else if (st == reject_host) { - // rejected due to excessive unique host/urls - snprintf(buf, sizeof(buf), priv.pc->host_limit_message); + char *msg = NULL; + string_set alive; + bool random = false; + bool limit = 0; + for (context_map::iterator i=env_to.begin(); i!=env_to.end(); i++) { + char *rcpt = (*i).first; + CONTEXT &con = *((*i).second); + if (!con.acceptable_content(priv.memory, msg)) { + // bad html tags or excessive hosts + smfi_delrcpt(ctx, rcpt); } else { + alive.insert(rcpt); + random |= con.get_host_random(); + limit = max(limit, con.get_host_limit()); + } + } + bool rejecting = alive.empty(); + if (!rejecting) { + rejecting = check_hosts(priv, random, limit, host, ip); + if (rejecting) { + static char buf[2000]; char adr[sizeof "255.255.255.255"]; adr[0] = '\0'; inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr)); - snprintf(buf, sizeof(buf), priv.pc->content_message, host, adr); + snprintf(buf, sizeof(buf), dc.get_content_message(), host, adr); + msg = buf; + } } - smfi_setreply(ctx, "550", "5.7.1", buf); + if (!rejecting) { + rc = SMFIS_CONTINUE; + } + else if (!priv.have_whites && alive.empty()) { + // can reject the entire message + smfi_setreply(ctx, "550", "5.7.1", msg); rc = SMFIS_REJECT; } else { // need to accept it but remove the recipients that don't want it - for (string_set::iterator i=priv.non_whites.begin(); i!=priv.non_whites.end(); i++) { + for (string_set::iterator i=alive.begin(); i!=alive.end(); i++) { char *rcpt = *i; smfi_delrcpt(ctx, rcpt); } @@ -1168,404 +915,11 @@ }; -static void dumpit(char *name, string_map map); -static void dumpit(char *name, string_map map) { - fprintf(stdout, "\n"); - for (string_map::iterator i=map.begin(); i!=map.end(); i++) { - fprintf(stdout, "%s %s->%s\n", name, (*i).first, (*i).second); - } -} - - -static void dumpit(from_map map); -static void dumpit(from_map map) { - for (from_map::iterator i=map.begin(); i!=map.end(); i++) { - char buf[2000]; - snprintf(buf, sizeof(buf), "envelope from map for %s", (*i).first); - string_map *sm = (*i).second; - dumpit(buf, *sm); - } -} - - -static void dumpit(CONFIG &dc); -static void dumpit(CONFIG &dc) { - dumpit(dc.env_from); - dumpit("envelope to (dnsbl list)", dc.env_to_dnsbll); - dumpit("envelope to (from map)", dc.env_to_chkfrom); - fprintf(stdout, "\ndnsbls\n"); - for (dnsblp_map::iterator i=dc.dnsbls.begin(); i!=dc.dnsbls.end(); i++) { - fprintf(stdout, "%s %s %s\n", (*i).first, (*i).second->suffix, (*i).second->message); - } - fprintf(stdout, "\ndnsbl_lists\n"); - for (dnsbllp_map::iterator i=dc.dnsblls.begin(); i!=dc.dnsblls.end(); i++) { - char *name = (*i).first; - DNSBLL &dl = *((*i).second); - fprintf(stdout, "%s", name); - for (DNSBLL::iterator j=dl.begin(); j!=dl.end(); j++) { - DNSBL &d = **j; - fprintf(stdout, " %s", d.suffix); - } - fprintf(stdout, "\n"); - } - if (dc.content_suffix) { - fprintf(stdout, "\ncontent filtering enabled with %s %s\n", dc.content_suffix, dc.content_message); - } - for (string_set::iterator i=dc.content_host_ignore.begin(); i!=dc.content_host_ignore.end(); i++) { - fprintf(stdout, "ignore %s\n", (*i)); - } - if (dc.host_limit && !dc.host_random) { - fprintf(stdout, "\ncontent filtering for host names hard limit %d %s\n", dc.host_limit, dc.host_limit_message); - } - if (dc.host_limit && dc.host_random) { - fprintf(stdout, "\ncontent filtering for host names soft limit %d\n", dc.host_limit); - } - if (dc.tag_limit) { - fprintf(stdout, "\ncontent filtering for excessive html tags enabled with limit %d %s\n", dc.tag_limit, dc.tag_limit_message); - } - fprintf(stdout, "\nfiles\n"); - for (string_list::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { - char *f = *i; - fprintf(stdout, "config includes %s\n", f); - } -} - - -//////////////////////////////////////////////// -// check for redundant or recursive include files -// -static bool ok_to_include(CONFIG &dc, char *fn); -static bool ok_to_include(CONFIG &dc, char *fn) { - if (!fn) return false; - bool ok = true; - for (string_list::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { - char *f = *i; - if (strcmp(f, fn) == 0) { - my_syslog("redundant or recursive include file detected"); - ok = false; - break; - } - } - return ok; -} - - -//////////////////////////////////////////////// -// load a single config file -// -static void load_conf_dcc(CONFIG &dc, char *name, char *fn); -static void load_conf_dcc(CONFIG &dc, char *name, char *fn) { - ifstream is(fn); - if (is.fail()) { - char buf[1000]; - snprintf(buf, sizeof(buf), "include file %s not found", fn); - my_syslog(buf); - return; - } - dc.config_files.push_back(fn); - const int LINE_SIZE = 2000; - char line[LINE_SIZE]; - char *list = BLACK; - char *delim = " \t"; - int curline = 0; - while (!is.eof()) { - is.getline(line, LINE_SIZE); - curline++; - int n = strlen(line); - if (!n) continue; - for (int i=0; i<n; i++) line[i] = tolower(line[i]); - if (line[0] == '#') continue; - char *head = line; - if (strspn(line, delim) == 0) { - // have a leading ok/many tag to fetch - char *cmd = strtok(line, delim); - if (strcmp(cmd, MANY) == 0) list = BLACK; - else if (strcmp(cmd, OK) == 0) list = WHITE; - head = cmd + strlen(cmd) + 1; - } - char *cmd = strtok(head, delim); - if (!cmd) continue; - if (strcmp(cmd, "env_from") == 0) { - char *from = next_token(delim); - if (from) { - string_map &fm = really_find_from_map(dc, name); - fm[from] = list; - } - } - else if (strcmp(cmd, "env_to") == 0) { - char *to = next_token(delim); - if (to) { - dc.env_to_dnsbll[to] = list; - dc.env_to_chkfrom[to] = list; - } - } - else if (strcmp(cmd, "substitute") == 0) { - char *tag = next_token(delim); - if (tag && (strcmp(tag, "mail_host") == 0)) { - char *from = next_token(delim); - if (from) { - string_map &fm = really_find_from_map(dc, name); - fm[from] = list; - } - } - } - else if (strcmp(cmd, "include") == 0) { - char *fn = next_token(delim); - if (ok_to_include(dc, fn)) { - load_conf_dcc(dc, name, fn); - } - } - - } - is.close(); -} - - -static void load_conf(CONFIG &dc, char *fn); -static void load_conf(CONFIG &dc, char *fn) { - ifstream is(fn); - if (is.fail()) { - char buf[1000]; - snprintf(buf, sizeof(buf), "include file %s not found", fn); - my_syslog(buf); - return; - } - dc.config_files.push_back(fn); - map<char*, int, ltstr> commands; - enum {dummy, tld, content, ignore, hostlimit, hostslimit, htmllimit, htmltag, dnsbl, dnsbll, envfrom, envto, include, includedcc}; - commands["tld" ] = tld; - commands["content" ] = content; - commands["ignore" ] = ignore; - commands["host_limit" ] = hostlimit; - commands["host_soft_limit"] = hostslimit; - commands["html_limit" ] = htmllimit; - commands["html_tag" ] = htmltag; - commands["dnsbl" ] = dnsbl; - commands["dnsbl_list" ] = dnsbll; - commands["env_from" ] = envfrom; - commands["env_to" ] = envto; - commands["include" ] = include; - commands["include_dcc" ] = includedcc; - const int LINE_SIZE = 2000; - char line[LINE_SIZE]; - char orig[LINE_SIZE]; - char *delim = " \t"; - int curline = 0; - while (!is.eof()) { - is.getline(line, LINE_SIZE); - snprintf(orig, sizeof(orig), "%s", line); - curline++; - int n = strlen(line); - for (int i=0; i<n; i++) line[i] = tolower(line[i]); - char *cmd = strtok(line, delim); - if (cmd && (cmd[0] != '#') && (cmd[0] != '\0')) { - // have a decent command - bool processed = false; - switch (commands[cmd]) { - case tld: { - char *tld = next_token(delim); - if (!tld) break; // no tld value - dc.tlds.insert(tld); - processed = true; - } break; - - case content: { - char *suff = strtok(NULL, delim); - if (!suff) break; // no dns suffix - char *msg = suff + strlen(suff); - if ((msg - line) >= strlen(orig)) break; // line ended with the dns suffix - msg = strchr(msg+1, '\''); - if (!msg) break; // no reply message template - msg++; // move over the leading ' - if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote - char *last = strchr(msg, '\''); - if (!last) break; // no trailing quote - *last = '\0'; // make it a null terminator - dc.content_suffix = register_string(suff); - dc.content_message = register_string(msg); - processed = true; - } break; - - case ignore: { - char *host = next_token(delim); - if (!host) break; - dc.content_host_ignore.insert(host); - processed = true; - } break; - - case hostlimit: { - char *limit = strtok(NULL, delim); - if (!limit) break; // no integer limit - char *msg = limit + strlen(limit); - if ((msg - line) >= strlen(orig)) break; // line ended with the limit - msg = strchr(msg+1, '\''); - if (!msg) break; // no reply message template - msg++; // move over the leading ' - if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote - char *last = strchr(msg, '\''); - if (!last) break; // no trailing quote - *last = '\0'; // make it a null terminator - dc.host_limit = atoi(limit); - dc.host_limit_message = register_string(msg); - dc.host_random = false; - processed = true; - } break; - - case hostslimit: { - char *limit = next_token(delim); - if (!limit) break; // no integer limit - dc.host_limit = atoi(limit); - dc.host_random = true; - processed = true; - } break; - - case htmllimit: { - char *limit = strtok(NULL, delim); - if (!limit) break; // no integer limit - char *msg = limit + strlen(limit); - if ((msg - line) >= strlen(orig)) break; // line ended with the limit - msg = strchr(msg+1, '\''); - if (!msg) break; // no reply message template - msg++; // move over the leading ' - if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote - char *last = strchr(msg, '\''); - if (!last) break; // no trailing quote - *last = '\0'; // make it a null terminator - dc.tag_limit = atoi(limit); - dc.tag_limit_message = register_string(msg); - processed = true; - } break; - - case htmltag: { - char *tag = next_token(delim); - if (!tag) break; // no html tag value - dc.html_tags.insert(tag); // base version - char buf[200]; - snprintf(buf, sizeof(buf), "/%s", tag); - dc.html_tags.insert(register_string(buf)); // leading / - snprintf(buf, sizeof(buf), "%s/", tag); - dc.html_tags.insert(register_string(buf)); // trailing / - processed = true; - } break; - - case dnsbl: { - // have a new dnsbl to use - char *name = next_token(delim); - if (!name) break; // no name name - if (find_dnsbl(dc, name)) break; // duplicate entry - char *suff = strtok(NULL, delim); - if (!suff) break; // no dns suffic - char *msg = suff + strlen(suff); - if ((msg - line) >= strlen(orig)) break; // line ended with the dns suffix - msg = strchr(msg+1, '\''); - if (!msg) break; // no reply message template - msg++; // move over the leading ' - if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote - char *last = strchr(msg, '\''); - if (!last) break; // no trailing quote - *last = '\0'; // make it a null terminator - dc.dnsbls[name] = new DNSBL(register_string(suff), register_string(msg)); - processed = true; - } break; - - case dnsbll: { - // define a new combination of dnsbls - char *name = next_token(delim); - if (!name) break; - if (find_dnsbll(dc, name)) break; // duplicate entry - char *list = next_token(delim); - if (!list || (*list == '\0') || (*list == '#')) break; - DNSBLLP d = new DNSBLL; - DNSBLP p = find_dnsbl(dc, list); - if (p) d->push_back(p); - while (true) { - list = next_token(delim); - if (!list || (*list == '\0') || (*list == '#')) break; - DNSBLP p = find_dnsbl(dc, list); - if (p) d->push_back(p); - } - dc.dnsblls[name] = d; - processed = true; - } break; - - case envfrom: { - // add an entry into the named string_map - char *name = next_token(delim); - if (!name) break; - char *from = next_token(delim); - if (!from) break; - char *list = next_token(delim); - if (!list) break; - if ((strcmp(list, WHITE) == 0) || - (strcmp(list, BLACK) == 0)) { - string_map &fm = really_find_from_map(dc, name); - fm[from] = list; - processed = true; - } - else { - // list may be the name of a previously defined from_map - string_map *m = find_from_map(dc, list); - if (m && (strcmp(list,name) != 0)) { - string_map &pm = *m; - string_map &fm = really_find_from_map(dc, name); - fm.insert(pm.begin(), pm.end()); - processed = true; - } - } - } break; - - case envto: { - // define the dnsbl_list and env_from maps to use for this recipient - char *to = next_token(delim); - if (!to) break; - char *list = next_token(delim); - if (!list) break; - char *from = next_token(delim); - if (!from) break; - dc.env_to_dnsbll[to] = list; - dc.env_to_chkfrom[to] = from; - processed = true; - } break; - - case include: { - char *fn = next_token(delim); - if (ok_to_include(dc, fn)) { - load_conf(dc, fn); - processed = true; - } - } break; - - case includedcc: { - char *name = next_token(delim); - if (!name) break; - char *fn = next_token(delim); - if (ok_to_include(dc, fn)) { - load_conf_dcc(dc, name, fn); - processed = true; - } - } break; - - default: { - } break; - } - if (!processed) { - pthread_mutex_lock(&syslog_mutex); - openlog("dnsbl", LOG_PID, LOG_MAIL); - syslog(LOG_ERR, "ignoring file %s line %d : %s\n", fn, curline, orig); - closelog(); - pthread_mutex_unlock(&syslog_mutex); - } - } - } - is.close(); -} - - //////////////////////////////////////////////// // reload the config // -static CONFIG* new_conf(); -static CONFIG* new_conf() { +CONFIG* new_conf(); +CONFIG* new_conf() { CONFIG *newc = new CONFIG; pthread_mutex_lock(&config_mutex); newc->generation = generation++; @@ -1573,10 +927,13 @@ char buf[200]; snprintf(buf, sizeof(buf), "loading configuration generation %d", newc->generation); my_syslog(buf); - load_conf(*newc, "dnsbl.conf"); + if (load_conf(*newc, "dnsbl.conf") { newc->load_time = time(NULL); return newc; } + delete newc; + return NULL; +} //////////////////////////////////////////////// @@ -1584,8 +941,8 @@ // and reload when needed. we also cleanup old // configs whose reference count has gone to zero. // -static void* config_loader(void *arg); -static void* config_loader(void *arg) { +void* config_loader(void *arg); +void* config_loader(void *arg) { typedef set<CONFIG *> configp_set; configp_set old_configs; while (loader_run) { @@ -1627,8 +984,8 @@ } -static void usage(char *prog); -static void usage(char *prog) +void usage(char *prog); +void usage(char *prog) { fprintf(stderr, "Usage: %s [-d] [-c] -r port -p sm-sock-addr [-t timeout]\n", prog); fprintf(stderr, "where port is for the connection to our own dns resolver processes\n"); @@ -1643,8 +1000,8 @@ -static void setup_socket(char *sock); -static void setup_socket(char *sock) { +void setup_socket(char *sock); +void setup_socket(char *sock) { unlink(sock); // sockaddr_un addr; // memset(&addr, '\0', sizeof addr); @@ -1672,6 +1029,7 @@ int main(int argc, char**argv) { + token_init(); bool check = false; bool setconn = false; bool setreso = false; @@ -1733,10 +1091,16 @@ } if (check) { - CONFIG &dc = *new_conf(); - dumpit(dc); + CONFIG *conf = new_conf(); + if (conf) { + conf->dump(); + delete conf; return 0; } + else { + return 1; // config failed to load + } + } if (!setconn) { fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);