1 /* $NetBSD: postscreen_dnsbl.c,v 1.4 2022/10/08 16:12:48 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* postscreen_dnsbl 3
6 /* SUMMARY
7 /* postscreen DNSBL support
8 /* SYNOPSIS
9 /* #include <postscreen.h>
10 /*
11 /* void psc_dnsbl_init(void)
12 /*
13 /* int psc_dnsbl_request(client_addr, callback, context)
14 /* char *client_addr;
15 /* void (*callback)(int, char *);
16 /* char *context;
17 /*
18 /* int psc_dnsbl_retrieve(client_addr, dnsbl_name, dnsbl_index,
19 /* dnsbl_ttl)
20 /* char *client_addr;
21 /* const char **dnsbl_name;
22 /* int dnsbl_index;
23 /* int *dnsbl_ttl;
24 /* DESCRIPTION
25 /* This module implements preliminary support for DNSBL lookups.
26 /* Multiple requests for the same information are handled with
27 /* reference counts.
28 /*
29 /* psc_dnsbl_init() initializes this module, and must be called
30 /* once before any of the other functions in this module.
31 /*
32 /* psc_dnsbl_request() requests a blocklist score for the
33 /* specified client IP address and increments the reference
34 /* count. The request completes in the background. The client
35 /* IP address must be in inet_ntop(3) output format. The
36 /* callback argument specifies a function that is called when
37 /* the requested result is available. The context is passed
38 /* on to the callback function. The callback should ignore its
39 /* first argument (it exists for compatibility with Postfix
40 /* generic event infrastructure).
41 /* The result value is the index for the psc_dnsbl_retrieve()
42 /* call.
43 /*
44 /* psc_dnsbl_retrieve() retrieves the result score and reply
45 /* TTL requested with psc_dnsbl_request(), and decrements the
46 /* reference count. The reply TTL value is clamped to
47 /* postscreen_dnsbl_min_ttl and postscreen_dnsbl_max_ttl. It
48 /* is an error to retrieve a score without requesting it first.
49 /* LICENSE
50 /* .ad
51 /* .fi
52 /* The Secure Mailer license must be distributed with this software.
53 /* AUTHOR(S)
54 /* Wietse Venema
55 /* IBM T.J. Watson Research
56 /* P.O. Box 704
57 /* Yorktown Heights, NY 10598, USA
58 /*
59 /* Wietse Venema
60 /* Google, Inc.
61 /* 111 8th Avenue
62 /* New York, NY 10011, USA
63 /*--*/
64
65 /* System library. */
66
67 #include <sys_defs.h>
68 #include <sys/socket.h> /* AF_INET */
69 #include <netinet/in.h> /* inet_pton() */
70 #include <arpa/inet.h> /* inet_pton() */
71 #include <stdio.h> /* sscanf */
72 #include <limits.h>
73
74 /* Utility library. */
75
76 #include <msg.h>
77 #include <mymalloc.h>
78 #include <argv.h>
79 #include <htable.h>
80 #include <events.h>
81 #include <vstream.h>
82 #include <connect.h>
83 #include <split_at.h>
84 #include <valid_hostname.h>
85 #include <ip_match.h>
86 #include <myaddrinfo.h>
87 #include <stringops.h>
88
89 /* Global library. */
90
91 #include <mail_params.h>
92 #include <mail_proto.h>
93
94 /* Application-specific. */
95
96 #include <postscreen.h>
97
98 /*
99 * Talking to the DNSBLOG service.
100 */
101 static char *psc_dnsbl_service;
102
103 /*
104 * Per-DNSBL filters and weights.
105 *
106 * The postscreen_dnsbl_sites parameter specifies zero or more DNSBL domains.
107 * We provide multiple access methods, one for quick iteration when sending
108 * queries to all DNSBL servers, and one for quick location when receiving a
109 * reply from one DNSBL server.
110 *
111 * Each DNSBL domain can be specified more than once, each time with a
112 * different (filter, weight) pair. We group (filter, weight) pairs in a
113 * linked list under their DNSBL domain name. The list head has a reference
114 * to a "safe name" for the DNSBL, in case the name includes a password.
115 */
116 static HTABLE *dnsbl_site_cache; /* indexed by DNSBNL domain */
117 static HTABLE_INFO **dnsbl_site_list; /* flattened cache */
118
119 typedef struct {
120 const char *safe_dnsbl; /* from postscreen_dnsbl_reply_map */
121 struct PSC_DNSBL_SITE *first; /* list of (filter, weight) tuples */
122 } PSC_DNSBL_HEAD;
123
124 typedef struct PSC_DNSBL_SITE {
125 char *filter; /* printable filter (default: null) */
126 char *byte_codes; /* encoded filter (default: null) */
127 int weight; /* reply weight (default: 1) */
128 struct PSC_DNSBL_SITE *next; /* linked list */
129 } PSC_DNSBL_SITE;
130
131 /*
132 * Per-client DNSBL scores.
133 *
134 * Some SMTP clients make parallel connections. This can trigger parallel
135 * blocklist score requests when the pre-handshake delays of the connections
136 * overlap.
137 *
138 * We combine requests for the same score under the client IP address in a
139 * single reference-counted entry. The reference count goes up with each
140 * request for a score, and it goes down with each score retrieval. Each
141 * score has one or more requestors that need to be notified when the result
142 * is ready, so that postscreen can terminate a pre-handshake delay when all
143 * pre-handshake tests are completed.
144 */
145 static HTABLE *dnsbl_score_cache; /* indexed by client address */
146
147 typedef struct {
148 void (*callback) (int, void *); /* generic call-back routine */
149 void *context; /* generic call-back argument */
150 } PSC_CALL_BACK_ENTRY;
151
152 typedef struct {
153 const char *dnsbl_name; /* DNSBL with largest contribution */
154 int dnsbl_weight; /* weight of largest contribution */
155 int total; /* combined allow+denylist score */
156 int fail_ttl; /* combined reply TTL */
157 int pass_ttl; /* combined reply TTL */
158 int refcount; /* score reference count */
159 int pending_lookups; /* nr of DNS requests in flight */
160 int request_id; /* duplicate suppression */
161 /* Call-back table support. */
162 int index; /* next table index */
163 int limit; /* last valid index */
164 PSC_CALL_BACK_ENTRY table[1]; /* actually a bunch */
165 } PSC_DNSBL_SCORE;
166
167 #define PSC_CALL_BACK_INIT(sp) do { \
168 (sp)->limit = 0; \
169 (sp)->index = 0; \
170 } while (0)
171
172 #define PSC_CALL_BACK_INDEX_OF_LAST(sp) ((sp)->index - 1)
173
174 #define PSC_CALL_BACK_CANCEL(sp, idx) do { \
175 PSC_CALL_BACK_ENTRY *_cb_; \
176 if ((idx) < 0 || (idx) >= (sp)->index) \
177 msg_panic("%s: index %d must be >= 0 and < %d", \
178 myname, (idx), (sp)->index); \
179 _cb_ = (sp)->table + (idx); \
180 event_cancel_timer(_cb_->callback, _cb_->context); \
181 _cb_->callback = 0; \
182 _cb_->context = 0; \
183 } while (0)
184
185 #define PSC_CALL_BACK_EXTEND(hp, sp) do { \
186 if ((sp)->index >= (sp)->limit) { \
187 int _count_ = ((sp)->limit ? (sp)->limit * 2 : 5); \
188 (hp)->value = myrealloc((void *) (sp), sizeof(*(sp)) + \
189 _count_ * sizeof((sp)->table)); \
190 (sp) = (PSC_DNSBL_SCORE *) (hp)->value; \
191 (sp)->limit = _count_; \
192 } \
193 } while (0)
194
195 #define PSC_CALL_BACK_ENTER(sp, fn, ctx) do { \
196 PSC_CALL_BACK_ENTRY *_cb_ = (sp)->table + (sp)->index++; \
197 _cb_->callback = (fn); \
198 _cb_->context = (ctx); \
199 } while (0)
200
201 #define PSC_CALL_BACK_NOTIFY(sp, ev) do { \
202 PSC_CALL_BACK_ENTRY *_cb_; \
203 for (_cb_ = (sp)->table; _cb_ < (sp)->table + (sp)->index; _cb_++) \
204 if (_cb_->callback != 0) \
205 _cb_->callback((ev), _cb_->context); \
206 } while (0)
207
208 #define PSC_NULL_EVENT (0)
209
210 /*
211 * Per-request state.
212 *
213 * This implementation stores the client IP address and DNSBL domain in the
214 * DNSBLOG query/reply stream. This simplifies code, and allows the DNSBLOG
215 * server to produce more informative logging.
216 */
217 static VSTRING *reply_client; /* client address in DNSBLOG reply */
218 static VSTRING *reply_dnsbl; /* domain in DNSBLOG reply */
219 static VSTRING *reply_addr; /* address list in DNSBLOG reply */
220
221 /* psc_dnsbl_add_site - add DNSBL site information */
222
psc_dnsbl_add_site(const char * site)223 static void psc_dnsbl_add_site(const char *site)
224 {
225 const char *myname = "psc_dnsbl_add_site";
226 char *saved_site = mystrdup(site);
227 VSTRING *byte_codes = 0;
228 PSC_DNSBL_HEAD *head;
229 PSC_DNSBL_SITE *new_site;
230 char junk;
231 const char *weight_text;
232 char *pattern_text;
233 int weight;
234 HTABLE_INFO *ht;
235 char *parse_err;
236 const char *safe_dnsbl;
237
238 /*
239 * Parse the required DNSBL domain name, the optional reply filter and
240 * the optional reply weight factor.
241 */
242 #define DO_GRIPE 1
243
244 /* Negative weight means allowlist. */
245 if ((weight_text = split_at(saved_site, '*')) != 0) {
246 if (sscanf(weight_text, "%d%c", &weight, &junk) != 1)
247 msg_fatal("bad DNSBL weight factor \"%s\" in \"%s\"",
248 weight_text, site);
249 } else {
250 weight = 1;
251 }
252 /* Reply filter. */
253 if ((pattern_text = split_at(saved_site, '=')) != 0) {
254 byte_codes = vstring_alloc(100);
255 if ((parse_err = ip_match_parse(byte_codes, pattern_text)) != 0)
256 msg_fatal("bad DNSBL filter syntax: %s", parse_err);
257 }
258 if (valid_hostname(saved_site, DO_GRIPE) == 0)
259 msg_fatal("bad DNSBL domain name \"%s\" in \"%s\"",
260 saved_site, site);
261
262 if (msg_verbose > 1)
263 msg_info("%s: \"%s\" -> domain=\"%s\" pattern=\"%s\" weight=%d",
264 myname, site, saved_site, pattern_text ? pattern_text :
265 "null", weight);
266
267 /*
268 * Look up or create the (filter, weight) list head for this DNSBL domain
269 * name.
270 */
271 if ((head = (PSC_DNSBL_HEAD *)
272 htable_find(dnsbl_site_cache, saved_site)) == 0) {
273 head = (PSC_DNSBL_HEAD *) mymalloc(sizeof(*head));
274 ht = htable_enter(dnsbl_site_cache, saved_site, (void *) head);
275 /* Translate the DNSBL name into a safe name if available. */
276 if (psc_dnsbl_reply == 0
277 || (safe_dnsbl = dict_get(psc_dnsbl_reply, saved_site)) == 0)
278 safe_dnsbl = ht->key;
279 head->safe_dnsbl = mystrdup(safe_dnsbl);
280 if (psc_dnsbl_reply && psc_dnsbl_reply->error)
281 msg_fatal("%s:%s lookup error", psc_dnsbl_reply->type,
282 psc_dnsbl_reply->name);
283 head->first = 0;
284 }
285
286 /*
287 * Append the new (filter, weight) node to the list for this DNSBL domain
288 * name.
289 */
290 new_site = (PSC_DNSBL_SITE *) mymalloc(sizeof(*new_site));
291 new_site->filter = (pattern_text ? mystrdup(pattern_text) : 0);
292 new_site->byte_codes = (byte_codes ? ip_match_save(byte_codes) : 0);
293 new_site->weight = weight;
294 new_site->next = head->first;
295 head->first = new_site;
296
297 myfree(saved_site);
298 if (byte_codes)
299 vstring_free(byte_codes);
300 }
301
302 /* psc_dnsbl_match - match DNSBL reply filter */
303
psc_dnsbl_match(const char * filter,ARGV * reply)304 static int psc_dnsbl_match(const char *filter, ARGV *reply)
305 {
306 char addr_buf[MAI_HOSTADDR_STRSIZE];
307 char **cpp;
308
309 /*
310 * Run the replies through the pattern-matching engine.
311 */
312 for (cpp = reply->argv; *cpp != 0; cpp++) {
313 if (inet_pton(AF_INET, *cpp, addr_buf) != 1)
314 msg_warn("address conversion error for %s -- ignoring this reply",
315 *cpp);
316 if (ip_match_execute(filter, addr_buf))
317 return (1);
318 }
319 return (0);
320 }
321
322 /* psc_dnsbl_retrieve - retrieve blocklist score, decrement reference count */
323
psc_dnsbl_retrieve(const char * client_addr,const char ** dnsbl_name,int dnsbl_index,int * dnsbl_ttl)324 int psc_dnsbl_retrieve(const char *client_addr, const char **dnsbl_name,
325 int dnsbl_index, int *dnsbl_ttl)
326 {
327 const char *myname = "psc_dnsbl_retrieve";
328 PSC_DNSBL_SCORE *score;
329 int result_score;
330 int result_ttl;
331
332 /*
333 * Sanity check.
334 */
335 if ((score = (PSC_DNSBL_SCORE *)
336 htable_find(dnsbl_score_cache, client_addr)) == 0)
337 msg_panic("%s: no blocklist score for %s", myname, client_addr);
338
339 /*
340 * Disable callbacks.
341 */
342 PSC_CALL_BACK_CANCEL(score, dnsbl_index);
343
344 /*
345 * Reads are destructive.
346 */
347 result_score = score->total;
348 *dnsbl_name = score->dnsbl_name;
349 result_ttl = (result_score > 0) ? score->fail_ttl : score->pass_ttl;
350 /* As with dnsblog(8), a value < 0 means no reply TTL. */
351 if (result_ttl < var_psc_dnsbl_min_ttl)
352 result_ttl = var_psc_dnsbl_min_ttl;
353 if (result_ttl > var_psc_dnsbl_max_ttl)
354 result_ttl = var_psc_dnsbl_max_ttl;
355 *dnsbl_ttl = result_ttl;
356 if (msg_verbose)
357 msg_info("%s: addr=%s score=%d ttl=%d",
358 myname, client_addr, result_score, result_ttl);
359 score->refcount -= 1;
360 if (score->refcount < 1) {
361 if (msg_verbose > 1)
362 msg_info("%s: delete blocklist score for %s", myname, client_addr);
363 htable_delete(dnsbl_score_cache, client_addr, myfree);
364 }
365 return (result_score);
366 }
367
368 /* psc_dnsbl_receive - receive DNSBL reply, update blocklist score */
369
psc_dnsbl_receive(int event,void * context)370 static void psc_dnsbl_receive(int event, void *context)
371 {
372 const char *myname = "psc_dnsbl_receive";
373 VSTREAM *stream = (VSTREAM *) context;
374 PSC_DNSBL_SCORE *score;
375 PSC_DNSBL_HEAD *head;
376 PSC_DNSBL_SITE *site;
377 ARGV *reply_argv;
378 int request_id;
379 int dnsbl_ttl;
380
381 PSC_CLEAR_EVENT_REQUEST(vstream_fileno(stream), psc_dnsbl_receive, context);
382
383 /*
384 * Receive the DNSBL lookup result.
385 *
386 * This is preliminary code to explore the field. Later, DNSBL lookup will
387 * be handled by an UDP-based DNS client that is built directly into some
388 * Postfix daemon.
389 *
390 * Don't bother looking up the blocklist score when the client IP address is
391 * not listed at the DNSBL.
392 *
393 * Don't panic when the blocklist score no longer exists. It may be deleted
394 * when the client triggers a "drop" action after pregreet, when the
395 * client does not pregreet and the DNSBL reply arrives late, or when the
396 * client triggers a "drop" action after hanging up.
397 */
398 if (event == EVENT_READ
399 && attr_scan(stream,
400 ATTR_FLAG_STRICT,
401 RECV_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, reply_dnsbl),
402 RECV_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, reply_client),
403 RECV_ATTR_INT(MAIL_ATTR_LABEL, &request_id),
404 RECV_ATTR_STR(MAIL_ATTR_RBL_ADDR, reply_addr),
405 RECV_ATTR_INT(MAIL_ATTR_TTL, &dnsbl_ttl),
406 ATTR_TYPE_END) == 5
407 && (score = (PSC_DNSBL_SCORE *)
408 htable_find(dnsbl_score_cache, STR(reply_client))) != 0
409 && score->request_id == request_id) {
410
411 /*
412 * Run this response past all applicable DNSBL filters and update the
413 * blocklist score for this client IP address.
414 *
415 * Don't panic when the DNSBL domain name is not found. The DNSBLOG
416 * server may be messed up.
417 */
418 if (msg_verbose > 1)
419 msg_info("%s: client=\"%s\" score=%d domain=\"%s\" reply=\"%d %s\"",
420 myname, STR(reply_client), score->total,
421 STR(reply_dnsbl), dnsbl_ttl, STR(reply_addr));
422 head = (PSC_DNSBL_HEAD *)
423 htable_find(dnsbl_site_cache, STR(reply_dnsbl));
424 if (head == 0) {
425 /* Bogus domain. Do nothing. */
426 } else if (*STR(reply_addr) != 0) {
427 /* DNS reputation record(s) found. */
428 reply_argv = 0;
429 for (site = head->first; site != 0; site = site->next) {
430 if (site->byte_codes == 0
431 || psc_dnsbl_match(site->byte_codes, reply_argv ? reply_argv :
432 (reply_argv = argv_split(STR(reply_addr), " ")))) {
433 if (score->dnsbl_name == 0
434 || score->dnsbl_weight < site->weight) {
435 score->dnsbl_name = head->safe_dnsbl;
436 score->dnsbl_weight = site->weight;
437 }
438 score->total += site->weight;
439 if (msg_verbose > 1)
440 msg_info("%s: filter=\"%s\" weight=%d score=%d",
441 myname, site->filter ? site->filter : "null",
442 site->weight, score->total);
443 }
444 /* As with dnsblog(8), a value < 0 means no reply TTL. */
445 if (site->weight > 0) {
446 if (score->fail_ttl < 0 || score->fail_ttl > dnsbl_ttl)
447 score->fail_ttl = dnsbl_ttl;
448 } else {
449 if (score->pass_ttl < 0 || score->pass_ttl > dnsbl_ttl)
450 score->pass_ttl = dnsbl_ttl;
451 }
452 }
453 if (reply_argv != 0)
454 argv_free(reply_argv);
455 } else {
456 /* No DNS reputation record found. */
457 for (site = head->first; site != 0; site = site->next) {
458 /* As with dnsblog(8), a value < 0 means no reply TTL. */
459 if (site->weight > 0) {
460 if (score->pass_ttl < 0 || score->pass_ttl > dnsbl_ttl)
461 score->pass_ttl = dnsbl_ttl;
462 } else {
463 if (score->fail_ttl < 0 || score->fail_ttl > dnsbl_ttl)
464 score->fail_ttl = dnsbl_ttl;
465 }
466 }
467 }
468
469 /*
470 * Notify the requestor(s) that the result is ready to be picked up.
471 * If this call isn't made, clients have to sit out the entire
472 * pre-handshake delay.
473 */
474 score->pending_lookups -= 1;
475 if (score->pending_lookups == 0)
476 PSC_CALL_BACK_NOTIFY(score, PSC_NULL_EVENT);
477 } else if (event == EVENT_TIME) {
478 msg_warn("dnsblog reply timeout %ds for %s",
479 var_psc_dnsbl_tmout, (char *) vstream_context(stream));
480 }
481 /* Here, score may be a null pointer. */
482 vstream_fclose(stream);
483 }
484
485 /* psc_dnsbl_request - send dnsbl query, increment reference count */
486
psc_dnsbl_request(const char * client_addr,void (* callback)(int,void *),void * context)487 int psc_dnsbl_request(const char *client_addr,
488 void (*callback) (int, void *),
489 void *context)
490 {
491 const char *myname = "psc_dnsbl_request";
492 int fd;
493 VSTREAM *stream;
494 HTABLE_INFO **ht;
495 PSC_DNSBL_SCORE *score;
496 HTABLE_INFO *hash_node;
497 static int request_count;
498
499 /*
500 * Some spambots make several connections at nearly the same time,
501 * causing their pregreet delays to overlap. Such connections can share
502 * the efforts of DNSBL lookup.
503 *
504 * We store a reference-counted DNSBL score under its client IP address. We
505 * increment the reference count with each score request, and decrement
506 * the reference count with each score retrieval.
507 *
508 * Do not notify the requestor NOW when the DNS replies are already in.
509 * Reason: we must not make a backwards call while we are still in the
510 * middle of executing the corresponding forward call. Instead we create
511 * a zero-delay timer request and call the notification function from
512 * there.
513 *
514 * psc_dnsbl_request() could instead return a result value to indicate that
515 * the DNSBL score is already available, but that would complicate the
516 * caller with two different notification code paths: one asynchronous
517 * code path via the callback invocation, and one synchronous code path
518 * via the psc_dnsbl_request() result value. That would be a source of
519 * future bugs.
520 */
521 if ((hash_node = htable_locate(dnsbl_score_cache, client_addr)) != 0) {
522 score = (PSC_DNSBL_SCORE *) hash_node->value;
523 score->refcount += 1;
524 PSC_CALL_BACK_EXTEND(hash_node, score);
525 PSC_CALL_BACK_ENTER(score, callback, context);
526 if (msg_verbose > 1)
527 msg_info("%s: reuse blocklist score for %s refcount=%d pending=%d",
528 myname, client_addr, score->refcount,
529 score->pending_lookups);
530 if (score->pending_lookups == 0)
531 event_request_timer(callback, context, EVENT_NULL_DELAY);
532 return (PSC_CALL_BACK_INDEX_OF_LAST(score));
533 }
534 if (msg_verbose > 1)
535 msg_info("%s: create blocklist score for %s", myname, client_addr);
536 score = (PSC_DNSBL_SCORE *) mymalloc(sizeof(*score));
537 score->request_id = request_count++;
538 score->dnsbl_name = 0;
539 score->dnsbl_weight = 0;
540 /* As with dnsblog(8), a value < 0 means no reply TTL. */
541 score->pass_ttl = -1;
542 score->fail_ttl = -1;
543 score->total = 0;
544 score->refcount = 1;
545 score->pending_lookups = 0;
546 PSC_CALL_BACK_INIT(score);
547 PSC_CALL_BACK_ENTER(score, callback, context);
548 (void) htable_enter(dnsbl_score_cache, client_addr, (void *) score);
549
550 /*
551 * Send a query to all DNSBL servers. Later, DNSBL lookup will be done
552 * with an UDP-based DNS client that is built directly into Postfix code.
553 * We therefore do not optimize the maximum out of this temporary
554 * implementation.
555 */
556 for (ht = dnsbl_site_list; *ht; ht++) {
557 if ((fd = LOCAL_CONNECT(psc_dnsbl_service, NON_BLOCKING, 1)) < 0) {
558 msg_warn("%s: connect to %s service: %m",
559 myname, psc_dnsbl_service);
560 continue;
561 }
562 stream = vstream_fdopen(fd, O_RDWR);
563 vstream_control(stream,
564 CA_VSTREAM_CTL_CONTEXT(ht[0]->key),
565 CA_VSTREAM_CTL_END);
566 attr_print(stream, ATTR_FLAG_NONE,
567 SEND_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, ht[0]->key),
568 SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, client_addr),
569 SEND_ATTR_INT(MAIL_ATTR_LABEL, score->request_id),
570 ATTR_TYPE_END);
571 if (vstream_fflush(stream) != 0) {
572 msg_warn("%s: error sending to %s service: %m",
573 myname, psc_dnsbl_service);
574 vstream_fclose(stream);
575 continue;
576 }
577 PSC_READ_EVENT_REQUEST(vstream_fileno(stream), psc_dnsbl_receive,
578 (void *) stream, var_psc_dnsbl_tmout);
579 score->pending_lookups += 1;
580 }
581 return (PSC_CALL_BACK_INDEX_OF_LAST(score));
582 }
583
584 /* psc_dnsbl_init - initialize */
585
psc_dnsbl_init(void)586 void psc_dnsbl_init(void)
587 {
588 const char *myname = "psc_dnsbl_init";
589 ARGV *dnsbl_site = argv_split(var_psc_dnsbl_sites, CHARS_COMMA_SP);
590 char **cpp;
591
592 /*
593 * Sanity check.
594 */
595 if (dnsbl_site_cache != 0)
596 msg_panic("%s: called more than once", myname);
597
598 /*
599 * pre-compute the DNSBLOG socket name.
600 */
601 psc_dnsbl_service = concatenate(MAIL_CLASS_PRIVATE, "/",
602 var_dnsblog_service, (char *) 0);
603
604 /*
605 * Prepare for quick iteration when sending out queries to all DNSBL
606 * servers, and for quick lookup when a reply arrives from a specific
607 * DNSBL server.
608 */
609 dnsbl_site_cache = htable_create(13);
610 for (cpp = dnsbl_site->argv; *cpp; cpp++)
611 psc_dnsbl_add_site(*cpp);
612 argv_free(dnsbl_site);
613 dnsbl_site_list = htable_list(dnsbl_site_cache);
614
615 /*
616 * The per-client blocklist score.
617 */
618 dnsbl_score_cache = htable_create(13);
619
620 /*
621 * Space for ad-hoc DNSBLOG server request/reply parameters.
622 */
623 reply_client = vstring_alloc(100);
624 reply_dnsbl = vstring_alloc(100);
625 reply_addr = vstring_alloc(100);
626 }
627