0
|
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 } |