comparison 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
comparison
equal deleted inserted replaced
71:dd21c8e13074 72:e6a2d0be7c5e
65 #include <arpa/nameser.h> 65 #include <arpa/nameser.h>
66 #include <resolv.h> 66 #include <resolv.h>
67 67
68 // misc stuff needed here 68 // misc stuff needed here
69 #include <ctype.h> 69 #include <ctype.h>
70 #include <fstream>
71 #include <syslog.h> 70 #include <syslog.h>
72 #include <pwd.h> 71 #include <pwd.h>
73 #include <sys/wait.h> /* header for waitpid() and various macros */ 72 #include <sys/wait.h> /* header for waitpid() and various macros */
74 #include <signal.h> /* header for signal functions */ 73 #include <signal.h> /* header for signal functions */
75 74
76 static char* dnsbl_version="$Id$"; 75 #include "includes.h"
77 76
78 #define DEFAULT "default" 77 static char* dnsbl_version="$Id:";
79 #define WHITE "white" 78
80 #define BLACK "black"
81 #define OK "ok"
82 #define MANY "many"
83
84 enum status {oksofar, // not rejected yet
85 white, // whitelisted by envelope from
86 black, // blacklisted by envelope from or to
87 reject, // rejected by a dns list
88 reject_tag, // too many bad html tags
89 reject_host}; // too many hosts/urls in body
90
91 using namespace std;
92 79
93 extern "C" { 80 extern "C" {
94 #include "libmilter/mfapi.h" 81 #include "libmilter/mfapi.h"
95 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr); 82 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr);
96 sfsistat mlfi_envfrom(SMFICTX *ctx, char **argv); 83 sfsistat mlfi_envfrom(SMFICTX *ctx, char **argv);
100 sfsistat mlfi_abort(SMFICTX *ctx); 87 sfsistat mlfi_abort(SMFICTX *ctx);
101 sfsistat mlfi_close(SMFICTX *ctx); 88 sfsistat mlfi_close(SMFICTX *ctx);
102 void sig_chld(int signo); 89 void sig_chld(int signo);
103 } 90 }
104 91
105 struct ltstr { 92 bool debug_syslog = false;
106 bool operator()(char* s1, char* s2) const { 93 bool syslog_opened = false;
107 return strcmp(s1, s2) < 0; 94 bool loader_run = true; // used to stop the config loader thread
108 } 95 CONFIG * config = NULL; // protected by the config_mutex
109 }; 96 int generation = 0; // protected by the config_mutex
110 97
111 struct DNSBL { 98 pthread_mutex_t config_mutex;
112 char *suffix; // blacklist suffix like blackholes.five-ten-sg.com 99 pthread_mutex_t syslog_mutex;
113 char *message; // error message with one or two %s operators for the ip address replacement 100 pthread_mutex_t resolve_mutex;
114 DNSBL(char *s, char *m); 101 pthread_mutex_t fd_pool_mutex;
115 }; 102
116 DNSBL::DNSBL(char *s, char *m) { 103 std::set<int> fd_pool;
117 suffix = s; 104 int NULL_SOCKET = -1;
118 message = m; 105 char *resolver_port = NULL; // unix domain socket to talk to the dns resolver process
119 } 106 int resolver_socket = NULL_SOCKET; // socket used to listen for resolver requests
120 107 time_t ERROR_SOCKET_TIME = 60; // number of seconds between attempts to open the spam filter socket
121 typedef DNSBL * DNSBLP; 108 time_t last_error_time;
122 typedef list<DNSBLP> DNSBLL; 109 int resolver_sock_count = 0; // protected with fd_pool_mutex
123 typedef DNSBLL * DNSBLLP; 110 int resolver_pool_size = 0; // protected with fd_pool_mutex
124 typedef map<char *, char *, ltstr> string_map; 111
125 typedef map<char *, string_map *, ltstr> from_map;
126 typedef map<char *, DNSBLP, ltstr> dnsblp_map;
127 typedef map<char *, DNSBLLP, ltstr> dnsbllp_map;
128 typedef set<char *, ltstr> string_set;
129 typedef set<int> int_set;
130 typedef list<char *> string_list;
131 typedef map<char *, int, ltstr> ns_mapper;
132 112
133 struct ns_map { 113 struct ns_map {
134 // all the strings are owned by the keys/values in the ns_host string map 114 // all the strings are owned by the keys/values in the ns_host string map
135 string_map ns_host; // nameserver name -> host name that uses this name server 115 string_map ns_host; // nameserver name -> host name that uses this name server
136 ns_mapper ns_ip; // nameserver name -> ip address of the name server 116 ns_mapper ns_ip; // nameserver name -> ip address of the name server
117 ~ns_map();
118 void add(char *name, char *refer);
137 }; 119 };
138 120
139 struct CONFIG { 121
140 // the only mutable stuff once it has been loaded from the config file 122 void ns_map::~ns_map() {
141 int reference_count; // protected by the global config_mutex 123 for (string_map::iterator i=ns_host.begin(); i!=ns_host.end(); i++) {
142 // all the rest is constant after loading from the config file 124 char *x = (*i).first;
143 int generation; 125 char *y = (*i).second;
144 time_t load_time; 126 free(x);
145 string_list config_files; 127 free(y);
146 dnsblp_map dnsbls; 128 }
147 dnsbllp_map dnsblls; 129 ns_ip.clear();
148 from_map env_from; 130 ns_host.clear();
149 string_map env_to_dnsbll; // map recipient to a named dnsbll 131 }
150 string_map env_to_chkfrom; // map recipient to a named from map 132
151 char * content_suffix; // for sbl url body filtering 133
152 char * content_message; // "" 134 void ns_map::add(char *name, char *refer) {
153 string_set content_host_ignore;// hosts to ignore for content sbl checking 135 string_map::iterator i = ns_host.find(name);
154 char * host_limit_message; // error message for excessive host names 136 if (i != ns_host.end()) return;
155 int host_limit; // limit on host names 137 char *x = strdup(name);
156 bool host_random; // pick a random selection of host names rather than error for excessive hosts 138 char *y = strdup(refer);
157 char * tag_limit_message; // error message for excessive bad html tags 139 ns_ip[x] = 0;
158 int tag_limit; // limit on bad html tags 140 ns_host[x] = y;
159 string_set html_tags; // set of valid html tags 141
160 string_set tlds; // set of valid tld components 142 }
161 CONFIG();
162 ~CONFIG();
163 };
164 CONFIG::CONFIG() {
165 reference_count = 0;
166 generation = 0;
167 load_time = 0;
168 content_suffix = NULL;
169 content_message = NULL;
170 host_limit_message = NULL;
171 host_limit = 0;
172 host_random = false;
173 tag_limit_message = NULL;
174 tag_limit = 0;
175 }
176 CONFIG::~CONFIG() {
177 for (dnsblp_map::iterator i=dnsbls.begin(); i!=dnsbls.end(); i++) {
178 DNSBLP d = (*i).second;
179 // delete the underlying DNSBL objects.
180 delete d;
181 }
182 for (dnsbllp_map::iterator i=dnsblls.begin(); i!=dnsblls.end(); i++) {
183 DNSBLLP d = (*i).second;
184 // *d is a list of pointers to DNSBL objects, but
185 // the underlying objects have already been deleted above.
186 delete d;
187 }
188 for (from_map::iterator i=env_from.begin(); i!=env_from.end(); i++) {
189 string_map *d = (*i).second;
190 delete d;
191 }
192 }
193
194 static bool debug_syslog = false;
195 static bool loader_run = true; // used to stop the config loader thread
196 static string_set all_strings; // owns all the strings, only modified by the config loader thread
197 static CONFIG * config = NULL; // protected by the config_mutex
198 static int generation = 0; // protected by the config_mutex
199
200 static pthread_mutex_t config_mutex;
201 static pthread_mutex_t syslog_mutex;
202 static pthread_mutex_t resolve_mutex;
203 static pthread_mutex_t fd_pool_mutex;
204
205 static std::set<int> fd_pool;
206 static int NULL_SOCKET = -1;
207 static char *resolver_port = NULL; // unix domain socket to talk to the dns resolver process
208 static int resolver_socket = NULL_SOCKET; // socket used to listen for resolver requests
209 static time_t ERROR_SOCKET_TIME = 60; // number of seconds between attempts to open the spam filter socket
210 static time_t last_error_time;
211 static int resolver_sock_count = 0; // protected with fd_pool_mutex
212 static int resolver_pool_size = 0; // protected with fd_pool_mutex
213
214 143
215 // packed structure to allow a single socket write to dump the 144 // packed structure to allow a single socket write to dump the
216 // length and the following answer. The packing attribute is gcc specific. 145 // length and the following answer. The packing attribute is gcc specific.
217 struct glommer { 146 struct glommer {
218 int length; 147 int length;
221 #else 150 #else
222 int answer; // without a resolver, we return a single ip4 address, 0 == no answer 151 int answer; // without a resolver, we return a single ip4 address, 0 == no answer
223 #endif 152 #endif
224 } __attribute__ ((packed)); 153 } __attribute__ ((packed));
225 154
226 struct mlfiPriv; 155
227 156 ////////////////////////////////////////////////
228 157 // helper to discard the strings held by a context_map
229 //////////////////////////////////////////////// 158 //
230 // helper to discard the strings and objects held by an ns_map 159 void discard(context_map &cm);
231 // 160 void discard(context_map &cm) {
232 static void discard(ns_map &s); 161 for (context_map::iterator i=cm.begin(); i!=cm.end(); i++) {
233 static void discard(ns_map &s) {
234 for (string_map::iterator i=s.ns_host.begin(); i!=s.ns_host.end(); i++) {
235 char *x = (*i).first; 162 char *x = (*i).first;
236 char *y = (*i).second;
237 free(x); 163 free(x);
238 free(y); 164 }
239 } 165 }
240 s.ns_ip.clear(); 166
241 s.ns_host.clear(); 167
242 } 168 ////////////////////////////////////////////////
243 169 // helper to register a string in a context_map
244 //////////////////////////////////////////////// 170 //
245 // helper to register a string in an ns_map 171 void register_string(context_map &cm, char *name, CONTEXT *con);
246 // 172 void register_string(context_map &cm, char *name, CONTEXT *con) {
247 static void register_string(ns_map &s, char *name, char *refer); 173 context_map::iterator i = cm.find(name);
248 static void register_string(ns_map &s, char *name, char *refer) { 174 if (i != cm.end()) return;
249 string_map::iterator i = s.ns_host.find(name);
250 if (i != s.ns_host.end()) return;
251 char *x = strdup(name); 175 char *x = strdup(name);
252 char *y = strdup(refer); 176 cm[x] = con;
253 s.ns_ip[x] = 0; 177 }
254 s.ns_host[x] = y;
255
256 }
257
258 ////////////////////////////////////////////////
259 // helper to discard the strings held by a string_set
260 //
261 static void discard(string_set &s);
262 static void discard(string_set &s) {
263 for (string_set::iterator i=s.begin(); i!=s.end(); i++) {
264 free(*i);
265 }
266 s.clear();
267 }
268
269 ////////////////////////////////////////////////
270 // helper to register a string in a string set
271 //
272 static char* register_string(string_set &s, char *name);
273 static char* register_string(string_set &s, char *name) {
274 string_set::iterator i = s.find(name);
275 if (i != s.end()) return *i;
276 char *x = strdup(name);
277 s.insert(x);
278 return x;
279 }
280
281 ////////////////////////////////////////////////
282 // syslog a message
283 //
284 static void my_syslog(mlfiPriv *priv, char *text);
285
286
287 // include the content scanner
288 #include "scanner.cpp"
289 178
290 179
291 //////////////////////////////////////////////// 180 ////////////////////////////////////////////////
292 // disconnect the fd from the dns resolver process 181 // disconnect the fd from the dns resolver process
293 // 182 //
294 void my_disconnect(int sock, bool decrement = true); 183 void my_disconnect(int sock, bool decrement = true);
295 void my_disconnect(int sock, bool decrement) 184 void my_disconnect(int sock, bool decrement) {
296 {
297 if (sock != NULL_SOCKET) { 185 if (sock != NULL_SOCKET) {
298 if (decrement) { 186 if (decrement) {
299 pthread_mutex_lock(&fd_pool_mutex); 187 pthread_mutex_lock(&fd_pool_mutex);
300 resolver_sock_count--; 188 resolver_sock_count--;
301 pthread_mutex_unlock(&fd_pool_mutex); 189 pthread_mutex_unlock(&fd_pool_mutex);
308 196
309 //////////////////////////////////////////////// 197 ////////////////////////////////////////////////
310 // return fd connected to the dns resolver process 198 // return fd connected to the dns resolver process
311 // 199 //
312 int my_connect(); 200 int my_connect();
313 int my_connect() 201 int my_connect() {
314 {
315 // if we have had recent errors, don't even try to open the socket 202 // if we have had recent errors, don't even try to open the socket
316 time_t now = time(NULL); 203 time_t now = time(NULL);
317 if ((now - last_error_time) < ERROR_SOCKET_TIME) return NULL_SOCKET; 204 if ((now - last_error_time) < ERROR_SOCKET_TIME) return NULL_SOCKET;
318 205
319 // nothing recent, maybe this time it will work 206 // nothing recent, maybe this time it will work
339 } 226 }
340 return sock; 227 return sock;
341 } 228 }
342 229
343 230
344 ////////////////////////////////////////////////
345 // mail filter private data, held for us by sendmail
346 //
347 struct mlfiPriv
348 {
349 // connection specific data
350 CONFIG *pc; // global context with our maps
351 int fd; // to talk to dns resolvers process
352 bool err; // did we get any errors on the resolver socket?
353 int ip; // ip4 address of the smtp client
354 map<DNSBLP, status> checked; // status from those lists
355 // message specific data
356 char *mailaddr; // envelope from value
357 char *queueid; // sendmail queue id
358 bool authenticated; // client authenticated? if so, suppress all dnsbl checks
359 bool have_whites; // have at least one whitelisted recipient? need to accept content and remove all non-whitelisted recipients if it fails
360 bool only_whites; // every recipient is whitelisted?
361 string_set non_whites; // remember the non-whitelisted recipients so we can remove them if need be
362 recorder *memory; // memory for the content scanner
363 url_scanner *scanner; // object to handle body scanning
364 mlfiPriv();
365 ~mlfiPriv();
366 void reset(bool final = false); // for a new message
367 void get_fd();
368 void return_fd();
369 int my_read(char *buf, int len);
370 int my_write(char *buf, int len);
371 };
372
373 mlfiPriv::mlfiPriv() { 231 mlfiPriv::mlfiPriv() {
374 pthread_mutex_lock(&config_mutex); 232 pthread_mutex_lock(&config_mutex);
375 pc = config; 233 pc = config;
376 pc->reference_count++; 234 pc->reference_count++;
377 pthread_mutex_unlock(&config_mutex); 235 pthread_mutex_unlock(&config_mutex);
380 mailaddr = NULL; 238 mailaddr = NULL;
381 queueid = NULL; 239 queueid = NULL;
382 authenticated = false; 240 authenticated = false;
383 have_whites = false; 241 have_whites = false;
384 only_whites = true; 242 only_whites = true;
385 memory = new recorder(this, &pc->html_tags, &pc->tlds); 243 memory = new recorder(this, pc->get_html_tags(), pc->get_content_tlds());
386 scanner = new url_scanner(memory); 244 scanner = new url_scanner(memory);
387 } 245 }
388 246
389 mlfiPriv::~mlfiPriv() { 247 mlfiPriv::~mlfiPriv() {
390 return_fd(); 248 return_fd();
395 } 253 }
396 254
397 void mlfiPriv::reset(bool final) { 255 void mlfiPriv::reset(bool final) {
398 if (mailaddr) free(mailaddr); 256 if (mailaddr) free(mailaddr);
399 if (queueid) free(queueid); 257 if (queueid) free(queueid);
400 discard(non_whites); 258 discard(env_to);
401 delete memory; 259 delete memory;
402 delete scanner; 260 delete scanner;
403 if (!final) { 261 if (!final) {
404 mailaddr = NULL; 262 mailaddr = NULL;
405 queueid = NULL; 263 queueid = NULL;
406 authenticated = false; 264 authenticated = false;
407 have_whites = false; 265 have_whites = false;
408 only_whites = true; 266 only_whites = true;
409 memory = new recorder(this, &pc->html_tags, &pc->tlds); 267 memory = new recorder(this, pc->get_html_tags(), pc->get_content_tlds());
410 scanner = new url_scanner(memory); 268 scanner = new url_scanner(memory);
411 } 269 }
412 } 270 }
413 271
414 void mlfiPriv::get_fd() 272 void mlfiPriv::get_fd() {
415 {
416 err = true; 273 err = true;
417 fd = NULL_SOCKET; 274 fd = NULL_SOCKET;
418 int result = pthread_mutex_lock(&fd_pool_mutex); 275 int result = pthread_mutex_lock(&fd_pool_mutex);
419 if (!result) { 276 if (!result) {
420 std::set<int>::iterator i; 277 std::set<int>::iterator i;
439 fd = my_connect(); 296 fd = my_connect();
440 err = (fd == NULL_SOCKET); 297 err = (fd == NULL_SOCKET);
441 } 298 }
442 } 299 }
443 300
444 void mlfiPriv::return_fd() 301 void mlfiPriv::return_fd() {
445 {
446 if (err) { 302 if (err) {
447 // this fd got a socket error, so close it, rather than returning it to the pool 303 // this fd got a socket error, so close it, rather than returning it to the pool
448 my_disconnect(fd); 304 my_disconnect(fd);
449 } 305 }
450 else { 306 else {
468 my_disconnect(fd); 324 my_disconnect(fd);
469 } 325 }
470 } 326 }
471 } 327 }
472 328
473 int mlfiPriv::my_write(char *buf, int len) 329 int mlfiPriv::my_write(char *buf, int len) {
474 {
475 if (err) return 0; 330 if (err) return 0;
476 int rs = 0; 331 int rs = 0;
477 while (len) { 332 while (len) {
478 int ws = write(fd, buf, len); 333 int ws = write(fd, buf, len);
479 if (ws > 0) { 334 if (ws > 0) {
489 } 344 }
490 } 345 }
491 return rs; 346 return rs;
492 } 347 }
493 348
494 int mlfiPriv::my_read(char *buf, int len) 349 int mlfiPriv::my_read(char *buf, int len) {
495 {
496 if (err) return 0; 350 if (err) return 0;
497 int rs = 0; 351 int rs = 0;
498 while (len > 1) { 352 while (len > 1) {
499 int ws = read(fd, buf, len); 353 int ws = read(fd, buf, len);
500 if (ws > 0) { 354 if (ws > 0) {
510 } 364 }
511 } 365 }
512 return rs; 366 return rs;
513 } 367 }
514 368
369 void mlfiPriv::need_content_filter(char *rcpt, CONTEXT &con) {
370 register_string(env_to, rcpt, &con);
371 }
372
515 #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) 373 #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
516 374
517 375
518 //////////////////////////////////////////////// 376 ////////////////////////////////////////////////
519 // syslog a message 377 // syslog a message
520 // 378 //
521 static void my_syslog(mlfiPriv *priv, char *text) { 379 void my_syslog(mlfiPriv *priv, char *text) {
522 char buf[1000]; 380 char buf[1000];
523 if (priv) { 381 if (priv) {
524 snprintf(buf, sizeof(buf), "%s: %s", priv->queueid, text); 382 snprintf(buf, sizeof(buf), "%s: %s", priv->queueid, text);
525 text = buf; 383 text = buf;
526 } 384 }
527 pthread_mutex_lock(&syslog_mutex); 385 pthread_mutex_lock(&syslog_mutex);
528 openlog("dnsbl", LOG_PID, LOG_MAIL); 386 if (!syslog_opened) {
387 openlog("dnsbl", LOG_PID, LOG_MAIL);
388 syslog_opened = true;
389 }
529 syslog(LOG_NOTICE, "%s", text); 390 syslog(LOG_NOTICE, "%s", text);
530 closelog();
531 pthread_mutex_unlock(&syslog_mutex); 391 pthread_mutex_unlock(&syslog_mutex);
532 } 392 }
533 393
534 static void my_syslog(char *text); 394 void my_syslog(char *text) {
535 static void my_syslog(char *text) {
536 my_syslog(NULL, text); 395 my_syslog(NULL, text);
537 } 396 }
538
539 ////////////////////////////////////////////////
540 // register a global string
541 //
542 static char* register_string(char *name);
543 static char* register_string(char *name) {
544 return register_string(all_strings, name);
545 }
546
547
548 static char* next_token(char *delim);
549 static char* next_token(char *delim) {
550 char *name = strtok(NULL, delim);
551 if (!name) return name;
552 return register_string(name);
553 }
554
555
556 ////////////////////////////////////////////////
557 // lookup an email address in the env_from or env_to maps
558 //
559 static char* lookup1(char *email, string_map map);
560 static char* lookup1(char *email, string_map map) {
561 string_map::iterator i = map.find(email);
562 if (i != map.end()) return (*i).second;
563 char *x = strchr(email, '@');
564 if (!x) return DEFAULT;
565 x++;
566 i = map.find(x);
567 if (i != map.end()) return (*i).second;
568 return DEFAULT;
569 }
570
571
572 ////////////////////////////////////////////////
573 // lookup an email address in the env_from or env_to maps
574 // this email address is passed in from sendmail, and will
575 // always be enclosed in <>. It may have mixed case, just
576 // as the mail client sent it.
577 //
578 static char* lookup(char* email, string_map map);
579 static char* lookup(char* email, string_map map) {
580 int n = strlen(email)-2;
581 if (n < 1) return DEFAULT; // malformed
582 char *key = strdup(email+1);
583 key[n] = '\0';
584 for (int i=0; i<n; i++) key[i] = tolower(key[i]);
585 char *rc = lookup1(key, map);
586 free(key);
587 return rc;
588 }
589
590
591 ////////////////////////////////////////////////
592 // find the dnsbl with a specific name
593 //
594 static DNSBLP find_dnsbl(CONFIG &dc, char *name);
595 static DNSBLP find_dnsbl(CONFIG &dc, char *name) {
596 dnsblp_map::iterator i = dc.dnsbls.find(name);
597 if (i == dc.dnsbls.end()) return NULL;
598 return (*i).second;
599 }
600
601
602 ////////////////////////////////////////////////
603 // find the dnsbll with a specific name
604 //
605 static DNSBLLP find_dnsbll(CONFIG &dc, char *name);
606 static DNSBLLP find_dnsbll(CONFIG &dc, char *name) {
607 dnsbllp_map::iterator i = dc.dnsblls.find(name);
608 if (i == dc.dnsblls.end()) return NULL;
609 return (*i).second;
610 }
611
612
613 ////////////////////////////////////////////////
614 // find the envfrom map with a specific name
615 //
616 static string_map* find_from_map(CONFIG &dc, char *name);
617 static string_map* find_from_map(CONFIG &dc, char *name) {
618 from_map::iterator i = dc.env_from.find(name);
619 if (i == dc.env_from.end()) return NULL;
620 return (*i).second;
621 }
622
623
624 static string_map& really_find_from_map(CONFIG &dc, char *name);
625 static string_map& really_find_from_map(CONFIG &dc, char *name) {
626 string_map *sm = find_from_map(dc, name);
627 if (!sm) {
628 sm = new string_map;
629 dc.env_from[name] = sm;
630 }
631 return *sm;
632 }
633
634 397
635 398
636 //////////////////////////////////////////////// 399 ////////////////////////////////////////////////
637 // read a resolver request from the socket, process it, and 400 // read a resolver request from the socket, process it, and
638 // write the result back to the socket. 401 // write the result back to the socket.
639 402
640 static void process_resolver_requests(int socket); 403 void process_resolver_requests(int socket);
641 static void process_resolver_requests(int socket) { 404 void process_resolver_requests(int socket) {
642 #ifdef NS_MAXDNAME 405 #ifdef NS_MAXDNAME
643 char question[NS_MAXDNAME]; 406 char question[NS_MAXDNAME];
644 #else 407 #else
645 char question[1000]; 408 char question[1000];
646 #endif 409 #endif
711 // ask a dns question and get an A record answer - we don't try 474 // ask a dns question and get an A record answer - we don't try
712 // very hard, just using the default resolver retry settings. 475 // very hard, just using the default resolver retry settings.
713 // If we cannot get an answer, we just accept the mail. 476 // If we cannot get an answer, we just accept the mail.
714 // 477 //
715 // 478 //
716 static int dns_interface(mlfiPriv &priv, char *question, bool maybe_ip, ns_map *nameservers); 479 int dns_interface(mlfiPriv &priv, char *question, bool maybe_ip, ns_map *nameservers);
717 static int dns_interface(mlfiPriv &priv, char *question, bool maybe_ip, ns_map *nameservers) { 480 int dns_interface(mlfiPriv &priv, char *question, bool maybe_ip, ns_map *nameservers) {
718 // this part can be done without locking the resolver mutex. Each 481 // this part can be done without locking the resolver mutex. Each
719 // milter thread is talking over its own socket to a separate resolver 482 // milter thread is talking over its own socket to a separate resolver
720 // process, which does the actual dns resolution. 483 // process, which does the actual dns resolution.
721 if (priv.err) return 0; // cannot ask more questions on this socket. 484 if (priv.err) return 0; // cannot ask more questions on this socket.
722 priv.my_write(question, strlen(question)+1); // write the question including the null terminator 485 priv.my_write(question, strlen(question)+1); // write the question including the null terminator
770 *(n++) = '.'; 533 *(n++) = '.';
771 } 534 }
772 } 535 }
773 if (n-nam) n--; // remove trailing . 536 if (n-nam) n--; // remove trailing .
774 *n = '\0'; // null terminate it 537 *n = '\0'; // null terminate it
775 register_string(ns, nam, question); // ns host to lookup later 538 ns.add(nam, question); // ns host to lookup later
776 } 539 }
777 } 540 }
778 rrnum = 0; 541 rrnum = 0;
779 while (ns_parserr(&handle, ns_s_ar, rrnum++, &rr) == 0) { 542 while (ns_parserr(&handle, ns_s_ar, rrnum++, &rr) == 0) {
780 if (ns_rr_type(rr) == ns_t_a) { 543 if (ns_rr_type(rr) == ns_t_a) {
815 578
816 579
817 //////////////////////////////////////////////// 580 ////////////////////////////////////////////////
818 // check a single dnsbl 581 // check a single dnsbl
819 // 582 //
820 static status check_single(mlfiPriv &priv, int ip, char *suffix); 583 bool check_single(mlfiPriv &priv, int ip, char *suffix);
821 static status check_single(mlfiPriv &priv, int ip, char *suffix) { 584 bool check_single(mlfiPriv &priv, int ip, char *suffix) {
822 // make a dns question 585 // make a dns question
823 const u_char *src = (const u_char *)&ip; 586 const u_char *src = (const u_char *)&ip;
824 if (src[0] == 127) return oksofar; // don't do dns lookups on localhost 587 if (src[0] == 127) return oksofar; // don't do dns lookups on localhost
825 #ifdef NS_MAXDNAME 588 #ifdef NS_MAXDNAME
826 char question[NS_MAXDNAME]; 589 char question[NS_MAXDNAME];
827 #else 590 #else
828 char question[1000]; 591 char question[1000];
829 #endif 592 #endif
830 snprintf(question, sizeof(question), "%u.%u.%u.%u.%s.", src[3], src[2], src[1], src[0], suffix); 593 snprintf(question, sizeof(question), "%u.%u.%u.%u.%s.", src[3], src[2], src[1], src[0], suffix);
831 // ask the question, if we get an A record it implies a blacklisted ip address 594 // ask the question, if we get an A record it implies a blacklisted ip address
832 return (dns_interface(priv, question, false, NULL)) ? reject : oksofar; 595 return dns_interface(priv, question, false, NULL);
833 } 596 }
834 597
835 598
836 //////////////////////////////////////////////// 599 ////////////////////////////////////////////////
837 // check a single dnsbl 600 // check a single dnsbl
838 // 601 //
839 static status check_single(mlfiPriv &priv, int ip, DNSBL &bl); 602 bool check_single(mlfiPriv &priv, int ip, DNSBL &bl);
840 static status check_single(mlfiPriv &priv, int ip, DNSBL &bl) { 603 bool check_single(mlfiPriv &priv, int ip, DNSBL &bl) {
841 return check_single(priv, ip, bl.suffix); 604 return check_single(priv, ip, bl.suffix);
842 } 605 }
843 606
844 607
845 //////////////////////////////////////////////// 608 ////////////////////////////////////////////////
846 // check the dnsbls specified for this recipient 609 // check the dnsbls specified for this recipient
847 // 610 //
848 static status check_dnsbl(mlfiPriv &priv, DNSBLLP dnsbllp, DNSBLP &rejectlist); 611 bool check_dnsbl(mlfiPriv &priv, dnsblp_list &dnsbll, DNSBLP &rejectlist);
849 static status check_dnsbl(mlfiPriv &priv, DNSBLLP dnsbllp, DNSBLP &rejectlist) { 612 bool check_dnsbl(mlfiPriv &priv, dnsblp_list &dnsbll, DNSBLP &rejectlist) {
850 if (priv.authenticated) return oksofar; 613 if (priv.authenticated) return oksofar;
851 if (!dnsbllp) return oksofar; 614 for (dnsblp_list::iterator i=dnsbll.begin(); i!=dnsbll.end(); i++) {
852 DNSBLL &dnsbll = *dnsbllp;
853 for (DNSBLL::iterator i=dnsbll.begin(); i!=dnsbll.end(); i++) {
854 DNSBLP dp = *i; // non null by construction 615 DNSBLP dp = *i; // non null by construction
855 status st; 616 bool st;
856 map<DNSBLP, status>::iterator f = priv.checked.find(dp); 617 map<DNSBLP, bool>::iterator f = priv.checked.find(dp);
857 if (f == priv.checked.end()) { 618 if (f == priv.checked.end()) {
858 // have not checked this list yet 619 // have not checked this list yet
859 st = check_single(priv, priv.ip, *dp); 620 st = check_single(priv, priv.ip, *dp);
860 rejectlist = dp; 621 rejectlist = dp;
861 priv.checked[dp] = st; 622 priv.checked[dp] = st;
862 } 623 }
863 else { 624 else {
864 st = (*f).second; 625 st = (*f).second;
865 rejectlist = (*f).first; 626 rejectlist = (*f).first;
866 } 627 }
867 if (st == reject) return st; 628 if (st) return st;
868 } 629 }
869 return oksofar; 630 return false;
870 } 631 }
871 632
872 633
873 //////////////////////////////////////////////// 634 ////////////////////////////////////////////////
874 // check the hosts from the body against the content dnsbl 635 // check the hosts from the body against the content dnsbl
875 // 636 //
876 static status check_hosts(mlfiPriv &priv, char *&host, int &ip); 637 bool check_hosts(mlfiPriv &priv, bool random, int limit, char *&host, int ip);
877 static status check_hosts(mlfiPriv &priv, char *&host, int &ip) { 638 bool check_hosts(mlfiPriv &priv) {
639 static buf[2000];
878 CONFIG &dc = *priv.pc; 640 CONFIG &dc = *priv.pc;
641 string_set &hosts = priv.memory->hosts;
642 string_set &ignore = dc.get_content_host_ignore();
643
879 int count = 0; 644 int count = 0;
880 ns_map nameservers; 645 int cnt = hosts.size(); // number of hosts we could look at
881 bool ran = priv.pc->host_random; 646 int_set ips;
882 int lim = priv.pc->host_limit; // we should not look at more than this many hosts 647 ns_map nameservers;
883 int cnt = priv.memory->hosts.size(); // number of hosts we could look at 648 for (string_set::iterator i=hosts.begin(); i!=hosts.end(); i++) {
884 int_set ips; // remove duplicate ip addresses 649 host = *i; // a reference into hosts, which will live until this smtp transaction is closed
885 for (string_set::iterator i=priv.memory->hosts.begin(); i!=priv.memory->hosts.end(); i++) {
886 host = *i; // a reference into priv.memory->hosts, which will live until this smtp transaction is closed
887 650
888 // don't bother looking up hosts on the ignore list 651 // don't bother looking up hosts on the ignore list
889 string_set::iterator j = priv.pc->content_host_ignore.find(host); 652 string_set::iterator j = ignore.find(host);
890 if (j != priv.pc->content_host_ignore.end()) continue; 653 if (j != ignore.end()) continue;
891 654
892 // try to only look at lim/cnt fraction of the available cnt host names in random mode 655 // try to only look at limit/cnt fraction of the available cnt host names in random mode
893 if ((cnt > lim) && (lim > 0) && ran) { 656 if ((cnt > limit) && (limit > 0) && random) {
894 int r = rand() % cnt; 657 int r = rand() % cnt;
895 if (r >= lim) { 658 if (r >= limit) {
896 char buf[1000]; 659 char buf[1000];
897 snprintf(buf, sizeof(buf), "host %s skipped", host); 660 snprintf(buf, sizeof(buf), "host %s skipped", host);
898 my_syslog(&priv, buf); 661 my_syslog(&priv, buf);
899 continue; 662 continue;
900 } 663 }
901 } 664 }
902 count++; 665 count++;
903 if ((count > lim) && (lim > 0) && (!ran)) {
904 discard(nameservers);
905 return reject_host;
906 }
907 ip = dns_interface(priv, host, true, &nameservers); 666 ip = dns_interface(priv, host, true, &nameservers);
908 if (debug_syslog) { 667 if (debug_syslog) {
909 char buf[1000]; 668 char buf[1000];
910 if (ip) { 669 if (ip) {
911 char adr[sizeof "255.255.255.255"]; 670 char adr[sizeof "255.255.255.255"];
920 } 679 }
921 if (ip) { 680 if (ip) {
922 int_set::iterator i = ips.find(ip); 681 int_set::iterator i = ips.find(ip);
923 if (i == ips.end()) { 682 if (i == ips.end()) {
924 ips.insert(ip); 683 ips.insert(ip);
925 status st = check_single(priv, ip, dc.content_suffix); 684 if (check_single(priv, ip, dc.content_suffix)) {
926 if (st == reject) { 685 return true;
927 discard(nameservers);
928 return st;
929 } 686 }
930 } 687 }
931 } 688 }
932 } 689 }
933 lim *= 4; // allow average of 3 ns per host name 690 limit *= 4; // allow average of 3 ns per host name
934 for (ns_mapper::iterator i=nameservers.ns_ip.begin(); i!=nameservers.ns_ip.end(); i++) { 691 for (ns_mapper::iterator i=nameservers.ns_ip.begin(); i!=nameservers.ns_ip.end(); i++) {
935 count++; 692 count++;
936 if ((count > lim) && (lim > 0)) { 693 if ((count > limit) && (limit > 0)) {
937 if (ran) continue; // don't complain 694 if (random) continue; // don't complain
938 discard(nameservers); 695 return true;
939 return reject_host;
940 } 696 }
941 host = (*i).first; // a transient reference that needs to be replaced before we return it 697 host = (*i).first; // a transient reference that needs to be replaced before we return it
942 ip = (*i).second; 698 ip = (*i).second;
943 if (!ip) ip = dns_interface(priv, host, false, NULL); 699 if (!ip) ip = dns_interface(priv, host, false, NULL);
944 if (debug_syslog) { 700 if (debug_syslog) {
956 } 712 }
957 if (ip) { 713 if (ip) {
958 int_set::iterator i = ips.find(ip); 714 int_set::iterator i = ips.find(ip);
959 if (i == ips.end()) { 715 if (i == ips.end()) {
960 ips.insert(ip); 716 ips.insert(ip);
961 status st = check_single(priv, ip, dc.content_suffix); 717 if (check_single(priv, ip, dc.content_suffix)) {
962 if (st == reject) {
963 string_map::iterator j = nameservers.ns_host.find(host); 718 string_map::iterator j = nameservers.ns_host.find(host);
964 if (j != nameservers.ns_host.end()) { 719 if (j != nameservers.ns_host.end()) {
965 char *refer = (*j).second; 720 char *refer = (*j).second;
966 char buf[1000]; 721 char buf[1000];
967 snprintf(buf, sizeof(buf), "%s with nameserver %s", refer, host); 722 snprintf(buf, sizeof(buf), "%s with nameserver %s", refer, host);
968 host = register_string(priv.memory->hosts, buf); // put a copy into priv.memory->hosts, and return that reference 723 host = register_string(hosts, buf); // put a copy into hosts, and return that reference
969 } 724 }
970 else { 725 else {
971 host = register_string(priv.memory->hosts, host); // put a copy into priv.memory->hosts, and return that reference 726 host = register_string(hosts, host); // put a copy into hosts, and return that reference
972 } 727 }
973 discard(nameservers); 728 return true;
974 return st;
975 } 729 }
976 } 730 }
977 } 731 }
978 } 732 }
979 discard(nameservers); 733 return false;
980 host = NULL;
981 int bin = priv.memory->binary_tags;
982 int bad = priv.memory->bad_html_tags;
983 lim = priv.pc->tag_limit;
984 if (3*bin > bad) return oksofar; // probably .zip or .tar.gz with random content
985 if ((bad > lim) && (lim > 0)) return reject_tag;
986 return oksofar;
987 } 734 }
988 735
989 736
990 //////////////////////////////////////////////// 737 ////////////////////////////////////////////////
991 // start of sendmail milter interfaces 738 // start of sendmail milter interfaces
1014 } 761 }
1015 762
1016 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **rcpt) 763 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **rcpt)
1017 { 764 {
1018 DNSBLP rejectlist = NULL; // list that caused the reject 765 DNSBLP rejectlist = NULL; // list that caused the reject
1019 status st = oksofar;
1020 mlfiPriv &priv = *MLFIPRIV; 766 mlfiPriv &priv = *MLFIPRIV;
1021 CONFIG &dc = *priv.pc; 767 CONFIG &dc = *priv.pc;
1022 if (!priv.queueid) priv.queueid = strdup(smfi_getsymval(ctx, "i")); 768 if (!priv.queueid) priv.queueid = strdup(smfi_getsymval(ctx, "i"));
1023 char *rcptaddr = rcpt[0]; 769 char *rcptaddr = rcpt[0];
1024 char *dnsname = lookup(rcptaddr, dc.env_to_dnsbll); 770 CONTEXT &con = *(dc.find_context(rcptaddr, priv.mailaddr));
1025 char *fromname = lookup(rcptaddr, dc.env_to_chkfrom); 771 char *fromvalue = con.find_from(priv.mailaddr);
1026 if ((strcmp(dnsname, BLACK) == 0) || 772 status st;
1027 (strcmp(fromname, BLACK) == 0)) { 773 if (fromvalue == token_black) {
1028 st = black; // two options to blacklist this recipient 774 st = black;
1029 } 775 }
1030 else if (strcmp(fromname, WHITE) == 0) { 776 else if (fromvalue == token_white) {
1031 st = white; 777 st = white;
1032 } 778 }
1033 else { 779 else {
1034 // check an env_from map 780 // check the dns based lists
1035 string_map *sm = find_from_map(dc, fromname); 781 st = check_dnsbl(priv, con.get_dnsbl_list(), rejectlist);
1036 if (sm != NULL) { 782 }
1037 fromname = lookup(priv.mailaddr, *sm); // returns default if name not in map
1038 if (strcmp(fromname, BLACK) == 0) {
1039 st = black; // blacklist this envelope from value
1040 }
1041 if (strcmp(fromname, WHITE) == 0) {
1042 st = white; // blacklist this envelope from value
1043 }
1044 }
1045 }
1046 if ((st == oksofar) && (strcmp(dnsname, WHITE) != 0)) {
1047 // check dns lists
1048 st = check_dnsbl(priv, find_dnsbll(dc, dnsname), rejectlist);
1049 }
1050
1051 if (st == reject) { 783 if (st == reject) {
1052 // reject the recipient based on some dnsbl 784 // reject the recipient based on some dnsbl
1053 char adr[sizeof "255.255.255.255"]; 785 char adr[sizeof "255.255.255.255"];
1054 adr[0] = '\0'; 786 adr[0] = '\0';
1055 inet_ntop(AF_INET, (const u_char *)&priv.ip, adr, sizeof(adr)); 787 inet_ntop(AF_INET, (const u_char *)&priv.ip, adr, sizeof(adr));
1063 smfi_setreply(ctx, "550", "5.7.1", "no such user"); 795 smfi_setreply(ctx, "550", "5.7.1", "no such user");
1064 return SMFIS_REJECT; 796 return SMFIS_REJECT;
1065 } 797 }
1066 else { 798 else {
1067 // accept the recipient 799 // accept the recipient
800 if (!con.get_content_filtering()) st = white;
1068 if (st == oksofar) { 801 if (st == oksofar) {
1069 // but remember the non-whites 802 // but remember the non-whites
1070 register_string(priv.non_whites, rcptaddr); 803 priv.need_content_filter(rcptaddr, con);
1071 priv.only_whites = false; 804 priv.only_whites = false;
1072 } 805 }
1073 if (st == white) { 806 if (st == white) {
1074 priv.have_whites = true; 807 priv.have_whites = true;
1075 } 808 }
1080 sfsistat mlfi_body(SMFICTX *ctx, u_char *data, size_t len) 813 sfsistat mlfi_body(SMFICTX *ctx, u_char *data, size_t len)
1081 { 814 {
1082 mlfiPriv &priv = *MLFIPRIV; 815 mlfiPriv &priv = *MLFIPRIV;
1083 if (priv.authenticated) return SMFIS_CONTINUE; 816 if (priv.authenticated) return SMFIS_CONTINUE;
1084 if (priv.only_whites) return SMFIS_CONTINUE; 817 if (priv.only_whites) return SMFIS_CONTINUE;
1085 if (!priv.pc->content_suffix) return SMFIS_CONTINUE;
1086 priv.scanner->scan(data, len); 818 priv.scanner->scan(data, len);
1087 return SMFIS_CONTINUE; 819 return SMFIS_CONTINUE;
1088 } 820 }
1089 821
1090 sfsistat mlfi_eom(SMFICTX *ctx) 822 sfsistat mlfi_eom(SMFICTX *ctx)
1091 { 823 {
1092 sfsistat rc; 824 sfsistat rc;
1093 mlfiPriv &priv = *MLFIPRIV; 825 mlfiPriv &priv = *MLFIPRIV;
826 CONFIG &dc = *priv.pc;
1094 char *host = NULL; 827 char *host = NULL;
1095 int ip; 828 int ip;
1096 status st; 829 status st;
1097 // process end of message 830 // process end of message
1098 if (priv.authenticated || 831 if (priv.authenticated || priv.only_whites) rc = SMFIS_CONTINUE;
1099 priv.only_whites ||
1100 (!priv.pc->content_suffix) ||
1101 ((st=check_hosts(priv, host, ip)) == oksofar)) rc = SMFIS_CONTINUE;
1102 else { 832 else {
1103 if (!priv.have_whites) { 833 char *msg = NULL;
1104 // can reject the entire message 834 string_set alive;
1105 char buf[2000]; 835 bool random = false;
1106 if (st == reject_tag) { 836 bool limit = 0;
1107 // rejected due to excessive bad html tags 837 for (context_map::iterator i=env_to.begin(); i!=env_to.end(); i++) {
1108 snprintf(buf, sizeof(buf), priv.pc->tag_limit_message); 838 char *rcpt = (*i).first;
1109 } 839 CONTEXT &con = *((*i).second);
1110 else if (st == reject_host) { 840 if (!con.acceptable_content(priv.memory, msg)) {
1111 // rejected due to excessive unique host/urls 841 // bad html tags or excessive hosts
1112 snprintf(buf, sizeof(buf), priv.pc->host_limit_message); 842 smfi_delrcpt(ctx, rcpt);
1113 } 843 }
1114 else { 844 else {
845 alive.insert(rcpt);
846 random |= con.get_host_random();
847 limit = max(limit, con.get_host_limit());
848 }
849 }
850 bool rejecting = alive.empty();
851 if (!rejecting) {
852 rejecting = check_hosts(priv, random, limit, host, ip);
853 if (rejecting) {
854 static char buf[2000];
1115 char adr[sizeof "255.255.255.255"]; 855 char adr[sizeof "255.255.255.255"];
1116 adr[0] = '\0'; 856 adr[0] = '\0';
1117 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr)); 857 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr));
1118 snprintf(buf, sizeof(buf), priv.pc->content_message, host, adr); 858 snprintf(buf, sizeof(buf), dc.get_content_message(), host, adr);
1119 } 859 msg = buf;
1120 smfi_setreply(ctx, "550", "5.7.1", buf); 860 }
861 }
862 if (!rejecting) {
863 rc = SMFIS_CONTINUE;
864 }
865 else if (!priv.have_whites && alive.empty()) {
866 // can reject the entire message
867 smfi_setreply(ctx, "550", "5.7.1", msg);
1121 rc = SMFIS_REJECT; 868 rc = SMFIS_REJECT;
1122 } 869 }
1123 else { 870 else {
1124 // need to accept it but remove the recipients that don't want it 871 // need to accept it but remove the recipients that don't want it
1125 for (string_set::iterator i=priv.non_whites.begin(); i!=priv.non_whites.end(); i++) { 872 for (string_set::iterator i=alive.begin(); i!=alive.end(); i++) {
1126 char *rcpt = *i; 873 char *rcpt = *i;
1127 smfi_delrcpt(ctx, rcpt); 874 smfi_delrcpt(ctx, rcpt);
1128 } 875 }
1129 rc = SMFIS_CONTINUE; 876 rc = SMFIS_CONTINUE;
1130 } 877 }
1166 mlfi_abort, // message aborted 913 mlfi_abort, // message aborted
1167 mlfi_close, // connection cleanup 914 mlfi_close, // connection cleanup
1168 }; 915 };
1169 916
1170 917
1171 static void dumpit(char *name, string_map map);
1172 static void dumpit(char *name, string_map map) {
1173 fprintf(stdout, "\n");
1174 for (string_map::iterator i=map.begin(); i!=map.end(); i++) {
1175 fprintf(stdout, "%s %s->%s\n", name, (*i).first, (*i).second);
1176 }
1177 }
1178
1179
1180 static void dumpit(from_map map);
1181 static void dumpit(from_map map) {
1182 for (from_map::iterator i=map.begin(); i!=map.end(); i++) {
1183 char buf[2000];
1184 snprintf(buf, sizeof(buf), "envelope from map for %s", (*i).first);
1185 string_map *sm = (*i).second;
1186 dumpit(buf, *sm);
1187 }
1188 }
1189
1190
1191 static void dumpit(CONFIG &dc);
1192 static void dumpit(CONFIG &dc) {
1193 dumpit(dc.env_from);
1194 dumpit("envelope to (dnsbl list)", dc.env_to_dnsbll);
1195 dumpit("envelope to (from map)", dc.env_to_chkfrom);
1196 fprintf(stdout, "\ndnsbls\n");
1197 for (dnsblp_map::iterator i=dc.dnsbls.begin(); i!=dc.dnsbls.end(); i++) {
1198 fprintf(stdout, "%s %s %s\n", (*i).first, (*i).second->suffix, (*i).second->message);
1199 }
1200 fprintf(stdout, "\ndnsbl_lists\n");
1201 for (dnsbllp_map::iterator i=dc.dnsblls.begin(); i!=dc.dnsblls.end(); i++) {
1202 char *name = (*i).first;
1203 DNSBLL &dl = *((*i).second);
1204 fprintf(stdout, "%s", name);
1205 for (DNSBLL::iterator j=dl.begin(); j!=dl.end(); j++) {
1206 DNSBL &d = **j;
1207 fprintf(stdout, " %s", d.suffix);
1208 }
1209 fprintf(stdout, "\n");
1210 }
1211 if (dc.content_suffix) {
1212 fprintf(stdout, "\ncontent filtering enabled with %s %s\n", dc.content_suffix, dc.content_message);
1213 }
1214 for (string_set::iterator i=dc.content_host_ignore.begin(); i!=dc.content_host_ignore.end(); i++) {
1215 fprintf(stdout, "ignore %s\n", (*i));
1216 }
1217 if (dc.host_limit && !dc.host_random) {
1218 fprintf(stdout, "\ncontent filtering for host names hard limit %d %s\n", dc.host_limit, dc.host_limit_message);
1219 }
1220 if (dc.host_limit && dc.host_random) {
1221 fprintf(stdout, "\ncontent filtering for host names soft limit %d\n", dc.host_limit);
1222 }
1223 if (dc.tag_limit) {
1224 fprintf(stdout, "\ncontent filtering for excessive html tags enabled with limit %d %s\n", dc.tag_limit, dc.tag_limit_message);
1225 }
1226 fprintf(stdout, "\nfiles\n");
1227 for (string_list::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) {
1228 char *f = *i;
1229 fprintf(stdout, "config includes %s\n", f);
1230 }
1231 }
1232
1233
1234 ////////////////////////////////////////////////
1235 // check for redundant or recursive include files
1236 //
1237 static bool ok_to_include(CONFIG &dc, char *fn);
1238 static bool ok_to_include(CONFIG &dc, char *fn) {
1239 if (!fn) return false;
1240 bool ok = true;
1241 for (string_list::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) {
1242 char *f = *i;
1243 if (strcmp(f, fn) == 0) {
1244 my_syslog("redundant or recursive include file detected");
1245 ok = false;
1246 break;
1247 }
1248 }
1249 return ok;
1250 }
1251
1252
1253 ////////////////////////////////////////////////
1254 // load a single config file
1255 //
1256 static void load_conf_dcc(CONFIG &dc, char *name, char *fn);
1257 static void load_conf_dcc(CONFIG &dc, char *name, char *fn) {
1258 ifstream is(fn);
1259 if (is.fail()) {
1260 char buf[1000];
1261 snprintf(buf, sizeof(buf), "include file %s not found", fn);
1262 my_syslog(buf);
1263 return;
1264 }
1265 dc.config_files.push_back(fn);
1266 const int LINE_SIZE = 2000;
1267 char line[LINE_SIZE];
1268 char *list = BLACK;
1269 char *delim = " \t";
1270 int curline = 0;
1271 while (!is.eof()) {
1272 is.getline(line, LINE_SIZE);
1273 curline++;
1274 int n = strlen(line);
1275 if (!n) continue;
1276 for (int i=0; i<n; i++) line[i] = tolower(line[i]);
1277 if (line[0] == '#') continue;
1278 char *head = line;
1279 if (strspn(line, delim) == 0) {
1280 // have a leading ok/many tag to fetch
1281 char *cmd = strtok(line, delim);
1282 if (strcmp(cmd, MANY) == 0) list = BLACK;
1283 else if (strcmp(cmd, OK) == 0) list = WHITE;
1284 head = cmd + strlen(cmd) + 1;
1285 }
1286 char *cmd = strtok(head, delim);
1287 if (!cmd) continue;
1288 if (strcmp(cmd, "env_from") == 0) {
1289 char *from = next_token(delim);
1290 if (from) {
1291 string_map &fm = really_find_from_map(dc, name);
1292 fm[from] = list;
1293 }
1294 }
1295 else if (strcmp(cmd, "env_to") == 0) {
1296 char *to = next_token(delim);
1297 if (to) {
1298 dc.env_to_dnsbll[to] = list;
1299 dc.env_to_chkfrom[to] = list;
1300 }
1301 }
1302 else if (strcmp(cmd, "substitute") == 0) {
1303 char *tag = next_token(delim);
1304 if (tag && (strcmp(tag, "mail_host") == 0)) {
1305 char *from = next_token(delim);
1306 if (from) {
1307 string_map &fm = really_find_from_map(dc, name);
1308 fm[from] = list;
1309 }
1310 }
1311 }
1312 else if (strcmp(cmd, "include") == 0) {
1313 char *fn = next_token(delim);
1314 if (ok_to_include(dc, fn)) {
1315 load_conf_dcc(dc, name, fn);
1316 }
1317 }
1318
1319 }
1320 is.close();
1321 }
1322
1323
1324 static void load_conf(CONFIG &dc, char *fn);
1325 static void load_conf(CONFIG &dc, char *fn) {
1326 ifstream is(fn);
1327 if (is.fail()) {
1328 char buf[1000];
1329 snprintf(buf, sizeof(buf), "include file %s not found", fn);
1330 my_syslog(buf);
1331 return;
1332 }
1333 dc.config_files.push_back(fn);
1334 map<char*, int, ltstr> commands;
1335 enum {dummy, tld, content, ignore, hostlimit, hostslimit, htmllimit, htmltag, dnsbl, dnsbll, envfrom, envto, include, includedcc};
1336 commands["tld" ] = tld;
1337 commands["content" ] = content;
1338 commands["ignore" ] = ignore;
1339 commands["host_limit" ] = hostlimit;
1340 commands["host_soft_limit"] = hostslimit;
1341 commands["html_limit" ] = htmllimit;
1342 commands["html_tag" ] = htmltag;
1343 commands["dnsbl" ] = dnsbl;
1344 commands["dnsbl_list" ] = dnsbll;
1345 commands["env_from" ] = envfrom;
1346 commands["env_to" ] = envto;
1347 commands["include" ] = include;
1348 commands["include_dcc" ] = includedcc;
1349 const int LINE_SIZE = 2000;
1350 char line[LINE_SIZE];
1351 char orig[LINE_SIZE];
1352 char *delim = " \t";
1353 int curline = 0;
1354 while (!is.eof()) {
1355 is.getline(line, LINE_SIZE);
1356 snprintf(orig, sizeof(orig), "%s", line);
1357 curline++;
1358 int n = strlen(line);
1359 for (int i=0; i<n; i++) line[i] = tolower(line[i]);
1360 char *cmd = strtok(line, delim);
1361 if (cmd && (cmd[0] != '#') && (cmd[0] != '\0')) {
1362 // have a decent command
1363 bool processed = false;
1364 switch (commands[cmd]) {
1365 case tld: {
1366 char *tld = next_token(delim);
1367 if (!tld) break; // no tld value
1368 dc.tlds.insert(tld);
1369 processed = true;
1370 } break;
1371
1372 case content: {
1373 char *suff = strtok(NULL, delim);
1374 if (!suff) break; // no dns suffix
1375 char *msg = suff + strlen(suff);
1376 if ((msg - line) >= strlen(orig)) break; // line ended with the dns suffix
1377 msg = strchr(msg+1, '\'');
1378 if (!msg) break; // no reply message template
1379 msg++; // move over the leading '
1380 if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote
1381 char *last = strchr(msg, '\'');
1382 if (!last) break; // no trailing quote
1383 *last = '\0'; // make it a null terminator
1384 dc.content_suffix = register_string(suff);
1385 dc.content_message = register_string(msg);
1386 processed = true;
1387 } break;
1388
1389 case ignore: {
1390 char *host = next_token(delim);
1391 if (!host) break;
1392 dc.content_host_ignore.insert(host);
1393 processed = true;
1394 } break;
1395
1396 case hostlimit: {
1397 char *limit = strtok(NULL, delim);
1398 if (!limit) break; // no integer limit
1399 char *msg = limit + strlen(limit);
1400 if ((msg - line) >= strlen(orig)) break; // line ended with the limit
1401 msg = strchr(msg+1, '\'');
1402 if (!msg) break; // no reply message template
1403 msg++; // move over the leading '
1404 if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote
1405 char *last = strchr(msg, '\'');
1406 if (!last) break; // no trailing quote
1407 *last = '\0'; // make it a null terminator
1408 dc.host_limit = atoi(limit);
1409 dc.host_limit_message = register_string(msg);
1410 dc.host_random = false;
1411 processed = true;
1412 } break;
1413
1414 case hostslimit: {
1415 char *limit = next_token(delim);
1416 if (!limit) break; // no integer limit
1417 dc.host_limit = atoi(limit);
1418 dc.host_random = true;
1419 processed = true;
1420 } break;
1421
1422 case htmllimit: {
1423 char *limit = strtok(NULL, delim);
1424 if (!limit) break; // no integer limit
1425 char *msg = limit + strlen(limit);
1426 if ((msg - line) >= strlen(orig)) break; // line ended with the limit
1427 msg = strchr(msg+1, '\'');
1428 if (!msg) break; // no reply message template
1429 msg++; // move over the leading '
1430 if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote
1431 char *last = strchr(msg, '\'');
1432 if (!last) break; // no trailing quote
1433 *last = '\0'; // make it a null terminator
1434 dc.tag_limit = atoi(limit);
1435 dc.tag_limit_message = register_string(msg);
1436 processed = true;
1437 } break;
1438
1439 case htmltag: {
1440 char *tag = next_token(delim);
1441 if (!tag) break; // no html tag value
1442 dc.html_tags.insert(tag); // base version
1443 char buf[200];
1444 snprintf(buf, sizeof(buf), "/%s", tag);
1445 dc.html_tags.insert(register_string(buf)); // leading /
1446 snprintf(buf, sizeof(buf), "%s/", tag);
1447 dc.html_tags.insert(register_string(buf)); // trailing /
1448 processed = true;
1449 } break;
1450
1451 case dnsbl: {
1452 // have a new dnsbl to use
1453 char *name = next_token(delim);
1454 if (!name) break; // no name name
1455 if (find_dnsbl(dc, name)) break; // duplicate entry
1456 char *suff = strtok(NULL, delim);
1457 if (!suff) break; // no dns suffic
1458 char *msg = suff + strlen(suff);
1459 if ((msg - line) >= strlen(orig)) break; // line ended with the dns suffix
1460 msg = strchr(msg+1, '\'');
1461 if (!msg) break; // no reply message template
1462 msg++; // move over the leading '
1463 if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote
1464 char *last = strchr(msg, '\'');
1465 if (!last) break; // no trailing quote
1466 *last = '\0'; // make it a null terminator
1467 dc.dnsbls[name] = new DNSBL(register_string(suff), register_string(msg));
1468 processed = true;
1469 } break;
1470
1471 case dnsbll: {
1472 // define a new combination of dnsbls
1473 char *name = next_token(delim);
1474 if (!name) break;
1475 if (find_dnsbll(dc, name)) break; // duplicate entry
1476 char *list = next_token(delim);
1477 if (!list || (*list == '\0') || (*list == '#')) break;
1478 DNSBLLP d = new DNSBLL;
1479 DNSBLP p = find_dnsbl(dc, list);
1480 if (p) d->push_back(p);
1481 while (true) {
1482 list = next_token(delim);
1483 if (!list || (*list == '\0') || (*list == '#')) break;
1484 DNSBLP p = find_dnsbl(dc, list);
1485 if (p) d->push_back(p);
1486 }
1487 dc.dnsblls[name] = d;
1488 processed = true;
1489 } break;
1490
1491 case envfrom: {
1492 // add an entry into the named string_map
1493 char *name = next_token(delim);
1494 if (!name) break;
1495 char *from = next_token(delim);
1496 if (!from) break;
1497 char *list = next_token(delim);
1498 if (!list) break;
1499 if ((strcmp(list, WHITE) == 0) ||
1500 (strcmp(list, BLACK) == 0)) {
1501 string_map &fm = really_find_from_map(dc, name);
1502 fm[from] = list;
1503 processed = true;
1504 }
1505 else {
1506 // list may be the name of a previously defined from_map
1507 string_map *m = find_from_map(dc, list);
1508 if (m && (strcmp(list,name) != 0)) {
1509 string_map &pm = *m;
1510 string_map &fm = really_find_from_map(dc, name);
1511 fm.insert(pm.begin(), pm.end());
1512 processed = true;
1513 }
1514 }
1515 } break;
1516
1517 case envto: {
1518 // define the dnsbl_list and env_from maps to use for this recipient
1519 char *to = next_token(delim);
1520 if (!to) break;
1521 char *list = next_token(delim);
1522 if (!list) break;
1523 char *from = next_token(delim);
1524 if (!from) break;
1525 dc.env_to_dnsbll[to] = list;
1526 dc.env_to_chkfrom[to] = from;
1527 processed = true;
1528 } break;
1529
1530 case include: {
1531 char *fn = next_token(delim);
1532 if (ok_to_include(dc, fn)) {
1533 load_conf(dc, fn);
1534 processed = true;
1535 }
1536 } break;
1537
1538 case includedcc: {
1539 char *name = next_token(delim);
1540 if (!name) break;
1541 char *fn = next_token(delim);
1542 if (ok_to_include(dc, fn)) {
1543 load_conf_dcc(dc, name, fn);
1544 processed = true;
1545 }
1546 } break;
1547
1548 default: {
1549 } break;
1550 }
1551 if (!processed) {
1552 pthread_mutex_lock(&syslog_mutex);
1553 openlog("dnsbl", LOG_PID, LOG_MAIL);
1554 syslog(LOG_ERR, "ignoring file %s line %d : %s\n", fn, curline, orig);
1555 closelog();
1556 pthread_mutex_unlock(&syslog_mutex);
1557 }
1558 }
1559 }
1560 is.close();
1561 }
1562
1563
1564 //////////////////////////////////////////////// 918 ////////////////////////////////////////////////
1565 // reload the config 919 // reload the config
1566 // 920 //
1567 static CONFIG* new_conf(); 921 CONFIG* new_conf();
1568 static CONFIG* new_conf() { 922 CONFIG* new_conf() {
1569 CONFIG *newc = new CONFIG; 923 CONFIG *newc = new CONFIG;
1570 pthread_mutex_lock(&config_mutex); 924 pthread_mutex_lock(&config_mutex);
1571 newc->generation = generation++; 925 newc->generation = generation++;
1572 pthread_mutex_unlock(&config_mutex); 926 pthread_mutex_unlock(&config_mutex);
1573 char buf[200]; 927 char buf[200];
1574 snprintf(buf, sizeof(buf), "loading configuration generation %d", newc->generation); 928 snprintf(buf, sizeof(buf), "loading configuration generation %d", newc->generation);
1575 my_syslog(buf); 929 my_syslog(buf);
1576 load_conf(*newc, "dnsbl.conf"); 930 if (load_conf(*newc, "dnsbl.conf") {
1577 newc->load_time = time(NULL); 931 newc->load_time = time(NULL);
1578 return newc; 932 return newc;
933 }
934 delete newc;
935 return NULL;
1579 } 936 }
1580 937
1581 938
1582 //////////////////////////////////////////////// 939 ////////////////////////////////////////////////
1583 // thread to watch the old config files for changes 940 // thread to watch the old config files for changes
1584 // and reload when needed. we also cleanup old 941 // and reload when needed. we also cleanup old
1585 // configs whose reference count has gone to zero. 942 // configs whose reference count has gone to zero.
1586 // 943 //
1587 static void* config_loader(void *arg); 944 void* config_loader(void *arg);
1588 static void* config_loader(void *arg) { 945 void* config_loader(void *arg) {
1589 typedef set<CONFIG *> configp_set; 946 typedef set<CONFIG *> configp_set;
1590 configp_set old_configs; 947 configp_set old_configs;
1591 while (loader_run) { 948 while (loader_run) {
1592 sleep(180); // look for modifications every 3 minutes 949 sleep(180); // look for modifications every 3 minutes
1593 if (!loader_run) break; 950 if (!loader_run) break;
1625 } 982 }
1626 return NULL; 983 return NULL;
1627 } 984 }
1628 985
1629 986
1630 static void usage(char *prog); 987 void usage(char *prog);
1631 static void usage(char *prog) 988 void usage(char *prog)
1632 { 989 {
1633 fprintf(stderr, "Usage: %s [-d] [-c] -r port -p sm-sock-addr [-t timeout]\n", prog); 990 fprintf(stderr, "Usage: %s [-d] [-c] -r port -p sm-sock-addr [-t timeout]\n", prog);
1634 fprintf(stderr, "where port is for the connection to our own dns resolver processes\n"); 991 fprintf(stderr, "where port is for the connection to our own dns resolver processes\n");
1635 fprintf(stderr, " and should be local-domain-socket-file-name\n"); 992 fprintf(stderr, " and should be local-domain-socket-file-name\n");
1636 fprintf(stderr, "where sm-sock-addr is for the connection to sendmail\n"); 993 fprintf(stderr, "where sm-sock-addr is for the connection to sendmail\n");
1641 fprintf(stderr, "-d will add some syslog debug messages\n"); 998 fprintf(stderr, "-d will add some syslog debug messages\n");
1642 } 999 }
1643 1000
1644 1001
1645 1002
1646 static void setup_socket(char *sock); 1003 void setup_socket(char *sock);
1647 static void setup_socket(char *sock) { 1004 void setup_socket(char *sock) {
1648 unlink(sock); 1005 unlink(sock);
1649 // sockaddr_un addr; 1006 // sockaddr_un addr;
1650 // memset(&addr, '\0', sizeof addr); 1007 // memset(&addr, '\0', sizeof addr);
1651 // addr.sun_family = AF_UNIX; 1008 // addr.sun_family = AF_UNIX;
1652 // strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1); 1009 // strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1);
1670 } 1027 }
1671 1028
1672 1029
1673 int main(int argc, char**argv) 1030 int main(int argc, char**argv)
1674 { 1031 {
1032 token_init();
1675 bool check = false; 1033 bool check = false;
1676 bool setconn = false; 1034 bool setconn = false;
1677 bool setreso = false; 1035 bool setreso = false;
1678 int c; 1036 int c;
1679 const char *args = "r:p:t:hcd"; 1037 const char *args = "r:p:t:hcd";
1731 exit(EX_USAGE); 1089 exit(EX_USAGE);
1732 } 1090 }
1733 } 1091 }
1734 1092
1735 if (check) { 1093 if (check) {
1736 CONFIG &dc = *new_conf(); 1094 CONFIG *conf = new_conf();
1737 dumpit(dc); 1095 if (conf) {
1738 return 0; 1096 conf->dump();
1097 delete conf;
1098 return 0;
1099 }
1100 else {
1101 return 1; // config failed to load
1102 }
1739 } 1103 }
1740 1104
1741 if (!setconn) { 1105 if (!setconn) {
1742 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); 1106 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
1743 usage(argv[0]); 1107 usage(argv[0]);