2 * Solanum: a slightly advanced ircd
3 * dnsbl.c: Manages DNSBL entries and lookups
5 * Copyright (C) 2006-2011 charybdis development team
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
34 /* Originally written for charybdis circa 2006 (by nenolod?).
35 * Tweaked for authd. Some functions and structs renamed. Public/private
36 * interfaces have been shifted around. Some code has been cleaned up too.
37 * -- Elizafox 24 March 2016
47 #define SELF_PID (dnsbl_provider.id)
55 /* dnsbl accepted IP types */
59 /* A configured DNSBL */
62 char host
[IRCD_RES_HOSTLEN
+ 1];
63 char reason
[BUFSIZE
]; /* Reason template (ircd fills in the blanks) */
64 uint8_t iptype
; /* IP types supported */
65 rb_dlink_list filters
; /* Filters for queries */
67 bool delete; /* If true delete when no clients */
68 int refcount
; /* When 0 and delete is set, remove this dnsbl */
71 time_t lastwarning
; /* Last warning about garbage replies sent */
74 /* A lookup in progress for a particular DNSBL for a particular client */
77 struct dnsbl
*bl
; /* dnsbl we're checking */
78 struct auth_client
*auth
; /* Client */
79 struct dns_query
*query
; /* DNS query pointer */
87 filter_t type
; /* Type of filter */
88 char filter
[HOSTIPLEN
]; /* The filter itself */
93 /* dnsbl user data attached to auth_client instance */
97 rb_dlink_list queries
; /* dnsbl queries in flight */
100 /* public interfaces */
101 static void dnsbls_destroy(void);
103 static bool dnsbls_start(struct auth_client
*);
104 static inline void dnsbls_generic_cancel(struct auth_client
*, const char *);
105 static void dnsbls_timeout(struct auth_client
*);
106 static void dnsbls_cancel(struct auth_client
*);
107 static void dnsbls_cancel_none(struct auth_client
*);
109 /* private interfaces */
110 static void unref_dnsbl(struct dnsbl
*);
111 static struct dnsbl
*new_dnsbl(const char *, const char *, uint8_t, rb_dlink_list
*);
112 static struct dnsbl
*find_dnsbl(const char *);
113 static bool dnsbl_check_reply(struct dnsbl_lookup
*, const char *);
114 static void dnsbl_dns_callback(const char *, bool, query_type
, void *);
115 static void initiate_dnsbl_dnsquery(struct dnsbl
*, struct auth_client
*);
118 static rb_dlink_list dnsbl_list
= { NULL
, NULL
, 0 };
119 static int dnsbl_timeout
= DNSBL_TIMEOUT_DEFAULT
;
121 /* private interfaces */
124 unref_dnsbl(struct dnsbl
*bl
)
126 rb_dlink_node
*ptr
, *nptr
;
129 if (bl
->delete && bl
->refcount
<= 0)
131 RB_DLINK_FOREACH_SAFE(ptr
, nptr
, bl
->filters
.head
)
133 rb_dlinkDelete(ptr
, &bl
->filters
);
137 rb_dlinkFindDestroy(bl
, &dnsbl_list
);
142 static struct dnsbl
*
143 new_dnsbl(const char *name
, const char *reason
, uint8_t iptype
, rb_dlink_list
*filters
)
147 if (name
== NULL
|| reason
== NULL
|| iptype
== 0)
150 if((bl
= find_dnsbl(name
)) == NULL
)
152 bl
= rb_malloc(sizeof(struct dnsbl
));
153 rb_dlinkAddAlloc(bl
, &dnsbl_list
);
158 rb_strlcpy(bl
->host
, name
, IRCD_RES_HOSTLEN
+ 1);
159 rb_strlcpy(bl
->reason
, reason
, BUFSIZE
);
162 rb_dlinkMoveList(filters
, &bl
->filters
);
169 static struct dnsbl
*
170 find_dnsbl(const char *name
)
174 RB_DLINK_FOREACH(ptr
, dnsbl_list
.head
)
176 struct dnsbl
*bl
= (struct dnsbl
*)ptr
->data
;
178 if (!strcasecmp(bl
->host
, name
))
186 dnsbl_check_reply(struct dnsbl_lookup
*bllookup
, const char *ipaddr
)
188 struct dnsbl
*bl
= bllookup
->bl
;
189 const char *lastoctet
;
192 /* No filters and entry found - thus positive match */
193 if (!rb_dlink_list_length(&bl
->filters
))
196 /* Below will prolly have to change if IPv6 address replies are sent back */
197 if ((lastoctet
= strrchr(ipaddr
, '.')) == NULL
|| *(++lastoctet
) == '\0')
200 RB_DLINK_FOREACH(ptr
, bl
->filters
.head
)
202 struct dnsbl_filter
*filter
= ptr
->data
;
205 if (filter
->type
== FILTER_ALL
)
207 else if (filter
->type
== FILTER_LAST
)
211 warn_opers(L_CRIT
, "dnsbl: Unknown dnsbl filter type (host %s): %d",
212 bl
->host
, filter
->type
);
213 exit(EX_PROVIDER_ERROR
);
216 if (strcmp(cmpstr
, filter
->filter
) == 0)
223 if (bl
->lastwarning
+ 3600 < rb_current_time())
225 warn_opers(L_WARN
, "Garbage/undecipherable reply received from dnsbl %s (reply %s)",
227 bl
->lastwarning
= rb_current_time();
234 dnsbl_dns_callback(const char *result
, bool status
, query_type type
, void *data
)
236 struct dnsbl_lookup
*bllookup
= (struct dnsbl_lookup
*)data
;
237 struct dnsbl_user
*bluser
;
239 struct auth_client
*auth
;
241 lrb_assert(bllookup
!= NULL
);
242 lrb_assert(bllookup
->auth
!= NULL
);
245 auth
= bllookup
->auth
;
247 if((bluser
= get_provider_data(auth
, SELF_PID
)) == NULL
)
250 if (result
!= NULL
&& status
&& dnsbl_check_reply(bllookup
, result
))
252 /* Match found, so proceed no further */
254 reject_client(auth
, SELF_PID
, bl
->host
, bl
->reason
);
260 cancel_query(bllookup
->query
); /* Ignore future responses */
261 rb_dlinkDelete(&bllookup
->node
, &bluser
->queries
);
264 if(!rb_dlink_list_length(&bluser
->queries
))
267 notice_client(auth
->cid
, "*** No DNSBL entry found for this IP");
269 set_provider_data(auth
, SELF_PID
, NULL
);
270 set_provider_timeout_absolute(auth
, SELF_PID
, 0);
271 provider_done(auth
, SELF_PID
);
273 auth_client_unref(auth
);
278 initiate_dnsbl_dnsquery(struct dnsbl
*bl
, struct auth_client
*auth
)
280 struct dnsbl_lookup
*bllookup
= rb_malloc(sizeof(struct dnsbl_lookup
));
281 struct dnsbl_user
*bluser
= get_provider_data(auth
, SELF_PID
);
282 char buf
[IRCD_RES_HOSTLEN
+ 1];
286 bllookup
->auth
= auth
;
288 aftype
= GET_SS_FAMILY(&auth
->c_addr
);
289 if((aftype
== AF_INET
&& (bl
->iptype
& IPTYPE_IPV4
) == 0) ||
290 (aftype
== AF_INET6
&& (bl
->iptype
& IPTYPE_IPV6
) == 0))
291 /* Incorrect dnsbl type for this IP... */
297 build_rdns(buf
, sizeof(buf
), &auth
->c_addr
, bl
->host
);
298 bllookup
->query
= lookup_ip(buf
, AF_INET
, dnsbl_dns_callback
, bllookup
);
300 rb_dlinkAdd(bllookup
, &bllookup
->node
, &bluser
->queries
);
305 lookup_all_dnsbls(struct auth_client
*auth
)
307 struct dnsbl_user
*bluser
= get_provider_data(auth
, SELF_PID
);
311 if(GET_SS_FAMILY(&auth
->c_addr
) == AF_INET
)
312 iptype
= IPTYPE_IPV4
;
313 else if(GET_SS_FAMILY(&auth
->c_addr
) == AF_INET6
)
314 iptype
= IPTYPE_IPV6
;
318 bluser
->started
= true;
319 notice_client(auth
->cid
, "*** Checking your IP against DNSBLs");
321 RB_DLINK_FOREACH(ptr
, dnsbl_list
.head
)
323 struct dnsbl
*bl
= (struct dnsbl
*)ptr
->data
;
325 if (!bl
->delete && (bl
->iptype
& iptype
))
326 initiate_dnsbl_dnsquery(bl
, auth
);
329 if(!rb_dlink_list_length(&bluser
->queries
))
333 set_provider_timeout_relative(auth
, SELF_PID
, dnsbl_timeout
);
339 delete_dnsbl(struct dnsbl
*bl
)
341 if (bl
->refcount
> 0)
345 rb_dlinkFindDestroy(bl
, &dnsbl_list
);
351 delete_all_dnsbls(void)
353 rb_dlink_node
*ptr
, *nptr
;
355 RB_DLINK_FOREACH_SAFE(ptr
, nptr
, dnsbl_list
.head
)
357 delete_dnsbl(ptr
->data
);
361 /* public interfaces */
363 dnsbls_start(struct auth_client
*auth
)
365 lrb_assert(get_provider_data(auth
, SELF_PID
) == NULL
);
367 if (!rb_dlink_list_length(&dnsbl_list
)) {
368 /* Nothing to do... */
369 provider_done(auth
, SELF_PID
);
373 auth_client_ref(auth
);
375 set_provider_data(auth
, SELF_PID
, rb_malloc(sizeof(struct dnsbl_user
)));
377 if (run_after_provider(auth
, "rdns") && run_after_provider(auth
, "ident")) {
378 /* Start the lookup if ident and rdns are finished, or not loaded. */
379 if (!lookup_all_dnsbls(auth
)) {
380 dnsbls_cancel_none(auth
);
388 /* This is called every time a provider is completed as long as we are marked not done */
390 dnsbls_initiate(struct auth_client
*auth
, uint32_t provider
)
392 struct dnsbl_user
*bluser
= get_provider_data(auth
, SELF_PID
);
394 lrb_assert(provider
!= SELF_PID
);
395 lrb_assert(!is_provider_done(auth
, SELF_PID
));
396 lrb_assert(rb_dlink_list_length(&dnsbl_list
) > 0);
398 if (bluser
== NULL
|| bluser
->started
) {
401 } else if (run_after_provider(auth
, "rdns") && run_after_provider(auth
, "ident")) {
402 /* Start the lookup if ident and rdns are finished, or not loaded. */
403 if (!lookup_all_dnsbls(auth
)) {
404 dnsbls_cancel_none(auth
);
410 dnsbls_generic_cancel(struct auth_client
*auth
, const char *message
)
412 rb_dlink_node
*ptr
, *nptr
;
413 struct dnsbl_user
*bluser
= get_provider_data(auth
, SELF_PID
);
418 if(rb_dlink_list_length(&bluser
->queries
))
420 notice_client(auth
->cid
, message
);
422 RB_DLINK_FOREACH_SAFE(ptr
, nptr
, bluser
->queries
.head
)
424 struct dnsbl_lookup
*bllookup
= ptr
->data
;
426 cancel_query(bllookup
->query
);
427 unref_dnsbl(bllookup
->bl
);
429 rb_dlinkDelete(&bllookup
->node
, &bluser
->queries
);
435 set_provider_data(auth
, SELF_PID
, NULL
);
436 set_provider_timeout_absolute(auth
, SELF_PID
, 0);
437 provider_done(auth
, SELF_PID
);
439 auth_client_unref(auth
);
443 dnsbls_timeout(struct auth_client
*auth
)
445 dnsbls_generic_cancel(auth
, "*** No response from DNSBLs");
449 dnsbls_cancel(struct auth_client
*auth
)
451 dnsbls_generic_cancel(auth
, "*** Aborting DNSBL checks");
455 dnsbls_cancel_none(struct auth_client
*auth
)
457 dnsbls_generic_cancel(auth
, "*** Could not check DNSBLs");
463 rb_dictionary_iter iter
;
464 struct auth_client
*auth
;
466 RB_DICTIONARY_FOREACH(auth
, &iter
, auth_clients
)
469 /* auth is now invalid as we have no reference */
476 add_conf_dnsbl(const char *key
, int parc
, const char **parv
)
478 rb_dlink_list filters
= { NULL
, NULL
, 0 };
479 char *tmp
, *elemlist
= rb_strdup(parv
[2]);
485 for(char *elem
= rb_strtok_r(elemlist
, ",", &tmp
); elem
; elem
= rb_strtok_r(NULL
, ",", &tmp
))
487 struct dnsbl_filter
*filter
= rb_malloc(sizeof(struct dnsbl_filter
));
489 filter_t type
= FILTER_LAST
;
491 /* Check dnsbl filter type and for validity */
492 for(char *c
= elem
; *c
!= '\0'; c
++)
498 warn_opers(L_CRIT
, "dnsbl: addr_conf_dnsbl got a bad filter (too many octets)");
499 exit(EX_PROVIDER_ERROR
);
504 else if(!isdigit(*c
))
506 warn_opers(L_CRIT
, "dnsbl: addr_conf_dnsbl got a bad filter (invalid character in dnsbl filter: %c)",
508 exit(EX_PROVIDER_ERROR
);
512 if(dot_c
> 0 && dot_c
< 3)
514 warn_opers(L_CRIT
, "dnsbl: addr_conf_dnsbl got a bad filter (insufficient octets)");
515 exit(EX_PROVIDER_ERROR
);
519 rb_strlcpy(filter
->filter
, elem
, sizeof(filter
->filter
));
520 rb_dlinkAdd(filter
, &filter
->node
, &filters
);
526 iptype
= atoi(parv
[1]) & 0x3;
527 if(new_dnsbl(parv
[0], parv
[3], iptype
, &filters
) == NULL
)
529 warn_opers(L_CRIT
, "dnsbl: addr_conf_dnsbl got a malformed dnsbl");
530 exit(EX_PROVIDER_ERROR
);
535 del_conf_dnsbl(const char *key
, int parc
, const char **parv
)
537 struct dnsbl
*bl
= find_dnsbl(parv
[0]);
540 /* Not fatal for now... */
541 warn_opers(L_WARN
, "dnsbl: tried to remove nonexistent dnsbl %s", parv
[0]);
549 del_conf_dnsbl_all(const char *key
, int parc
, const char **parv
)
555 add_conf_dnsbl_timeout(const char *key
, int parc
, const char **parv
)
557 int timeout
= atoi(parv
[0]);
561 warn_opers(L_CRIT
, "dnsbl: dnsbl timeout < 0 (value: %d)", timeout
);
562 exit(EX_PROVIDER_ERROR
);
565 dnsbl_timeout
= timeout
;
570 dnsbl_stats(uint32_t rid
, char letter
)
574 RB_DLINK_FOREACH(ptr
, dnsbl_list
.head
)
576 struct dnsbl
*bl
= ptr
->data
;
581 stats_result(rid
, letter
, "%s %hhu %u", bl
->host
, bl
->iptype
, bl
->hits
);
584 stats_done(rid
, letter
);
588 struct auth_opts_handler dnsbl_options
[] =
590 { "rbl", 4, add_conf_dnsbl
},
591 { "rbl_del", 1, del_conf_dnsbl
},
592 { "rbl_del_all", 0, del_conf_dnsbl_all
},
593 { "rbl_timeout", 1, add_conf_dnsbl_timeout
},
597 struct auth_provider dnsbl_provider
=
601 .destroy
= dnsbls_destroy
,
602 .start
= dnsbls_start
,
603 .cancel
= dnsbls_cancel
,
604 .timeout
= dnsbls_timeout
,
605 .completed
= dnsbls_initiate
,
606 .opt_handlers
= dnsbl_options
,
607 /* .stats_handler = { 'B', dnsbl_stats }, */