comparison src/new.cpp @ 71:dd21c8e13074

start coding on new config syntax
author carl
date Sat, 09 Jul 2005 19:24:41 -0700
parents
children
comparison
equal deleted inserted replaced
70:c812a06f87bf 71:dd21c8e13074
1 /*
2
3 Copyright (c) 2004, 2005 Carl Byington - 510 Software Group, released
4 under the GPL version 2 or any later version at your choice available at
5 http://www.fsf.org/licenses/gpl.txt
6
7 Based on a sample milter Copyright (c) 2000-2003 Sendmail, Inc. and its
8 suppliers. Inspired by the DCC by Rhyolite Software
9
10 -r port The port used to talk to our internal dns resolver processes
11 -p port The port through which the MTA will connect to this milter.
12 -t sec The timeout value.
13 -c Check the config, and print a copy to stdout. Don't start the
14 milter or do anything with the socket.
15 -d Add debug syslog entries
16
17
18 TODO:
19 1) Add config for max_recipients for each mail domain. Recipients in
20 excess of that limit will be rejected, and the entire data will be
21 rejected if it is sent.
22
23 2) Add config for poison addresses. If any recipient is poison, all
24 recipients are rejected even if they would be whitelisted, and the
25 data is rejected if sent.
26
27 3) Add option to only allow one recipient if the return path is empty.
28
29 4) Check if the envelope from domain name primary MX points 127.0.0.0/8
30
31 5) Add option for using smtp connections to verify addresses from backup
32 mx machines. This allows the backup mx to learn the valid addresses
33 on the primary machine.
34
35 */
36
37
38 // from sendmail sample
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <errno.h>
42 #include <sysexits.h>
43 #include <unistd.h>
44
45 // needed for socket io
46 #include <sys/ioctl.h>
47 #include <net/if.h>
48 #include <arpa/inet.h>
49 #include <netinet/in.h>
50 #include <netinet/tcp.h>
51 #include <netdb.h>
52 #include <sys/socket.h>
53 #include <sys/un.h>
54
55 // needed for thread
56 #include <pthread.h>
57
58 // needed for std c++ collections
59 #include <set>
60 #include <map>
61 #include <list>
62
63 // for the dns resolver
64 #include <netinet/in.h>
65 #include <arpa/nameser.h>
66 #include <resolv.h>
67
68 // misc stuff needed here
69 #include <ctype.h>
70 #include <syslog.h>
71 #include <pwd.h>
72 #include <sys/wait.h> /* header for waitpid() and various macros */
73 #include <signal.h> /* header for signal functions */
74
75 #include "context.h"
76
77 static char* dnsbl_version="$Id:";
78
79 extern "C" {
80 #include "libmilter/mfapi.h"
81 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr);
82 sfsistat mlfi_envfrom(SMFICTX *ctx, char **argv);
83 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **argv);
84 sfsistat mlfi_body(SMFICTX *ctx, u_char *data, size_t len);
85 sfsistat mlfi_eom(SMFICTX *ctx);
86 sfsistat mlfi_abort(SMFICTX *ctx);
87 sfsistat mlfi_close(SMFICTX *ctx);
88 void sig_chld(int signo);
89 }
90
91 struct ns_map {
92 // all the strings are owned by the keys/values in the ns_host string map
93 string_map ns_host; // nameserver name -> host name that uses this name server
94 ns_mapper ns_ip; // nameserver name -> ip address of the name server
95 };
96
97 static bool debug_syslog = false;
98 static bool loader_run = true; // used to stop the config loader thread
99 static CONFIG * config = NULL; // protected by the config_mutex
100 static int generation = 0; // protected by the config_mutex
101
102 static pthread_mutex_t config_mutex;
103 static pthread_mutex_t syslog_mutex;
104 static pthread_mutex_t resolve_mutex;
105 static pthread_mutex_t fd_pool_mutex;
106
107 static std::set<int> fd_pool;
108 static int NULL_SOCKET = -1;
109 static char *resolver_port = NULL; // unix domain socket to talk to the dns resolver process
110 static int resolver_socket = NULL_SOCKET; // socket used to listen for resolver requests
111 static time_t ERROR_SOCKET_TIME = 60; // number of seconds between attempts to open the spam filter socket
112 static time_t last_error_time;
113 static int resolver_sock_count = 0; // protected with fd_pool_mutex
114 static int resolver_pool_size = 0; // protected with fd_pool_mutex
115
116
117 // packed structure to allow a single socket write to dump the
118 // length and the following answer. The packing attribute is gcc specific.
119 struct glommer {
120 int length;
121 #ifdef NS_PACKETSZ
122 u_char answer[NS_PACKETSZ]; // with a resolver, we return resolver answers
123 #else
124 int answer; // without a resolver, we return a single ip4 address, 0 == no answer
125 #endif
126 } __attribute__ ((packed));
127
128 struct mlfiPriv;
129
130
131 ////////////////////////////////////////////////
132 // helper to discard the strings and objects held by an ns_map
133 //
134 static void discard(ns_map &s);
135 static void discard(ns_map &s) {
136 for (string_map::iterator i=s.ns_host.begin(); i!=s.ns_host.end(); i++) {
137 char *x = (*i).first;
138 char *y = (*i).second;
139 free(x);
140 free(y);
141 }
142 s.ns_ip.clear();
143 s.ns_host.clear();
144 }
145
146 ////////////////////////////////////////////////
147 // helper to register a string in an ns_map
148 //
149 static void register_string(ns_map &s, char *name, char *refer);
150 static void register_string(ns_map &s, char *name, char *refer) {
151 string_map::iterator i = s.ns_host.find(name);
152 if (i != s.ns_host.end()) return;
153 char *x = strdup(name);
154 char *y = strdup(refer);
155 s.ns_ip[x] = 0;
156 s.ns_host[x] = y;
157
158 }
159
160 ////////////////////////////////////////////////
161 // syslog a message
162 //
163 static void my_syslog(mlfiPriv *priv, char *text);
164
165
166 // include the content scanner
167 #include "scanner.cpp"
168
169
170 ////////////////////////////////////////////////
171 // disconnect the fd from the dns resolver process
172 //
173 void my_disconnect(int sock, bool decrement = true);
174 void my_disconnect(int sock, bool decrement)
175 {
176 if (sock != NULL_SOCKET) {
177 if (decrement) {
178 pthread_mutex_lock(&fd_pool_mutex);
179 resolver_sock_count--;
180 pthread_mutex_unlock(&fd_pool_mutex);
181 }
182 shutdown(sock, SHUT_RDWR);
183 close(sock);
184 }
185 }
186
187
188 ////////////////////////////////////////////////
189 // return fd connected to the dns resolver process
190 //
191 int my_connect();
192 int my_connect()
193 {
194 // if we have had recent errors, don't even try to open the socket
195 time_t now = time(NULL);
196 if ((now - last_error_time) < ERROR_SOCKET_TIME) return NULL_SOCKET;
197
198 // nothing recent, maybe this time it will work
199 int sock = NULL_SOCKET;
200 sockaddr_un server;
201 memset(&server, '\0', sizeof(server));
202 server.sun_family = AF_UNIX;
203 strncpy(server.sun_path, resolver_port, sizeof(server.sun_path)-1);
204 sock = socket(AF_UNIX, SOCK_STREAM, 0);
205 if (sock != NULL_SOCKET) {
206 bool rc = (connect(sock, (sockaddr *)&server, sizeof(server)) == 0);
207 if (!rc) {
208 my_disconnect(sock, false);
209 sock = NULL_SOCKET;
210 last_error_time = now;
211 }
212 }
213 else last_error_time = now;
214 if (sock != NULL_SOCKET) {
215 pthread_mutex_lock(&fd_pool_mutex);
216 resolver_sock_count++;
217 pthread_mutex_unlock(&fd_pool_mutex);
218 }
219 return sock;
220 }
221
222
223 ////////////////////////////////////////////////
224 // mail filter private data, held for us by sendmail
225 //
226 struct mlfiPriv
227 {
228 // connection specific data
229 CONFIG *pc; // global context with our maps
230 int fd; // to talk to dns resolvers process
231 bool err; // did we get any errors on the resolver socket?
232 int ip; // ip4 address of the smtp client
233 map<DNSBLP, status> checked; // status from those lists
234 // message specific data
235 char *mailaddr; // envelope from value
236 char *queueid; // sendmail queue id
237 bool authenticated; // client authenticated? if so, suppress all dnsbl checks
238 bool have_whites; // have at least one whitelisted recipient? need to accept content and remove all non-whitelisted recipients if it fails
239 bool only_whites; // every recipient is whitelisted?
240 string_set non_whites; // remember the non-whitelisted recipients so we can remove them if need be
241 recorder *memory; // memory for the content scanner
242 url_scanner *scanner; // object to handle body scanning
243 mlfiPriv();
244 ~mlfiPriv();
245 void reset(bool final = false); // for a new message
246 void get_fd();
247 void return_fd();
248 int my_read(char *buf, int len);
249 int my_write(char *buf, int len);
250 };
251
252 mlfiPriv::mlfiPriv() {
253 pthread_mutex_lock(&config_mutex);
254 pc = config;
255 pc->reference_count++;
256 pthread_mutex_unlock(&config_mutex);
257 get_fd();
258 ip = 0;
259 mailaddr = NULL;
260 queueid = NULL;
261 authenticated = false;
262 have_whites = false;
263 only_whites = true;
264 memory = new recorder(this, &pc->html_tags, &pc->tlds);
265 scanner = new url_scanner(memory);
266 }
267
268 mlfiPriv::~mlfiPriv() {
269 return_fd();
270 pthread_mutex_lock(&config_mutex);
271 pc->reference_count--;
272 pthread_mutex_unlock(&config_mutex);
273 reset(true);
274 }
275
276 void mlfiPriv::reset(bool final) {
277 if (mailaddr) free(mailaddr);
278 if (queueid) free(queueid);
279 discard(non_whites);
280 delete memory;
281 delete scanner;
282 if (!final) {
283 mailaddr = NULL;
284 queueid = NULL;
285 authenticated = false;
286 have_whites = false;
287 only_whites = true;
288 memory = new recorder(this, &pc->html_tags, &pc->tlds);
289 scanner = new url_scanner(memory);
290 }
291 }
292
293 void mlfiPriv::get_fd()
294 {
295 err = true;
296 fd = NULL_SOCKET;
297 int result = pthread_mutex_lock(&fd_pool_mutex);
298 if (!result) {
299 std::set<int>::iterator i;
300 i = fd_pool.begin();
301 if (i != fd_pool.end()) {
302 // have at least one fd in the pool
303 err = false;
304 fd = *i;
305 fd_pool.erase(fd);
306 resolver_pool_size--;
307 pthread_mutex_unlock(&fd_pool_mutex);
308 }
309 else {
310 // pool is empty, get a new fd
311 pthread_mutex_unlock(&fd_pool_mutex);
312 fd = my_connect();
313 err = (fd == NULL_SOCKET);
314 }
315 }
316 else {
317 // cannot lock the pool, just get a new fd
318 fd = my_connect();
319 err = (fd == NULL_SOCKET);
320 }
321 }
322
323 void mlfiPriv::return_fd()
324 {
325 if (err) {
326 // this fd got a socket error, so close it, rather than returning it to the pool
327 my_disconnect(fd);
328 }
329 else {
330 int result = pthread_mutex_lock(&fd_pool_mutex);
331 if (!result) {
332 if ((resolver_sock_count > resolver_pool_size*5) || (resolver_pool_size < 5)) {
333 // return the fd to the pool
334 fd_pool.insert(fd);
335 resolver_pool_size++;
336 pthread_mutex_unlock(&fd_pool_mutex);
337 }
338 else {
339 // more than 20% of the open resolver sockets are in the pool, and the
340 // pool as at least 5 sockets. that is enough, so just close this one.
341 pthread_mutex_unlock(&fd_pool_mutex);
342 my_disconnect(fd);
343 }
344 }
345 else {
346 // could not lock the pool, so just close the fd
347 my_disconnect(fd);
348 }
349 }
350 }
351
352 int mlfiPriv::my_write(char *buf, int len)
353 {
354 if (err) return 0;
355 int rs = 0;
356 while (len) {
357 int ws = write(fd, buf, len);
358 if (ws > 0) {
359 rs += ws;
360 len -= ws;
361 buf += ws;
362 }
363 else {
364 // peer closed the socket!
365 rs = 0;
366 err = true;
367 break;
368 }
369 }
370 return rs;
371 }
372
373 int mlfiPriv::my_read(char *buf, int len)
374 {
375 if (err) return 0;
376 int rs = 0;
377 while (len > 1) {
378 int ws = read(fd, buf, len);
379 if (ws > 0) {
380 rs += ws;
381 len -= ws;
382 buf += ws;
383 }
384 else {
385 // peer closed the socket!
386 rs = 0;
387 err = true;
388 break;
389 }
390 }
391 return rs;
392 }
393
394 #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
395
396
397 ////////////////////////////////////////////////
398 // syslog a message
399 //
400 static void my_syslog(mlfiPriv *priv, char *text) {
401 char buf[1000];
402 if (priv) {
403 snprintf(buf, sizeof(buf), "%s: %s", priv->queueid, text);
404 text = buf;
405 }
406 pthread_mutex_lock(&syslog_mutex);
407 openlog("dnsbl", LOG_PID, LOG_MAIL);
408 syslog(LOG_NOTICE, "%s", text);
409 closelog();
410 pthread_mutex_unlock(&syslog_mutex);
411 }
412
413 static void my_syslog(char *text);
414 static void my_syslog(char *text) {
415 my_syslog(NULL, text);
416 }
417
418 ////////////////////////////////////////////////
419 // register a global string
420 //
421 static char* register_string(char *name);
422 static char* register_string(char *name) {
423 return register_string(all_strings, name);
424 }
425
426
427 static char* next_token(char *delim);
428 static char* next_token(char *delim) {
429 char *name = strtok(NULL, delim);
430 if (!name) return name;
431 return register_string(name);
432 }
433
434
435 ////////////////////////////////////////////////
436 // lookup an email address in the env_from or env_to maps
437 //
438 static char* lookup1(char *email, string_map map);
439 static char* lookup1(char *email, string_map map) {
440 string_map::iterator i = map.find(email);
441 if (i != map.end()) return (*i).second;
442 char *x = strchr(email, '@');
443 if (!x) return DEFAULT;
444 x++;
445 i = map.find(x);
446 if (i != map.end()) return (*i).second;
447 return DEFAULT;
448 }
449
450
451 ////////////////////////////////////////////////
452 // lookup an email address in the env_from or env_to maps
453 // this email address is passed in from sendmail, and will
454 // always be enclosed in <>. It may have mixed case, just
455 // as the mail client sent it.
456 //
457 static char* lookup(char* email, string_map map);
458 static char* lookup(char* email, string_map map) {
459 int n = strlen(email)-2;
460 if (n < 1) return DEFAULT; // malformed
461 char *key = strdup(email+1);
462 key[n] = '\0';
463 for (int i=0; i<n; i++) key[i] = tolower(key[i]);
464 char *rc = lookup1(key, map);
465 free(key);
466 return rc;
467 }
468
469
470 ////////////////////////////////////////////////
471 // find the dnsbl with a specific name
472 //
473 static DNSBLP find_dnsbl(CONFIG &dc, char *name);
474 static DNSBLP find_dnsbl(CONFIG &dc, char *name) {
475 dnsblp_map::iterator i = dc.dnsbls.find(name);
476 if (i == dc.dnsbls.end()) return NULL;
477 return (*i).second;
478 }
479
480
481 ////////////////////////////////////////////////
482 // find the dnsbll with a specific name
483 //
484 static DNSBLLP find_dnsbll(CONFIG &dc, char *name);
485 static DNSBLLP find_dnsbll(CONFIG &dc, char *name) {
486 dnsbllp_map::iterator i = dc.dnsblls.find(name);
487 if (i == dc.dnsblls.end()) return NULL;
488 return (*i).second;
489 }
490
491
492 ////////////////////////////////////////////////
493 // find the envfrom map with a specific name
494 //
495 static string_map* find_from_map(CONFIG &dc, char *name);
496 static string_map* find_from_map(CONFIG &dc, char *name) {
497 from_map::iterator i = dc.env_from.find(name);
498 if (i == dc.env_from.end()) return NULL;
499 return (*i).second;
500 }
501
502
503 static string_map& really_find_from_map(CONFIG &dc, char *name);
504 static string_map& really_find_from_map(CONFIG &dc, char *name) {
505 string_map *sm = find_from_map(dc, name);
506 if (!sm) {
507 sm = new string_map;
508 dc.env_from[name] = sm;
509 }
510 return *sm;
511 }
512
513
514
515 ////////////////////////////////////////////////
516 // read a resolver request from the socket, process it, and
517 // write the result back to the socket.
518
519 static void process_resolver_requests(int socket);
520 static void process_resolver_requests(int socket) {
521 #ifdef NS_MAXDNAME
522 char question[NS_MAXDNAME];
523 #else
524 char question[1000];
525 #endif
526 glommer glom;
527
528 int maxq = sizeof(question);
529 while (true) {
530 // read a question
531 int rs = 0;
532 while (true) {
533 int ns = read(socket, question+rs, maxq-rs);
534 if (ns > 0) {
535 rs += ns;
536 if (question[rs-1] == '\0') {
537 // last byte read was the null terminator, we are done
538 break;
539 }
540 }
541 else {
542 // peer closed the socket
543 //my_syslog("!!child worker process, peer closed socket while reading question");
544 shutdown(socket, SHUT_RDWR);
545 close(socket);
546 return;
547 }
548 }
549
550 // find the answer
551 #ifdef NS_PACKETSZ
552 //char text[1000];
553 //snprintf(text, sizeof(text), "!!child worker process has a question %s", question);
554 //my_syslog(text);
555 glom.length = res_search(question, ns_c_in, ns_t_a, glom.answer, sizeof(glom.answer));
556 if (glom.length < 0) glom.length = 0; // represent all errors as zero length answers
557 #else
558 glom.length = sizeof(glom.answer);
559 glom.answer = 0;
560 struct hostent *host = gethostbyname(question);
561 if (host && (host->h_addrtype == AF_INET)) {
562 memcpy(&glom.answer, host->h_addr, sizeof(glom.answer));
563 }
564 #endif
565
566 // write the answer
567 char *buf = (char *)&glom;
568 int len = glom.length + sizeof(glom.length);
569 //snprintf(text, sizeof(text), "!!child worker process writing answer length %d for total %d", glom.length, len);
570 //my_syslog(text);
571 int ws = 0;
572 while (len > ws) {
573 int ns = write(socket, buf+ws, len-ws);
574 if (ns > 0) {
575 ws += ns;
576 }
577 else {
578 // peer closed the socket!
579 //my_syslog("!!child worker process, peer closed socket while writing answer");
580 shutdown(socket, SHUT_RDWR);
581 close(socket);
582 return;
583 }
584 }
585 }
586 }
587
588
589 ////////////////////////////////////////////////
590 // ask a dns question and get an A record answer - we don't try
591 // very hard, just using the default resolver retry settings.
592 // If we cannot get an answer, we just accept the mail.
593 //
594 //
595 static int dns_interface(mlfiPriv &priv, char *question, bool maybe_ip, ns_map *nameservers);
596 static int dns_interface(mlfiPriv &priv, char *question, bool maybe_ip, ns_map *nameservers) {
597 // this part can be done without locking the resolver mutex. Each
598 // milter thread is talking over its own socket to a separate resolver
599 // process, which does the actual dns resolution.
600 if (priv.err) return 0; // cannot ask more questions on this socket.
601 priv.my_write(question, strlen(question)+1); // write the question including the null terminator
602 glommer glom;
603 char *buf = (char *)&glom;
604 priv.my_read(buf, sizeof(glom.length));
605 buf += sizeof(glom.length);
606 ///char text[1000];
607 ///snprintf(text, sizeof(text), "!!milter thread wrote question %s and has answer length %d", question, glom.length);
608 ///my_syslog(text);
609 if ((glom.length < 0) || (glom.length > sizeof(glom.answer))) {
610 priv.err = true;
611 return 0; // cannot process overlarge answers
612 }
613 priv.my_read(buf, glom.length);
614
615 #ifdef NS_PACKETSZ
616 // now we need to lock the resolver mutex to keep the milter threads from
617 // stepping on each other while parsing the dns answer.
618 int ret_address = 0;
619 pthread_mutex_lock(&resolve_mutex);
620 if (glom.length > 0) {
621 // parse the answer
622 ns_msg handle;
623 ns_rr rr;
624 if (ns_initparse(glom.answer, glom.length, &handle) == 0) {
625 // look for ns names
626 if (nameservers) {
627 ns_map &ns = *nameservers;
628 int rrnum = 0;
629 while (ns_parserr(&handle, ns_s_ns, rrnum++, &rr) == 0) {
630 if (ns_rr_type(rr) == ns_t_ns) {
631 char nam[NS_MAXDNAME+1];
632 char *n = nam;
633 const u_char *p = ns_rr_rdata(rr);
634 while (((n-nam) < NS_MAXDNAME) && ((p-glom.answer) < glom.length) && *p) {
635 size_t s = *(p++);
636 if (s > 191) {
637 // compression pointer
638 s = (s-192)*256 + *(p++);
639 if (s >= glom.length) break; // pointer outside bounds of answer
640 p = glom.answer + s;
641 s = *(p++);
642 }
643 if (s > 0) {
644 if ((n-nam) >= (NS_MAXDNAME-s)) break; // destination would overflow name buffer
645 if ((p-glom.answer) >= (glom.length-s)) break; // source outside bounds of answer
646 memcpy(n, p, s);
647 n += s;
648 p += s;
649 *(n++) = '.';
650 }
651 }
652 if (n-nam) n--; // remove trailing .
653 *n = '\0'; // null terminate it
654 register_string(ns, nam, question); // ns host to lookup later
655 }
656 }
657 rrnum = 0;
658 while (ns_parserr(&handle, ns_s_ar, rrnum++, &rr) == 0) {
659 if (ns_rr_type(rr) == ns_t_a) {
660 char* nam = (char*)ns_rr_name(rr);
661 ns_mapper::iterator i = ns.ns_ip.find(nam);
662 if (i != ns.ns_ip.end()) {
663 // we want this ip address
664 int address;
665 memcpy(&address, ns_rr_rdata(rr), sizeof(address));
666 ns.ns_ip[nam] = address;
667 }
668 }
669 }
670 }
671 int rrnum = 0;
672 while (ns_parserr(&handle, ns_s_an, rrnum++, &rr) == 0) {
673 if (ns_rr_type(rr) == ns_t_a) {
674 int address;
675 memcpy(&address, ns_rr_rdata(rr), sizeof(address));
676 ret_address = address;
677 }
678 }
679 }
680 }
681 if (maybe_ip && !ret_address) {
682 // might be a bare ip address
683 in_addr ip;
684 if (inet_aton(question, &ip)) {
685 ret_address = ip.s_addr;
686 }
687 }
688 pthread_mutex_unlock(&resolve_mutex);
689 return ret_address;
690 #else
691 return glom.answer;
692 #endif
693 }
694
695
696 ////////////////////////////////////////////////
697 // check a single dnsbl
698 //
699 static status check_single(mlfiPriv &priv, int ip, char *suffix);
700 static status check_single(mlfiPriv &priv, int ip, char *suffix) {
701 // make a dns question
702 const u_char *src = (const u_char *)&ip;
703 if (src[0] == 127) return oksofar; // don't do dns lookups on localhost
704 #ifdef NS_MAXDNAME
705 char question[NS_MAXDNAME];
706 #else
707 char question[1000];
708 #endif
709 snprintf(question, sizeof(question), "%u.%u.%u.%u.%s.", src[3], src[2], src[1], src[0], suffix);
710 // ask the question, if we get an A record it implies a blacklisted ip address
711 return (dns_interface(priv, question, false, NULL)) ? reject : oksofar;
712 }
713
714
715 ////////////////////////////////////////////////
716 // check a single dnsbl
717 //
718 static status check_single(mlfiPriv &priv, int ip, DNSBL &bl);
719 static status check_single(mlfiPriv &priv, int ip, DNSBL &bl) {
720 return check_single(priv, ip, bl.suffix);
721 }
722
723
724 ////////////////////////////////////////////////
725 // check the dnsbls specified for this recipient
726 //
727 static status check_dnsbl(mlfiPriv &priv, DNSBLLP dnsbllp, DNSBLP &rejectlist);
728 static status check_dnsbl(mlfiPriv &priv, DNSBLLP dnsbllp, DNSBLP &rejectlist) {
729 if (priv.authenticated) return oksofar;
730 if (!dnsbllp) return oksofar;
731 DNSBLL &dnsbll = *dnsbllp;
732 for (DNSBLL::iterator i=dnsbll.begin(); i!=dnsbll.end(); i++) {
733 DNSBLP dp = *i; // non null by construction
734 status st;
735 map<DNSBLP, status>::iterator f = priv.checked.find(dp);
736 if (f == priv.checked.end()) {
737 // have not checked this list yet
738 st = check_single(priv, priv.ip, *dp);
739 rejectlist = dp;
740 priv.checked[dp] = st;
741 }
742 else {
743 st = (*f).second;
744 rejectlist = (*f).first;
745 }
746 if (st == reject) return st;
747 }
748 return oksofar;
749 }
750
751
752 ////////////////////////////////////////////////
753 // check the hosts from the body against the content dnsbl
754 //
755 static status check_hosts(mlfiPriv &priv, char *&host, int &ip);
756 static status check_hosts(mlfiPriv &priv, char *&host, int &ip) {
757 CONFIG &dc = *priv.pc;
758 int count = 0;
759 ns_map nameservers;
760 bool ran = priv.pc->host_random;
761 int lim = priv.pc->host_limit; // we should not look at more than this many hosts
762 int cnt = priv.memory->hosts.size(); // number of hosts we could look at
763 int_set ips; // remove duplicate ip addresses
764 for (string_set::iterator i=priv.memory->hosts.begin(); i!=priv.memory->hosts.end(); i++) {
765 host = *i; // a reference into priv.memory->hosts, which will live until this smtp transaction is closed
766
767 // don't bother looking up hosts on the ignore list
768 string_set::iterator j = priv.pc->content_host_ignore.find(host);
769 if (j != priv.pc->content_host_ignore.end()) continue;
770
771 // try to only look at lim/cnt fraction of the available cnt host names in random mode
772 if ((cnt > lim) && (lim > 0) && ran) {
773 int r = rand() % cnt;
774 if (r >= lim) {
775 char buf[1000];
776 snprintf(buf, sizeof(buf), "host %s skipped", host);
777 my_syslog(&priv, buf);
778 continue;
779 }
780 }
781 count++;
782 if ((count > lim) && (lim > 0) && (!ran)) {
783 discard(nameservers);
784 return reject_host;
785 }
786 ip = dns_interface(priv, host, true, &nameservers);
787 if (debug_syslog) {
788 char buf[1000];
789 if (ip) {
790 char adr[sizeof "255.255.255.255"];
791 adr[0] = '\0';
792 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr));
793 snprintf(buf, sizeof(buf), "host %s found at %s", host, adr);
794 }
795 else {
796 snprintf(buf, sizeof(buf), "host %s not found", host);
797 }
798 my_syslog(&priv, buf);
799 }
800 if (ip) {
801 int_set::iterator i = ips.find(ip);
802 if (i == ips.end()) {
803 ips.insert(ip);
804 status st = check_single(priv, ip, dc.content_suffix);
805 if (st == reject) {
806 discard(nameservers);
807 return st;
808 }
809 }
810 }
811 }
812 lim *= 4; // allow average of 3 ns per host name
813 for (ns_mapper::iterator i=nameservers.ns_ip.begin(); i!=nameservers.ns_ip.end(); i++) {
814 count++;
815 if ((count > lim) && (lim > 0)) {
816 if (ran) continue; // don't complain
817 discard(nameservers);
818 return reject_host;
819 }
820 host = (*i).first; // a transient reference that needs to be replaced before we return it
821 ip = (*i).second;
822 if (!ip) ip = dns_interface(priv, host, false, NULL);
823 if (debug_syslog) {
824 char buf[200];
825 if (ip) {
826 char adr[sizeof "255.255.255.255"];
827 adr[0] = '\0';
828 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr));
829 snprintf(buf, sizeof(buf), "ns %s found at %s", host, adr);
830 }
831 else {
832 snprintf(buf, sizeof(buf), "ns %s not found", host);
833 }
834 my_syslog(&priv, buf);
835 }
836 if (ip) {
837 int_set::iterator i = ips.find(ip);
838 if (i == ips.end()) {
839 ips.insert(ip);
840 status st = check_single(priv, ip, dc.content_suffix);
841 if (st == reject) {
842 string_map::iterator j = nameservers.ns_host.find(host);
843 if (j != nameservers.ns_host.end()) {
844 char *refer = (*j).second;
845 char buf[1000];
846 snprintf(buf, sizeof(buf), "%s with nameserver %s", refer, host);
847 host = register_string(priv.memory->hosts, buf); // put a copy into priv.memory->hosts, and return that reference
848 }
849 else {
850 host = register_string(priv.memory->hosts, host); // put a copy into priv.memory->hosts, and return that reference
851 }
852 discard(nameservers);
853 return st;
854 }
855 }
856 }
857 }
858 discard(nameservers);
859 host = NULL;
860 int bin = priv.memory->binary_tags;
861 int bad = priv.memory->bad_html_tags;
862 lim = priv.pc->tag_limit;
863 if (3*bin > bad) return oksofar; // probably .zip or .tar.gz with random content
864 if ((bad > lim) && (lim > 0)) return reject_tag;
865 return oksofar;
866 }
867
868
869 ////////////////////////////////////////////////
870 // start of sendmail milter interfaces
871 //
872 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
873 {
874 // allocate some private memory
875 mlfiPriv *priv = new mlfiPriv;
876 if (hostaddr->sa_family == AF_INET) {
877 priv->ip = ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr;
878 }
879
880 // save the private data
881 smfi_setpriv(ctx, (void*)priv);
882
883 // continue processing
884 return SMFIS_CONTINUE;
885 }
886
887 sfsistat mlfi_envfrom(SMFICTX *ctx, char **from)
888 {
889 mlfiPriv &priv = *MLFIPRIV;
890 priv.mailaddr = strdup(from[0]);
891 priv.authenticated = (smfi_getsymval(ctx, "{auth_authen}") != NULL);
892 return SMFIS_CONTINUE;
893 }
894
895 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **rcpt)
896 {
897 DNSBLP rejectlist = NULL; // list that caused the reject
898 status st = oksofar;
899 mlfiPriv &priv = *MLFIPRIV;
900 CONFIG &dc = *priv.pc;
901 if (!priv.queueid) priv.queueid = strdup(smfi_getsymval(ctx, "i"));
902 char *rcptaddr = rcpt[0];
903 char *dnsname = lookup(rcptaddr, dc.env_to_dnsbll);
904 char *fromname = lookup(rcptaddr, dc.env_to_chkfrom);
905 if ((strcmp(dnsname, BLACK) == 0) ||
906 (strcmp(fromname, BLACK) == 0)) {
907 st = black; // two options to blacklist this recipient
908 }
909 else if (strcmp(fromname, WHITE) == 0) {
910 st = white;
911 }
912 else {
913 // check an env_from map
914 string_map *sm = find_from_map(dc, fromname);
915 if (sm != NULL) {
916 fromname = lookup(priv.mailaddr, *sm); // returns default if name not in map
917 if (strcmp(fromname, BLACK) == 0) {
918 st = black; // blacklist this envelope from value
919 }
920 if (strcmp(fromname, WHITE) == 0) {
921 st = white; // blacklist this envelope from value
922 }
923 }
924 }
925 if ((st == oksofar) && (strcmp(dnsname, WHITE) != 0)) {
926 // check dns lists
927 st = check_dnsbl(priv, find_dnsbll(dc, dnsname), rejectlist);
928 }
929
930 if (st == reject) {
931 // reject the recipient based on some dnsbl
932 char adr[sizeof "255.255.255.255"];
933 adr[0] = '\0';
934 inet_ntop(AF_INET, (const u_char *)&priv.ip, adr, sizeof(adr));
935 char buf[2000];
936 snprintf(buf, sizeof(buf), rejectlist->message, adr, adr);
937 smfi_setreply(ctx, "550", "5.7.1", buf);
938 return SMFIS_REJECT;
939 }
940 else if (st == black) {
941 // reject the recipient based on blacklisting either from or to
942 smfi_setreply(ctx, "550", "5.7.1", "no such user");
943 return SMFIS_REJECT;
944 }
945 else {
946 // accept the recipient
947 if (st == oksofar) {
948 // but remember the non-whites
949 register_string(priv.non_whites, rcptaddr);
950 priv.only_whites = false;
951 }
952 if (st == white) {
953 priv.have_whites = true;
954 }
955 return SMFIS_CONTINUE;
956 }
957 }
958
959 sfsistat mlfi_body(SMFICTX *ctx, u_char *data, size_t len)
960 {
961 mlfiPriv &priv = *MLFIPRIV;
962 if (priv.authenticated) return SMFIS_CONTINUE;
963 if (priv.only_whites) return SMFIS_CONTINUE;
964 if (!priv.pc->content_suffix) return SMFIS_CONTINUE;
965 priv.scanner->scan(data, len);
966 return SMFIS_CONTINUE;
967 }
968
969 sfsistat mlfi_eom(SMFICTX *ctx)
970 {
971 sfsistat rc;
972 mlfiPriv &priv = *MLFIPRIV;
973 char *host = NULL;
974 int ip;
975 status st;
976 // process end of message
977 if (priv.authenticated ||
978 priv.only_whites ||
979 (!priv.pc->content_suffix) ||
980 ((st=check_hosts(priv, host, ip)) == oksofar)) rc = SMFIS_CONTINUE;
981 else {
982 if (!priv.have_whites) {
983 // can reject the entire message
984 char buf[2000];
985 if (st == reject_tag) {
986 // rejected due to excessive bad html tags
987 snprintf(buf, sizeof(buf), priv.pc->tag_limit_message);
988 }
989 else if (st == reject_host) {
990 // rejected due to excessive unique host/urls
991 snprintf(buf, sizeof(buf), priv.pc->host_limit_message);
992 }
993 else {
994 char adr[sizeof "255.255.255.255"];
995 adr[0] = '\0';
996 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr));
997 snprintf(buf, sizeof(buf), priv.pc->content_message, host, adr);
998 }
999 smfi_setreply(ctx, "550", "5.7.1", buf);
1000 rc = SMFIS_REJECT;
1001 }
1002 else {
1003 // need to accept it but remove the recipients that don't want it
1004 for (string_set::iterator i=priv.non_whites.begin(); i!=priv.non_whites.end(); i++) {
1005 char *rcpt = *i;
1006 smfi_delrcpt(ctx, rcpt);
1007 }
1008 rc = SMFIS_CONTINUE;
1009 }
1010 }
1011 // reset for a new message on the same connection
1012 mlfi_abort(ctx);
1013 return rc;
1014 }
1015
1016 sfsistat mlfi_abort(SMFICTX *ctx)
1017 {
1018 mlfiPriv &priv = *MLFIPRIV;
1019 priv.reset();
1020 return SMFIS_CONTINUE;
1021 }
1022
1023 sfsistat mlfi_close(SMFICTX *ctx)
1024 {
1025 mlfiPriv *priv = MLFIPRIV;
1026 if (!priv) return SMFIS_CONTINUE;
1027 delete priv;
1028 smfi_setpriv(ctx, NULL);
1029 return SMFIS_CONTINUE;
1030 }
1031
1032 struct smfiDesc smfilter =
1033 {
1034 "DNSBL", // filter name
1035 SMFI_VERSION, // version code -- do not change
1036 SMFIF_DELRCPT, // flags
1037 mlfi_connect, // connection info filter
1038 NULL, // SMTP HELO command filter
1039 mlfi_envfrom, // envelope sender filter
1040 mlfi_envrcpt, // envelope recipient filter
1041 NULL, // header filter
1042 NULL, // end of header
1043 mlfi_body, // body block filter
1044 mlfi_eom, // end of message
1045 mlfi_abort, // message aborted
1046 mlfi_close, // connection cleanup
1047 };
1048
1049
1050 ////////////////////////////////////////////////
1051 // reload the config
1052 //
1053 static CONFIG* new_conf();
1054 static CONFIG* new_conf() {
1055 CONFIG *newc = new CONFIG;
1056 pthread_mutex_lock(&config_mutex);
1057 newc->generation = generation++;
1058 pthread_mutex_unlock(&config_mutex);
1059 char buf[200];
1060 snprintf(buf, sizeof(buf), "loading configuration generation %d", newc->generation);
1061 my_syslog(buf);
1062 if (load_conf(*newc, "dnsbl.conf") {
1063 newc->load_time = time(NULL);
1064 return newc;
1065 }
1066 delete newc;
1067 return NULL;
1068 }
1069
1070
1071 ////////////////////////////////////////////////
1072 // thread to watch the old config files for changes
1073 // and reload when needed. we also cleanup old
1074 // configs whose reference count has gone to zero.
1075 //
1076 static void* config_loader(void *arg);
1077 static void* config_loader(void *arg) {
1078 typedef set<CONFIG *> configp_set;
1079 configp_set old_configs;
1080 while (loader_run) {
1081 sleep(180); // look for modifications every 3 minutes
1082 if (!loader_run) break;
1083 CONFIG &dc = *config;
1084 time_t then = dc.load_time;
1085 struct stat st;
1086 bool reload = false;
1087 for (string_list::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) {
1088 char *fn = *i;
1089 if (stat(fn, &st)) reload = true; // file disappeared
1090 else if (st.st_mtime > then) reload = true; // file modified
1091 if (reload) break;
1092 }
1093 if (reload) {
1094 CONFIG *newc = new_conf();
1095 // replace the global config pointer
1096 pthread_mutex_lock(&config_mutex);
1097 CONFIG *old = config;
1098 config = newc;
1099 pthread_mutex_unlock(&config_mutex);
1100 if (old) old_configs.insert(old);
1101 }
1102 // now look for old configs with zero ref counts
1103 for (configp_set::iterator i=old_configs.begin(); i!=old_configs.end(); ) {
1104 CONFIG *old = *i;
1105 if (!old->reference_count) {
1106 char buf[200];
1107 snprintf(buf, sizeof(buf), "freeing memory for old configuration generation %d", old->generation);
1108 my_syslog(buf);
1109 delete old; // destructor does all the work
1110 old_configs.erase(i++);
1111 }
1112 else i++;
1113 }
1114 }
1115 return NULL;
1116 }
1117
1118
1119 static void usage(char *prog);
1120 static void usage(char *prog)
1121 {
1122 fprintf(stderr, "Usage: %s [-d] [-c] -r port -p sm-sock-addr [-t timeout]\n", prog);
1123 fprintf(stderr, "where port is for the connection to our own dns resolver processes\n");
1124 fprintf(stderr, " and should be local-domain-socket-file-name\n");
1125 fprintf(stderr, "where sm-sock-addr is for the connection to sendmail\n");
1126 fprintf(stderr, " and should be one of\n");
1127 fprintf(stderr, " inet:port@ip-address\n");
1128 fprintf(stderr, " local:local-domain-socket-file-name\n");
1129 fprintf(stderr, "-c will load and dump the config to stdout\n");
1130 fprintf(stderr, "-d will add some syslog debug messages\n");
1131 }
1132
1133
1134
1135 static void setup_socket(char *sock);
1136 static void setup_socket(char *sock) {
1137 unlink(sock);
1138 // sockaddr_un addr;
1139 // memset(&addr, '\0', sizeof addr);
1140 // addr.sun_family = AF_UNIX;
1141 // strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1);
1142 // int s = socket(AF_UNIX, SOCK_STREAM, 0);
1143 // bind(s, (sockaddr*)&addr, sizeof(addr));
1144 // close(s);
1145 }
1146
1147
1148 /*
1149 * The signal handler function -- only gets called when a SIGCHLD
1150 * is received, ie when a child terminates
1151 */
1152 void sig_chld(int signo)
1153 {
1154 int status;
1155 /* Wait for any child without blocking */
1156 while (waitpid(-1, &status, WNOHANG) > 0) {
1157 // ignore child exit status, we only do this to cleanup zombies
1158 }
1159 }
1160
1161
1162 int main(int argc, char**argv)
1163 {
1164 token_init();
1165 bool check = false;
1166 bool setconn = false;
1167 bool setreso = false;
1168 int c;
1169 const char *args = "r:p:t:hcd";
1170 extern char *optarg;
1171
1172 // Process command line options
1173 while ((c = getopt(argc, argv, args)) != -1) {
1174 switch (c) {
1175 case 'r':
1176 if (optarg == NULL || *optarg == '\0') {
1177 fprintf(stderr, "Illegal resolver socket: %s\n", optarg);
1178 exit(EX_USAGE);
1179 }
1180 resolver_port = strdup(optarg);
1181 setup_socket(resolver_port);
1182 setreso = true;
1183 break;
1184
1185 case 'p':
1186 if (optarg == NULL || *optarg == '\0') {
1187 fprintf(stderr, "Illegal sendmail socket: %s\n", optarg);
1188 exit(EX_USAGE);
1189 }
1190 if (smfi_setconn(optarg) == MI_FAILURE) {
1191 fprintf(stderr, "smfi_setconn failed\n");
1192 exit(EX_SOFTWARE);
1193 }
1194 if (strncasecmp(optarg, "unix:", 5) == 0) setup_socket(optarg + 5);
1195 else if (strncasecmp(optarg, "local:", 6) == 0) setup_socket(optarg + 6);
1196 setconn = true;
1197 break;
1198
1199 case 't':
1200 if (optarg == NULL || *optarg == '\0') {
1201 fprintf(stderr, "Illegal timeout: %s\n", optarg);
1202 exit(EX_USAGE);
1203 }
1204 if (smfi_settimeout(atoi(optarg)) == MI_FAILURE) {
1205 fprintf(stderr, "smfi_settimeout failed\n");
1206 exit(EX_SOFTWARE);
1207 }
1208 break;
1209
1210 case 'c':
1211 check = true;
1212 break;
1213
1214 case 'd':
1215 debug_syslog = true;
1216 break;
1217
1218 case 'h':
1219 default:
1220 usage(argv[0]);
1221 exit(EX_USAGE);
1222 }
1223 }
1224
1225 if (check) {
1226 CONFIG *conf = new_conf();
1227 if (conf) {
1228 conf->dump();
1229 delete conf;
1230 return 0;
1231 }
1232 else {
1233 return 1; // config failed to load
1234 }
1235 }
1236
1237 if (!setconn) {
1238 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
1239 usage(argv[0]);
1240 exit(EX_USAGE);
1241 }
1242
1243 if (!setreso) {
1244 fprintf(stderr, "%s: Missing required -r argument\n", argv[0]);
1245 usage(argv[0]);
1246 exit(EX_USAGE);
1247 }
1248
1249 if (smfi_register(smfilter) == MI_FAILURE) {
1250 fprintf(stderr, "smfi_register failed\n");
1251 exit(EX_UNAVAILABLE);
1252 }
1253
1254 // switch to background mode
1255 if (daemon(1,0) < 0) {
1256 fprintf(stderr, "daemon() call failed\n");
1257 exit(EX_UNAVAILABLE);
1258 }
1259
1260 // write the pid
1261 const char *pidpath = "/var/run/dnsbl.pid";
1262 unlink(pidpath);
1263 FILE *f = fopen(pidpath, "w");
1264 if (f) {
1265 #ifdef linux
1266 // from a comment in the DCC source code:
1267 // Linux threads are broken. Signals given the
1268 // original process are delivered to only the
1269 // thread that happens to have that PID. The
1270 // sendmail libmilter thread that needs to hear
1271 // SIGINT and other signals does not, and that breaks
1272 // scripts that need to stop milters.
1273 // However, signaling the process group works.
1274 fprintf(f, "-%d\n", (u_int)getpgrp());
1275 #else
1276 fprintf(f, "%d\n", (u_int)getpid());
1277 #endif
1278 fclose(f);
1279 }
1280
1281 // initialize the thread sync objects
1282 pthread_mutex_init(&config_mutex, 0);
1283 pthread_mutex_init(&syslog_mutex, 0);
1284 pthread_mutex_init(&resolve_mutex, 0);
1285 pthread_mutex_init(&fd_pool_mutex, 0);
1286
1287 // drop root privs
1288 struct passwd *pw = getpwnam("dnsbl");
1289 if (pw) {
1290 if (setgid(pw->pw_gid) == -1) {
1291 my_syslog("failed to switch to group dnsbl");
1292 }
1293 if (setuid(pw->pw_uid) == -1) {
1294 my_syslog("failed to switch to user dnsbl");
1295 }
1296 }
1297
1298 // fork off the resolver listener process
1299 pid_t child = fork();
1300 if (child < 0) {
1301 my_syslog("failed to create resolver listener process");
1302 exit(0);
1303 }
1304 if (child == 0) {
1305 // we are the child - dns resolver listener process
1306 resolver_socket = socket(AF_UNIX, SOCK_STREAM, 0);
1307 if (resolver_socket < 0) {
1308 my_syslog("child failed to create resolver socket");
1309 exit(0); // failed
1310 }
1311 sockaddr_un server;
1312 memset(&server, '\0', sizeof(server));
1313 server.sun_family = AF_UNIX;
1314 strncpy(server.sun_path, resolver_port, sizeof(server.sun_path)-1);
1315 //try to bind the address to the socket.
1316 if (bind(resolver_socket, (sockaddr *)&server, sizeof(server)) < 0) {
1317 // bind failed
1318 shutdown(resolver_socket, SHUT_RDWR);
1319 close(resolver_socket);
1320 my_syslog("child failed to bind resolver socket");
1321 exit(0); // failed
1322 }
1323 //listen on the socket.
1324 if (listen(resolver_socket, 10) < 0) {
1325 // listen failed
1326 shutdown(resolver_socket, SHUT_RDWR);
1327 close(resolver_socket);
1328 my_syslog("child failed to listen to resolver socket");
1329 exit(0); // failed
1330 }
1331 // setup sigchld handler to prevent zombies
1332 struct sigaction act;
1333 act.sa_handler = sig_chld; // Assign sig_chld as our SIGCHLD handler
1334 sigemptyset(&act.sa_mask); // We don't want to block any other signals in this example
1335 act.sa_flags = SA_NOCLDSTOP; // only want children that have terminated
1336 if (sigaction(SIGCHLD, &act, NULL) < 0) {
1337 my_syslog("child failed to setup SIGCHLD handler");
1338 exit(0); // failed
1339 }
1340 while (true) {
1341 sockaddr_un client;
1342 socklen_t clientlen = sizeof(client);
1343 int s = accept(resolver_socket, (sockaddr *)&client, &clientlen);
1344 if (s > 0) {
1345 // accept worked, it did not get cancelled before we could accept it
1346 // fork off a process to handle this connection
1347 int newchild = fork();
1348 if (newchild == 0) {
1349 // this is the worker process
1350 // child does not need the listening socket
1351 close(resolver_socket);
1352 //my_syslog("child forked a worker process");
1353 process_resolver_requests(s);
1354 //my_syslog("child terminated a worker process");
1355 exit(0);
1356 }
1357 else {
1358 // this is the parent
1359 // parent does not need the accepted socket
1360 close(s);
1361 }
1362 }
1363 }
1364 exit(0); // make sure we don't fall thru.
1365 }
1366 else {
1367 sleep(2); // allow child to get started
1368 }
1369
1370 // load the initial config
1371 config = new_conf();
1372
1373 // only create threads after the fork() in daemon
1374 pthread_t tid;
1375 if (pthread_create(&tid, 0, config_loader, 0))
1376 my_syslog("failed to create config loader thread");
1377 if (pthread_detach(tid))
1378 my_syslog("failed to detach config loader thread");
1379
1380 time_t starting = time(NULL);
1381 int rc = smfi_main();
1382 if ((rc != MI_SUCCESS) && (time(NULL) > starting+5*60)) {
1383 my_syslog("trying to restart after smfi_main()");
1384 loader_run = false; // eventually the config loader thread will terminate
1385 execvp(argv[0], argv);
1386 }
1387 exit((rc == MI_SUCCESS) ? 0 : EX_UNAVAILABLE);
1388 }
1389