Mercurial > notdcc
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 } |