dnsbl

view src/dnsbl.cpp @ 239:72c8bfc38d16

Added tag stable-6-0-25 for changeset 7b818a4e21a4
author Carl Byington <carl@five-ten-sg.com>
date Tue Sep 29 12:19:42 2009 -0700 (11 months ago)
parents c0d2e99c0a1d
children
line source
1 /*
3 Copyright (c) 2009 Carl Byington - 510 Software Group, released under
4 the GPL version 3 or any later version at your choice available at
5 http://www.gnu.org/licenses/gpl-3.0.txt
7 Based on a sample milter Copyright (c) 2000-2003 Sendmail, Inc. and its
8 suppliers. Inspired by the DCC by Rhyolite Software
10 -b port The port used to talk to the dcc interface daemon
11 -r port The port used to talk to our internal dns resolver processes
12 -p port The port through which the MTA will connect to this milter.
13 -t sec The timeout value.
14 -c Check the config, and print a copy to stdout. Don't start the
15 milter or do anything with the socket.
16 -s Stress test by loading and deleting the current config in a loop.
17 -d level set the debug level
18 -e f|t Print the results of looking up from address f and to address
19 t in the current config
21 */
24 // from sendmail sample
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <errno.h>
28 #include <sysexits.h>
29 #include <unistd.h>
31 // needed for socket io
32 #include <sys/ioctl.h>
33 #include <net/if.h>
34 #include <arpa/inet.h>
35 #include <netinet/in.h>
36 #include <netinet/tcp.h>
37 #include <netdb.h>
38 #include <sys/socket.h>
39 #include <sys/un.h>
41 // needed for thread
42 #include <pthread.h>
44 // needed for std c++ collections
45 #include <set>
46 #include <map>
47 #include <list>
49 // for the dns resolver
50 #include <netinet/in.h>
51 #include <arpa/nameser.h>
52 #include <resolv.h>
54 // misc stuff needed here
55 #include <ctype.h>
56 #include <syslog.h>
57 #include <pwd.h>
58 #include <sys/wait.h> /* header for waitpid() and various macros */
59 #include <signal.h> /* header for signal functions */
61 #include "includes.h"
63 #ifndef HAVE_DAEMON
64 #include "daemon.h"
65 #include "daemon.c"
66 #endif
68 #ifndef HAVE_MEMRCHR
69 void *memrchr(const void *a, int c, size_t len);
70 void *memrchr(const void *a, int c, size_t len) {
71 const unsigned char *p = (const unsigned char *)a;
72 for (p += len-1; (const void *)p >= a; p--)
73 if (*p == c)
74 return (void *)p;
75 return (void *)0;
76 }
77 #endif
79 extern "C" {
80 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr);
81 sfsistat mlfi_helo(SMFICTX * ctx, char *helohost);
82 sfsistat mlfi_envfrom(SMFICTX *ctx, char **argv);
83 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **argv);
84 sfsistat mlfi_header(SMFICTX* ctx, char* headerf, char* headerv);
85 sfsistat mlfi_body(SMFICTX *ctx, u_char *data, size_t len);
86 sfsistat mlfi_eom(SMFICTX *ctx);
87 sfsistat mlfi_abort(SMFICTX *ctx);
88 sfsistat mlfi_close(SMFICTX *ctx);
89 void sig_chld(int signo);
90 }
92 int debug_syslog = 0;
93 bool syslog_opened = false;
94 bool use_syslog = true; // false to printf
95 bool loader_run = true; // used to stop the config loader thread
96 CONFIG *config = NULL; // protected by the config_mutex
97 int generation = 0; // protected by the config_mutex
98 const int maxlen = 1000; // used for snprintf buffers
99 regex_t srs_pattern; // used to detect srs coding in mail addresses
101 pthread_mutex_t config_mutex;
102 pthread_mutex_t syslog_mutex;
103 pthread_mutex_t resolve_mutex;
104 pthread_mutex_t fd_pool_mutex;
105 pthread_mutex_t rate_mutex;
107 std::set<int> fd_pool;
108 int NULL_SOCKET = -1;
109 const time_t ERROR_SOCKET_TIME = 60; // number of seconds between attempts to open a socket to the dns resolver process
110 const char *resolver_port = NULL; // unix domain socket to talk to the dns resolver process
111 int resolver_socket = NULL_SOCKET; // socket used to listen for resolver requests
112 const char *dccifd_port = NULL; // unix domain socket to talk to the dcc interface daemon
113 time_t last_error_time;
114 int resolver_sock_count = 0; // protected with fd_pool_mutex
115 int resolver_pool_size = 0; // protected with fd_pool_mutex
116 rcpt_rates rcpt_counts; // protected with rate_mutex
119 struct ns_map {
120 // all the strings are owned by the keys/values in the ns_host string map
121 string_map ns_host; // nameserver name -> host name that uses this name server
122 ns_mapper ns_ip; // nameserver name -> ip address of the name server
123 ~ns_map();
124 void add(const char *name, const char *refer);
125 };
128 ns_map::~ns_map() {
129 for (string_map::iterator i=ns_host.begin(); i!=ns_host.end(); i++) {
130 const char *x = (*i).first;
131 const char *y = (*i).second;
132 free((void*)x);
133 free((void*)y);
134 }
135 ns_ip.clear();
136 ns_host.clear();
137 }
140 void ns_map::add(const char *name, const char *refer) {
141 string_map::iterator i = ns_host.find(name);
142 if (i != ns_host.end()) return;
143 char *x = strdup(name);
144 char *y = strdup(refer);
145 ns_ip[x] = 0;
146 ns_host[x] = y;
148 }
150 // packed structure to allow a single socket write to dump the
151 // length and the following answer. The packing attribute is gcc specific.
152 struct glommer {
153 size_t length;
154 #ifdef NS_PACKETSZ
155 u_char answer[NS_PACKETSZ*4]; // with a resolver, we return resolver answers
156 #else
157 int answer; // without a resolver, we return a single ip4 address, 0 == no answer
158 #endif
159 } __attribute__ ((packed));
162 ////////////////////////////////////////////////
163 // helper to manipulate recipient counts
164 //
165 int incr_rcpt_count(const char *user);
166 int incr_rcpt_count(const char *user) {
167 pthread_mutex_lock(&rate_mutex);
168 rcpt_rates::iterator i = rcpt_counts.find(user);
169 int c = 1;
170 if (i == rcpt_counts.end()) {
171 user = strdup(user);
172 rcpt_counts[user] = c;
173 }
174 else {
175 c = ++((*i).second);
176 }
177 pthread_mutex_unlock(&rate_mutex);
178 return c;
179 }
181 ////////////////////////////////////////////////
182 // helper to discard the strings held by a context_map
183 //
184 void discard(context_map &cm);
185 void discard(context_map &cm) {
186 for (context_map::iterator i=cm.begin(); i!=cm.end(); i++) {
187 const char *x = (*i).first;
188 free((void*)x);
189 }
190 cm.clear();
191 }
194 ////////////////////////////////////////////////
195 // helper to register a string in a context_map
196 //
197 void register_string(context_map &cm, const char *name, CONTEXT *con);
198 void register_string(context_map &cm, const char *name, CONTEXT *con) {
199 context_map::iterator i = cm.find(name);
200 if (i != cm.end()) return;
201 char *x = strdup(name);
202 cm[x] = con;
203 }
206 ////////////////////////////////////////////////
207 // disconnect the fd from the dns resolver process
208 //
209 void my_disconnect(int sock, bool decrement = true);
210 void my_disconnect(int sock, bool decrement) {
211 if (sock != NULL_SOCKET) {
212 if (decrement) {
213 pthread_mutex_lock(&fd_pool_mutex);
214 resolver_sock_count--;
215 pthread_mutex_unlock(&fd_pool_mutex);
216 }
217 shutdown(sock, SHUT_RDWR);
218 close(sock);
219 }
220 }
223 ////////////////////////////////////////////////
224 // return fd connected to the dns resolver process
225 //
226 int my_connect();
227 int my_connect() {
228 // if we have had recent errors, don't even try to open the socket
229 if ((time(NULL) - last_error_time) < ERROR_SOCKET_TIME) return NULL_SOCKET;
231 // nothing recent, maybe this time it will work
232 int sock = NULL_SOCKET;
233 sockaddr_un server;
234 memset(&server, '\0', sizeof(server));
235 server.sun_family = AF_UNIX;
236 strncpy(server.sun_path, resolver_port, sizeof(server.sun_path)-1);
237 sock = socket(AF_UNIX, SOCK_STREAM, 0);
238 if (sock != NULL_SOCKET) {
239 bool rc = (connect(sock, (sockaddr *)&server, sizeof(server)) == 0);
240 if (!rc) {
241 my_disconnect(sock, false);
242 sock = NULL_SOCKET;
243 last_error_time = time(NULL);
244 }
245 }
246 else last_error_time = time(NULL);
247 if (sock != NULL_SOCKET) {
248 pthread_mutex_lock(&fd_pool_mutex);
249 resolver_sock_count++;
250 pthread_mutex_unlock(&fd_pool_mutex);
251 }
252 return sock;
253 }
256 ////////////////////////////////////////////////
257 // ask a dns question and get an A record answer - we don't try
258 // very hard, just using the default resolver retry settings.
259 // If we cannot get an answer, we just accept the mail.
260 //
261 //
262 int dns_interface(mlfiPriv &priv, const char *question, bool maybe_ip, ns_map *nameservers);
263 int dns_interface(mlfiPriv &priv, const char *question, bool maybe_ip, ns_map *nameservers) {
264 // tell sendmail we are still working
265 #if _FFR_SMFI_PROGRESS
266 if (priv.eom) smfi_progress(priv.ctx);
267 #endif
269 // this part can be done without locking the resolver mutex. Each
270 // milter thread is talking over its own socket to a separate resolver
271 // process, which does the actual dns resolution.
272 if (priv.err) return 0; // cannot ask more questions on this socket.
273 if (maybe_ip) {
274 // might be a bare ip address, try this first to avoid dns lookups that may not be needed
275 in_addr ip;
276 if (inet_aton(question, &ip)) {
277 return (int)ip.s_addr;
278 }
279 }
280 int n = strlen(question);
281 if (question[n-1] == '.') {
282 priv.my_write(question, n+1); // write the question including the null terminator
283 }
284 else {
285 priv.my_write(question, n); // write the question
286 priv.my_write(".", 2); // and the fully qualified . terminator and null string terminator
287 }
288 glommer glom;
289 char *buf = (char *)&glom;
290 priv.my_read(buf, sizeof(glom.length));
291 buf += sizeof(glom.length);
292 #ifdef RESOLVER_DEBUG
293 char text[1000];
294 snprintf(text, sizeof(text), "dns_interface() wrote question %s and has answer length %d", question, glom.length);
295 my_syslog(text);
296 #endif
297 if (glom.length == 0) return 0;
298 if (glom.length > sizeof(glom.answer)) {
299 priv.err = true;
300 return 0; // cannot process overlarge answers
301 }
302 priv.my_read(buf, glom.length);
304 #ifdef NS_PACKETSZ
305 // now we need to lock the resolver mutex to keep the milter threads from
306 // stepping on each other while parsing the dns answer.
307 int ret_address = 0;
308 pthread_mutex_lock(&resolve_mutex);
309 // parse the answer
310 ns_msg handle;
311 ns_rr rr;
312 if (ns_initparse(glom.answer, glom.length, &handle) == 0) {
313 // look for ns names
314 if (nameservers) {
315 ns_map &ns = *nameservers;
316 int rrnum = 0;
317 while (ns_parserr(&handle, ns_s_ns, rrnum++, &rr) == 0) {
318 if (ns_rr_type(rr) == ns_t_ns) {
319 char nam[NS_MAXDNAME+1];
320 char *n = nam;
321 const u_char *p = ns_rr_rdata(rr);
322 while (((n-nam) < NS_MAXDNAME) && ((size_t)(p-glom.answer) < glom.length) && *p) {
323 size_t s = *(p++);
324 if (s > 191) {
325 // compression pointer
326 s = (s-192)*256 + *(p++);
327 if (s >= glom.length) break; // pointer outside bounds of answer
328 p = glom.answer + s;
329 s = *(p++);
330 }
331 if (s > 0) {
332 if ((size_t)(n-nam) >= (NS_MAXDNAME-s)) break; // destination would overflow name buffer
333 if ((size_t)(p-glom.answer) >= (glom.length-s)) break; // source outside bounds of answer
334 memcpy(n, p, s);
335 n += s;
336 p += s;
337 *(n++) = '.';
338 }
339 }
340 if (n-nam) n--; // remove trailing .
341 *n = '\0'; // null terminate it
342 ns.add(nam, question); // ns host to lookup later
343 }
344 }
345 rrnum = 0;
346 while (ns_parserr(&handle, ns_s_ar, rrnum++, &rr) == 0) {
347 if (ns_rr_type(rr) == ns_t_a) {
348 char* nam = (char*)ns_rr_name(rr);
349 ns_mapper::iterator i = ns.ns_ip.find(nam);
350 if (i != ns.ns_ip.end()) {
351 // we want this ip address
352 int address;
353 memcpy(&address, ns_rr_rdata(rr), sizeof(address));
354 ns.ns_ip[nam] = address;
355 }
356 }
357 }
358 }
359 int rrnum = 0;
360 while (ns_parserr(&handle, ns_s_an, rrnum++, &rr) == 0) {
361 if (ns_rr_type(rr) == ns_t_a) {
362 int address;
363 memcpy(&address, ns_rr_rdata(rr), sizeof(address));
364 ret_address = address;
365 }
366 }
367 }
368 pthread_mutex_unlock(&resolve_mutex);
369 #ifdef RESOLVER_DEBUG
370 snprintf(text, sizeof(text), "dns_interface() found ip %d", ret_address);
371 my_syslog(text);
372 #endif
373 return ret_address;
374 #else
375 return glom.answer;
376 #endif
377 }
380 ////////////////////////////////////////////////
381 // lookup the domain name part of a hostname on the uribl
382 //
383 // if we find part of the hostname on the uribl, return
384 // true and point found to the part of the hostname that we found
385 // as a string registered in hosts.
386 // otherwise, return false and preserve the value of found.
387 //
388 bool uriblookup(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *top, const char *&found) ;
389 bool uriblookup(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *top, const char *&found) {
390 // top is pointer to '.' char at end of base domain, or null for ip address form
391 // so for hostname of www.fred.mydomain.co.uk
392 // top points to-----------------------^
393 // and we end up looking at only mydomain.co.uk, ignoring the www.fred stuff
394 char buf[maxlen];
395 if (top) {
396 // add one more component
397 const char *x = (const char *)memrchr(hostname, '.', top-hostname);
398 if (x) hostname = x+1;
399 }
400 snprintf(buf, sizeof(buf), "%s.%s.", hostname, priv.uribl_suffix);
401 if (dns_interface(priv, buf, false, NULL)) {
402 if (debug_syslog > 2) {
403 char tmp[maxlen];
404 snprintf(tmp, sizeof(tmp), "found %s on %s", hostname, priv.uribl_suffix);
405 my_syslog(tmp);
406 }
407 found = register_string(hosts, hostname);
408 return true;
409 }
410 return false;
411 }
414 ////////////////////////////////////////////////
415 // uribl checker
416 // -------------
417 // hostname MUST not have a trailing dot
418 // If tld, two level lookup.
419 // Else, look up three level domain.
420 //
421 // if we find part of the hostname on the uribl, return
422 // true and point found to the part of the hostname that we found
423 // as a string registered in hosts.
424 // otherwise, return false and preserve the value of found.
425 //
426 bool check_uribl(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *&found) ;
427 bool check_uribl(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *&found) {
428 in_addr ip;
429 if (inet_aton(hostname, &ip)) {
430 const u_char *src = (const u_char *)&ip.s_addr;
431 if (src[0] == 127) return false; // don't do dns lookups on localhost
432 if (src[0] == 10) return false; // don't do dns lookups on rfc1918 space
433 if ((src[0] == 192) && (src[1] == 168)) return false;
434 if ((src[0] == 172) && (16 <= src[1]) && (src[1] <= 31)) return false;
435 char adr[sizeof "255.255.255.255 "];
436 snprintf(adr, sizeof(adr), "%u.%u.%u.%u", src[3], src[2], src[1], src[0]);
437 // cannot use inet_ntop here since we want the octets reversed.
438 return (uriblookup(priv, hosts, adr, NULL, found));
439 }
441 const char *top, *top2, *top3;
442 top = strrchr(hostname, '.');
443 if (top) {
444 top2 = (const char *)memrchr(hostname, '.', top-hostname);
446 if (top2) {
447 string_set::iterator i = priv.memory->get_cctlds()->find(top2+1);
448 string_set::iterator x = priv.memory->get_cctlds()->end();
449 // if we have a 2-level-cctld, just look at top three levels of the name
450 if (i != x) return uriblookup(priv, hosts, hostname, top2, found);
452 // if we have more than 3 levels in the name, look at the top three levels of the name
453 top3 = (const char *)memrchr(hostname, '.', top2-hostname);
454 if (top3 && uriblookup(priv, hosts, hostname, top2, found)) return true;
456 // if that was not found, fall thru to looking at the top two levels
457 }
458 // look at the top two levels of the name
459 return uriblookup(priv, hosts, hostname, top, found);
460 }
461 return false;
462 }
465 mlfiPriv::mlfiPriv() {
466 pthread_mutex_lock(&config_mutex);
467 pc = config;
468 pc->reference_count++;
469 pthread_mutex_unlock(&config_mutex);
470 get_fd();
471 ctx = NULL;
472 eom = false;
473 ip = 0;
474 helo = NULL;
475 mailaddr = NULL;
476 queueid = NULL;
477 authenticated = NULL;
478 client_name = NULL;
479 host_uribl = NULL;
480 helo_uribl = false;
481 client_uribl = false;
482 from_uribl = false;
483 have_whites = false;
484 only_whites = true;
485 want_spamassassin = false;
486 want_dccgrey = false;
487 want_dccbulk = false;
488 allow_autowhitelisting = true;
489 content_context = NULL;
490 memory = NULL;
491 scanner = NULL;
492 content_suffix = NULL;
493 content_message = NULL;
494 uribl_suffix = NULL;
495 uribl_message = NULL;
496 content_host_ignore = NULL;
497 assassin = NULL;
498 dccifd = NULL;
499 }
501 mlfiPriv::~mlfiPriv() {
502 return_fd();
503 pthread_mutex_lock(&config_mutex);
504 pc->reference_count--;
505 bool last = (!pc->reference_count) && (pc != config);
506 pthread_mutex_unlock(&config_mutex);
507 if (last) delete pc; // free this config, since we were the last reference to it
508 if (helo) free((void*)helo);
509 reset(true);
510 }
512 void mlfiPriv::reset(bool final) {
513 while (!delayer.empty()) {
514 DELAYWHITEP dwp = delayer.front();
515 const char *loto = dwp->get_loto();
516 if (loto) free((void*)loto);
517 delete dwp;
518 delayer.pop_front();
519 }
520 if (mailaddr) free((void*)mailaddr);
521 if (queueid) free((void*)queueid);
522 if (authenticated) free((void*)authenticated);
523 if (client_name) free((void*)client_name);
524 discard(hosts_uribl);
525 delayer.clear();
526 discard(env_to);
527 if (memory) delete memory;
528 if (scanner) delete scanner;
529 if (assassin) delete assassin;
530 if (dccifd) delete dccifd;
531 if (!final) {
532 ctx = NULL;
533 eom = false;
534 mailaddr = NULL;
535 queueid = NULL;
536 authenticated = NULL;
537 client_name = NULL;
538 host_uribl = NULL;
539 helo_uribl = false;
540 client_uribl = false;
541 from_uribl = false;
542 have_whites = false;
543 only_whites = true;
544 want_spamassassin = false;
545 want_dccgrey = false;
546 want_dccbulk = false;
547 allow_autowhitelisting = true;
548 content_context = NULL;
549 memory = NULL;
550 scanner = NULL;
551 content_suffix = NULL;
552 content_message = NULL;
553 uribl_suffix = NULL;
554 uribl_message = NULL;
555 content_host_ignore = NULL;
556 assassin = NULL;
557 dccifd = NULL;
558 }
559 }
561 void mlfiPriv::get_fd() {
562 err = true;
563 fd = NULL_SOCKET;
564 int result = pthread_mutex_lock(&fd_pool_mutex);
565 if (!result) {
566 std::set<int>::iterator i;
567 i = fd_pool.begin();
568 if (i != fd_pool.end()) {
569 // have at least one fd in the pool
570 err = false;
571 fd = *i;
572 fd_pool.erase(fd);
573 resolver_pool_size--;
574 pthread_mutex_unlock(&fd_pool_mutex);
575 }
576 else {
577 // pool is empty, get a new fd
578 pthread_mutex_unlock(&fd_pool_mutex);
579 fd = my_connect();
580 err = (fd == NULL_SOCKET);
581 }
582 }
583 else {
584 // cannot lock the pool, just get a new fd
585 fd = my_connect();
586 err = (fd == NULL_SOCKET);
587 }
588 }
590 void mlfiPriv::return_fd() {
591 if (err) {
592 // this fd got a socket error, so close it, rather than returning it to the pool
593 my_disconnect(fd);
594 }
595 else {
596 int result = pthread_mutex_lock(&fd_pool_mutex);
597 if (!result) {
598 if ((resolver_sock_count > resolver_pool_size*5) || (resolver_pool_size < 5)) {
599 // return the fd to the pool
600 fd_pool.insert(fd);
601 resolver_pool_size++;
602 pthread_mutex_unlock(&fd_pool_mutex);
603 }
604 else {
605 // more than 20% of the open resolver sockets are in the pool, and the
606 // pool as at least 5 sockets. that is enough, so just close this one.
607 pthread_mutex_unlock(&fd_pool_mutex);
608 my_disconnect(fd);
609 }
610 }
611 else {
612 // could not lock the pool, so just close the fd
613 my_disconnect(fd);
614 }
615 }
616 }
618 size_t mlfiPriv::my_write(const char *buf, size_t len) {
619 if (err) return 0;
620 size_t rs = 0;
621 while (len) {
622 size_t ws = write(fd, buf, len);
623 if (ws > 0) {
624 rs += ws;
625 len -= ws;
626 buf += ws;
627 }
628 else {
629 // peer closed the socket!
630 rs = 0;
631 err = true;
632 break;
633 }
634 }
635 return rs;
636 }
638 size_t mlfiPriv::my_read(char *buf, size_t len) {
639 if (err) return 0;
640 size_t rs = 0;
641 while (len) {
642 size_t ws = read(fd, buf, len);
643 if (ws > 0) {
644 rs += ws;
645 len -= ws;
646 buf += ws;
647 }
648 else {
649 // peer closed the socket!
650 rs = 0;
651 err = true;
652 break;
653 }
654 }
655 return rs;
656 }
658 void mlfiPriv::need_content_filter(const char *rcpt, CONTEXT &con) {
659 if (!memory) {
660 // first recipient that needs content filtering sets
661 // some of the content filtering parameters
662 memory = new recorder(this, con.get_html_tags(), con.get_content_tlds(), con.get_content_cctlds());
663 scanner = new url_scanner(memory);
664 content_suffix = con.get_content_suffix();
665 content_message = con.get_content_message();
666 uribl_suffix = con.get_uribl_suffix();
667 uribl_message = con.get_uribl_message();
668 content_host_ignore = &con.get_content_host_ignore();
669 // if we are using uribl, test helo and client names here
670 if (uribl_suffix) {
671 if (helo) {
672 helo_uribl = check_uribl(*this, hosts_uribl, helo, host_uribl);
673 if (client_name && !helo_uribl) {
674 client_uribl = check_uribl(*this, hosts_uribl, client_name, host_uribl);
675 if (mailaddr && !client_uribl) {
676 char *f = strchr(mailaddr, '@');
677 if (f) from_uribl = check_uribl(*this, hosts_uribl, f+1, host_uribl);
678 }
679 }
680 }
681 }
682 }
683 }
686 mlfiPriv* fetch_priv_from_ctx(SMFICTX *ctx);
687 mlfiPriv* fetch_priv_from_ctx(SMFICTX *ctx)
688 {
689 mlfiPriv *priv = (struct mlfiPriv *)smfi_getpriv(ctx);
690 priv->ctx = ctx;
691 return priv;
692 }
693 #define MLFIPRIV fetch_priv_from_ctx(ctx)
697 ////////////////////////////////////////////////
698 // syslog a message
699 //
700 void my_syslog(mlfiPriv *priv, const char *text) {
701 char buf[maxlen];
702 if (priv) {
703 snprintf(buf, sizeof(buf), "%s: %s", priv->queueid, text);
704 text = buf;
705 }
706 if (use_syslog) {
707 pthread_mutex_lock(&syslog_mutex);
708 if (!syslog_opened) {
709 openlog("dnsbl", LOG_PID, LOG_MAIL);
710 syslog_opened = true;
711 }
712 syslog(LOG_NOTICE, "%s", text);
713 pthread_mutex_unlock(&syslog_mutex);
714 }
715 else {
716 printf("%s \n", text);
717 }
718 }
720 void my_syslog(mlfiPriv *priv, const string text) {
721 if (debug_syslog > 3) {
722 char buf[maxlen];
723 strncpy(buf, text.c_str(), sizeof(buf));
724 buf[maxlen-1] = '\0'; // ensure null termination
725 my_syslog(priv, buf);
726 }
727 }
729 void my_syslog(const char *text) {
730 my_syslog(NULL, text);
731 }
734 ////////////////////////////////////////////////
735 // read a resolver request from the socket, process it, and
736 // write the result back to the socket.
738 void process_resolver_requests(int socket);
739 void process_resolver_requests(int socket) {
740 #ifdef NS_MAXDNAME
741 char question[NS_MAXDNAME];
742 #else
743 char question[1000];
744 #endif
745 glommer glom;
747 int maxq = sizeof(question);
748 while (true) {
749 // read a question
750 int rs = 0;
751 while (rs < maxq) {
752 int ns = read(socket, question+rs, maxq-rs);
753 if (ns > 0) {
754 rs += ns;
755 if (question[rs-1] == '\0') {
756 // last byte read was the null terminator, we are done
757 break;
758 }
759 }
760 else {
761 // peer closed the socket
762 #ifdef RESOLVER_DEBUG
763 my_syslog("process_resolver_requests() peer closed socket while reading question");
764 #endif
765 shutdown(socket, SHUT_RDWR);
766 close(socket);
767 return;
768 }
769 }
770 question[rs-1] = '\0'; // ensure null termination
772 // find the answer
773 #ifdef NS_PACKETSZ
774 #ifdef RESOLVER_DEBUG
775 char text[1000];
776 snprintf(text, sizeof(text), "process_resolver_requests() has a question %s", question);
777 my_syslog(text);
778 #endif
779 int res_result = res_search(question, ns_c_in, ns_t_a, glom.answer, sizeof(glom.answer));
780 if (res_result < 0) glom.length = 0; // represent all errors as zero length answers
781 else glom.length = (size_t)res_result;
782 #else
783 glom.length = sizeof(glom.answer);
784 glom.answer = 0;
785 struct hostent *host = gethostbyname(question);
786 if (host && (host->h_addrtype == AF_INET)) {
787 memcpy(&glom.answer, host->h_addr, sizeof(glom.answer));
788 }
789 #endif
791 // write the answer
792 char *buf = (char *)&glom;
793 int len = glom.length + sizeof(glom.length);
794 #ifdef RESOLVER_DEBUG
795 snprintf(text, sizeof(text), "process_resolver_requests() writing answer length %d for total %d", glom.length, len);
796 my_syslog(text);
797 #endif
798 int ws = 0;
799 while (len > ws) {
800 int ns = write(socket, buf+ws, len-ws);
801 if (ns > 0) {
802 ws += ns;
803 }
804 else {
805 // peer closed the socket!
806 #ifdef RESOLVER_DEBUG
807 my_syslog("process_resolver_requests() peer closed socket while writing answer");
808 #endif
809 shutdown(socket, SHUT_RDWR);
810 close(socket);
811 return;
812 }
813 }
814 }
815 }
818 ////////////////////////////////////////////////
819 // check a single dnsbl
820 //
821 bool check_single(mlfiPriv &priv, int ip, const char *suffix);
822 bool check_single(mlfiPriv &priv, int ip, const char *suffix) {
823 // make a dns question
824 const u_char *src = (const u_char *)&ip;
825 if (src[0] == 127) return false; // don't do dns lookups on localhost
826 if (src[0] == 10) return false; // don't do dns lookups on rfc1918 space
827 if ((src[0] == 192) && (src[1] == 168)) return false;
828 if ((src[0] == 172) && (16 <= src[1]) && (src[1] <= 31)) return false;
829 #ifdef NS_MAXDNAME
830 char question[NS_MAXDNAME];
831 #else
832 char question[1000];
833 #endif
834 snprintf(question, sizeof(question), "%u.%u.%u.%u.%s.", src[3], src[2], src[1], src[0], suffix);
835 // ask the question, if we get an A record it implies a blacklisted ip address
836 return dns_interface(priv, question, false, NULL);
837 }
840 ////////////////////////////////////////////////
841 // check a single dnsbl
842 //
843 bool check_single(mlfiPriv &priv, int ip, DNSBL &bl);
844 bool check_single(mlfiPriv &priv, int ip, DNSBL &bl) {
845 return check_single(priv, ip, bl.suffix);
846 }
849 ////////////////////////////////////////////////
850 // check the dnsbls specified for this recipient
851 //
852 bool check_dnsbl(mlfiPriv &priv, dnsblp_list &dnsbll, DNSBLP &rejectlist);
853 bool check_dnsbl(mlfiPriv &priv, dnsblp_list &dnsbll, DNSBLP &rejectlist) {
854 for (dnsblp_list::iterator i=dnsbll.begin(); i!=dnsbll.end(); i++) {
855 DNSBLP dp = *i; // non null by construction
856 bool st;
857 map<DNSBLP, bool>::iterator f = priv.checked.find(dp);
858 if (f == priv.checked.end()) {
859 // have not checked this list yet
860 st = check_single(priv, priv.ip, *dp);
861 rejectlist = dp;
862 priv.checked[dp] = st;
863 }
864 else {
865 st = (*f).second;
866 rejectlist = (*f).first;
867 }
868 if (st) return st;
869 }
870 return false;
871 }
874 ////////////////////////////////////////////////
875 // check the hosts from the body against the content filter and uribl dnsbls
876 //
877 //
878 bool check_hosts(mlfiPriv &priv, bool random, int limit, const char *&msg, const char *&host, int &ip, const char *&found);
879 bool check_hosts(mlfiPriv &priv, bool random, int limit, const char *&msg, const char *&host, int &ip, const char *&found) {
880 found = NULL; // normally ip address style
881 if (!priv.content_suffix && !priv.uribl_suffix) return false; // nothing to check
882 string_set &hosts = priv.memory->get_hosts();
883 string_set &ignore = *priv.content_host_ignore;
885 int count = 0;
886 int cnt = hosts.size(); // number of hosts we could look at
887 int_set ips;
888 ns_map nameservers;
889 for (string_set::iterator i=hosts.begin(); i!=hosts.end(); i++) {
890 host = *i; // a reference into hosts, which will live until this smtp transaction is closed
892 // don't bother looking up hosts on the ignore list
893 string_set::iterator j = ignore.find(host);
894 if (j != ignore.end()) continue;
896 // try to only look at limit/cnt fraction of the available cnt host names in random mode
897 if ((cnt > limit) && (limit > 0) && random) {
898 int r = rand() % cnt;
899 if (r >= limit) {
900 if (debug_syslog > 2) {
901 char buf[maxlen];
902 snprintf(buf, sizeof(buf), "host %s skipped", host);
903 my_syslog(&priv, buf);
904 }
905 continue;
906 }
907 }
908 count++;
909 ip = dns_interface(priv, host, true, &nameservers);
910 if (debug_syslog > 2) {
911 char buf[maxlen];
912 if (ip) {
913 char adr[sizeof "255.255.255.255 "];
914 adr[0] = '\0';
915 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr));
916 snprintf(buf, sizeof(buf), "host %s found at %s", host, adr);
917 }
918 else {
919 snprintf(buf, sizeof(buf), "host %s not found", host);
920 }
921 my_syslog(&priv, buf);
922 }
923 if (ip) {
924 int_set::iterator i = ips.find(ip);
925 if (i == ips.end()) {
926 // we haven't looked this up yet
927 ips.insert(ip);
928 // check dnsbl style list
929 if (priv.content_suffix && check_single(priv, ip, priv.content_suffix)) {
930 msg = priv.content_message;
931 return true;
932 }
933 // Check uribl & surbl style list
934 if (priv.uribl_suffix && check_uribl(priv, hosts, host, found)) {
935 msg = priv.uribl_message;
936 return true;
937 }
938 }
939 }
940 }
941 limit *= 4; // allow average of 3 ns per host name
942 for (ns_mapper::iterator i=nameservers.ns_ip.begin(); i!=nameservers.ns_ip.end(); i++) {
943 count++;
944 if ((count > limit) && (limit > 0)) return false; // too many name servers to check them all
945 host = (*i).first; // a transient reference that needs to be replaced before we return it
946 ip = (*i).second;
947 if (!ip) ip = dns_interface(priv, host, false, NULL);
948 if (debug_syslog > 2) {
949 char buf[maxlen];
950 if (ip) {
951 char adr[sizeof "255.255.255.255 "];
952 adr[0] = '\0';
953 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr));
954 snprintf(buf, sizeof(buf), "ns %s found at %s", host, adr);
955 }
956 else {
957 snprintf(buf, sizeof(buf), "ns %s not found", host);
958 }
959 my_syslog(&priv, buf);
960 }
961 if (ip) {
962 int_set::iterator i = ips.find(ip);
963 if (i == ips.end()) {
964 ips.insert(ip);
965 if (check_single(priv, ip, priv.content_suffix)) {
966 msg = priv.content_message;
967 string_map::iterator j = nameservers.ns_host.find(host);
968 if (j != nameservers.ns_host.end()) {
969 const char *refer = (*j).second;
970 char buf[maxlen];
971 snprintf(buf, sizeof(buf), "%s with nameserver %s", refer, host);
972 host = register_string(hosts, buf); // put a copy into hosts, and return that reference
973 }
974 else {
975 host = register_string(hosts, host); // put a copy into hosts, and return that reference
976 }
977 return true;
978 }
979 }
980 }
981 }
982 return false;
983 }
986 ////////////////////////////////////////////////
987 //
988 // this email address is passed in from sendmail, and will normally be
989 // enclosed in <>. I think older versions of sendmail supplied the <>
990 // wrapper if the mail client did not, but the current version does not do
991 // that. So the <> wrapper is now optional. It may have mixed case, just
992 // as the mail client sent it. We dup the string and convert the duplicate
993 // to lower case. Some clients enclose the entire address in single quotes,
994 // so we strip those as well. We also remove the SRS coding.
995 //
996 const char *to_lower_string(const char *email);
997 const char *to_lower_string(const char *email) {
998 int n = strlen(email);
999 if (email[0] == '<') {
1000 // assume it also ends with >
1001 n -= 2;
1002 if (n < 1) return strdup(email); // return "<>"
1003 email++;
1005 if ((email[0] == '\'') && (email[n-1] == '\'') && (n > 2)) {
1006 n -= 2;
1007 email++;
1009 char *key = strdup(email);
1010 key[n] = '\0';
1011 for (int i=0; i<n; i++) key[i] = tolower(key[i]);
1012 if ((n > 12) && (strncmp(key, "srs", 3) == 0)) {
1013 // might have srs coding to be removed
1014 const int nmatch = 6;
1015 regmatch_t match[nmatch];
1016 if (0 == regexec(&srs_pattern, key, nmatch, match, 0)) {
1017 int s4 = match[4].rm_so; // domain
1018 int e4 = match[4].rm_eo;
1019 int s5 = match[5].rm_so; // user
1020 int e5 = match[5].rm_eo;
1021 if ((s4 != -1) && (s5 != -1)) {
1022 char *newkey = strdup(key); // large enough
1023 key[e4] = '\0';
1024 key[e5] = '\0';
1025 strcpy(newkey, key+s5); // user
1026 strcat(newkey, "@"); // @
1027 strcat(newkey, key+s4); // domain
1028 free(key);
1029 key = newkey;
1033 return key;
1037 ////////////////////////////////////////////////
1038 // start of sendmail milter interfaces
1039 //
1040 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
1042 // allocate some private memory
1043 mlfiPriv *priv = new mlfiPriv;
1044 if (hostaddr->sa_family == AF_INET) {
1045 priv->ip = ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr;
1047 // save the private data
1048 smfi_setpriv(ctx, (void*)priv);
1050 // continue processing
1051 return SMFIS_CONTINUE;
1054 sfsistat mlfi_helo(SMFICTX * ctx, char *helohost)
1056 mlfiPriv &priv = *MLFIPRIV;
1057 priv.helo = strdup(helohost);
1058 return SMFIS_CONTINUE;
1061 sfsistat mlfi_envfrom(SMFICTX *ctx, char **from)
1063 mlfiPriv &priv = *MLFIPRIV;
1064 priv.mailaddr = to_lower_string(from[0]);
1065 priv.queueid = strdup(smfi_getsymval(ctx, (char*)"i"));
1066 priv.authenticated = smfi_getsymval(ctx, (char*)"{auth_authen}");
1067 priv.client_name = smfi_getsymval(ctx, (char*)"_");
1068 if (!priv.helo) priv.helo = strdup("unknown");
1069 if (priv.authenticated) priv.authenticated = strdup(priv.authenticated);
1070 if (priv.client_name) priv.client_name = strdup(priv.client_name);
1071 if (spamc != spamc_empty) {
1072 priv.assassin = new SpamAssassin(&priv, priv.ip, priv.helo, priv.mailaddr, priv.queueid);
1074 if (dccifd_port) {
1075 priv.dccifd = new DccInterface(dccifd_port, &priv, priv.ip, priv.helo, priv.mailaddr);
1077 return SMFIS_CONTINUE;
1080 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **rcpt)
1082 DNSBLP rejectlist = NULL; // list that caused the reject
1083 mlfiPriv &priv = *MLFIPRIV;
1084 CONFIG &dc = *priv.pc;
1085 const char *rcptaddr = rcpt[0];
1086 const char *loto = to_lower_string(rcptaddr);
1087 bool self = (strcmp(loto, priv.mailaddr) == 0);
1089 // some version of sendmail allowed rcpt to:<> and passed it thru to the milters
1090 if (strcmp(loto, "<>") == 0) {
1091 smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", (char*)"bogus recipient");
1092 return SMFIS_REJECT;
1094 // priv.mailaddr sending original message to loto
1095 CONTEXT &con = *(dc.find_context(loto)->find_context(priv.mailaddr));
1096 VERIFYP ver = con.find_verify(loto);
1097 const char *fromvalue = con.find_from(priv.mailaddr, true, priv.queueid);
1098 // tell spam assassin and dccifd about this recipient
1099 if (priv.assassin) priv.assassin->mlfi_envrcpt(ctx, loto);
1100 if (priv.dccifd) priv.dccifd->mlfi_envrcpt(ctx, loto, con.get_grey() && !priv.authenticated);
1101 // loto sending a reply back to priv.mailaddr
1102 CONTEXT &con2 = *(dc.find_context(priv.mailaddr)->find_context(loto));
1103 const char *replyvalue = con2.find_from(loto);
1104 if (debug_syslog > 1) {
1105 char buf[maxlen];
1106 char msg[maxlen];
1107 snprintf(msg, sizeof(msg), "from <%s> to <%s> using context %s state %s reply state %s", priv.mailaddr, loto, con.get_full_name(buf,maxlen), fromvalue, replyvalue);
1108 my_syslog(&priv, msg);
1110 free((void*)loto);
1111 status st;
1112 if (replyvalue == token_black) {
1113 smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", (char*)"recipient can not reply due to blacklisting");
1114 return SMFIS_REJECT;
1116 if (priv.authenticated) {
1117 int c = incr_rcpt_count(priv.authenticated);
1118 int l = dc.default_context->find_rate(priv.authenticated);
1119 if (debug_syslog > 1) {
1120 char msg[maxlen];
1121 snprintf(msg, sizeof(msg), "authenticated id %s (%d recipients, %d limit)", priv.authenticated, c, l);
1122 my_syslog(&priv, msg);
1124 if (c > l) {
1125 smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", (char*)"recipient rate limit exceeded");
1126 return SMFIS_REJECT;
1128 st = white;
1130 else if (fromvalue == token_black) {
1131 st = black;
1133 else if ((fromvalue == token_white) && !self) {
1134 st = white;
1136 else {
1137 // check the dns based lists
1138 st = (check_dnsbl(priv, con.get_dnsbl_list(), rejectlist)) ? reject : oksofar;
1140 if (st == reject) {
1141 // reject the recipient based on some dnsbl
1142 char adr[sizeof "255.255.255.255 "];
1143 adr[0] = '\0';
1144 inet_ntop(AF_INET, (const u_char *)&priv.ip, adr, sizeof(adr));
1145 char buf[maxlen];
1146 snprintf(buf, sizeof(buf), rejectlist->message, adr, adr);
1147 smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", buf);
1148 return SMFIS_REJECT;
1150 if (st == oksofar) {
1151 const char *msg = con.generic_match(priv.client_name);
1152 if (msg) {
1153 // reject the recipient based on generic reverse dns
1154 char buf[maxlen];
1155 snprintf(buf, sizeof(buf), msg, priv.client_name);
1156 smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", buf);
1157 return SMFIS_REJECT;
1160 if (st == black) {
1161 // reject the recipient based on blacklisting either from or to
1162 smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", (char*)"no such user");
1163 return SMFIS_REJECT;
1165 if (ver) {
1166 // try to verify this from/to pair of addresses even if it might be explicitly whitelisted
1167 const char *loto = to_lower_string(rcptaddr);
1168 bool rc = ver->ok(priv.mailaddr, loto);
1169 free((void*)loto);
1170 if (!rc) {
1171 smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", (char*)"no such user");
1172 return SMFIS_REJECT;
1175 // we will accept the recipient, but add an auto-whitelist entry
1176 // if needed to ensure we can accept replies
1177 loto = to_lower_string(rcptaddr);
1178 WHITELISTERP w = con2.find_autowhite(loto, priv.mailaddr);
1179 // check if local part is too big
1180 const int max_local_size = 30;
1181 const char *p = strchr(loto, '@');
1182 int len = (p) ? p-loto : max_local_size;
1183 if (len >= max_local_size) w = NULL; // too big, pretend we don't have a whitelister
1184 // record it if we have a whitelister
1185 if (w) {
1186 DELAYWHITEP dwp = new DELAYWHITE(loto, w, &con2); // dwp takes ownership of the string
1187 priv.delayer.push_back(dwp);
1189 else {
1190 free((void*)loto); // or we free it here
1193 // accept the recipient
1194 if (!con.get_content_filtering()) st = white;
1196 if (st == oksofar) {
1197 // remember first content filtering context
1198 if (con.get_content_filtering()) {
1199 if (!priv.content_context) priv.content_context = &con;
1200 else if (con.get_require() && (priv.content_context != &con)) {
1201 smfi_setreply(ctx, (char*)"452", (char*)"4.2.1", (char*)"incompatible filtering contexts");
1202 return SMFIS_TEMPFAIL;
1204 priv.need_content_filter(rcptaddr, con);
1205 char bu[maxlen];
1206 bool uri = false;
1207 // content filtering implies also checking helo name on uribl (if enabled)
1208 if (priv.helo_uribl) {
1209 snprintf(bu, sizeof(bu), "(helo %s)", priv.host_uribl);
1210 uri = true;
1212 // content filterint implies also checking client reverse dns name on uribl (if enabled)
1213 if (priv.client_uribl) {
1214 snprintf(bu, sizeof(bu), "(rdns %s)", priv.host_uribl);
1215 uri = true;
1217 // content filterint implies also checking mail from domain name on uribl (if enabled)
1218 if (priv.from_uribl) {
1219 snprintf(bu, sizeof(bu), "(from %s)", priv.host_uribl);
1220 uri = true;
1222 if (uri) {
1223 char buf[maxlen];
1224 snprintf(buf, sizeof(buf), priv.uribl_message, bu, priv.host_uribl);
1225 smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", buf);
1226 return SMFIS_REJECT;
1229 // remember the non-whites
1230 register_string(priv.env_to, rcptaddr, &con);
1231 priv.only_whites = false;
1232 priv.want_spamassassin |= (priv.assassin) && // have spam assassin available and
1233 (con.get_spamassassin_limit() != 0); // want to use it with a non-zero score
1234 priv.want_dccgrey |= (priv.dccifd) && // have dcc interface and
1235 (con.get_grey()); // want to use it for greylisting
1236 priv.want_dccbulk |= (priv.dccifd) && // have dcc interface and
1237 (con.get_bulk() != 0); // want to use it for bulk detection
1239 if (st == white) {
1240 priv.have_whites = true;
1242 return SMFIS_CONTINUE;
1245 sfsistat mlfi_header(SMFICTX* ctx, char* headerf, char* headerv)
1247 mlfiPriv &priv = *MLFIPRIV;
1248 // headers that avoid autowhitelisting
1249 if (((strcasecmp(headerf, "precedence") == 0) && (strcasecmp(headerv, "bulk") == 0)) ||
1250 ((strcasecmp(headerf, "content-type") == 0) && (strncasecmp(headerv, "multipart/report", 16) == 0))) {
1251 priv.allow_autowhitelisting = false;
1254 // other headers are only needed for content filtering
1255 if (priv.authenticated) return SMFIS_CONTINUE;
1256 if (priv.only_whites) return SMFIS_CONTINUE;
1257 if (priv.want_spamassassin) priv.assassin->mlfi_header(headerf, headerv);
1258 if (priv.want_dccgrey || priv.want_dccbulk) priv.dccifd->mlfi_header(ctx, headerf, headerv);
1259 return SMFIS_CONTINUE;
1262 sfsistat mlfi_eoh(SMFICTX* ctx)
1264 mlfiPriv &priv = *MLFIPRIV;
1265 // delayed autowhitelisting
1266 while (!priv.delayer.empty()) {
1267 DELAYWHITEP dwp = priv.delayer.front();
1268 const char *loto = dwp->get_loto();
1269 if (priv.allow_autowhitelisting) {
1270 WHITELISTERP w = dwp->get_w();
1271 CONTEXTP con2 = dwp->get_con();
1272 if (debug_syslog > 1) {
1273 char buf[maxlen];
1274 char msg[maxlen];
1275 snprintf(msg, sizeof(msg), "whitelist reply from <%s> in context %s", loto, con2->get_full_name(buf,maxlen));
1276 my_syslog(&priv, msg);
1278 w->sent(loto); // don't free it, the whitelister takes ownership of the string
1280 else {
1281 if (debug_syslog > 1) {
1282 char msg[maxlen];
1283 snprintf(msg, sizeof(msg), "avoid whitelist reply from <%s> for outgoing auto-responder", loto);
1284 my_syslog(&priv, msg);
1286 if (loto) free((void*)loto);// or we free it here
1288 delete dwp;
1289 priv.delayer.pop_front();
1291 // content filtering
1292 if (priv.authenticated) return SMFIS_CONTINUE;
1293 if (priv.only_whites) return SMFIS_CONTINUE;
1294 if (priv.want_spamassassin) priv.assassin->mlfi_eoh();
1295 if (priv.want_dccgrey || priv.want_dccbulk) priv.dccifd->mlfi_eoh();
1296 return SMFIS_CONTINUE;
1299 sfsistat mlfi_body(SMFICTX *ctx, u_char *data, size_t len)
1301 mlfiPriv &priv = *MLFIPRIV;
1302 if (priv.authenticated) return SMFIS_CONTINUE;
1303 if (priv.only_whites) return SMFIS_CONTINUE;
1304 if (priv.want_spamassassin) priv.assassin->mlfi_body(data, len);
1305 if (priv.want_dccgrey || priv.want_dccbulk) priv.dccifd->mlfi_body(data, len);
1306 priv.scanner->scan(data, len);
1307 return SMFIS_CONTINUE;
1310 sfsistat mlfi_eom(SMFICTX *ctx)
1312 sfsistat rc;
1313 mlfiPriv &priv = *MLFIPRIV;
1314 const char *host = NULL;
1315 int ip;
1316 // process end of message
1317 priv.eom = true;
1318 if (priv.authenticated || priv.only_whites) rc = SMFIS_CONTINUE;
1319 else {
1320 // assert env_to not empty, it contains the
1321 // non-whitelisted folks that want content filtering
1322 int score = (priv.want_spamassassin) ? priv.assassin->mlfi_eom() : 0;
1323 bool grey = false;
1324 int bulk = 0;
1325 if (priv.want_dccgrey || priv.want_dccbulk) priv.dccifd->mlfi_eom(grey, bulk);
1327 char buf[maxlen];
1328 string msg;
1329 string_set alive;
1330 bool random = false;
1331 int limit = 0;
1332 for (context_map::iterator i=priv.env_to.begin(); i!=priv.env_to.end(); i++) {
1333 const char *rcpt = (*i).first;
1334 CONTEXT &con = *((*i).second);
1335 if (!con.acceptable_content(*priv.memory, score, bulk, msg)) {
1336 // bad html tags or excessive hosts or
1337 // high spam assassin score or dcc bulk threshold exceedeed
1338 smfi_delrcpt(ctx, (char*)rcpt);
1340 else {
1341 alive.insert(rcpt);
1342 random |= con.get_host_random();
1343 limit = max(limit, con.get_host_limit());
1346 bool rejecting = alive.empty(); // if alive is empty, we must have set msg above in acceptable_content()
1347 if (!rejecting) {
1348 const char *fmt;
1349 const char *found;
1350 if (check_hosts(priv, random, limit, fmt, host, ip, found)) {
1351 if (found) {
1352 // uribl style
1353 snprintf(buf, sizeof(buf), fmt, host, found);
1355 else {
1356 // dnsbl style
1357 char adr[sizeof "255.255.255.255 "];
1358 adr[0] = '\0';
1359 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr));
1360 snprintf(buf, sizeof(buf), fmt, host, adr);
1362 msg = string(buf);
1363 rejecting = true;
1366 if (!rejecting) {
1367 if (priv.want_dccgrey && grey) {
1368 smfi_setreply(ctx, (char*)"452", (char*)"4.2.1", (char*)"temporary greylist embargoed");
1369 rc = SMFIS_TEMPFAIL;
1371 else rc = SMFIS_CONTINUE;
1373 else if (!priv.have_whites) {
1374 // can reject the entire message
1375 snprintf(buf, sizeof(buf), "%s", msg.c_str());
1376 smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", buf);
1377 rc = SMFIS_REJECT;
1379 else {
1380 // need to accept it but remove the recipients that don't want it
1381 for (string_set::iterator i=alive.begin(); i!=alive.end(); i++) {
1382 const char *rcpt = *i;
1383 smfi_delrcpt(ctx, (char*)rcpt);
1385 rc = SMFIS_CONTINUE;
1388 // reset for a new message on the same connection
1389 mlfi_abort(ctx);
1390 return rc;
1393 sfsistat mlfi_abort(SMFICTX *ctx)
1395 mlfiPriv &priv = *MLFIPRIV;
1396 priv.reset();
1397 return SMFIS_CONTINUE;
1400 sfsistat mlfi_close(SMFICTX *ctx)
1402 mlfiPriv *priv = MLFIPRIV;
1403 if (!priv) return SMFIS_CONTINUE;
1404 delete priv;
1405 smfi_setpriv(ctx, NULL);
1406 return SMFIS_CONTINUE;
1409 struct smfiDesc smfilter =
1411 (char*)"DNSBL", // filter name
1412 SMFI_VERSION, // version code -- do not change
1413 SMFIF_DELRCPT, // flags
1414 mlfi_connect, // connection info filter
1415 mlfi_helo, // SMTP HELO command filter
1416 mlfi_envfrom, // envelope sender filter
1417 mlfi_envrcpt, // envelope recipient filter
1418 mlfi_header, // header filter
1419 mlfi_eoh, // end of header
1420 mlfi_body, // body block filter
1421 mlfi_eom, // end of message
1422 mlfi_abort, // message aborted
1423 mlfi_close, // connection cleanup
1424 };
1427 ////////////////////////////////////////////////
1428 // reload the config
1429 //
1430 CONFIG* new_conf();
1431 CONFIG* new_conf() {
1432 CONFIG *newc = new CONFIG;
1433 pthread_mutex_lock(&config_mutex);
1434 newc->generation = generation++;
1435 pthread_mutex_unlock(&config_mutex);
1436 if (debug_syslog) {
1437 char buf[maxlen];
1438 snprintf(buf, sizeof(buf), "loading configuration generation %d", newc->generation);
1439 my_syslog(buf);
1441 if (load_conf(*newc, "dnsbl.conf")) {
1442 newc->load_time = time(NULL);
1443 return newc;
1445 delete newc;
1446 return NULL;
1450 ////////////////////////////////////////////////
1451 // thread to watch the old config files for changes
1452 // and reload when needed.
1453 // we also clear the SMTP AUTH recipient counts hourly
1454 //
1455 extern "C" {void* config_loader(void *arg);}
1456 void* config_loader(void *arg) {
1457 int loop = 0;
1458 while (loader_run) {
1459 sleep(180); // look for modifications every 3 minutes
1460 if (!loader_run) break;
1461 loop++;
1462 if (loop == 20) {
1463 // three minutes thru each loop, 20 loops per hour
1464 // clear the recipient counts
1465 pthread_mutex_lock(&rate_mutex);
1466 for (rcpt_rates::iterator i=rcpt_counts.begin(); i!=rcpt_counts.end(); i++) {
1467 (*i).second = 0;
1469 pthread_mutex_unlock(&rate_mutex);
1470 loop = 0;
1472 CONFIG &dc = *config;
1473 time_t then = dc.load_time;
1474 struct stat st;
1475 bool reload = false;
1476 for (string_set::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) {
1477 const char *fn = *i;
1478 if (stat(fn, &st)) reload = true; // file disappeared
1479 else if (st.st_mtime > then) reload = true; // file modified
1480 if (reload) break;
1482 if (reload) {
1483 CONFIG *newc = new_conf();
1484 if (newc) {
1485 // replace the global config pointer
1486 pthread_mutex_lock(&config_mutex);
1487 CONFIG *pc = config;
1488 bool last = pc && (!pc->reference_count);
1489 config = newc;
1490 pthread_mutex_unlock(&config_mutex);
1491 if (last) delete pc; // there were no references to this config
1493 else {
1494 // failed to load new config
1495 my_syslog("failed to load new configuration");
1496 system("echo 'failed to load new dnsbl configuration from /etc/dnsbl' | mail -s 'error in /etc/dnsbl configuration' root");
1497 // update the load time on the current config to prevent complaining every 3 minutes
1498 dc.load_time = time(NULL);
1502 return NULL;
1506 void usage(const char *prog);
1507 void usage(const char *prog)
1509 fprintf(stderr, "Usage: %s [-d [level]] [-c] [-s] [-e from|to] [-b dccifd-addr] -r port -p sm-sock-addr [-t timeout]\n", prog);
1510 fprintf(stderr, "where dccifd_addr is for the connection to dccifd\n");
1511 fprintf(stderr, " and should be local-domain-socket-file-name\n");
1512 fprintf(stderr, "where port is for the connection to our own dns resolver processes\n");
1513 fprintf(stderr, " and should be local-domain-socket-file-name\n");
1514 fprintf(stderr, "where sm-sock-addr is for the connection to sendmail\n");
1515 fprintf(stderr, " and should be one of\n");
1516 fprintf(stderr, " inet:port@ip-address\n");
1517 fprintf(stderr, " local:local-domain-socket-file-name\n");
1518 fprintf(stderr, "-c will load and dump the config to stdout\n");
1519 fprintf(stderr, "-s will stress test the config loading code by repeating the load/free cycle\n");
1520 fprintf(stderr, " in an infinte loop.\n");
1521 fprintf(stderr, "-d will set the syslog message level, currently 0 to 3\n");
1522 fprintf(stderr, "-e will print the results of looking up the from and to addresses in the\n");
1523 fprintf(stderr, " current config. The | character is used to separate the from and to\n");
1524 fprintf(stderr, " addresses in the argument to the -e switch\n");
1529 void setup_socket(const char *sock);
1530 void setup_socket(const char *sock) {
1531 unlink(sock);
1535 /*
1536 * The signal handler function -- only gets called when a SIGCHLD
1537 * is received, ie when a child terminates
1538 */
1539 void sig_chld(int signo)
1541 int status;
1542 /* Wait for any child without blocking */
1543 while (waitpid(-1, &status, WNOHANG) > 0) {
1544 // ignore child exit status, we only do this to cleanup zombies
1549 int main(int argc, char**argv)
1551 token_init();
1552 bool check = false;
1553 bool stress = false;
1554 bool setconn = false;
1555 bool setreso = false;
1556 const char *email = NULL;
1557 int c;
1558 const char *args = "b:r:p:t:e:d:chs";
1559 extern char *optarg;
1561 // setup srs coding detection
1562 if (regcomp(&srs_pattern, "^srs(0|1)=([^=]*)=([^=]*)=([^=]*)=([^@]*)@", REG_ICASE | REG_EXTENDED)) {
1563 printf("cannot compile regex pattern to find srs coding in mail addresses\n");
1564 exit(3);
1567 // Process command line options
1568 while ((c = getopt(argc, argv, args)) != -1) {
1569 switch (c) {
1570 case 'b':
1571 if (optarg == NULL || *optarg == '\0') {
1572 fprintf(stderr, "Illegal dccifd socket: %s\n", optarg);
1573 exit(EX_USAGE);
1575 dccifd_port = strdup(optarg);
1576 break;
1578 case 'r':
1579 if (optarg == NULL || *optarg == '\0') {
1580 fprintf(stderr, "Illegal resolver socket: %s\n", optarg);
1581 exit(EX_USAGE);
1583 resolver_port = strdup(optarg);
1584 setup_socket(resolver_port);
1585 setreso = true;
1586 break;
1588 case 'p':
1589 if (optarg == NULL || *optarg == '\0') {
1590 fprintf(stderr, "Illegal sendmail socket: %s\n", optarg);
1591 exit(EX_USAGE);
1593 if (smfi_setconn(optarg) == MI_FAILURE) {
1594 fprintf(stderr, "smfi_setconn failed\n");
1595 exit(EX_SOFTWARE);
1597 if (strncasecmp(optarg, "unix:", 5) == 0) setup_socket(optarg + 5);
1598 else if (strncasecmp(optarg, "local:", 6) == 0) setup_socket(optarg + 6);
1599 setconn = true;
1600 break;
1602 case 't':
1603 if (optarg == NULL || *optarg == '\0') {
1604 fprintf(stderr, "Illegal timeout: %s\n", optarg);
1605 exit(EX_USAGE);
1607 if (smfi_settimeout(atoi(optarg)) == MI_FAILURE) {
1608 fprintf(stderr, "smfi_settimeout failed\n");
1609 exit(EX_SOFTWARE);
1611 break;
1613 case 'e':
1614 if (email) free((void*)email);
1615 email = strdup(optarg);
1616 break;
1618 case 'c':
1619 check = true;
1620 break;
1622 case 's':
1623 stress = true;
1624 break;
1626 case 'd':
1627 if (optarg == NULL || *optarg == '\0') debug_syslog = 1;
1628 else debug_syslog = atoi(optarg);
1629 break;
1631 case 'h':
1632 default:
1633 usage(argv[0]);
1634 exit(EX_USAGE);
1638 if (check) {
1639 use_syslog = false;
1640 debug_syslog = 10;
1641 CONFIG *conf = new_conf();
1642 if (conf) {
1643 conf->dump();
1644 delete conf;
1645 clear_strings(); // for valgrind checking
1646 return 0;
1648 else {
1649 return 1; // config failed to load
1653 if (stress) {
1654 fprintf(stdout, "stress testing\n");
1655 while (1) {
1656 for (int i=0; i<10; i++) {
1657 CONFIG *conf = new_conf();
1658 if (conf) delete conf;
1660 fprintf(stdout, ".");
1661 fflush(stdout);
1662 sleep(1);
1666 if (email) {
1667 char *x = strchr(email, '|');
1668 if (x) {
1669 *x = '\0';
1670 const char *from = to_lower_string(email);
1671 const char *to = to_lower_string(x+1);
1672 use_syslog = false;
1673 CONFIG *conf = new_conf();
1674 if (conf) {
1675 CONTEXTP con = conf->find_context(to);
1676 char buf[maxlen];
1677 fprintf(stdout, "envelope to <%s> finds context %s\n", to, con->get_full_name(buf,maxlen));
1678 CONTEXTP fc = con->find_context(from);
1679 fprintf(stdout, "envelope from <%s> finds context %s\n", from, fc->get_full_name(buf,maxlen));
1680 const char *st = fc->find_from(from);
1681 fprintf(stdout, "envelope from <%s> finds status %s\n", from, st);
1682 bool self = (strcmp(from, to) == 0);
1683 if ((st == token_white) && self) fprintf(stdout, "ignore self whitelisting\n");
1684 delete conf;
1687 return 0;
1690 if (!setconn) {
1691 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
1692 usage(argv[0]);
1693 exit(EX_USAGE);
1696 if (!setreso) {
1697 fprintf(stderr, "%s: Missing required -r argument\n", argv[0]);
1698 usage(argv[0]);
1699 exit(EX_USAGE);
1702 if (smfi_register(smfilter) == MI_FAILURE) {
1703 fprintf(stderr, "smfi_register failed\n");
1704 exit(EX_UNAVAILABLE);
1707 // switch to background mode
1708 if (daemon(1,0) < 0) {
1709 fprintf(stderr, "daemon() call failed\n");
1710 exit(EX_UNAVAILABLE);
1713 // write the pid
1714 const char *pidpath = "/var/run/dnsbl.pid";
1715 unlink(pidpath);
1716 FILE *f = fopen(pidpath, "w");
1717 if (f) {
1718 #ifdef linux
1719 // from a comment in the DCC source code:
1720 // Linux threads are broken. Signals given the
1721 // original process are delivered to only the
1722 // thread that happens to have that PID. The
1723 // sendmail libmilter thread that needs to hear
1724 // SIGINT and other signals does not, and that breaks
1725 // scripts that need to stop milters.
1726 // However, signaling the process group works.
1727 fprintf(f, "-%d\n", (u_int)getpgrp());
1728 #else
1729 fprintf(f, "%d\n", (u_int)getpid());
1730 #endif
1731 fclose(f);
1734 // initialize the thread sync objects
1735 pthread_mutex_init(&config_mutex, 0);
1736 pthread_mutex_init(&syslog_mutex, 0);
1737 pthread_mutex_init(&resolve_mutex, 0);
1738 pthread_mutex_init(&fd_pool_mutex, 0);
1739 pthread_mutex_init(&verifier_mutex, 0);
1740 pthread_mutex_init(&whitelister_mutex, 0);
1742 // drop root privs
1743 struct passwd *pw = getpwnam("dnsbl");
1744 if (pw) {
1745 if (setgid(pw->pw_gid) == -1) {
1746 my_syslog("failed to switch to group dnsbl");
1748 if (setuid(pw->pw_uid) == -1) {
1749 my_syslog("failed to switch to user dnsbl");
1753 // load the initial config
1754 config = new_conf();
1755 if (!config) {
1756 my_syslog("failed to load initial configuration, quitting");
1757 exit(1);
1760 // fork off the resolver listener process
1761 pid_t child = fork();
1762 if (child < 0) {
1763 my_syslog("failed to create resolver listener process");
1764 exit(0);
1766 if (child == 0) {
1767 // we are the child - dns resolver listener process
1768 resolver_socket = socket(AF_UNIX, SOCK_STREAM, 0);
1769 if (resolver_socket < 0) {
1770 my_syslog("child failed to create resolver socket");
1771 exit(0); // failed
1773 sockaddr_un server;
1774 memset(&server, '\0', sizeof(server));
1775 server.sun_family = AF_UNIX;
1776 strncpy(server.sun_path, resolver_port, sizeof(server.sun_path)-1);
1777 //try to bind the address to the socket.
1778 if (bind(resolver_socket, (sockaddr *)&server, sizeof(server)) < 0) {
1779 // bind failed
1780 shutdown(resolver_socket, SHUT_RDWR);
1781 close(resolver_socket);
1782 my_syslog("child failed to bind resolver socket");
1783 exit(0); // failed
1785 //listen on the socket.
1786 if (listen(resolver_socket, 10) < 0) {
1787 // listen failed
1788 shutdown(resolver_socket, SHUT_RDWR);
1789 close(resolver_socket);
1790 my_syslog("child failed to listen to resolver socket");
1791 exit(0); // failed
1793 // setup sigchld handler to prevent zombies
1794 struct sigaction act;
1795 act.sa_handler = sig_chld; // Assign sig_chld as our SIGCHLD handler
1796 sigemptyset(&act.sa_mask); // We don't want to block any other signals in this example
1797 act.sa_flags = SA_NOCLDSTOP; // only want children that have terminated
1798 if (sigaction(SIGCHLD, &act, NULL) < 0) {
1799 my_syslog("child failed to setup SIGCHLD handler");
1800 exit(0); // failed
1802 while (true) {
1803 sockaddr_un client;
1804 socklen_t clientlen = sizeof(client);
1805 int s = accept(resolver_socket, (sockaddr *)&client, &clientlen);
1806 if (s > 0) {
1807 // accept worked, it did not get cancelled before we could accept it
1808 // fork off a process to handle this connection
1809 int newchild = fork();
1810 if (newchild == 0) {
1811 // this is the worker process
1812 // child does not need the listening socket
1813 close(resolver_socket);
1814 #ifdef NS_PACKETSZ
1815 res_init();
1816 _res.retry = 2;
1817 _res.retrans = RES_TIMEOUT;
1818 #endif
1819 process_resolver_requests(s);
1820 exit(0);
1822 else {
1823 // this is the parent
1824 // parent does not need the accepted socket
1825 close(s);
1829 exit(0); // make sure we don't fall thru.
1831 else {
1832 sleep(2); // allow child to get started
1835 // only create threads after the fork() in daemon
1836 pthread_t tid;
1837 if (pthread_create(&tid, 0, config_loader, 0))
1838 my_syslog("failed to create config loader thread");
1839 if (pthread_detach(tid))
1840 my_syslog("failed to detach config loader thread");
1842 if (pthread_create(&tid, 0, verify_closer, 0))
1843 my_syslog("failed to create verify closer thread");
1844 if (pthread_detach(tid))
1845 my_syslog("failed to detach verify closer thread");
1847 if (pthread_create(&tid, 0, whitelister_writer, 0))
1848 my_syslog("failed to create autowhite writer thread");
1849 if (pthread_detach(tid))
1850 my_syslog("failed to detach autowhite writer thread");
1852 time_t starting = time(NULL);
1853 int rc = smfi_main();
1854 if ((rc != MI_SUCCESS) && (time(NULL) > starting+5*60)) {
1855 my_syslog("trying to restart after smfi_main()");
1856 loader_run = false; // eventually the config loader thread will terminate
1857 execvp(argv[0], argv);
1859 exit((rc == MI_SUCCESS) ? 0 : EX_UNAVAILABLE);