Mercurial > dnsbl
comparison src/dnsbl.cpp @ 236:c0d2e99c0a1d
Add surbl checks on the smtp helo value, client reverse dns name, and mail from domain name
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Tue, 29 Sep 2009 11:36:15 -0700 |
parents | e6c66640f6f9 |
children | 7b818a4e21a4 |
comparison
equal
deleted
inserted
replaced
235:e6c66640f6f9 | 236:c0d2e99c0a1d |
---|---|
248 pthread_mutex_lock(&fd_pool_mutex); | 248 pthread_mutex_lock(&fd_pool_mutex); |
249 resolver_sock_count++; | 249 resolver_sock_count++; |
250 pthread_mutex_unlock(&fd_pool_mutex); | 250 pthread_mutex_unlock(&fd_pool_mutex); |
251 } | 251 } |
252 return sock; | 252 return sock; |
253 } | |
254 | |
255 | |
256 mlfiPriv::mlfiPriv() { | |
257 pthread_mutex_lock(&config_mutex); | |
258 pc = config; | |
259 pc->reference_count++; | |
260 pthread_mutex_unlock(&config_mutex); | |
261 get_fd(); | |
262 ctx = NULL; | |
263 eom = false; | |
264 ip = 0; | |
265 helo = NULL; | |
266 mailaddr = NULL; | |
267 queueid = NULL; | |
268 authenticated = NULL; | |
269 client_name = NULL; | |
270 have_whites = false; | |
271 only_whites = true; | |
272 want_spamassassin = false; | |
273 want_dccgrey = false; | |
274 want_dccbulk = false; | |
275 allow_autowhitelisting = true; | |
276 content_context = NULL; | |
277 memory = NULL; | |
278 scanner = NULL; | |
279 content_suffix = NULL; | |
280 content_message = NULL; | |
281 uribl_suffix = NULL; | |
282 uribl_message = NULL; | |
283 content_host_ignore = NULL; | |
284 assassin = NULL; | |
285 dccifd = NULL; | |
286 } | |
287 | |
288 mlfiPriv::~mlfiPriv() { | |
289 return_fd(); | |
290 pthread_mutex_lock(&config_mutex); | |
291 pc->reference_count--; | |
292 bool last = (!pc->reference_count) && (pc != config); | |
293 pthread_mutex_unlock(&config_mutex); | |
294 if (last) delete pc; // free this config, since we were the last reference to it | |
295 if (helo) free((void*)helo); | |
296 reset(true); | |
297 } | |
298 | |
299 void mlfiPriv::reset(bool final) { | |
300 while (!delayer.empty()) { | |
301 DELAYWHITEP dwp = delayer.front(); | |
302 const char *loto = dwp->get_loto(); | |
303 if (loto) free((void*)loto); | |
304 delete dwp; | |
305 delayer.pop_front(); | |
306 } | |
307 if (mailaddr) free((void*)mailaddr); | |
308 if (queueid) free((void*)queueid); | |
309 if (authenticated) free((void*)authenticated); | |
310 if (client_name) free((void*)client_name); | |
311 delayer.clear(); | |
312 discard(env_to); | |
313 if (memory) delete memory; | |
314 if (scanner) delete scanner; | |
315 if (assassin) delete assassin; | |
316 if (dccifd) delete dccifd; | |
317 if (!final) { | |
318 ctx = NULL; | |
319 eom = false; | |
320 mailaddr = NULL; | |
321 queueid = NULL; | |
322 authenticated = NULL; | |
323 client_name = NULL; | |
324 have_whites = false; | |
325 only_whites = true; | |
326 want_spamassassin = false; | |
327 want_dccgrey = false; | |
328 want_dccbulk = false; | |
329 allow_autowhitelisting = true; | |
330 content_context = NULL; | |
331 memory = NULL; | |
332 scanner = NULL; | |
333 content_suffix = NULL; | |
334 content_message = NULL; | |
335 uribl_suffix = NULL; | |
336 uribl_message = NULL; | |
337 content_host_ignore = NULL; | |
338 assassin = NULL; | |
339 dccifd = NULL; | |
340 } | |
341 } | |
342 | |
343 void mlfiPriv::get_fd() { | |
344 err = true; | |
345 fd = NULL_SOCKET; | |
346 int result = pthread_mutex_lock(&fd_pool_mutex); | |
347 if (!result) { | |
348 std::set<int>::iterator i; | |
349 i = fd_pool.begin(); | |
350 if (i != fd_pool.end()) { | |
351 // have at least one fd in the pool | |
352 err = false; | |
353 fd = *i; | |
354 fd_pool.erase(fd); | |
355 resolver_pool_size--; | |
356 pthread_mutex_unlock(&fd_pool_mutex); | |
357 } | |
358 else { | |
359 // pool is empty, get a new fd | |
360 pthread_mutex_unlock(&fd_pool_mutex); | |
361 fd = my_connect(); | |
362 err = (fd == NULL_SOCKET); | |
363 } | |
364 } | |
365 else { | |
366 // cannot lock the pool, just get a new fd | |
367 fd = my_connect(); | |
368 err = (fd == NULL_SOCKET); | |
369 } | |
370 } | |
371 | |
372 void mlfiPriv::return_fd() { | |
373 if (err) { | |
374 // this fd got a socket error, so close it, rather than returning it to the pool | |
375 my_disconnect(fd); | |
376 } | |
377 else { | |
378 int result = pthread_mutex_lock(&fd_pool_mutex); | |
379 if (!result) { | |
380 if ((resolver_sock_count > resolver_pool_size*5) || (resolver_pool_size < 5)) { | |
381 // return the fd to the pool | |
382 fd_pool.insert(fd); | |
383 resolver_pool_size++; | |
384 pthread_mutex_unlock(&fd_pool_mutex); | |
385 } | |
386 else { | |
387 // more than 20% of the open resolver sockets are in the pool, and the | |
388 // pool as at least 5 sockets. that is enough, so just close this one. | |
389 pthread_mutex_unlock(&fd_pool_mutex); | |
390 my_disconnect(fd); | |
391 } | |
392 } | |
393 else { | |
394 // could not lock the pool, so just close the fd | |
395 my_disconnect(fd); | |
396 } | |
397 } | |
398 } | |
399 | |
400 size_t mlfiPriv::my_write(const char *buf, size_t len) { | |
401 if (err) return 0; | |
402 size_t rs = 0; | |
403 while (len) { | |
404 size_t ws = write(fd, buf, len); | |
405 if (ws > 0) { | |
406 rs += ws; | |
407 len -= ws; | |
408 buf += ws; | |
409 } | |
410 else { | |
411 // peer closed the socket! | |
412 rs = 0; | |
413 err = true; | |
414 break; | |
415 } | |
416 } | |
417 return rs; | |
418 } | |
419 | |
420 size_t mlfiPriv::my_read(char *buf, size_t len) { | |
421 if (err) return 0; | |
422 size_t rs = 0; | |
423 while (len) { | |
424 size_t ws = read(fd, buf, len); | |
425 if (ws > 0) { | |
426 rs += ws; | |
427 len -= ws; | |
428 buf += ws; | |
429 } | |
430 else { | |
431 // peer closed the socket! | |
432 rs = 0; | |
433 err = true; | |
434 break; | |
435 } | |
436 } | |
437 return rs; | |
438 } | |
439 | |
440 void mlfiPriv::need_content_filter(const char *rcpt, CONTEXT &con) { | |
441 register_string(env_to, rcpt, &con); | |
442 if (!memory) { | |
443 // first recipient that needs content filtering sets | |
444 // some of the content filtering parameters | |
445 memory = new recorder(this, con.get_html_tags(), con.get_content_tlds(), con.get_content_cctlds()); | |
446 scanner = new url_scanner(memory); | |
447 content_suffix = con.get_content_suffix(); | |
448 content_message = con.get_content_message(); | |
449 uribl_suffix = con.get_uribl_suffix(); | |
450 uribl_message = con.get_uribl_message(); | |
451 content_host_ignore = &con.get_content_host_ignore(); | |
452 } | |
453 } | |
454 | |
455 | |
456 mlfiPriv* fetch_priv_from_ctx(SMFICTX *ctx); | |
457 mlfiPriv* fetch_priv_from_ctx(SMFICTX *ctx) | |
458 { | |
459 mlfiPriv *priv = (struct mlfiPriv *)smfi_getpriv(ctx); | |
460 priv->ctx = ctx; | |
461 return priv; | |
462 } | |
463 #define MLFIPRIV fetch_priv_from_ctx(ctx) | |
464 | |
465 | |
466 | |
467 //////////////////////////////////////////////// | |
468 // syslog a message | |
469 // | |
470 void my_syslog(mlfiPriv *priv, const char *text) { | |
471 char buf[maxlen]; | |
472 if (priv) { | |
473 snprintf(buf, sizeof(buf), "%s: %s", priv->queueid, text); | |
474 text = buf; | |
475 } | |
476 if (use_syslog) { | |
477 pthread_mutex_lock(&syslog_mutex); | |
478 if (!syslog_opened) { | |
479 openlog("dnsbl", LOG_PID, LOG_MAIL); | |
480 syslog_opened = true; | |
481 } | |
482 syslog(LOG_NOTICE, "%s", text); | |
483 pthread_mutex_unlock(&syslog_mutex); | |
484 } | |
485 else { | |
486 printf("%s \n", text); | |
487 } | |
488 } | |
489 | |
490 void my_syslog(mlfiPriv *priv, const string text) { | |
491 if (debug_syslog > 3) { | |
492 char buf[maxlen]; | |
493 strncpy(buf, text.c_str(), sizeof(buf)); | |
494 buf[maxlen-1] = '\0'; // ensure null termination | |
495 my_syslog(priv, buf); | |
496 } | |
497 } | |
498 | |
499 void my_syslog(const char *text) { | |
500 my_syslog(NULL, text); | |
501 } | |
502 | |
503 | |
504 //////////////////////////////////////////////// | |
505 // read a resolver request from the socket, process it, and | |
506 // write the result back to the socket. | |
507 | |
508 void process_resolver_requests(int socket); | |
509 void process_resolver_requests(int socket) { | |
510 #ifdef NS_MAXDNAME | |
511 char question[NS_MAXDNAME]; | |
512 #else | |
513 char question[1000]; | |
514 #endif | |
515 glommer glom; | |
516 | |
517 int maxq = sizeof(question); | |
518 while (true) { | |
519 // read a question | |
520 int rs = 0; | |
521 while (rs < maxq) { | |
522 int ns = read(socket, question+rs, maxq-rs); | |
523 if (ns > 0) { | |
524 rs += ns; | |
525 if (question[rs-1] == '\0') { | |
526 // last byte read was the null terminator, we are done | |
527 break; | |
528 } | |
529 } | |
530 else { | |
531 // peer closed the socket | |
532 #ifdef RESOLVER_DEBUG | |
533 my_syslog("process_resolver_requests() peer closed socket while reading question"); | |
534 #endif | |
535 shutdown(socket, SHUT_RDWR); | |
536 close(socket); | |
537 return; | |
538 } | |
539 } | |
540 question[rs-1] = '\0'; // ensure null termination | |
541 | |
542 // find the answer | |
543 #ifdef NS_PACKETSZ | |
544 #ifdef RESOLVER_DEBUG | |
545 char text[1000]; | |
546 snprintf(text, sizeof(text), "process_resolver_requests() has a question %s", question); | |
547 my_syslog(text); | |
548 #endif | |
549 int res_result = res_search(question, ns_c_in, ns_t_a, glom.answer, sizeof(glom.answer)); | |
550 if (res_result < 0) glom.length = 0; // represent all errors as zero length answers | |
551 else glom.length = (size_t)res_result; | |
552 #else | |
553 glom.length = sizeof(glom.answer); | |
554 glom.answer = 0; | |
555 struct hostent *host = gethostbyname(question); | |
556 if (host && (host->h_addrtype == AF_INET)) { | |
557 memcpy(&glom.answer, host->h_addr, sizeof(glom.answer)); | |
558 } | |
559 #endif | |
560 | |
561 // write the answer | |
562 char *buf = (char *)&glom; | |
563 int len = glom.length + sizeof(glom.length); | |
564 #ifdef RESOLVER_DEBUG | |
565 snprintf(text, sizeof(text), "process_resolver_requests() writing answer length %d for total %d", glom.length, len); | |
566 my_syslog(text); | |
567 #endif | |
568 int ws = 0; | |
569 while (len > ws) { | |
570 int ns = write(socket, buf+ws, len-ws); | |
571 if (ns > 0) { | |
572 ws += ns; | |
573 } | |
574 else { | |
575 // peer closed the socket! | |
576 #ifdef RESOLVER_DEBUG | |
577 my_syslog("process_resolver_requests() peer closed socket while writing answer"); | |
578 #endif | |
579 shutdown(socket, SHUT_RDWR); | |
580 close(socket); | |
581 return; | |
582 } | |
583 } | |
584 } | |
585 } | 253 } |
586 | 254 |
587 | 255 |
588 //////////////////////////////////////////////// | 256 //////////////////////////////////////////////// |
589 // ask a dns question and get an A record answer - we don't try | 257 // ask a dns question and get an A record answer - we don't try |
708 #endif | 376 #endif |
709 } | 377 } |
710 | 378 |
711 | 379 |
712 //////////////////////////////////////////////// | 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 } | |
412 | |
413 | |
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 } | |
440 | |
441 const char *top, *top2, *top3; | |
442 top = strrchr(hostname, '.'); | |
443 if (top) { | |
444 top2 = (const char *)memrchr(hostname, '.', top-hostname); | |
445 | |
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); | |
451 | |
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; | |
455 | |
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 } | |
463 | |
464 | |
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 helo_uribl = false; | |
480 client_uribl = false; | |
481 from_uribl = false; | |
482 have_whites = false; | |
483 only_whites = true; | |
484 want_spamassassin = false; | |
485 want_dccgrey = false; | |
486 want_dccbulk = false; | |
487 allow_autowhitelisting = true; | |
488 content_context = NULL; | |
489 memory = NULL; | |
490 scanner = NULL; | |
491 content_suffix = NULL; | |
492 content_message = NULL; | |
493 uribl_suffix = NULL; | |
494 uribl_message = NULL; | |
495 content_host_ignore = NULL; | |
496 assassin = NULL; | |
497 dccifd = NULL; | |
498 } | |
499 | |
500 mlfiPriv::~mlfiPriv() { | |
501 return_fd(); | |
502 pthread_mutex_lock(&config_mutex); | |
503 pc->reference_count--; | |
504 bool last = (!pc->reference_count) && (pc != config); | |
505 pthread_mutex_unlock(&config_mutex); | |
506 if (last) delete pc; // free this config, since we were the last reference to it | |
507 if (helo) free((void*)helo); | |
508 reset(true); | |
509 } | |
510 | |
511 void mlfiPriv::reset(bool final) { | |
512 while (!delayer.empty()) { | |
513 DELAYWHITEP dwp = delayer.front(); | |
514 const char *loto = dwp->get_loto(); | |
515 if (loto) free((void*)loto); | |
516 delete dwp; | |
517 delayer.pop_front(); | |
518 } | |
519 if (mailaddr) free((void*)mailaddr); | |
520 if (queueid) free((void*)queueid); | |
521 if (authenticated) free((void*)authenticated); | |
522 if (client_name) free((void*)client_name); | |
523 delayer.clear(); | |
524 discard(env_to); | |
525 if (memory) delete memory; | |
526 if (scanner) delete scanner; | |
527 if (assassin) delete assassin; | |
528 if (dccifd) delete dccifd; | |
529 if (!final) { | |
530 ctx = NULL; | |
531 eom = false; | |
532 mailaddr = NULL; | |
533 queueid = NULL; | |
534 authenticated = NULL; | |
535 client_name = NULL; | |
536 helo_uribl = false; | |
537 client_uribl = false; | |
538 from_uribl = false; | |
539 have_whites = false; | |
540 only_whites = true; | |
541 want_spamassassin = false; | |
542 want_dccgrey = false; | |
543 want_dccbulk = false; | |
544 allow_autowhitelisting = true; | |
545 content_context = NULL; | |
546 memory = NULL; | |
547 scanner = NULL; | |
548 content_suffix = NULL; | |
549 content_message = NULL; | |
550 uribl_suffix = NULL; | |
551 uribl_message = NULL; | |
552 content_host_ignore = NULL; | |
553 assassin = NULL; | |
554 dccifd = NULL; | |
555 } | |
556 } | |
557 | |
558 void mlfiPriv::get_fd() { | |
559 err = true; | |
560 fd = NULL_SOCKET; | |
561 int result = pthread_mutex_lock(&fd_pool_mutex); | |
562 if (!result) { | |
563 std::set<int>::iterator i; | |
564 i = fd_pool.begin(); | |
565 if (i != fd_pool.end()) { | |
566 // have at least one fd in the pool | |
567 err = false; | |
568 fd = *i; | |
569 fd_pool.erase(fd); | |
570 resolver_pool_size--; | |
571 pthread_mutex_unlock(&fd_pool_mutex); | |
572 } | |
573 else { | |
574 // pool is empty, get a new fd | |
575 pthread_mutex_unlock(&fd_pool_mutex); | |
576 fd = my_connect(); | |
577 err = (fd == NULL_SOCKET); | |
578 } | |
579 } | |
580 else { | |
581 // cannot lock the pool, just get a new fd | |
582 fd = my_connect(); | |
583 err = (fd == NULL_SOCKET); | |
584 } | |
585 } | |
586 | |
587 void mlfiPriv::return_fd() { | |
588 if (err) { | |
589 // this fd got a socket error, so close it, rather than returning it to the pool | |
590 my_disconnect(fd); | |
591 } | |
592 else { | |
593 int result = pthread_mutex_lock(&fd_pool_mutex); | |
594 if (!result) { | |
595 if ((resolver_sock_count > resolver_pool_size*5) || (resolver_pool_size < 5)) { | |
596 // return the fd to the pool | |
597 fd_pool.insert(fd); | |
598 resolver_pool_size++; | |
599 pthread_mutex_unlock(&fd_pool_mutex); | |
600 } | |
601 else { | |
602 // more than 20% of the open resolver sockets are in the pool, and the | |
603 // pool as at least 5 sockets. that is enough, so just close this one. | |
604 pthread_mutex_unlock(&fd_pool_mutex); | |
605 my_disconnect(fd); | |
606 } | |
607 } | |
608 else { | |
609 // could not lock the pool, so just close the fd | |
610 my_disconnect(fd); | |
611 } | |
612 } | |
613 } | |
614 | |
615 size_t mlfiPriv::my_write(const char *buf, size_t len) { | |
616 if (err) return 0; | |
617 size_t rs = 0; | |
618 while (len) { | |
619 size_t ws = write(fd, buf, len); | |
620 if (ws > 0) { | |
621 rs += ws; | |
622 len -= ws; | |
623 buf += ws; | |
624 } | |
625 else { | |
626 // peer closed the socket! | |
627 rs = 0; | |
628 err = true; | |
629 break; | |
630 } | |
631 } | |
632 return rs; | |
633 } | |
634 | |
635 size_t mlfiPriv::my_read(char *buf, size_t len) { | |
636 if (err) return 0; | |
637 size_t rs = 0; | |
638 while (len) { | |
639 size_t ws = read(fd, buf, len); | |
640 if (ws > 0) { | |
641 rs += ws; | |
642 len -= ws; | |
643 buf += ws; | |
644 } | |
645 else { | |
646 // peer closed the socket! | |
647 rs = 0; | |
648 err = true; | |
649 break; | |
650 } | |
651 } | |
652 return rs; | |
653 } | |
654 | |
655 void mlfiPriv::need_content_filter(const char *rcpt, CONTEXT &con) { | |
656 if (!memory) { | |
657 // first recipient that needs content filtering sets | |
658 // some of the content filtering parameters | |
659 memory = new recorder(this, con.get_html_tags(), con.get_content_tlds(), con.get_content_cctlds()); | |
660 scanner = new url_scanner(memory); | |
661 content_suffix = con.get_content_suffix(); | |
662 content_message = con.get_content_message(); | |
663 uribl_suffix = con.get_uribl_suffix(); | |
664 uribl_message = con.get_uribl_message(); | |
665 content_host_ignore = &con.get_content_host_ignore(); | |
666 // if we are using uribl, test helo and client names here | |
667 if (uribl_suffix) { | |
668 const char *found = NULL; | |
669 string_set hosts; | |
670 if (helo) { | |
671 helo_uribl = check_uribl(*this, hosts, helo, found); | |
672 } | |
673 if (client_name && !helo_uribl) { | |
674 client_uribl = check_uribl(*this, hosts, client_name, found); | |
675 } | |
676 if (mailaddr && !client_uribl) { | |
677 char *f = strchr(mailaddr, '@'); | |
678 if (f) from_uribl = check_uribl(*this, hosts, f+1, found); | |
679 } | |
680 discard(hosts); | |
681 } | |
682 } | |
683 } | |
684 | |
685 | |
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) | |
694 | |
695 | |
696 | |
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 } | |
719 | |
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 } | |
728 | |
729 void my_syslog(const char *text) { | |
730 my_syslog(NULL, text); | |
731 } | |
732 | |
733 | |
734 //////////////////////////////////////////////// | |
735 // read a resolver request from the socket, process it, and | |
736 // write the result back to the socket. | |
737 | |
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; | |
746 | |
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 | |
771 | |
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 | |
790 | |
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 } | |
816 | |
817 | |
818 //////////////////////////////////////////////// | |
713 // check a single dnsbl | 819 // check a single dnsbl |
714 // | 820 // |
715 bool check_single(mlfiPriv &priv, int ip, const char *suffix); | 821 bool check_single(mlfiPriv &priv, int ip, const char *suffix); |
716 bool check_single(mlfiPriv &priv, int ip, const char *suffix) { | 822 bool check_single(mlfiPriv &priv, int ip, const char *suffix) { |
717 // make a dns question | 823 // make a dns question |
758 else { | 864 else { |
759 st = (*f).second; | 865 st = (*f).second; |
760 rejectlist = (*f).first; | 866 rejectlist = (*f).first; |
761 } | 867 } |
762 if (st) return st; | 868 if (st) return st; |
763 } | |
764 return false; | |
765 } | |
766 | |
767 | |
768 //////////////////////////////////////////////// | |
769 // lookup the domain name part of a hostname on the uribl | |
770 // | |
771 // if we find part of the hostname on the uribl, return | |
772 // true and point found to the part of the hostname that we found | |
773 // as a string registered in hosts. | |
774 // otherwise, return false and preserve the value of found. | |
775 // | |
776 bool uriblookup(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *top, const char *&found) ; | |
777 bool uriblookup(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *top, const char *&found) { | |
778 // top is pointer to '.' char at end of base domain, or null for ip address form | |
779 // so for hostname of www.fred.mydomain.co.uk | |
780 // top points to-----------------------^ | |
781 // and we end up looking at only mydomain.co.uk, ignoring the www.fred stuff | |
782 char buf[maxlen]; | |
783 if (top) { | |
784 // add one more component | |
785 const char *x = (const char *)memrchr(hostname, '.', top-hostname); | |
786 if (x) hostname = x+1; | |
787 } | |
788 snprintf(buf, sizeof(buf), "%s.%s.", hostname, priv.uribl_suffix); | |
789 if (dns_interface(priv, buf, false, NULL)) { | |
790 if (debug_syslog > 2) { | |
791 char tmp[maxlen]; | |
792 snprintf(tmp, sizeof(tmp), "found %s on %s", hostname, priv.uribl_suffix); | |
793 my_syslog(tmp); | |
794 } | |
795 found = register_string(hosts, hostname); | |
796 return true; | |
797 } | |
798 return false; | |
799 } | |
800 | |
801 | |
802 //////////////////////////////////////////////// | |
803 // uribl checker | |
804 // ------------- | |
805 // hostname MUST not have a trailing dot | |
806 // If tld, two level lookup. | |
807 // Else, look up three level domain. | |
808 // | |
809 // if we find part of the hostname on the uribl, return | |
810 // true and point found to the part of the hostname that we found | |
811 // as a string registered in hosts. | |
812 // otherwise, return false and preserve the value of found. | |
813 // | |
814 bool check_uribl(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *&found) ; | |
815 bool check_uribl(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *&found) { | |
816 in_addr ip; | |
817 if (inet_aton(hostname, &ip)) { | |
818 const u_char *src = (const u_char *)&ip.s_addr; | |
819 if (src[0] == 127) return false; // don't do dns lookups on localhost | |
820 if (src[0] == 10) return false; // don't do dns lookups on rfc1918 space | |
821 if ((src[0] == 192) && (src[1] == 168)) return false; | |
822 if ((src[0] == 172) && (16 <= src[1]) && (src[1] <= 31)) return false; | |
823 char adr[sizeof "255.255.255.255 "]; | |
824 snprintf(adr, sizeof(adr), "%u.%u.%u.%u", src[3], src[2], src[1], src[0]); | |
825 // cannot use inet_ntop here since we want the octets reversed. | |
826 return (uriblookup(priv, hosts, adr, NULL, found)); | |
827 } | |
828 | |
829 const char *top, *top2, *top3; | |
830 top = strrchr(hostname, '.'); | |
831 if (top) { | |
832 top2 = (const char *)memrchr(hostname, '.', top-hostname); | |
833 | |
834 if (top2) { | |
835 string_set::iterator i = priv.memory->get_cctlds()->find(top2+1); | |
836 string_set::iterator x = priv.memory->get_cctlds()->end(); | |
837 // if we have a 2-level-cctld, just look at top three levels of the name | |
838 if (i != x) return uriblookup(priv, hosts, hostname, top2, found); | |
839 | |
840 // if we have more than 3 levels in the name, look at the top three levels of the name | |
841 top3 = (const char *)memrchr(hostname, '.', top2-hostname); | |
842 if (top3 && uriblookup(priv, hosts, hostname, top2, found)) return true; | |
843 | |
844 // if that was not found, fall thru to looking at the top two levels | |
845 } | |
846 // look at the top two levels of the name | |
847 return uriblookup(priv, hosts, hostname, top, found); | |
848 } | 869 } |
849 return false; | 870 return false; |
850 } | 871 } |
851 | 872 |
852 | 873 |
1178 if (!priv.content_context) priv.content_context = &con; | 1199 if (!priv.content_context) priv.content_context = &con; |
1179 else if (con.get_require() && (priv.content_context != &con)) { | 1200 else if (con.get_require() && (priv.content_context != &con)) { |
1180 smfi_setreply(ctx, (char*)"452", (char*)"4.2.1", (char*)"incompatible filtering contexts"); | 1201 smfi_setreply(ctx, (char*)"452", (char*)"4.2.1", (char*)"incompatible filtering contexts"); |
1181 return SMFIS_TEMPFAIL; | 1202 return SMFIS_TEMPFAIL; |
1182 } | 1203 } |
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.helo); | |
1210 uri = true; | |
1211 } | |
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.client_name); | |
1215 uri = true; | |
1216 } | |
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.mailaddr); | |
1220 uri = true; | |
1221 } | |
1222 if (uri) { | |
1223 char buf[maxlen]; | |
1224 snprintf(buf, sizeof(buf), priv.uribl_message, bu); | |
1225 smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", buf); | |
1226 return SMFIS_REJECT; | |
1227 } | |
1183 } | 1228 } |
1184 // remember the non-whites | 1229 // remember the non-whites |
1185 priv.need_content_filter(rcptaddr, con); | 1230 register_string(priv.env_to, rcptaddr, &con); |
1186 priv.only_whites = false; | 1231 priv.only_whites = false; |
1187 priv.want_spamassassin |= (priv.assassin) && // have spam assassin available and | 1232 priv.want_spamassassin |= (priv.assassin) && // have spam assassin available and |
1188 (con.get_spamassassin_limit() != 0); // want to use it with a non-zero score | 1233 (con.get_spamassassin_limit() != 0); // want to use it with a non-zero score |
1189 priv.want_dccgrey |= (priv.dccifd) && // have dcc interface and | 1234 priv.want_dccgrey |= (priv.dccifd) && // have dcc interface and |
1190 (con.get_grey()); // want to use it for greylisting | 1235 (con.get_grey()); // want to use it for greylisting |
1620 | 1665 |
1621 if (email) { | 1666 if (email) { |
1622 char *x = strchr(email, '|'); | 1667 char *x = strchr(email, '|'); |
1623 if (x) { | 1668 if (x) { |
1624 *x = '\0'; | 1669 *x = '\0'; |
1625 char *from = strdup(email); | 1670 const char *from = to_lower_string(email); |
1626 char *to = strdup(x+1); | 1671 const char *to = to_lower_string(x+1); |
1627 use_syslog = false; | 1672 use_syslog = false; |
1628 CONFIG *conf = new_conf(); | 1673 CONFIG *conf = new_conf(); |
1629 if (conf) { | 1674 if (conf) { |
1630 CONTEXTP con = conf->find_context(to); | 1675 CONTEXTP con = conf->find_context(to); |
1631 char buf[maxlen]; | 1676 char buf[maxlen]; |