comparison dcclib/dnsbl.c @ 0:c7f6b056b673

First import of vendor version
author Peter Gervai <grin@grin.hu>
date Tue, 10 Mar 2009 13:49:58 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:c7f6b056b673
1 /* Distributed Checksum Clearinghouse
2 *
3 * reject messages contain URLs that resolve to DNS blacklisted IP addresses
4 *
5 * Copyright (c) 2008 by Rhyolite Software, LLC
6 *
7 * This agreement is not applicable to any entity which sells anti-spam
8 * solutions to others or provides an anti-spam solution as part of a
9 * security solution sold to other entities, or to a private network
10 * which employs the DCC or uses data provided by operation of the DCC
11 * but does not provide corresponding data to other users.
12 *
13 * Permission to use, copy, modify, and distribute this software without
14 * changes for any purpose with or without fee is hereby granted, provided
15 * that the above copyright notice and this permission notice appear in all
16 * copies and any distributed versions or copies are either unchanged
17 * or not called anything similar to "DCC" or "Distributed Checksum
18 * Clearinghouse".
19 *
20 * Parties not eligible to receive a license under this agreement can
21 * obtain a commercial license to use DCC by contacting Rhyolite Software
22 * at sales@rhyolite.com.
23 *
24 * A commercial license would be for Distributed Checksum and Reputation
25 * Clearinghouse software. That software includes additional features. This
26 * free license for Distributed ChecksumClearinghouse Software does not in any
27 * way grant permision to use Distributed Checksum and Reputation Clearinghouse
28 * software
29 *
30 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
31 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
32 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
33 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
34 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
35 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
36 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
37 * SOFTWARE.
38 *
39 * Rhyolite Software DCC 1.3.103-1.100 $Revision$
40 */
41
42 #include "helper.h"
43 #include "dcc_heap_debug.h"
44 #ifndef DCC_WIN32
45 #include <sys/wait.h>
46 #include <arpa/inet.h>
47 #endif
48 #ifdef HAVE_RESOLV_H
49 #include <resolv.h>
50 #endif
51 #ifdef HAVE_ARPA_NAMESER_H
52 #include <arpa/nameser.h>
53 #endif
54
55 /* can check MX and NS addresses only with a standard resolver library */
56 #define MXNS_DNSBL
57 #if !defined(HAVE__RES) || !defined(HAVE_RES_INIT)
58 #undef HAVE__RES
59 #undef HAVE_RES_INIT
60 #undef MXNS_DNSBL
61 #endif
62 #if !defined (HAVE_RES_QUERY) || !defined(HAVE_DN_EXPAND)
63 #undef MXNS_DNSBL
64 #endif
65 #if !defined(HAVE_HSTRERROR)
66 #undef MXNS_DNSBL /* MX lookups need raw hsterror() */
67 #endif
68 #if !defined(T_MX) || !defined(T_A) || !defined(T_AAAA) || !defined(T_NS)
69 #undef MXNS_DNSBL
70 #endif
71 #if !defined(C_IN) || !defined(C_IN) || !defined(PACKETSZ)
72 #undef MXNS_DNSBL
73 #endif
74 #if !defined(RES_DEFNAMES) || !defined(RES_DNSRCH) || !defined(RES_NOALIASES)
75 #undef MXNS_DNSBL
76 #endif
77
78
79 DNSBL *dnsbls;
80 static DNSBL_UNHIT dnsbl_groups;
81 u_char have_dnsbl_groups;
82
83 HELPER helper;
84 u_char have_helpers;
85 DCC_PATH dnsbl_progpath;
86
87 static u_char is_helper;
88 static const char *helper_str = "";
89
90 static u_char have_ipv4_dnsbl;
91 static u_char have_ipv6_dnsbl;
92 static u_char have_name_dnsbl;
93
94 #define MAX_MSG_SECS 1000
95 #ifndef RES_TIMEOUT
96 #define RES_TIMEOUT 3
97 #endif
98 static int msg_secs = 25; /* total seconds/mail message */
99 static time_t msg_us;
100 static int url_secs = 11; /* total seconds/host name */
101 static time_t url_us;
102
103
104 /* Parse a string of the form:
105 * "domain[,[IPaddr][,name|ipv4|ipv6]]
106 * or of one of the forms:
107 * set:progpath path of helper program
108 * set:debug=X more logging
109 * set:msg-secs=S total seconds checking blacklists/message
110 * set:url-secs=S total seconds per host name
111 * set:[no-]client client IP address checks
112 * set:[no-]mail_host envelope mail_from checks
113 * set:[no-]URL body URL checks
114 * set:[no-]MX MX checks
115 * set:[no-]NS NS checks
116 * set:defaults restore defaults
117 * set:group=X start group of DNSBLs
118 * set:[no-]temp-fail timeout temporarily fails SMTP transaction
119 * set:max_helpers=X override dccm or dccifd max_work
120 *
121 * set:[no-]envelope obsolete mail host & client
122 *
123 * set:helper=soc,fd,X start DNS resolver process
124 */
125 u_char /* 0=bad */
126 dcc_parse_dnsbl(DCC_EMSG emsg, const char *entry, const char *progpath,
127 u_char tfail)
128 {
129 static DNSBL_FGS cur_fgs = DNSBL_FG_DEFAULT;
130 static const REPLY_TPLT *cur_reply = 0;
131 static int bl_num = 0;
132 static int cur_group = 0;
133 DNSBL_GBITS gbit;
134 DNSBL *dp, *dp1, **dpp;
135 const char *tgt_ip; /* "hit" IP address of this blacklist */
136 DNSBL_DOM tgt_ip_buf;
137 DNSBL_TYPE bl_type;
138 struct in6_addr tgt, tgt_mask;
139 int tgt_bits;
140 u_char tgt_use_ipv6;
141 DCC_EMSG addr_emsg;
142 int error, bl_dom_len;
143 int val;
144 char *p;
145 #ifdef HAVE_HELPERS
146 int soc;
147 int fd;
148 int total_helpers;
149 # define SAVE_ARG(arg) helper_save_arg("-B", arg)
150 #else
151 # define SAVE_ARG(arg)
152 #endif
153
154 if (progpath && dnsbl_progpath[0] == '\0') {
155 const char *slash = strrchr(progpath, '/');
156 if (slash)
157 ++slash;
158 else
159 slash = progpath;
160 snprintf(dnsbl_progpath, sizeof(dnsbl_progpath),
161 "%.*sdns-helper", (int)(slash-progpath), progpath);
162 }
163
164 /* pretend to turn on temp-fail for dccproc to get
165 * DNSBLx(timeout) messages */
166 if (tfail)
167 cur_fgs |= DNSBL_FG_TFAIL;
168
169 /* handle parameter settings */
170 if (!CLITCMP(entry, "set:")) {
171 const char *parm = entry+LITZ("set:");
172 #ifdef HAVE_HELPERS
173 /* start running a helper process on the last, magic -B */
174 if (3 == sscanf(parm, HELPER_PAT, &soc, &fd, &total_helpers)) {
175 helper_child(soc, fd, total_helpers);
176 }
177 #endif
178 if (!CLITCMP(parm, "progpath=")) {
179 BUFCPY(dnsbl_progpath, parm+LITZ("progpath="));
180 return 1;
181 }
182 if (!strcasecmp(parm, "debug")) {
183 ++helper.debug;
184 SAVE_ARG(entry);
185 return 1;
186 }
187 if (1 == sscanf(parm, "debug=%d", &val)) {
188 helper.debug = val;
189 SAVE_ARG(entry);
190 return 1;
191 }
192 if (!strcasecmp(parm, "envelope")) { /* obsolete */
193 cur_fgs |= (DNSBL_FG_CLIENT | DNSBL_FG_MAIL_HOST);
194 SAVE_ARG(entry);
195 return 1;
196 }
197 if (!strcasecmp(parm, "no-envelope")) { /* obsolete */
198 cur_fgs &= ~(DNSBL_FG_CLIENT | DNSBL_FG_MAIL_HOST);
199 SAVE_ARG(entry);
200 return 1;
201 }
202 if (!strcasecmp(parm, "client")) {
203 /* obsolete */
204 cur_fgs |= DNSBL_FG_CLIENT;
205 SAVE_ARG(entry);
206 return 1;
207 }
208 if (!strcasecmp(parm, "no-client")) {
209 /* obsolete */
210 cur_fgs &= ~DNSBL_FG_CLIENT;
211 SAVE_ARG(entry);
212 return 1;
213 }
214 if (!strcasecmp(parm, "mail_host")) {
215 /* obsolete */
216 cur_fgs |= DNSBL_FG_MAIL_HOST;
217 SAVE_ARG(entry);
218 return 1;
219 }
220 if (!strcasecmp(parm, "no-mail_host")) {
221 /* obsolete */
222 cur_fgs &= ~DNSBL_FG_MAIL_HOST;
223 SAVE_ARG(entry);
224 return 1;
225 }
226 if (!strcasecmp(parm, "body")) { /* obsolete */
227 cur_fgs |= DNSBL_FG_URL;
228 SAVE_ARG(entry);
229 return 1;
230 }
231 if (!strcasecmp(parm, "url")) {
232 cur_fgs |= DNSBL_FG_URL;
233 SAVE_ARG(entry);
234 return 1;
235 }
236 if (!strcasecmp(parm, "no-body")) { /* obsolete */
237 cur_fgs &= ~DNSBL_FG_URL;
238 SAVE_ARG(entry);
239 return 1;
240 }
241 if (!strcasecmp(parm, "no-URL")) {
242 cur_fgs &= ~DNSBL_FG_URL;
243 SAVE_ARG(entry);
244 return 1;
245 }
246 if (!strcasecmp(parm, "mx")) {
247 #ifdef MXNS_DNSBL
248 cur_fgs |= DNSBL_FG_MX;
249 SAVE_ARG(entry);
250 return 1;
251 #else
252 dcc_pemsg(EX_USAGE, emsg,
253 "MX DNS blacklist checks not supported");
254 return 0;
255 #endif
256 }
257 if (!strcasecmp(parm, "no-mx")) {
258 cur_fgs &= ~DNSBL_FG_MX;
259 SAVE_ARG(entry);
260 return 1;
261 }
262 if (!strcasecmp(parm, "ns")) {
263 #ifdef MXNS_DNSBL
264 cur_fgs |= DNSBL_FG_MX;
265 SAVE_ARG(entry);
266 return 1;
267 #else
268 dcc_pemsg(EX_USAGE, emsg,
269 "NS DNS blacklist checks not supported");
270 return 0;
271 #endif
272 }
273 if (!strcasecmp(parm, "no-ns")) {
274 cur_fgs &= ~DNSBL_FG_NS;
275 SAVE_ARG(entry);
276 return 1;
277 }
278 if (!strcasecmp(parm, "defaults")) {
279 cur_fgs = DNSBL_FG_DEFAULT;
280 SAVE_ARG(entry);
281 return 1;
282 }
283 if (!CLITCMP(parm, "rej-msg=")) {
284 parm += LITZ("rej-msg=");
285 if (*parm == '\0')
286 cur_reply = 0;
287 else
288 cur_reply = dnsbl_parse_reply(parm);
289 /* do not save for helpers */
290 return 1;
291 }
292 if (!CLITCMP(parm, "msg-secs=")) {
293 parm += LITZ("msg-secs=");
294 val = strtoul(parm, &p, 10);
295 if (*p != '\0' || val < 1 || val > MAX_MSG_SECS) {
296 dcc_pemsg(EX_USAGE, emsg,
297 "bad number of seconds in \"-B %s\"",
298 entry);
299 return 0;
300 }
301 if (msg_secs != val) {
302 msg_secs = val;
303 SAVE_ARG(entry);
304 }
305 return 1;
306 }
307 if (!CLITCMP(parm, "url-secs=")) {
308 parm += LITZ("url-secs=");
309 val = strtoul(parm, &p, 10);
310 if (*p != '\0' || val < 1 || val > MAX_MSG_SECS) {
311 dcc_pemsg(EX_USAGE, emsg,
312 "bad number of seconds in \"-B %s\"",
313 entry);
314 return 0;
315 }
316 if (url_secs != val) {
317 url_secs = val;
318 SAVE_ARG(entry);
319 }
320 return 1;
321 }
322 if (1 == sscanf(parm, "group=%d", &val)
323 && val >= 1 && val <= MAX_DNSBL_GROUPS) {
324 cur_group = val-1;
325 have_dnsbl_groups = 1;
326 SAVE_ARG(entry);
327 return 1;
328 }
329 if (!strcasecmp(parm, "temp-fail")) {
330 cur_fgs |= DNSBL_FG_TFAIL;
331 SAVE_ARG(entry);
332 return 1;
333 }
334 if (!strcasecmp(parm, "no-temp-fail")) {
335 cur_fgs &= ~DNSBL_FG_TFAIL;
336 SAVE_ARG(entry);
337 return 1;
338 }
339 if (1 == sscanf(parm, "max_helpers=%d", &val)
340 && val >= 1 && val < 1000) {
341 helper.max_helpers = val;
342 SAVE_ARG(entry);
343 return 1;
344 }
345 dcc_pemsg(EX_USAGE, emsg, "unrecognized \"-B %s\"",
346 entry);
347 return 0;
348 }
349
350 /* we must have a DNSBL specification */
351 bl_type = DNSBL_TYPE_IPV4; /* assume it is a simple IPv4 DNSBL */
352 tgt_ip = strchr(entry, ',');
353 if (!tgt_ip) {
354 bl_dom_len = strlen(entry);
355 have_ipv4_dnsbl = 1;
356 } else {
357 bl_dom_len = tgt_ip - entry;
358 ++tgt_ip;
359
360 /* notice trailing ",name" or ",IPv4" */
361 p = strchr(tgt_ip, ',');
362 if (!p) {
363 have_ipv4_dnsbl = 1;
364 } else {
365 ++p;
366 if (!strcasecmp(p, "name")) {
367 bl_type = DNSBL_TYPE_NAME;
368 have_name_dnsbl = 1;
369 } else if (!strcasecmp(p, "IPV4")) {
370 bl_type = DNSBL_TYPE_IPV4;
371 have_ipv4_dnsbl = 1;
372 } else if (!strcasecmp(p, "IPV6")) {
373 bl_type = DNSBL_TYPE_IPV6;
374 have_ipv6_dnsbl = 1;
375 } else {
376 dcc_pemsg(EX_NOHOST, emsg,
377 "unknown DNSBL type in \"%s\"",
378 entry);
379 return 0;
380 }
381 STRLCPY(tgt_ip_buf.c, tgt_ip,
382 min(ISZ(tgt_ip_buf), p-tgt_ip));
383 tgt_ip = tgt_ip_buf.c;
384 }
385 }
386
387 if (entry[0] == '.') {
388 ++entry;
389 --bl_dom_len;
390 }
391 if (bl_dom_len < 1) {
392 dcc_pemsg(EX_NOHOST, emsg,
393 "invalid DNS blacklist \"%s\"", entry);
394 return 0;
395 }
396
397 /* assume 127.0.0.2 if the target address is missing */
398 if (!tgt_ip || *tgt_ip == '\0')
399 tgt_ip = "127.0.0.2";
400
401 if (!strcasecmp(tgt_ip, "any")) {
402 /* That we get a result when we lookup up an address in the
403 * in the DNSBL is all that matters in this case.
404 * Whether it is IPv6 or IPv4 does not */
405 tgt_use_ipv6 = 2;
406 memset(&tgt, 0, sizeof(tgt));
407 memset(&tgt_mask, 0, sizeof(tgt_mask));
408
409 } else if (0 != (tgt_bits = dcc_str2cidr(emsg, &tgt, &tgt_mask,
410 &tgt_use_ipv6, tgt_ip,
411 0, 0))) {
412 if (tgt_bits < 0) {
413 dcc_pemsg(EX_NOHOST, addr_emsg,
414 "invalid DNS blacklist %s \"%s\"",
415 addr_emsg, entry);
416 return 0;
417 }
418
419 } else {
420 dcc_host_lock();
421 if (dcc_get_host(tgt_ip, 3, &error)) {
422 /* prefer an IPv4 target address */
423 if (dcc_hostaddrs[0].sa.sa_family == AF_INET) {
424 tgt_use_ipv6 = 0;
425 dcc_ipv4toipv6(&tgt,
426 dcc_hostaddrs[0].ipv4.sin_addr);
427 } else {
428 tgt_use_ipv6 = 1;
429 tgt = dcc_hostaddrs[0].ipv6.sin6_addr;
430 }
431 dcc_host_unlock();
432 dcc_bits2mask(&tgt_mask, 128);
433
434 } else {
435 dcc_host_unlock();
436 dcc_pemsg(EX_NOHOST, emsg,
437 "invalid DNSBL target IP address \"%s\": %s",
438 tgt_ip, DCC_HSTRERROR(error));
439 return 0;
440 }
441 }
442
443 if (bl_dom_len >= ISZ(DNSBL_DOM) - INET6_ADDRSTRLEN*2) {
444 dcc_host_unlock();
445 /* we cannot fit the DNSBL base and the target name or address
446 * into blw->probe. We need to do DNS lookups of names
447 * like 33.22.11.10.dnsbl.example.com or
448 * domain.dom.bl.example.com */
449 dcc_pemsg(EX_NOHOST, emsg, "DNSBL name \"%s\" too long", entry);
450 return 0;
451 }
452
453 dp = dcc_malloc(sizeof(*dp));
454 memset(dp, 0, sizeof(*dp));
455
456 dp->tgt_use_ipv6 = tgt_use_ipv6;
457 dp->tgt = tgt;
458 dp->tgt_mask = tgt_mask;
459
460 dp->bl_type = bl_type;
461 dp->fgs = cur_fgs;
462 dp->reply = cur_reply;
463 memcpy(&dp->bl_dom.c, entry, bl_dom_len);
464 dp->bl_dom_len = bl_dom_len+1; /* count trailing '\0' */
465 dp->bl_num = ++bl_num;
466 dp->group = cur_group;
467 gbit = DNSBL_G2B(dp->group);
468 dnsbl_groups.all |= gbit;
469 if (cur_fgs & DNSBL_FG_CLIENT)
470 dnsbl_groups.client |= gbit;
471 if (cur_fgs & DNSBL_FG_MAIL_HOST) {
472 dnsbl_groups.mail_host |= gbit;
473 if (cur_fgs & DNSBL_FG_MX)
474 dnsbl_groups.mail_host_mx |= gbit;
475 if (cur_fgs & DNSBL_FG_NS)
476 dnsbl_groups.mail_host_ns |= gbit;
477 }
478 if (cur_fgs & DNSBL_FG_URL) {
479 dnsbl_groups.url |= gbit;
480 if (cur_fgs & DNSBL_FG_MX)
481 dnsbl_groups.url_mx |= gbit;
482 if (cur_fgs & DNSBL_FG_NS)
483 dnsbl_groups.url_ns |= gbit;
484 }
485
486 /* link the DNSBLs in the order they are declared so that they
487 * can be prioritized */
488 dpp = &dnsbls;
489 for (;;) {
490 dp1 = *dpp;
491 if (!dp1) {
492 *dpp = dp;
493 break;
494 }
495 /* notice sufficiently duplicate DNSBLS */
496 if (!(dp->fgs & DNSBL_FG_DUP) && !dp1->dup
497 && dp->tgt_use_ipv6 == dp1->tgt_use_ipv6
498 && dp->bl_type == dp1->bl_type
499 && dp->bl_dom_len == dp1->bl_dom_len
500 && !memcmp(dp->bl_dom.c, dp1->bl_dom.c, dp->bl_dom_len)) {
501 dp->fgs |= DNSBL_FG_DUP;
502 dp1->dup = dp;
503 }
504 dpp = &dp1->fwd;
505 }
506
507 SAVE_ARG(entry);
508 return 1;
509 #undef SAVE_ARG
510 }
511
512
513
514 /* resolve inconsistencies among the -B parameters */
515 static inline void
516 fix_url_secs(void)
517 {
518 if (url_secs > msg_secs)
519 url_secs = msg_secs;
520 msg_us = msg_secs * DCC_US;
521 url_us = url_secs * DCC_US;
522 }
523
524
525
526 static char *
527 type_str(char *buf, int buf_len, const DNSBL_WORK *blw, DNSBL_FGS fgs)
528 {
529 const char *type;
530 char sustr[DCC_SU2STR_SIZE];
531 int i;
532
533 switch (fgs & DNSBL_FG_TYPES) {
534 case DNSBL_FG_CLIENT:
535 type = "SMTP client";
536 if (blw->tgt.dom.c[0] == '\0') {
537 snprintf(buf, buf_len, "%s %s",
538 type, dcc_ipv6tostr2(sustr, sizeof(sustr),
539 &blw->tgt.addr));
540 return buf;
541 }
542 break;
543 case DNSBL_FG_MAIL_HOST:
544 type = "mail_host";
545 break;
546 case DNSBL_FG_URL:
547 type = "URL";
548 break;
549 case DNSBL_FG_MX | DNSBL_FG_MAIL_HOST :
550 type = "mail_host MX";
551 break;
552 case DNSBL_FG_MX | DNSBL_FG_URL:
553 type = "URL MX";
554 break;
555 case DNSBL_FG_NS | DNSBL_FG_MAIL_HOST:
556 type = "mail_host NS";
557 break;
558 case DNSBL_FG_NS | DNSBL_FG_URL:
559 type = "URL NS";
560 break;
561
562 default:
563 dcc_logbad(EX_SOFTWARE, "impossible DNSBL hit type %#x", fgs);
564 break;
565 }
566
567 i = snprintf(buf, buf_len, "%s %s", type, blw->tgt.dom.c);
568 if (i >= buf_len && buf_len > 4)
569 strcpy(&buf[buf_len-4], "...");
570 return buf;
571 }
572
573
574
575 static void PATTRIB(4,5)
576 dnsbl_log(const DNSBL_WORK *blw, const DNSBL_GROUP *blg, DNSBL_FGS fgs,
577 const char *pat, ...)
578 {
579 char type_buf[sizeof(blg->btype_buf)];
580 const char *type0, *type1;
581 char gbuf[8];
582 const char *gnum;
583 char msg[256];
584 va_list args;
585
586 va_start(args, pat);
587 vsnprintf(msg, sizeof(msg), pat, args);
588 va_end(args);
589
590 if (fgs) {
591 type0 = " ";
592 type1 = type_str(type_buf, sizeof(type_buf), blw, fgs);
593 } else {
594 type0 = "";
595 type1 = "";
596 }
597 if (!have_dnsbl_groups) {
598 gnum = "";
599 } else if (!blg) {
600 gnum = "*";
601 } else {
602 snprintf(gbuf, sizeof(gbuf), "%d",
603 (int)(blg - blw->groups)+1);
604 gnum = gbuf;
605 }
606
607 if (helper.debug) {
608 if (dcc_no_syslog)
609 thr_trace_msg(blw->log_ctxt, "DNSBL%s%s%s%s %s",
610 gnum, helper_str, type0, type1, msg);
611 else
612 thr_trace_msg(blw->log_ctxt, "%s DNSBL%s%s%s%s %s",
613 blw->id,
614 gnum, helper_str, type0, type1, msg);
615 } else {
616 thr_log_print(blw->log_ctxt, 1, "DNSBL%s%s%s%s %s\n",
617 helper_str, gnum, type0, type1, msg);
618 }
619 }
620
621
622
623 void
624 dcc_dnsbl_result(ASK_ST *ask_stp, DNSBL_WORK *blw)
625 {
626 DNSBL *dp;
627 DNSBL_GROUP *blg;
628 int gnum;
629
630 if (!blw)
631 return;
632
633 for (gnum = 0, blg = blw->groups;
634 gnum < MAX_DNSBL_GROUPS;
635 ++gnum, ++blg) {
636 if (blg->fgs & DNSBL_FG_HITS) {
637 *ask_stp |= (ASK_ST_DNSBL_HIT(gnum) | ASK_ST_LOGIT);
638 dnsbl_log(blw, blg, 0, "%s %s=%s",
639 blg->btype, blg->probe.c, blg->result);
640 } else if (blg->fgs & DNSBL_FG_TIMEO) {
641 *ask_stp |= ASK_ST_DNSBL_TIMEO(gnum);
642 for (dp = dnsbls; dp; dp = dp->fwd) {
643 if (dp->group != gnum)
644 continue;
645 if (dp->fgs & DNSBL_FG_TFAIL) {
646 *ask_stp |= ASK_ST_DNSBL_TFAIL(gnum);
647 break;
648 }
649 }
650 }
651 }
652 }
653
654
655
656 /* There are several timing requirements:
657 * - Do not spend too much time on any single URL or envelope value.
658 * - At helper.debug >=1, log the first DNSBL check in a group
659 * unfinished for lack of time. Also log checks for single URLs that
660 * are partially unfinished.
661 * - At helper.debug >=2, log things that take a long time
662 * - Minimize log messages, because the noise can be deafening.
663 * - Mark unfinished groups */
664
665
666 /* A check has been abandoned or timed out. */
667 static void
668 set_timeo(DNSBL_WORK *blw, DNSBL_FGS fgs)
669 {
670 DNSBL *dp;
671 DNSBL_GROUP *blg;
672 int gnum;
673
674 /* groups that are marked as having suffered timeouts can be
675 * hit by later URLs */
676 if (fgs == 0) {
677 /* mark all groups if we don't know the type of request */
678 for (gnum = 0, blg = blw->groups;
679 gnum < MAX_DNSBL_GROUPS;
680 ++gnum, ++blg) {
681 /* ignore groups that have hit or timed out */
682 if (!(blw->unhit.all & DNSBL_G2B(gnum))
683 || (blg->fgs & DNSBL_FG_TIMEO))
684 continue;
685 blg->fgs |= DNSBL_FG_TIMEO;
686 }
687
688 } else {
689 /* mark only groups that still care about this type */
690 for (dp = dnsbls; dp; dp = dp->fwd) {
691 /* this DNSBL does not care about this type
692 * or the group to which it belongs has been hit */
693 if (!(dp->fgs & fgs & DNSBL_FG_HITS)
694 || !(blw->unhit.all & DNSBL_G2B(dp->group)))
695 continue;
696 blw->groups[dp->group].fgs |= DNSBL_FG_TIMEO;
697 }
698 }
699
700 /* no complaint if time remains */
701 if (blw->url_us >= blw->url_us_used)
702 return;
703
704 /* no more log messages if everything has been complained about */
705 if (blw->url_us < 0 || blw->msg_us < 0)
706 return;
707
708 /* only one final message/URL */
709 blw->url_us = -1;
710 if (helper.debug < 2)
711 return;
712
713 if (is_helper) {
714 /* Messages from the helper process go to the system
715 * log but not the per-message log file.
716 * The helper works on a single URL or envelope value
717 * and so does not know about the time limit for the
718 * entire message. */
719 dnsbl_log(blw, 0, fgs, "failed after %.1f url_secs used",
720 blw->url_us_used / (DCC_US*1.0));
721
722 } else if (blw->msg_us > blw->url_us_used) {
723 /* time remains for later URLs */
724 dnsbl_log(blw, 0, fgs,
725 "failed after using %.1f url_secs;"
726 " %.1f msg-secs remain",
727 blw->url_us_used / (DCC_US*1.0),
728 (blw->msg_us - blw->url_us_used) / (DCC_US*1.0));
729
730 } else {
731 dnsbl_log(blw, 0, fgs, "failed after using %.1f sec",
732 blw->url_us_used / (DCC_US*1.0));
733 }
734 }
735
736
737
738 /* see if we are out of time before doing something */
739 static inline u_char /* 0=too late */
740 time_ck_pre(DNSBL_WORK *blw, DNSBL_FGS fgs)
741 {
742 /* don't worry if there is plenty of time */
743 if (blw->url_us >= blw->url_us_used)
744 return 1;
745
746 /* There is no more time. Ether previous operations succeeded slowly
747 * or failed and were logged.
748 * In the first case, log this operation and mark the groups. */
749 set_timeo(blw, fgs);
750 return 0;
751 }
752
753
754
755 /* see if we ran out of time after doing something */
756 static u_char /* 0=out of time */
757 time_ck_post(DNSBL_WORK *blw, DNSBL_FGS fgs,
758 u_char timedout) /* 1=operation timed out */
759 {
760 struct timeval now;
761 time_t used_us;
762
763 if (blw->url_us <= 0)
764 return 0; /* previously out of time */
765
766 gettimeofday(&now, 0);
767 used_us = tv_diff2us(&now, &blw->url_start);
768
769 if (blw->url_us >= used_us && !timedout) {
770 if (helper.debug > 1
771 && (used_us-blw->url_us_used) > url_us/4)
772 dnsbl_log(blw, 0, fgs, "%s after using %.1f url_secs",
773 timedout ? "failed" : "succeeded",
774 (used_us - blw->url_us_used) / (DCC_US*1.0));
775 blw->url_us_used = used_us;
776 return 1;
777 }
778
779 blw->url_us_used = used_us;
780 set_timeo(blw, fgs);
781 return 0;
782 }
783
784
785
786 /* start timer before we start to check something in the DNS blacklists
787 * give up if we are already out of time */
788 static u_char /* 0=already too much time spent */
789 msg_secs_start(DNSBL_WORK *blw, DNSBL_FGS fgs)
790 {
791 blw->url_us = url_us;
792 blw->url_us_used = 0;
793
794 if (blw->msg_us <= 0) {
795 if (blw->msg_us == 0) {
796 /* out of time for next URL before we start it */
797 if (helper.debug)
798 dnsbl_log(blw, 0, fgs,
799 "%d msg-secs already exhausted",
800 msg_secs);
801 blw->msg_us = -1; /* only one log message */
802 blw->url_us = -1;
803 }
804 /* mark the groups but do not log anything */
805 set_timeo(blw, fgs);
806 return 0;
807 }
808
809 gettimeofday(&blw->url_start, 0);
810 return 1;
811 }
812
813
814
815 /* account for time used */
816 static void
817 msg_secs_fin(DNSBL_WORK *blw)
818 {
819 if (blw->msg_us < 0)
820 return; /* prevously out of time */
821
822 blw->msg_us -= blw->url_us_used;
823 if (blw->msg_us > 0)
824 return;
825
826 if (blw->url_us >= blw->url_us_used) {
827 /* The clock had no time for more DNS work for this
828 * name or address, but we finished and so it might
829 * not matter.
830 * Ensure a log message on the next check, if any */
831 blw->msg_us = 0;
832 }
833 }
834
835
836
837 #ifndef HAVE__RES
838 static void
839 dnsbl_res_delays(const DNSBL_WORK *blw UATTRIB)
840 {
841 return;
842 }
843 #else
844 /* Limit resolver delays to as much as we are willing to wait
845 * We should be talking to a local caching resolver. If it does not answer
846 * immediately, it is unlikely to later. If it does eventually get an
847 * answer, the answer will probably ready the next time we ask.
848 * dcc_host_lock() must be held. */
849 static void
850 dnsbl_res_delays(const DNSBL_WORK *blw)
851 {
852 static int init_res_retrans;
853 static int init_res_retry;
854 int budget; /* seconds we can afford */
855 int res_retrans; /* retransmition delay */
856 int res_retry; /* # of retransmissions */
857 int total; /* retrans*retry = worst case delay */
858
859 /* get the current value */
860 if (!_res.options & RES_INIT) {
861 res_init();
862 init_res_retry = _res.retry;
863 if (!init_res_retry)
864 init_res_retry = 4;
865 init_res_retrans = _res.retrans;
866 if (!init_res_retrans)
867 init_res_retrans = RES_TIMEOUT;
868 }
869 res_retry = init_res_retry;
870 res_retrans = init_res_retrans;
871
872 /* assume binary exponential backoffs as in the BIND resolver */
873 total = ((1<<res_retry) -1) * res_retrans;
874
875 /* If the default values could take too long, then try 2 seconds.
876 * If that is still too long, go to 1 retransmission and an initial
877 * retransmission delay of 1/3 of the total allowed delay.
878 * 1/3 from one exponential backoff for a total of 2**2-1=3 times
879 * the initial delay.
880 * We should be using a local caching DNS server and so should not
881 * see many lost packets */
882 budget = (blw->url_us - blw->url_us_used + DCC_US/2) / DCC_US;
883 if (budget < 1)
884 budget = 1;
885
886 if (total >= budget) {
887 res_retry = 2; /* 2 retries are often few enough */
888 total = ((1<<res_retry) -1) * res_retrans;
889
890 /* if that is not few enough,
891 * then reduce the retransmission delay to fit */
892 if (total >= budget) {
893 res_retrans = budget/3;
894 if (res_retrans == 0)
895 res_retrans = 1;
896 }
897 }
898
899 if (_res.retry != res_retry
900 || _res.retrans != res_retrans) {
901 _res.retry = res_retry;
902 _res.retrans = res_retrans;
903 if (helper.debug > 4)
904 dnsbl_log(blw, 0, 0,
905 "budget=%d _res.retry=%d"
906 " _res.retrans=%d seconds",
907 budget, res_retry, res_retrans);
908 }
909 }
910 #endif /* !HAVE__RES */
911
912
913
914 static inline void
915 blw_clear(DNSBL_WORK *blw)
916 {
917 DNSBL_GROUP *blg;
918
919 blw->tgt.dom.c[0] = '\0';
920 blw->tgt_dom_len = 0;
921 for (blg = blw->groups; blg <= LAST(blw->groups); ++blg) {
922 blg->fgs = 0;
923 blg->btype = 0;
924 blg->result[0] = '\0';
925 blg->tgt.c[0] = '\0';
926 blg->probe.c[0] = '\0';
927 }
928 }
929
930
931
932 /* get ready to handle a mail message */
933 void
934 dcc_dnsbl_init(DCC_GOT_CKS *cks,
935 DCC_CLNT_CTXT *dcc_ctxt, void *log_ctxt, const char *id)
936 {
937 DNSBL_WORK *blw;
938 int i;
939
940 if (!dnsbls)
941 return;
942
943 blw = cks->dnsbl;
944 if (!blw) {
945 blw = dcc_malloc(sizeof(*blw));
946 memset(blw, 0, sizeof(*blw));
947 cks->dnsbl = blw;
948
949 /* general initializations on the first use of DNS blacklists */
950 fix_url_secs();
951 }
952
953 blw_clear(blw);
954 blw->msg_us = msg_us;
955 blw->id = id;
956 blw->dcc_ctxt = dcc_ctxt;
957 blw->log_ctxt = log_ctxt;
958 blw->unhit = dnsbl_groups;
959 for (i = 0; i < DIM(blw->tgt_cache); ++i)
960 blw->tgt_cache[i].dom.c[0] = '\0';
961 blw->tgt_cache_pos = 0;
962 }
963
964
965
966 /* look for a host name or IP address in a DNS blacklist and its duplicates
967 * and any hit groups of DNSBLs.
968 * These DNS operations should be done with local default values for
969 * RES_DEFNAMES, RES_DNSRCH, and RES_NOALIASES because the blacklist
970 * might be something local and strange. */
971 static u_char /* 0=no more time or blacklists */
972 lookup(DNSBL_WORK *blw,
973 const DNSBL_DOM *probe, /* look for this */
974 const DNSBL *dp, /* check this blacklist & its dups */
975 DNSBL_FGS fgs, /* type of lookup */
976 const void *tgt) /* IP address or whatever for tracing */
977 {
978 # define NUM_SUSTRS 3
979 char sustrs[DCC_SU2STR_SIZE*NUM_SUSTRS+1+sizeof("...")];
980 const DCC_SOCKU *hap;
981 struct in6_addr addr6;
982 const struct in6_addr *addr6p;
983 int error;
984 DNSBL_GROUP *blg;
985 DNSBL_GBITS gbit;
986 u_char hit;
987 char *p;
988 int i;
989
990 if (!time_ck_pre(blw, fgs))
991 return 0;
992
993 /* resolve a list of IP addresses for the probe in the DNSBL */
994 dcc_host_lock();
995 dnsbl_res_delays(blw);
996 if (!dcc_get_host(probe->c, dp->tgt_use_ipv6, &error)) {
997 dcc_host_unlock();
998 if (helper.debug > 1)
999 dnsbl_log(blw, 0, fgs, "gethostbyname(%s): %s",
1000 probe->c, DCC_HSTRERROR(error));
1001 return time_ck_post(blw, fgs, error == TRY_AGAIN);
1002 }
1003
1004 /* check each address obtained for the probe in the DNSBL
1005 * for a hit in the list of duplicate references to this DNSBL */
1006 hit = 0;
1007 do {
1008 /* skip this reference if it is for the wrong type of DNSBL */
1009 if ((dp->fgs & fgs) != fgs)
1010 continue;
1011 /* skip this reference if the containing group has a hit */
1012 blg = &blw->groups[dp->group];
1013 if (blg->fgs & DNSBL_FG_HITS)
1014 continue;
1015 /* check all of the addresses for a hit with this reference */
1016 for (hap = dcc_hostaddrs; hap < dcc_hostaddrs_end; ++hap) {
1017 /* finished if any answer is good enough
1018 * or if we get a match */
1019 if (hap->sa.sa_family == AF_INET6) {
1020 addr6p = &hap->ipv6.sin6_addr;
1021 } else {
1022 addr6p = &addr6;
1023 dcc_ipv4toipv6(&addr6, hap->ipv4.sin_addr);
1024 }
1025 if (!DCC_IN_BLOCK(*addr6p, dp->tgt, dp->tgt_mask))
1026 continue;
1027
1028 /* got a hit */
1029 blg->dnsbl = dp;
1030 blg->fgs = fgs;
1031 if (dp->bl_type == DNSBL_TYPE_IPV4)
1032 dcc_ipv4tostr(blg->tgt.c, sizeof(blg->tgt),
1033 tgt);
1034 else if (dp->bl_type == DNSBL_TYPE_IPV6)
1035 dcc_ipv6tostr(blg->tgt.c, sizeof(blg->tgt),
1036 tgt);
1037 else if (dp->bl_type == DNSBL_TYPE_NAME)
1038 BUFCPY(blg->tgt.c, tgt);
1039 blg->btype = type_str(blg->btype_buf,
1040 sizeof(blg->btype_buf),
1041 blw, blg->fgs);
1042 BUFCPY(blg->probe.c, probe->c);
1043 dcc_su2str2(blg->result, sizeof(blg->result), hap);
1044 if (helper.debug > 1 && (is_helper || !have_helpers))
1045 dnsbl_log(blw, blg, fgs, "hit %s=%s",
1046 probe->c,
1047 dcc_su2str2(sustrs, sizeof(sustrs),
1048 hap));
1049 gbit = DNSBL_G2B(dp->group);
1050 if (fgs & DNSBL_FG_CLIENT)
1051 blw->unhit.client &= ~gbit;
1052 if (fgs & DNSBL_FG_MAIL_HOST) {
1053 blw->unhit.mail_host &= ~gbit;
1054 if (fgs & DNSBL_FG_MX)
1055 blw->unhit.mail_host_mx &= ~gbit;
1056 if (fgs & DNSBL_FG_NS)
1057 blw->unhit.mail_host_ns &= ~gbit;
1058 }
1059 if (fgs & DNSBL_FG_URL) {
1060 blw->unhit.url &= ~gbit;
1061 if (fgs & DNSBL_FG_MX)
1062 blw->unhit.url_mx &= ~gbit;
1063 if (fgs & DNSBL_FG_NS)
1064 blw->unhit.url_ns &= ~gbit;
1065 }
1066 blw->unhit.all &= ~gbit;
1067 if (!blw->unhit.all) {
1068 dcc_host_unlock();
1069 time_ck_post(blw, fgs, 0);
1070 return 0;
1071 }
1072 hit = 1;
1073 break;
1074 }
1075
1076 /* check the results in duplicate references to the DNSBL */
1077 } while ((dp = dp->dup) != 0);
1078
1079 if (hit || helper.debug < 2) {
1080 dcc_host_unlock();
1081 return time_ck_post(blw, fgs, 0);
1082 }
1083
1084 p = sustrs;
1085 i = 0;
1086 do {
1087 dcc_su2str2(p, DCC_SU2STR_SIZE, &dcc_hostaddrs[i]);
1088 p += strlen(p);
1089 *p++ = ' ';
1090 *p = '\0';
1091 if (p >= &sustrs[DCC_SU2STR_SIZE*NUM_SUSTRS]) {
1092 strcpy(p, "...");
1093 break;
1094 }
1095 } while (dcc_hostaddrs_end > &dcc_hostaddrs[++i]);
1096 dcc_host_unlock();
1097 dnsbl_log(blw, 0, fgs, "miss; gethostbyname(%s)=%s", probe->c, sustrs);
1098 return time_ck_post(blw, fgs, 0);
1099
1100 #undef NUM_SUSTRS
1101 }
1102
1103
1104
1105 /* check one IP address against the DNS blacklists */
1106 static u_char /* 0=no more time or blacklists */
1107 ip_lookup(DNSBL_WORK *blw,
1108 DNSBL_TYPE tgt_type,
1109 const void *tgt, /* in_addr* or in6_addr*, not aligned */
1110 DNSBL_FGS fgs) /* type of lookup */
1111 {
1112 const DNSBL *dp;
1113 const u_char *bp;
1114 DNSBL_DOM probe;
1115
1116 /* check all DNSBLs for a group without a hit */
1117 for (dp = dnsbls; dp; dp = dp->fwd) {
1118 if (dp->bl_type != tgt_type)
1119 continue;
1120 if ((dp->fgs & fgs) != fgs)
1121 continue;
1122 if (dp->fgs & DNSBL_FG_DUP)
1123 continue;
1124
1125 if (!(blw->unhit.all & DNSBL_G2B(dp->group)))
1126 continue;
1127
1128 bp = (u_char *)tgt;
1129 if (tgt_type == DNSBL_TYPE_IPV4)
1130 snprintf(probe.c, sizeof(probe.c),
1131 "%d.%d.%d.%d.%s",
1132 bp[3], bp[2], bp[1], bp[0],
1133 dp->bl_dom.c);
1134 else
1135 snprintf(probe.c, sizeof(probe.c),
1136 "%d.%d.%d.%d.%d.%d.%d.%d"
1137 ".%d.%d.%d.%d.%d.%d.%d.%d.%s",
1138 bp[15], bp[14], bp[13], bp[12],
1139 bp[11], bp[10], bp[9], bp[8],
1140 bp[7], bp[6], bp[5], bp[4],
1141 bp[3], bp[2], bp[1], bp[0],
1142 dp->bl_dom.c);
1143
1144 if (!lookup(blw, &probe, dp, fgs, tgt))
1145 return 0;
1146 }
1147 return 1;
1148 }
1149
1150
1151
1152 /* convert a name to one or more IP addresses to be checked in a DNS blacklist.
1153 * dcc_host_lock() must be held.
1154 * These DNS operations need RES_DEFNAMES and RES_DNSRCH off and
1155 * RES_NOALIASES on when the name is an MX or NS server name.
1156 */
1157 static u_char /* 0=failed */
1158 dnsbl_get_host(DNSBL_WORK *blw,
1159 const DNSBL_DOM *dom,
1160 u_char use_ipv6,
1161 int *errorp, /* put error number here */
1162 DNSBL_FGS fgs /* 0, DNSBL_FG_MX, or DNSBL_FG_NS */
1163 #ifndef MXNS_DNSBL
1164 UATTRIB
1165 #endif
1166 )
1167 {
1168 #ifdef MXNS_DNSBL
1169 u_long save_options;
1170 #endif
1171 u_char result;
1172
1173 #ifdef MXNS_DNSBL
1174 save_options = _res.options;
1175 if (fgs & (DNSBL_FG_MX | DNSBL_FG_NS)) {
1176 _res.options &= ~(RES_DEFNAMES | RES_DNSRCH);
1177 _res.options |= RES_NOALIASES;
1178 }
1179 #endif
1180 dnsbl_res_delays(blw);
1181 result = dcc_get_host(dom->c, use_ipv6, errorp);
1182 #ifdef MXNS_DNSBL
1183 if (fgs & (DNSBL_FG_MX | DNSBL_FG_NS))
1184 _res.options = save_options;
1185 #endif
1186 return result;
1187 }
1188
1189
1190
1191 /* look for a domain name in the DNS blacklists */
1192 static u_char /* 0=no more time or blacklists */
1193 name_lookup(DNSBL_WORK *blw,
1194 const DNSBL_DOM *tgt,
1195 DNSBL_FGS fgs) /* type of lookup */
1196 {
1197 const DNSBL *dp;
1198 DNSBL_DOM probe;
1199 const char *p;
1200 int tgt_len, i;
1201
1202 if (!have_name_dnsbl)
1203 return 1;
1204
1205 for (dp = dnsbls; dp; dp = dp->fwd) {
1206 if (dp->bl_type != DNSBL_TYPE_NAME)
1207 continue;
1208 if ((dp->fgs & fgs) != fgs)
1209 continue;
1210 if (dp->fgs & DNSBL_FG_DUP)
1211 continue;
1212
1213 if (!(blw->unhit.all & DNSBL_G2B(dp->group)))
1214 continue;
1215
1216 /* trim trailing '.' from names */
1217 p = tgt->c;
1218 tgt_len = strlen(p);
1219 if (tgt_len > 0 && p[tgt_len-1] == '.')
1220 --tgt_len;
1221
1222 if (tgt_len != 0) { /* handle empty name */
1223 /* truncate long names on the left and complain */
1224 i = (tgt_len + dp->bl_dom_len) - (sizeof(probe.c) - 1);
1225 if (i > 0) {
1226 if (helper.debug)
1227 dnsbl_log(blw, 0, 0,
1228 "target \"%s\""
1229 " is %d bytes too long",
1230 tgt->c, i);
1231 p += i;
1232 tgt_len -= i;
1233 }
1234 memcpy(&probe.c[0], p, tgt_len);
1235 probe.c[tgt_len++] = '.';
1236 }
1237 memcpy(&probe.c[tgt_len], dp->bl_dom.c, dp->bl_dom_len);
1238
1239 if (!lookup(blw, &probe, dp, fgs, tgt))
1240 return 0;
1241 }
1242
1243 return 1;
1244 }
1245
1246
1247
1248 /* look for a domain name and its IP addresses in the DNS blacklists */
1249 static u_char /* 0=no more time or blacklists */
1250 name_ip_lookup(DNSBL_WORK *blw,
1251 const DNSBL_DOM *tgt,
1252 DNSBL_FGS fgs) /* type of lookup */
1253 {
1254 const DCC_SOCKU *sup;
1255 struct in_addr ipv4[8];
1256 struct in6_addr ipv6[4];
1257 int i, error;
1258
1259 /* check the name */
1260 if (!name_lookup(blw, tgt, fgs))
1261 return 0;
1262
1263 /* cannot resolve a null name into an address to try in the DNSBLs */
1264 if (tgt->c[0] == '\0')
1265 return 1;
1266
1267 if (!time_ck_pre(blw, fgs))
1268 return 0;
1269
1270 /* check IPv4 addresses for the name in IPv4 DNSBLs */
1271 if (have_ipv4_dnsbl) {
1272 dcc_host_lock();
1273 /* first resolve IPv4 addresses for the URL or client name */
1274 if (!dnsbl_get_host(blw, tgt, 0, &error, fgs)) {
1275 dcc_host_unlock();
1276 if (helper.debug > 1)
1277 dnsbl_log(blw, 0, fgs, "gethostbyname(%s): %s",
1278 tgt->c, DCC_HSTRERROR(error));
1279 if (!time_ck_post(blw, fgs, error == TRY_AGAIN))
1280 return 0;
1281 } else {
1282 /* Try several of the IP addresses for the domain.
1283 * Save any IP addresses we want to check before we
1284 * check them, because checking changes the array
1285 * of addresses. */
1286 for (sup = dcc_hostaddrs, i = 0;
1287 sup < dcc_hostaddrs_end && i < DIM(ipv4);
1288 ++sup, ++i) {
1289 ipv4[i] = sup->ipv4.sin_addr;
1290 }
1291 dcc_host_unlock();
1292 if (!time_ck_post(blw, fgs, 0))
1293 return 0;
1294 /* check the addresses in all of the DNS blacklists */
1295 do {
1296 if (!ip_lookup(blw, DNSBL_TYPE_IPV4,
1297 &ipv4[--i], fgs))
1298 return 0;
1299 } while (i > 0);
1300 }
1301 }
1302
1303 /* finally try IPv6 addresses for the name */
1304 if (have_ipv6_dnsbl) {
1305 dcc_host_lock();
1306 if (!dnsbl_get_host(blw, tgt, 1, &error, fgs)) {
1307 dcc_host_unlock();
1308 if (helper.debug > 1)
1309 dnsbl_log(blw, 0, fgs, "gethostbyname(%s): %s",
1310 tgt->c, DCC_HSTRERROR(error));
1311 if (!time_ck_post(blw, fgs, error == TRY_AGAIN))
1312 return 0;
1313 } else {
1314 for (sup = dcc_hostaddrs, i = 0;
1315 sup < dcc_hostaddrs_end && i < DIM(ipv6);
1316 ++sup, ++i) {
1317 ipv6[i] = sup->ipv6.sin6_addr;
1318 }
1319 dcc_host_unlock();
1320 if (!time_ck_post(blw, fgs, 0))
1321 return 0;
1322 do {
1323 if (!ip_lookup(blw, DNSBL_TYPE_IPV6,
1324 &ipv4[--i], fgs))
1325 return 0;
1326 } while (i > 0);
1327 }
1328 }
1329
1330 return 1;
1331 }
1332
1333
1334
1335 /* check an SMTP client */
1336 static void
1337 dnsbl_client(DNSBL_WORK *blw)
1338 {
1339 struct in_addr ipv4;
1340
1341 if (blw->tgt.dom.c[0] != '\0'
1342 && blw->tgt.dom.c[0] != '[')
1343 name_lookup(blw, &blw->tgt.dom, DNSBL_FG_CLIENT);
1344
1345 if (have_ipv4_dnsbl
1346 && dcc_ipv6toipv4(&ipv4, &blw->tgt.addr)) {
1347 if (!ip_lookup(blw, DNSBL_TYPE_IPV4,
1348 &ipv4, DNSBL_FG_CLIENT))
1349 return;
1350 }
1351
1352 if (have_ipv6_dnsbl
1353 && !ip_lookup(blw, DNSBL_TYPE_IPV6,
1354 &blw->tgt.addr, DNSBL_FG_CLIENT))
1355 return;
1356 }
1357
1358
1359
1360
1361 #ifdef MXNS_DNSBL
1362 /* look at DNS resource records */
1363 static u_char /* 1=continue 0=stop looking */
1364 rr_lookup(DNSBL_WORK *blw,
1365 const char *name, /* see if this domain is blacklisted */
1366 DNSBL_FGS fgs,
1367 u_short req_type) /* T_A, T_AAAA, or T_MX */
1368 {
1369 union {
1370 u_char buf[PACKETSZ+20];
1371 HEADER hdr;
1372 } answer;
1373 DNSBL_DOM dom;
1374 const u_char *ap, *ap1, *eom;
1375 int cnt, skip;
1376 u_short resp_type;
1377 u_short resp_class, rdlength;
1378 int i;
1379
1380 if (!time_ck_pre(blw, fgs))
1381 return 0;
1382
1383 /* resolve a set of RRs of the desired type for a name */
1384 dnsbl_res_delays(blw);
1385 dcc_host_lock();
1386 i = res_query(name, C_IN, req_type, answer.buf, sizeof(answer.buf));
1387 dcc_host_unlock();
1388 if (i < 0) {
1389 /* use raw hstrerror() here because we are using the
1390 * raw resolver */
1391 if (helper.debug > 1)
1392 dnsbl_log(blw, 0, fgs, "res_query(%s): %s",
1393 name, hstrerror(h_errno));
1394 /* stop looking after too much time or NXDOMAIN */
1395 return (time_ck_post(blw, fgs, h_errno == TRY_AGAIN)
1396 && h_errno != HOST_NOT_FOUND);
1397 }
1398 if (!time_ck_post(blw, fgs, 0))
1399 return 0;
1400
1401 ap = &answer.buf[HFIXEDSZ];
1402 if (i > ISZ(answer.buf))
1403 i = ISZ(answer.buf);
1404 eom = &answer.buf[i];
1405
1406 /* skip the question */
1407 cnt = ntohs(answer.hdr.qdcount);
1408 while (--cnt >= 0) {
1409 skip = dn_skipname(ap, eom);
1410 if (skip < 0) {
1411 if (helper.debug > 1)
1412 dnsbl_log(blw, 0, fgs, "dn_skipname(%s)=%d",
1413 name, skip);
1414 /* look for other RRs */
1415 return 1;
1416 }
1417 ap += skip+QFIXEDSZ;
1418 }
1419
1420
1421 /* check each name or address RR in the answer section */
1422 for (cnt = ntohs(answer.hdr.ancount);
1423 --cnt >= 0 && ap < eom;
1424 ap += rdlength) {
1425 /* get the name */
1426 skip = dn_expand(answer.buf, eom, ap, dom.c, sizeof(dom.c));
1427 if (skip < 0) {
1428 if (helper.debug > 1)
1429 dnsbl_log(blw, 0, fgs,
1430 "answer dn_expand(%s)=%d",
1431 name, skip);
1432 return 1;
1433 }
1434 ap += skip;
1435
1436 /* get RR type and class and skip strange RRs */
1437 GETSHORT(resp_type, ap);
1438 GETSHORT(resp_class, ap);
1439 ap += 4; /* skip TTL */
1440 GETSHORT(rdlength, ap); /* get rdlength */
1441
1442 /* we care only about relevant answers */
1443 if (resp_type != req_type
1444 || resp_class != C_IN)
1445 continue;
1446
1447 if (req_type == T_MX) {
1448 /* check MX name */
1449 ap1 = ap + 2; /* skip MX preference */
1450 skip = dn_expand(answer.buf, eom, ap1,
1451 dom.c, sizeof(dom.c));
1452 if (skip < 0) {
1453 if (helper.debug > 1)
1454 dnsbl_log(blw, 0, fgs,
1455 "MX dn_expand(%s)=%d",
1456 name, skip);
1457 return 1;
1458 }
1459
1460 if (dom.c[0] == '\0'
1461 && helper.debug > 1)
1462 dnsbl_log(blw, 0, fgs, "null MX name");
1463
1464 if (!name_ip_lookup(blw, &dom, fgs))
1465 return 0;
1466
1467 } else if (req_type == T_A) {
1468 if (!ip_lookup(blw, DNSBL_TYPE_IPV4, ap, fgs))
1469 return 0;
1470 #ifndef NO_IPV6
1471 } else if (req_type == T_AAAA) {
1472 if (!ip_lookup(blw, DNSBL_TYPE_IPV6, ap, fgs))
1473 return 0;
1474 #endif
1475 }
1476 }
1477
1478 /* check the authority section only if we care about name servers
1479 * and we are not looking for MX servers */
1480 if ((fgs & DNSBL_FG_MX)
1481 || 0 == ((fgs & DNSBL_FG_URL) ? blw->unhit.url_ns
1482 : blw->unhit.mail_host_ns))
1483 return 1;
1484
1485 /* we could look at the additional section, but it can be incomplete */
1486 fgs |= DNSBL_FG_NS;
1487 for (cnt = ntohs(answer.hdr.nscount);
1488 --cnt >= 0 && ap < eom;
1489 ap += rdlength) {
1490 /* get the name */
1491 skip = dn_expand(answer.buf, eom, ap, dom.c, sizeof(dom.c));
1492 if (skip < 0) {
1493 if (helper.debug > 1)
1494 dnsbl_log(blw, 0, fgs,
1495 "ns dn_expand(%s)=%d",
1496 name, skip);
1497 return 1;
1498 }
1499 ap += skip;
1500
1501 /* get RR type and class */
1502 GETSHORT(resp_type, ap);
1503 GETSHORT(resp_class, ap);
1504 ap += 4; /* skip TTL */
1505 GETSHORT(rdlength, ap); /* get rdlength */
1506
1507 /* we care only about NS RRs */
1508 if (resp_type != T_NS
1509 || resp_class != C_IN)
1510 continue;
1511
1512 skip = dn_expand(answer.buf, eom, ap, dom.c, sizeof(dom.c));
1513 if (skip < 0) {
1514 if (helper.debug > 1)
1515 dnsbl_log(blw, 0, fgs,
1516 "ns answer dn_expand(%s)=%d",
1517 name, skip);
1518 return 1;
1519 }
1520
1521 if (dom.c[0] == '\0'
1522 && helper.debug > 1)
1523 dnsbl_log(blw, 0, fgs, "null NS name");
1524
1525 if (!name_ip_lookup(blw, &dom, fgs))
1526 return 0;
1527 }
1528
1529 return 1;
1530 }
1531 #endif /* MXNS_DNSBL */
1532
1533
1534
1535 /* look for a domain in the DNS blacklists, including its MX & NS servers */
1536 static void
1537 dnsbl_name_host_url(DNSBL_WORK *blw,
1538 DNSBL_FGS fg)
1539 {
1540 struct in_addr ipv4;
1541 #ifndef NO_IPV6
1542 struct dcc_in6_addr ipv6;
1543 #endif
1544
1545 /* recognize an ASCII IP address */
1546 ipv4.s_addr = inet_addr(blw->tgt.dom.c);
1547 if (INADDR_NONE != ipv4.s_addr) {
1548 ip_lookup(blw, DNSBL_TYPE_IPV4, &ipv4, fg);
1549 return;
1550 }
1551 #ifndef NO_IPV6
1552 if (0 < inet_pton(AF_INET6, blw->tgt.dom.c, &ipv6)) {
1553 ip_lookup(blw, DNSBL_TYPE_IPV6, &ipv6, fg);
1554 return;
1555 }
1556 #endif
1557
1558 #ifndef MXNS_DNSBL
1559 /* check simple name and its addresses if we do not have a
1560 * resolver library */
1561 name_ip_lookup(blw, &blw->tgt.dom, fg);
1562
1563 #else /* MXNS_DNSBL */
1564 /* If we have a resolver library, check first for the name itself */
1565 if (!name_lookup(blw, &blw->tgt.dom, fg))
1566 return;
1567
1568 /* Look for name servers in the authority section after asking for
1569 * A RRs. You cannot rely on the bad guys' DNS servers to answer an
1570 * ANY request */
1571 if (have_ipv4_dnsbl && !rr_lookup(blw, blw->tgt.dom.c, fg, T_A))
1572 return;
1573 #ifndef NO_IPV6
1574 if (have_ipv6_dnsbl && !rr_lookup(blw, blw->tgt.dom.c, fg, T_AAAA))
1575 return;
1576 #endif
1577
1578 /* Check MX servers if allowed by at least one DNS blacklist.
1579 * To ape the fall back to A RRs when MX RRs are missing, we need to
1580 * check A RR for evil. However, we've already done that */
1581 if (0 != ((fg & DNSBL_FG_URL) ? blw->unhit.url_mx
1582 : blw->unhit.mail_host_mx))
1583 rr_lookup(blw, blw->tgt.dom.c, fg | DNSBL_FG_MX, T_MX);
1584 #endif /* MXNS_DNSBL */
1585 }
1586
1587
1588
1589 #ifdef HAVE_HELPERS
1590 /* do some DNSBL work in a helper process */
1591 u_char /* 1=try to send the response */
1592 dnsbl_work(const DNSBL_REQ *req, DNSBL_RESP *resp)
1593 {
1594 DNSBL_WORK blw;
1595 DNSBL_RESP_GROUP *rst;
1596 DNSBL_GROUP *blg;
1597 int gnum, i;
1598
1599 blw_clear(&blw);
1600 blw.url_start = req->hdr.start;
1601 blw.msg_us = MAX_MSG_SECS*DCC_US*2;
1602 blw.url_us = req->hdr.avail_us;
1603 blw.url_us_used = 0;
1604 blw.id = req->hdr.id;
1605 blw.dcc_ctxt = 0;
1606 blw.log_ctxt = 0;
1607
1608 if (!is_helper) {
1609 /* this must be the first job for this helper process */
1610 is_helper = 1;
1611 helper_str = " helper";
1612 fix_url_secs();
1613 }
1614
1615 blw.unhit = req->unhit;
1616 for (i = 0, blg = blw.groups; i < MAX_DNSBL_GROUPS; ++i, ++blg)
1617 blg->fgs = req->fgs[i];
1618 switch (req->fg) {
1619 case DNSBL_FG_CLIENT: /* SMTP client IP address */
1620 blw.tgt.addr = req->tgt.addr;
1621 BUFCPY(blw.tgt.dom.c, req->tgt.dom.c);
1622 dnsbl_client(&blw);
1623 break;
1624 case DNSBL_FG_MAIL_HOST: /* envelope mail_from */
1625 case DNSBL_FG_URL: /* URL in body */
1626 BUFCPY(blw.tgt.dom.c, req->tgt.dom.c);
1627 dnsbl_name_host_url(&blw, req->fg);
1628 break;
1629 default:
1630 dcc_logbad(EX_SOFTWARE, "%s DNSBL%s unknown type %d",
1631 req->hdr.id, helper_str, req->fg);
1632 }
1633
1634 resp->unhit = blw.unhit;
1635 for (gnum = 0, blg = blw.groups, rst = resp->groups;
1636 gnum < MAX_DNSBL_GROUPS;
1637 ++gnum, ++rst, ++blg) {
1638 rst->fgs = blg->fgs; /* copy hit & timeout flags */
1639 if (0 == ((resp->unhit.all ^ req->unhit.all)
1640 & DNSBL_G2B(gnum))) {
1641 /* no new hit here in the helper
1642 * or had a hit in the parent */
1643 rst->bl_num = -1;
1644 rst->probe.c[0] = '\0';
1645 rst->result[0] = '\0';
1646 } else {
1647 rst->bl_num = blg->dnsbl->bl_num;
1648 BUFCPY(rst->tgt.c, blg->tgt.c);
1649 BUFCPY(rst->probe.c, blg->probe.c);
1650 BUFCPY(rst->result, blg->result);
1651 }
1652 }
1653
1654 return 1;
1655 }
1656
1657
1658
1659 /* ask a helper process to check for something in the DNS blacklists */
1660 static void
1661 use_helper(DNSBL_WORK *blw, DNSBL_FGS fg)
1662 {
1663 DNSBL_REQ req;
1664 DNSBL_RESP resp;
1665 DNSBL_RESP_GROUP *rst;
1666 DNSBL_GROUP *blg;
1667 const DNSBL *dp;
1668 int gnum, i;
1669
1670 memset(&req, 0, sizeof(req));
1671
1672 BUFCPY(req.hdr.id, blw->id);
1673 req.unhit = blw->unhit;
1674 for (gnum = 0, blg = blw->groups;
1675 gnum < MAX_DNSBL_GROUPS;
1676 ++gnum, ++blg)
1677 req.fgs[gnum] = blg->fgs;
1678 req.fg = fg;
1679 switch (fg) {
1680 case DNSBL_FG_CLIENT:
1681 BUFCPY(req.tgt.dom.c, blw->tgt.dom.c);
1682 req.tgt.addr = blw->tgt.addr;
1683 break;
1684 case DNSBL_FG_MAIL_HOST:
1685 case DNSBL_FG_URL:
1686 BUFCPY(req.tgt.dom.c, blw->tgt.dom.c);
1687 break;
1688 default:
1689 dcc_logbad(EX_SOFTWARE, "unknown DNSBL type %d", fg);
1690 }
1691
1692 if (!ask_helper(blw->dcc_ctxt, blw->log_ctxt, url_us,
1693 &req.hdr, sizeof(req), &resp.hdr, sizeof(resp))) {
1694 time_ck_post(blw, fg, 1);
1695 msg_secs_fin(blw);
1696 return;
1697 }
1698
1699 blw->unhit = resp.unhit;
1700 /* record hits and timeouts seen by the helper */
1701 for (gnum = 0, blg = blw->groups, rst = resp.groups;
1702 gnum < MAX_DNSBL_GROUPS;
1703 ++gnum, ++rst, ++blg) {
1704 blg->fgs = rst->fgs;
1705 if (0 == ((resp.unhit.all ^ req.unhit.all) & DNSBL_G2B(gnum)))
1706 continue;
1707
1708 /* This group has a new hit.
1709 * Discover the right DNSBL so we can use the right
1710 * template for the SMTP status message */
1711 for (i = rst->bl_num, dp = dnsbls; i > 1; --i, dp = dp->fwd) {
1712 if (!dp)
1713 dcc_logbad(EX_SOFTWARE,
1714 "bad helper DNSBL #%d",
1715 rst->bl_num);
1716 }
1717 blg->dnsbl = dp;
1718 BUFCPY(blg->tgt.c, rst->tgt.c);
1719 BUFCPY(blg->probe.c, rst->probe.c);
1720 BUFCPY(blg->result, rst->result);
1721 blg->btype = type_str(blg->btype_buf, sizeof(blg->btype_buf),
1722 blw, blg->fgs);
1723 }
1724
1725 msg_secs_fin(blw);
1726 }
1727 #endif /* HAVE_HELPERS */
1728
1729
1730 void
1731 dcc_client_dnsbl(DNSBL_WORK *blw, const struct in6_addr *addr, const char *name)
1732 {
1733 /* nothing to do if no client DNS blacklists have been configured */
1734 if (!blw || !blw->unhit.client)
1735 return;
1736
1737 /* or if we have already spent too much time checking blacklists */
1738 if (!msg_secs_start(blw, DNSBL_FG_CLIENT))
1739 return;
1740
1741 BUFCPY(blw->tgt.dom.c, name);
1742 blw->tgt.addr = *addr;
1743
1744 #ifdef HAVE_HELPERS
1745 if (have_helpers) {
1746 use_helper(blw, DNSBL_FG_CLIENT);
1747 return;
1748 }
1749 #endif
1750
1751 dnsbl_client(blw);
1752 msg_secs_fin(blw);
1753 }
1754
1755
1756
1757 void
1758 dcc_mail_host_dnsbl(DNSBL_WORK *blw, const char *host)
1759 {
1760 /* nothing to do if no DNS blacklists have been configured
1761 * or we do not have an envelope Mail_From host name
1762 * or no recipients care about mail_host hits */
1763 if (!blw || !blw->unhit.mail_host
1764 || !host || *host == '\0')
1765 return;
1766
1767 /* or no still active DNSBLs care about the Mail_From host name */
1768
1769 /* or if we have already spent too much time checking blacklists */
1770 if (!msg_secs_start(blw, DNSBL_FG_MAIL_HOST))
1771 return;
1772
1773 BUFCPY(blw->tgt.dom.c, host);
1774
1775 #ifdef HAVE_HELPERS
1776 if (have_helpers) {
1777 use_helper(blw, DNSBL_FG_MAIL_HOST);
1778 return;
1779 }
1780 #endif
1781
1782 dnsbl_name_host_url(blw, DNSBL_FG_MAIL_HOST);
1783 msg_secs_fin(blw);
1784 }
1785
1786
1787
1788 void
1789 url_dnsbl(DNSBL_WORK *blw)
1790 {
1791 int i;
1792
1793 /* nothing to do if no body URL DNSBLs have been configured
1794 * or if we already have hits for all DNSBLs */
1795 if (!blw || !blw->unhit.url)
1796 return;
1797
1798 /* or if we have already spent too much time checking blacklists */
1799 if (!msg_secs_start(blw, DNSBL_FG_URL))
1800 return;
1801
1802 /* avoid checking the same names */
1803 for (i = 0; i < DIM(blw->tgt_cache); ++i) {
1804 if (!strcmp(blw->tgt.dom.c, blw->tgt_cache[i].dom.c)) {
1805 if (helper.debug > 1)
1806 dnsbl_log(blw, 0, DNSBL_FG_URL,
1807 "tgt_cache hit");
1808 blw->tgt_cache_pos = i;
1809 return;
1810 }
1811 }
1812 BUFCPY(blw->tgt_cache[blw->tgt_cache_pos].dom.c, blw->tgt.dom.c);
1813 blw->tgt_cache_pos = (blw->tgt_cache_pos + 1) % DIM(blw->tgt_cache);
1814
1815 #ifdef HAVE_HELPERS
1816 if (have_helpers) {
1817 use_helper(blw, DNSBL_FG_URL);
1818 return;
1819 }
1820 #endif
1821
1822 dnsbl_name_host_url(blw, DNSBL_FG_URL);
1823 msg_secs_fin(blw);
1824 }