Mercurial > dnsbl
comparison src/dnsbl.cpp @ 59:510a511ad554
Add resolver processes to allow better performance on busy machines
author | carl |
---|---|
date | Mon, 03 Jan 2005 18:35:50 -0800 |
parents | 7bb8bbf79285 |
children | 390ed250c5d2 |
comparison
equal
deleted
inserted
replaced
58:7bb8bbf79285 | 59:510a511ad554 |
---|---|
1 /* | 1 /* |
2 | 2 |
3 Copyright (c) 2004 Carl Byington - 510 Software Group, released under | 3 Copyright (c) 2004, 2005 Carl Byington - 510 Software Group, released |
4 the GPL version 2 or any later version at your choice available at | 4 under the GPL version 2 or any later version at your choice available at |
5 http://www.fsf.org/licenses/gpl.txt | 5 http://www.fsf.org/licenses/gpl.txt |
6 | 6 |
7 Based on a sample milter Copyright (c) 2000-2003 Sendmail, Inc. and its | 7 Based on a sample milter Copyright (c) 2000-2003 Sendmail, Inc. and its |
8 suppliers. Inspired by the DCC by Rhyolite Software | 8 suppliers. Inspired by the DCC by Rhyolite Software |
9 | 9 |
10 -r port The port used to talk to our internal dns resolver processes | |
10 -p port The port through which the MTA will connect to this milter. | 11 -p port The port through which the MTA will connect to this milter. |
11 -t sec The timeout value. | 12 -t sec The timeout value. |
12 -c Check the config, and print a copy to stdout. Don't start the | 13 -c Check the config, and print a copy to stdout. Don't start the |
13 milter or do anything with the socket. | 14 milter or do anything with the socket. |
14 -d Add debug syslog entries | 15 -d Add debug syslog entries |
24 data is rejected if sent. | 25 data is rejected if sent. |
25 | 26 |
26 3) Add option to only allow one recipient if the return path is empty. | 27 3) Add option to only allow one recipient if the return path is empty. |
27 | 28 |
28 4) Check if the envelope from domain name primary MX points 127.0.0.0/8 | 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. | |
29 | 34 |
30 */ | 35 */ |
31 | 36 |
32 | 37 |
33 // from sendmail sample | 38 // from sendmail sample |
190 static int generation = 0; // protected by the config_mutex | 195 static int generation = 0; // protected by the config_mutex |
191 | 196 |
192 static pthread_mutex_t config_mutex; | 197 static pthread_mutex_t config_mutex; |
193 static pthread_mutex_t syslog_mutex; | 198 static pthread_mutex_t syslog_mutex; |
194 static pthread_mutex_t resolve_mutex; | 199 static pthread_mutex_t resolve_mutex; |
200 static pthread_mutex_t fd_pool_mutex; | |
201 static std::set<int> fd_pool; | |
202 | |
203 static int NULL_SOCKET = -1; | |
204 static int resolver_port = 0; // global port number to talk to the dns resolver process | |
205 static int resolver_socket = NULL_SOCKET; // socket used to listen for resolver requests | |
206 static time_t ERROR_SOCKET_TIME = 60; // number of seconds between attempts to open the spam filter socket | |
207 static time_t last_error_time; | |
208 | |
209 #ifdef NS_PACKETSZ | |
210 // packed structure to allow a single socket write to dump the | |
211 // length and the following answer. The packing attribute is gcc specific. | |
212 struct glommer { | |
213 int length; | |
214 u_char answer[NS_PACKETSZ]; | |
215 } __attribute__ ((packed)); | |
216 #endif | |
195 | 217 |
196 struct mlfiPriv; | 218 struct mlfiPriv; |
197 | 219 |
198 | 220 |
199 //////////////////////////////////////////////// | 221 //////////////////////////////////////////////// |
257 // include the content scanner | 279 // include the content scanner |
258 #include "scanner.cpp" | 280 #include "scanner.cpp" |
259 | 281 |
260 | 282 |
261 //////////////////////////////////////////////// | 283 //////////////////////////////////////////////// |
284 // disconnect the fd from the dns resolver process | |
285 // | |
286 void my_disconnect(int sock); | |
287 void my_disconnect(int sock) | |
288 { | |
289 if (sock != NULL_SOCKET) { | |
290 shutdown(sock, SHUT_RDWR); | |
291 close(sock); | |
292 } | |
293 } | |
294 | |
295 | |
296 //////////////////////////////////////////////// | |
297 // return fd connected to the dns resolver process | |
298 // | |
299 int my_connect(); | |
300 int my_connect() | |
301 { | |
302 // if we have had recent errors, don't even try to open the socket | |
303 time_t now = time(NULL); | |
304 if ((now - last_error_time) < ERROR_SOCKET_TIME) return NULL_SOCKET; | |
305 | |
306 // nothing recent, maybe this time it will work | |
307 int sock = NULL_SOCKET; | |
308 hostent *host = gethostbyname("localhost"); | |
309 if (host) { | |
310 sockaddr_in server; | |
311 server.sin_family = host->h_addrtype; | |
312 server.sin_port = htons(resolver_port); | |
313 memcpy(&server.sin_addr, host->h_addr_list[0], host->h_length); | |
314 sock = socket(PF_INET, SOCK_STREAM, 0); | |
315 if (sock != NULL_SOCKET) { | |
316 bool rc = (connect(sock, (sockaddr *)&server, sizeof(server)) == 0); | |
317 if (!rc) { | |
318 int er = errno; | |
319 my_disconnect(sock); | |
320 sock = NULL_SOCKET; | |
321 last_error_time = now; | |
322 } | |
323 } | |
324 else last_error_time = now; | |
325 } | |
326 return sock; | |
327 } | |
328 | |
329 | |
330 //////////////////////////////////////////////// | |
262 // mail filter private data, held for us by sendmail | 331 // mail filter private data, held for us by sendmail |
263 // | 332 // |
264 struct mlfiPriv | 333 struct mlfiPriv |
265 { | 334 { |
266 // connection specific data | 335 // connection specific data |
267 CONFIG *pc; // global context with our maps | 336 CONFIG *pc; // global context with our maps |
337 int fd; // to talk to dns resolvers process | |
338 bool err; // did we get any errors on the resolver socket? | |
268 int ip; // ip4 address of the smtp client | 339 int ip; // ip4 address of the smtp client |
269 map<DNSBLP, status> checked; // status from those lists | 340 map<DNSBLP, status> checked; // status from those lists |
270 // message specific data | 341 // message specific data |
271 char *mailaddr; // envelope from value | 342 char *mailaddr; // envelope from value |
272 char *queueid; // sendmail queue id | 343 char *queueid; // sendmail queue id |
277 recorder *memory; // memory for the content scanner | 348 recorder *memory; // memory for the content scanner |
278 url_scanner *scanner; // object to handle body scanning | 349 url_scanner *scanner; // object to handle body scanning |
279 mlfiPriv(); | 350 mlfiPriv(); |
280 ~mlfiPriv(); | 351 ~mlfiPriv(); |
281 void reset(bool final = false); // for a new message | 352 void reset(bool final = false); // for a new message |
353 void get_fd(); | |
354 void return_fd(); | |
355 int my_read(char *buf, int len); | |
356 int my_write(char *buf, int len); | |
282 }; | 357 }; |
358 | |
283 mlfiPriv::mlfiPriv() { | 359 mlfiPriv::mlfiPriv() { |
284 pthread_mutex_lock(&config_mutex); | 360 pthread_mutex_lock(&config_mutex); |
285 pc = config; | 361 pc = config; |
286 pc->reference_count++; | 362 pc->reference_count++; |
287 pthread_mutex_unlock(&config_mutex); | 363 pthread_mutex_unlock(&config_mutex); |
364 get_fd(); | |
288 ip = 0; | 365 ip = 0; |
289 mailaddr = NULL; | 366 mailaddr = NULL; |
290 queueid = NULL; | 367 queueid = NULL; |
291 authenticated = false; | 368 authenticated = false; |
292 have_whites = false; | 369 have_whites = false; |
293 only_whites = true; | 370 only_whites = true; |
294 memory = new recorder(this, &pc->html_tags, &pc->tlds); | 371 memory = new recorder(this, &pc->html_tags, &pc->tlds); |
295 scanner = new url_scanner(memory); | 372 scanner = new url_scanner(memory); |
296 } | 373 } |
374 | |
297 mlfiPriv::~mlfiPriv() { | 375 mlfiPriv::~mlfiPriv() { |
376 return_fd(); | |
298 pthread_mutex_lock(&config_mutex); | 377 pthread_mutex_lock(&config_mutex); |
299 pc->reference_count--; | 378 pc->reference_count--; |
300 pthread_mutex_unlock(&config_mutex); | 379 pthread_mutex_unlock(&config_mutex); |
301 reset(true); | 380 reset(true); |
302 } | 381 } |
382 | |
303 void mlfiPriv::reset(bool final) { | 383 void mlfiPriv::reset(bool final) { |
304 if (mailaddr) free(mailaddr); | 384 if (mailaddr) free(mailaddr); |
305 if (queueid) free(queueid); | 385 if (queueid) free(queueid); |
306 discard(non_whites); | 386 discard(non_whites); |
307 delete memory; | 387 delete memory; |
315 memory = new recorder(this, &pc->html_tags, &pc->tlds); | 395 memory = new recorder(this, &pc->html_tags, &pc->tlds); |
316 scanner = new url_scanner(memory); | 396 scanner = new url_scanner(memory); |
317 } | 397 } |
318 } | 398 } |
319 | 399 |
400 void mlfiPriv::get_fd() | |
401 { | |
402 err = true; | |
403 fd = NULL_SOCKET; | |
404 int result = pthread_mutex_lock(&fd_pool_mutex); | |
405 if (!result) { | |
406 std::set<int>::iterator i; | |
407 i = fd_pool.begin(); | |
408 if (i != fd_pool.end()) { | |
409 // have at least one fd in the pool | |
410 err = false; | |
411 fd = *i; | |
412 fd_pool.erase(fd); | |
413 } | |
414 else { | |
415 // pool is empty, get a new fd | |
416 fd = my_connect(); | |
417 err = (fd == NULL_SOCKET); | |
418 } | |
419 pthread_mutex_unlock(&fd_pool_mutex); | |
420 } | |
421 else { | |
422 // cannot lock the pool, just get a new fd | |
423 fd = my_connect(); | |
424 err = (fd == NULL_SOCKET); | |
425 } | |
426 } | |
427 | |
428 void mlfiPriv::return_fd() | |
429 { | |
430 if (err) { | |
431 // this fd got a socket error, so close it, rather than returning it to the pool | |
432 my_disconnect(fd); | |
433 } | |
434 else { | |
435 int result = pthread_mutex_lock(&fd_pool_mutex); | |
436 if (!result) { | |
437 // return the fd to the pool | |
438 fd_pool.insert(fd); | |
439 pthread_mutex_unlock(&fd_pool_mutex); | |
440 } | |
441 else { | |
442 // could not lock the pool, so just close the fd | |
443 my_disconnect(fd); | |
444 } | |
445 } | |
446 } | |
447 | |
448 int mlfiPriv::my_write(char *buf, int len) | |
449 { | |
450 if (err) return 0; | |
451 int rs = 0; | |
452 while (len) { | |
453 int ws = write(fd, buf, len); | |
454 if (ws > 0) { | |
455 rs += ws; | |
456 len -= ws; | |
457 buf += ws; | |
458 } | |
459 else { | |
460 // peer closed the socket! | |
461 rs = 0; | |
462 err = true; | |
463 break; | |
464 } | |
465 } | |
466 return rs; | |
467 } | |
468 | |
469 int mlfiPriv::my_read(char *buf, int len) | |
470 { | |
471 if (err) return 0; | |
472 int rs = 0; | |
473 while (len > 1) { | |
474 int ws = read(fd, buf, len); | |
475 if (ws > 0) { | |
476 rs += ws; | |
477 len -= ws; | |
478 buf += ws; | |
479 } | |
480 else { | |
481 // peer closed the socket! | |
482 rs = 0; | |
483 err = true; | |
484 break; | |
485 } | |
486 } | |
487 return rs; | |
488 } | |
489 | |
320 #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) | 490 #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) |
321 | 491 |
322 | 492 |
323 //////////////////////////////////////////////// | 493 //////////////////////////////////////////////// |
324 // syslog a message | 494 // syslog a message |
435 } | 605 } |
436 return *sm; | 606 return *sm; |
437 } | 607 } |
438 | 608 |
439 | 609 |
440 //////////////////////////////////////////////// | 610 |
441 // | 611 //////////////////////////////////////////////// |
612 // read a resolver request from the socket, process it, and | |
613 // write the result back to the socket. | |
614 | |
615 #ifdef NS_PACKETSZ | |
616 static void process_resolver_requests(int socket); | |
617 static void process_resolver_requests(int socket) { | |
618 #ifdef NS_MAXDNAME | |
619 char question[NS_MAXDNAME]; | |
620 #else | |
621 char question[1000]; | |
622 #endif | |
623 glommer glom; | |
624 | |
625 int maxq = sizeof(question); | |
626 while (true) { | |
627 // read a question | |
628 int rs = 0; | |
629 while (true) { | |
630 int ns = read(socket, question+rs, maxq-rs); | |
631 if (ns > 0) { | |
632 rs += ns; | |
633 if (question[rs-1] == '\0') { | |
634 // last byte read was the null terminator, we are done | |
635 break; | |
636 } | |
637 } | |
638 else { | |
639 // peer closed the socket | |
640 //my_syslog("child worker process, peer closed socket while reading question"); | |
641 shutdown(socket, SHUT_RDWR); | |
642 close(socket); | |
643 return; | |
644 } | |
645 } | |
646 | |
647 // find the answer | |
648 //char text[1000]; | |
649 //snprintf(text, sizeof(text), "child worker process has a question %s", question); | |
650 //my_syslog(text); | |
651 glom.length = res_search(question, ns_c_in, ns_t_a, glom.answer, sizeof(glom.answer)); | |
652 if (glom.length < 0) glom.length = 0; // represent all errors as zero length answers | |
653 | |
654 // write the answer | |
655 char *buf = (char *)&glom; | |
656 int len = glom.length + sizeof(glom.length); | |
657 //snprintf(text, sizeof(text), "child worker process writing answer length %d for total %d", glom.length, len); | |
658 //my_syslog(text); | |
659 int ws = 0; | |
660 while (len > ws) { | |
661 int ns = write(socket, buf+ws, len-ws); | |
662 if (ns > 0) { | |
663 ws += ns; | |
664 } | |
665 else { | |
666 // peer closed the socket! | |
667 //my_syslog("child worker process, peer closed socket while writing answer"); | |
668 shutdown(socket, SHUT_RDWR); | |
669 close(socket); | |
670 return; | |
671 } | |
672 } | |
673 } | |
674 } | |
675 #endif | |
676 | |
677 | |
678 //////////////////////////////////////////////// | |
442 // ask a dns question and get an A record answer - we don't try | 679 // ask a dns question and get an A record answer - we don't try |
443 // very hard, just using the default resolver retry settings. | 680 // very hard, just using the default resolver retry settings. |
444 // If we cannot get an answer, we just accept the mail. The | 681 // If we cannot get an answer, we just accept the mail. |
445 // caller must ensure thread safety. | 682 // |
446 // | 683 // |
447 // | 684 static int dns_interface(mlfiPriv &priv, char *question, bool maybe_ip, ns_map *nameservers); |
448 static int dns_interface(char *question, bool maybe_ip, ns_map *nameservers); | 685 static int dns_interface(mlfiPriv &priv, char *question, bool maybe_ip, ns_map *nameservers) { |
449 static int dns_interface(char *question, bool maybe_ip, ns_map *nameservers) { | 686 int ret_address = 0; |
450 #ifdef NS_PACKETSZ | 687 #ifdef NS_PACKETSZ |
451 u_char answer[NS_PACKETSZ]; | 688 |
452 int length = res_search(question, ns_c_in, ns_t_a, answer, sizeof(answer)); | 689 // this part can be done without locking the resolver mutex. Each |
453 if (length >= 0) { // no error yet | 690 // milter thread is talking over its own socket to a separate resolver |
454 // parse the answer | 691 // process, which does the actual dns resolution. |
455 ns_msg handle; | 692 if (priv.err) return 0; // cannot ask more questions on this socket. |
456 ns_rr rr; | 693 priv.my_write(question, strlen(question)+1); // write the question including the null terminator |
457 if (ns_initparse(answer, length, &handle) == 0) { | 694 glommer glom; |
458 // look for ns names | 695 char *buf = (char *)&glom; |
459 if (nameservers) { | 696 priv.my_read(buf, sizeof(glom.length)); |
460 ns_map &ns = *nameservers; | 697 buf += sizeof(glom.length); |
461 int rrnum = 0; | 698 //char text[1000]; |
462 while (ns_parserr(&handle, ns_s_ns, rrnum++, &rr) == 0) { | 699 //snprintf(text, sizeof(text), "milter thread wrote question %s and has answer length %d", question, glom.length); |
463 if (ns_rr_type(rr) == ns_t_ns) { | 700 //my_syslog(text); |
464 char nam[NS_MAXDNAME+1]; | 701 if ((glom.length < 0) || (glom.length > sizeof(glom.answer))) { |
465 char *n = nam; | 702 priv.err = true; |
466 const u_char *p = ns_rr_rdata(rr); | 703 return 0; // cannot process overlarge answers |
467 while (((n-nam) < NS_MAXDNAME) && ((p-answer) < length) && *p) { | 704 } |
468 size_t s = *(p++); | 705 priv.my_read(buf, glom.length); |
469 if (s > 191) { | 706 |
470 // compression pointer | 707 // now we need to lock the resolver mutex to keep the milter threads from |
471 s = (s-192)*256 + *(p++); | 708 // stepping on each other while parsing the dns answer. |
472 if (s >= length) break; // pointer outside bounds of answer | 709 pthread_mutex_lock(&resolve_mutex); |
473 p = answer + s; | 710 if (glom.length > 0) { |
474 s = *(p++); | 711 // parse the answer |
712 ns_msg handle; | |
713 ns_rr rr; | |
714 if (ns_initparse(glom.answer, glom.length, &handle) == 0) { | |
715 // look for ns names | |
716 if (nameservers) { | |
717 ns_map &ns = *nameservers; | |
718 int rrnum = 0; | |
719 while (ns_parserr(&handle, ns_s_ns, rrnum++, &rr) == 0) { | |
720 if (ns_rr_type(rr) == ns_t_ns) { | |
721 char nam[NS_MAXDNAME+1]; | |
722 char *n = nam; | |
723 const u_char *p = ns_rr_rdata(rr); | |
724 while (((n-nam) < NS_MAXDNAME) && ((p-glom.answer) < glom.length) && *p) { | |
725 size_t s = *(p++); | |
726 if (s > 191) { | |
727 // compression pointer | |
728 s = (s-192)*256 + *(p++); | |
729 if (s >= glom.length) break; // pointer outside bounds of answer | |
730 p = glom.answer + s; | |
731 s = *(p++); | |
732 } | |
733 if (s > 0) { | |
734 if ((n-nam) >= (NS_MAXDNAME-s)) break; // destination would overflow name buffer | |
735 if ((p-glom.answer) >= (glom.length-s)) break; // source outside bounds of answer | |
736 memcpy(n, p, s); | |
737 n += s; | |
738 p += s; | |
739 *(n++) = '.'; | |
740 } | |
475 } | 741 } |
476 if (s > 0) { | 742 if (n-nam) n--; // remove trailing . |
477 if ((n-nam) >= (NS_MAXDNAME-s)) break; // destination would overflow name buffer | 743 *n = '\0'; // null terminate it |
478 if ((p-answer) >= (length-s)) break; // source outside bounds of answer | 744 register_string(ns, nam, question); // ns host to lookup later |
479 memcpy(n, p, s); | 745 } |
480 n += s; | 746 } |
481 p += s; | 747 rrnum = 0; |
482 *(n++) = '.'; | 748 while (ns_parserr(&handle, ns_s_ar, rrnum++, &rr) == 0) { |
749 if (ns_rr_type(rr) == ns_t_a) { | |
750 char* nam = (char*)ns_rr_name(rr); | |
751 ns_mapper::iterator i = ns.ns_ip.find(nam); | |
752 if (i != ns.ns_ip.end()) { | |
753 // we want this ip address | |
754 int address; | |
755 memcpy(&address, ns_rr_rdata(rr), sizeof(address)); | |
756 ns.ns_ip[nam] = address; | |
483 } | 757 } |
484 } | |
485 if (n-nam) n--; // remove trailing . | |
486 *n = '\0'; // null terminate it | |
487 register_string(ns, nam, question); // ns host to lookup later | |
488 } | |
489 } | |
490 rrnum = 0; | |
491 while (ns_parserr(&handle, ns_s_ar, rrnum++, &rr) == 0) { | |
492 if (ns_rr_type(rr) == ns_t_a) { | |
493 char* nam = (char*)ns_rr_name(rr); | |
494 ns_mapper::iterator i = ns.ns_ip.find(nam); | |
495 if (i != ns.ns_ip.end()) { | |
496 // we want this ip address | |
497 int address; | |
498 memcpy(&address, ns_rr_rdata(rr), sizeof(address)); | |
499 ns.ns_ip[nam] = address; | |
500 } | 758 } |
501 } | 759 } |
502 } | 760 } |
503 } | 761 int rrnum = 0; |
504 int rrnum = 0; | 762 while (ns_parserr(&handle, ns_s_an, rrnum++, &rr) == 0) { |
505 while (ns_parserr(&handle, ns_s_an, rrnum++, &rr) == 0) { | 763 if (ns_rr_type(rr) == ns_t_a) { |
506 if (ns_rr_type(rr) == ns_t_a) { | 764 int address; |
507 int address; | 765 memcpy(&address, ns_rr_rdata(rr), sizeof(address)); |
508 memcpy(&address, ns_rr_rdata(rr), sizeof(address)); | 766 ret_address = address; |
509 return address; | 767 } |
510 } | 768 } |
511 } | 769 } |
512 } | 770 } |
513 } | 771 if (maybe_ip && !ret_address) { |
514 if (maybe_ip) { | 772 // might be a bare ip address |
515 // might be a bare ip address | 773 in_addr ip; |
516 in_addr ip; | 774 if (inet_aton(question, &ip)) { |
517 if (inet_aton(question, &ip)) { | 775 ret_address = ip.s_addr; |
518 return ip.s_addr; | 776 } |
519 } | 777 } |
520 } | 778 pthread_mutex_unlock(&resolve_mutex); |
521 return 0; | 779 return ret_address; |
780 | |
522 #else | 781 #else |
523 struct hostent *host = gethostbyname(question); | 782 // systems without the resolver interface |
524 if (!host) return 0; | 783 pthread_mutex_lock(&resolve_mutex); |
525 if (host->h_addrtype != AF_INET) return 0; | 784 struct hostent *host = gethostbyname(question); |
526 int address; | 785 if (host && (host->h_addrtype == AF_INET)) { |
527 memcpy(&address, host->h_addr, sizeof(address)); | 786 memcpy(&ret_address, host->h_addr, sizeof(ret_address)); |
528 return address; | 787 } |
788 pthread_mutex_unlock(&resolve_mutex); | |
789 return ret_address; | |
529 #endif | 790 #endif |
530 } | 791 } |
531 | 792 |
532 static int protected_dns_interface(char *question, bool maybe_ip, ns_map *nameservers); | |
533 static int protected_dns_interface(char *question, bool maybe_ip, ns_map *nameservers) { | |
534 int ans; | |
535 pthread_mutex_lock(&resolve_mutex); | |
536 ans = dns_interface(question, maybe_ip, nameservers); | |
537 pthread_mutex_unlock(&resolve_mutex); | |
538 return ans; | |
539 | |
540 } | |
541 | 793 |
542 //////////////////////////////////////////////// | 794 //////////////////////////////////////////////// |
543 // check a single dnsbl | 795 // check a single dnsbl |
544 // | 796 // |
545 static status check_single(int ip, char *suffix); | 797 static status check_single(mlfiPriv &priv, int ip, char *suffix); |
546 static status check_single(int ip, char *suffix) { | 798 static status check_single(mlfiPriv &priv, int ip, char *suffix) { |
547 // make a dns question | 799 // make a dns question |
548 const u_char *src = (const u_char *)&ip; | 800 const u_char *src = (const u_char *)&ip; |
549 if (src[0] == 127) return oksofar; // don't do dns lookups on localhost | 801 if (src[0] == 127) return oksofar; // don't do dns lookups on localhost |
550 #ifdef NS_MAXDNAME | 802 #ifdef NS_MAXDNAME |
551 char question[NS_MAXDNAME]; | 803 char question[NS_MAXDNAME]; |
552 #else | 804 #else |
553 char question[1000]; | 805 char question[1000]; |
554 #endif | 806 #endif |
555 snprintf(question, sizeof(question), "%u.%u.%u.%u.%s.", src[3], src[2], src[1], src[0], suffix); | 807 snprintf(question, sizeof(question), "%u.%u.%u.%u.%s.", src[3], src[2], src[1], src[0], suffix); |
556 // ask the question, if we get an A record it implies a blacklisted ip address | 808 // ask the question, if we get an A record it implies a blacklisted ip address |
557 return (protected_dns_interface(question, false, NULL)) ? reject : oksofar; | 809 return (dns_interface(priv, question, false, NULL)) ? reject : oksofar; |
558 } | 810 } |
559 | 811 |
560 | 812 |
561 //////////////////////////////////////////////// | 813 //////////////////////////////////////////////// |
562 // check a single dnsbl | 814 // check a single dnsbl |
563 // | 815 // |
564 static status check_single(int ip, DNSBL &bl); | 816 static status check_single(mlfiPriv &priv, int ip, DNSBL &bl); |
565 static status check_single(int ip, DNSBL &bl) { | 817 static status check_single(mlfiPriv &priv, int ip, DNSBL &bl) { |
566 return check_single(ip, bl.suffix); | 818 return check_single(priv, ip, bl.suffix); |
567 } | 819 } |
568 | 820 |
569 | 821 |
570 //////////////////////////////////////////////// | 822 //////////////////////////////////////////////// |
571 // check the dnsbls specified for this recipient | 823 // check the dnsbls specified for this recipient |
579 DNSBLP dp = *i; // non null by construction | 831 DNSBLP dp = *i; // non null by construction |
580 status st; | 832 status st; |
581 map<DNSBLP, status>::iterator f = priv.checked.find(dp); | 833 map<DNSBLP, status>::iterator f = priv.checked.find(dp); |
582 if (f == priv.checked.end()) { | 834 if (f == priv.checked.end()) { |
583 // have not checked this list yet | 835 // have not checked this list yet |
584 st = check_single(priv.ip, *dp); | 836 st = check_single(priv, priv.ip, *dp); |
585 rejectlist = dp; | 837 rejectlist = dp; |
586 priv.checked[dp] = st; | 838 priv.checked[dp] = st; |
587 } | 839 } |
588 else { | 840 else { |
589 st = (*f).second; | 841 st = (*f).second; |
627 count++; | 879 count++; |
628 if ((count > lim) && (lim > 0) && (!ran)) { | 880 if ((count > lim) && (lim > 0) && (!ran)) { |
629 discard(nameservers); | 881 discard(nameservers); |
630 return reject_host; | 882 return reject_host; |
631 } | 883 } |
632 ip = protected_dns_interface(host, true, &nameservers); | 884 ip = dns_interface(priv, host, true, &nameservers); |
633 if (debug_syslog) { | 885 if (debug_syslog) { |
634 char buf[1000]; | 886 char buf[1000]; |
635 if (ip) { | 887 if (ip) { |
636 char adr[sizeof "255.255.255.255"]; | 888 char adr[sizeof "255.255.255.255"]; |
637 adr[0] = '\0'; | 889 adr[0] = '\0'; |
645 } | 897 } |
646 if (ip) { | 898 if (ip) { |
647 int_set::iterator i = ips.find(ip); | 899 int_set::iterator i = ips.find(ip); |
648 if (i == ips.end()) { | 900 if (i == ips.end()) { |
649 ips.insert(ip); | 901 ips.insert(ip); |
650 status st = check_single(ip, dc.content_suffix); | 902 status st = check_single(priv, ip, dc.content_suffix); |
651 if (st == reject) { | 903 if (st == reject) { |
652 discard(nameservers); | 904 discard(nameservers); |
653 return st; | 905 return st; |
654 } | 906 } |
655 } | 907 } |
663 discard(nameservers); | 915 discard(nameservers); |
664 return reject_host; | 916 return reject_host; |
665 } | 917 } |
666 host = (*i).first; // a transient reference that needs to be replaced before we return it | 918 host = (*i).first; // a transient reference that needs to be replaced before we return it |
667 ip = (*i).second; | 919 ip = (*i).second; |
668 if (!ip) ip = protected_dns_interface(host, false, NULL); | 920 if (!ip) ip = dns_interface(priv, host, false, NULL); |
669 if (debug_syslog) { | 921 if (debug_syslog) { |
670 char buf[200]; | 922 char buf[200]; |
671 if (ip) { | 923 if (ip) { |
672 char adr[sizeof "255.255.255.255"]; | 924 char adr[sizeof "255.255.255.255"]; |
673 adr[0] = '\0'; | 925 adr[0] = '\0'; |
681 } | 933 } |
682 if (ip) { | 934 if (ip) { |
683 int_set::iterator i = ips.find(ip); | 935 int_set::iterator i = ips.find(ip); |
684 if (i == ips.end()) { | 936 if (i == ips.end()) { |
685 ips.insert(ip); | 937 ips.insert(ip); |
686 status st = check_single(ip, dc.content_suffix); | 938 status st = check_single(priv, ip, dc.content_suffix); |
687 if (st == reject) { | 939 if (st == reject) { |
688 string_map::iterator j = nameservers.ns_host.find(host); | 940 string_map::iterator j = nameservers.ns_host.find(host); |
689 if (j != nameservers.ns_host.end()) { | 941 if (j != nameservers.ns_host.end()) { |
690 char *refer = (*j).second; | 942 char *refer = (*j).second; |
691 char buf[1000]; | 943 char buf[1000]; |
1353 | 1605 |
1354 | 1606 |
1355 static void usage(char *prog); | 1607 static void usage(char *prog); |
1356 static void usage(char *prog) | 1608 static void usage(char *prog) |
1357 { | 1609 { |
1358 fprintf(stderr, "Usage: %s [-d] [-c] -p socket-addr [-t timeout]\n", prog); | 1610 fprintf(stderr, "Usage: %s [-d] [-c] -r port -p sm-sock-addr [-t timeout]\n", prog); |
1359 fprintf(stderr, "where socket-addr is for the connection to sendmail and should be one of\n"); | 1611 fprintf(stderr, "where port is for the connection to our own dns resolver processes\n"); |
1360 fprintf(stderr, " inet:port@local-ip-address\n"); | 1612 fprintf(stderr, "where sm-sock-addr is for the connection to sendmail\n"); |
1361 fprintf(stderr, " local:local-domain-socket-file-name\n"); | 1613 fprintf(stderr, " and should be one of\n"); |
1614 fprintf(stderr, " inet:port@ip-address\n"); | |
1615 fprintf(stderr, " local:local-domain-socket-file-name\n"); | |
1362 fprintf(stderr, "-c will load and dump the config to stdout\n"); | 1616 fprintf(stderr, "-c will load and dump the config to stdout\n"); |
1363 fprintf(stderr, "-d will add some syslog debug messages\n"); | 1617 fprintf(stderr, "-d will add some syslog debug messages\n"); |
1364 } | 1618 } |
1365 | 1619 |
1366 | 1620 |
1380 | 1634 |
1381 int main(int argc, char**argv) | 1635 int main(int argc, char**argv) |
1382 { | 1636 { |
1383 bool check = false; | 1637 bool check = false; |
1384 bool setconn = false; | 1638 bool setconn = false; |
1639 bool setreso = false; | |
1385 int c; | 1640 int c; |
1386 const char *args = "p:t:hcd"; | 1641 const char *args = "r:p:t:hcd"; |
1387 extern char *optarg; | 1642 extern char *optarg; |
1388 | 1643 |
1389 // Process command line options | 1644 // Process command line options |
1390 while ((c = getopt(argc, argv, args)) != -1) { | 1645 while ((c = getopt(argc, argv, args)) != -1) { |
1391 switch (c) { | 1646 switch (c) { |
1647 case 'r': | |
1648 if (optarg == NULL || *optarg == '\0') { | |
1649 fprintf(stderr, "Illegal resolver socket: %s\n", optarg); | |
1650 exit(EX_USAGE); | |
1651 } | |
1652 resolver_port = atoi(optarg); | |
1653 setreso = true; | |
1654 break; | |
1655 | |
1392 case 'p': | 1656 case 'p': |
1393 if (optarg == NULL || *optarg == '\0') { | 1657 if (optarg == NULL || *optarg == '\0') { |
1394 fprintf(stderr, "Illegal conn: %s\n", optarg); | 1658 fprintf(stderr, "Illegal sendmail socket: %s\n", optarg); |
1395 exit(EX_USAGE); | 1659 exit(EX_USAGE); |
1396 } | 1660 } |
1397 if (smfi_setconn(optarg) == MI_FAILURE) { | 1661 if (smfi_setconn(optarg) == MI_FAILURE) { |
1398 fprintf(stderr, "smfi_setconn failed\n"); | 1662 fprintf(stderr, "smfi_setconn failed\n"); |
1399 exit(EX_SOFTWARE); | 1663 exit(EX_SOFTWARE); |
1400 } | 1664 } |
1401 | |
1402 if (strncasecmp(optarg, "unix:", 5) == 0) setup_socket(optarg + 5); | 1665 if (strncasecmp(optarg, "unix:", 5) == 0) setup_socket(optarg + 5); |
1403 else if (strncasecmp(optarg, "local:", 6) == 0) setup_socket(optarg + 6); | 1666 else if (strncasecmp(optarg, "local:", 6) == 0) setup_socket(optarg + 6); |
1404 setconn = true; | 1667 setconn = true; |
1405 break; | 1668 break; |
1406 | 1669 |
1436 return 0; | 1699 return 0; |
1437 } | 1700 } |
1438 | 1701 |
1439 if (!setconn) { | 1702 if (!setconn) { |
1440 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); | 1703 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); |
1704 usage(argv[0]); | |
1705 exit(EX_USAGE); | |
1706 } | |
1707 | |
1708 if (!setreso) { | |
1709 fprintf(stderr, "%s: Missing required -r argument\n", argv[0]); | |
1441 usage(argv[0]); | 1710 usage(argv[0]); |
1442 exit(EX_USAGE); | 1711 exit(EX_USAGE); |
1443 } | 1712 } |
1444 | 1713 |
1445 if (smfi_register(smfilter) == MI_FAILURE) { | 1714 if (smfi_register(smfilter) == MI_FAILURE) { |
1472 fprintf(f, "%d\n", (u_int)getpid()); | 1741 fprintf(f, "%d\n", (u_int)getpid()); |
1473 #endif | 1742 #endif |
1474 fclose(f); | 1743 fclose(f); |
1475 } | 1744 } |
1476 | 1745 |
1746 // initialize the thread sync objects | |
1747 pthread_mutex_init(&config_mutex, 0); | |
1748 pthread_mutex_init(&syslog_mutex, 0); | |
1749 pthread_mutex_init(&resolve_mutex, 0); | |
1750 pthread_mutex_init(&fd_pool_mutex, 0); | |
1751 | |
1752 #ifdef NS_PACKETSZ | |
1753 // fork off the resolver listener process | |
1754 pid_t child = fork(); | |
1755 if (child < 0) { | |
1756 my_syslog("failed to create resolver listener process"); | |
1757 exit(0); | |
1758 } | |
1759 if (child == 0) { | |
1760 // we are the child - dns resolver listener process | |
1761 resolver_socket = socket(PF_INET, SOCK_STREAM, 0); | |
1762 if (resolver_socket < 0) { | |
1763 my_syslog("child failed to create resolver socket"); | |
1764 exit(0); // failed | |
1765 } | |
1766 sockaddr_in server; | |
1767 server.sin_family = AF_INET; | |
1768 server.sin_port = htons(resolver_port); | |
1769 server.sin_addr.s_addr = 0; | |
1770 // set the socket options | |
1771 int reuse_addr = 1; | |
1772 linger linger_opt; | |
1773 linger_opt.l_onoff = 0; // off | |
1774 linger_opt.l_linger = 0; | |
1775 setsockopt(resolver_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&reuse_addr), sizeof(reuse_addr)); | |
1776 setsockopt(resolver_socket, SOL_SOCKET, SO_LINGER, reinterpret_cast<char*>(&linger_opt), sizeof(linger_opt)); | |
1777 ///// set nonblocking mode | |
1778 ///int dummy = 0; | |
1779 ///int flags = fcntl(resolver_socket, F_GETFL, dummy); | |
1780 ///if (flags >= 0) fcntl(resolver_socket, F_SETFL, flags | O_NONBLOCK); | |
1781 //try to bind the address to the socket. | |
1782 if (bind(resolver_socket, (sockaddr *)&server, sizeof(server)) < 0) { | |
1783 // bind failed | |
1784 shutdown(resolver_socket, SHUT_RDWR); | |
1785 close(resolver_socket); | |
1786 my_syslog("child failed to bind resolver socket"); | |
1787 exit(0); // failed | |
1788 } | |
1789 | |
1790 //listen on the socket. | |
1791 if (listen(resolver_socket, 10) < 0) { | |
1792 // listen failed | |
1793 shutdown(resolver_socket, SHUT_RDWR); | |
1794 close(resolver_socket); | |
1795 my_syslog("child failed to listen to the resolver socket"); | |
1796 return -1; | |
1797 } | |
1798 } | |
1799 else { | |
1800 sleep(2); // allow child to get started | |
1801 } | |
1802 #endif | |
1803 | |
1477 // drop root privs | 1804 // drop root privs |
1478 struct passwd *pw = getpwnam("dnsbl"); | 1805 struct passwd *pw = getpwnam("dnsbl"); |
1479 if (pw) { | 1806 if (pw) { |
1480 if (setgid(pw->pw_gid) == -1) { | 1807 if (setgid(pw->pw_gid) == -1) { |
1481 my_syslog("failed to switch to group dnsbl"); | 1808 my_syslog("failed to switch to group dnsbl"); |
1483 if (setuid(pw->pw_uid) == -1) { | 1810 if (setuid(pw->pw_uid) == -1) { |
1484 my_syslog("failed to switch to user dnsbl"); | 1811 my_syslog("failed to switch to user dnsbl"); |
1485 } | 1812 } |
1486 } | 1813 } |
1487 | 1814 |
1488 // initialize the thread sync objects | 1815 #ifdef NS_PACKETSZ |
1489 pthread_mutex_init(&config_mutex, 0); | 1816 if (child == 0) { |
1490 pthread_mutex_init(&syslog_mutex, 0); | 1817 while (true) { |
1491 pthread_mutex_init(&resolve_mutex, 0); | 1818 sockaddr_in client; |
1819 socklen_t clientlen = sizeof(client); | |
1820 int s = accept(resolver_socket, (sockaddr *)&client, &clientlen); | |
1821 if (s > 0) { | |
1822 // accept worked, it did not get cancelled before we could accept it | |
1823 // fork off a process to handle this connection | |
1824 int newchild = fork(); | |
1825 if (newchild == 0) { | |
1826 // this is the worker process | |
1827 my_syslog("child forked a worker process"); | |
1828 process_resolver_requests(s); | |
1829 my_syslog("child terminated a worker process"); | |
1830 exit(0); | |
1831 } | |
1832 } | |
1833 } | |
1834 exit(0); // make sure we don't fall thru. | |
1835 } | |
1836 #endif | |
1492 | 1837 |
1493 // load the initial config | 1838 // load the initial config |
1494 config = new_conf(); | 1839 config = new_conf(); |
1495 | 1840 |
1496 // only create threads after the fork() in daemon | 1841 // only create threads after the fork() in daemon |