616 lines
16 KiB
C
616 lines
16 KiB
C
/*
|
|
* charybdis: A slightly useful ircd.
|
|
* blacklist.c: Manages DNS blacklist entries and lookups
|
|
*
|
|
* Copyright (C) 2006-2008 charybdis development team
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
/* To configure/use, add a block to the general{} section of your atheme.conf
|
|
* like this:
|
|
*
|
|
* blacklists {
|
|
* "dnsbl.dronebl.org";
|
|
* "rbl.efnetrbl.org";
|
|
* };
|
|
*/
|
|
|
|
#include "atheme.h"
|
|
#include "conf.h"
|
|
|
|
DECLARE_MODULE_V1
|
|
(
|
|
"contrib/dnsbl", false, _modinit, _moddeinit,
|
|
PACKAGE_STRING,
|
|
"Atheme Development Group <http://www.atheme.org>"
|
|
);
|
|
|
|
mowgli_list_t blacklist_list = { NULL, NULL, 0 };
|
|
mowgli_patricia_t **os_set_cmdtree;
|
|
static char *action = NULL;
|
|
|
|
/* A configured DNSBL */
|
|
struct Blacklist {
|
|
unsigned int status; /* If CONF_ILLEGAL, delete when no clients */
|
|
int refcount;
|
|
char host[IRCD_RES_HOSTLEN + 1];
|
|
unsigned int hits;
|
|
time_t lastwarning;
|
|
};
|
|
|
|
/* A lookup in progress for a particular DNSBL for a particular client */
|
|
struct BlacklistClient {
|
|
struct Blacklist *blacklist;
|
|
user_t *u;
|
|
dns_query_t dns_query;
|
|
mowgli_node_t node;
|
|
};
|
|
|
|
struct dnsbl_exempt_ {
|
|
char *ip;
|
|
time_t exempt_ts;
|
|
char *creator;
|
|
char *reason;
|
|
};
|
|
|
|
typedef struct dnsbl_exempt_ dnsbl_exempt_t;
|
|
|
|
mowgli_list_t dnsbl_elist;
|
|
|
|
static void os_cmd_set_dnsblaction(sourceinfo_t *si, int parc, char *parv[]);
|
|
static void dnsbl_hit(user_t *u, struct Blacklist *blptr);
|
|
static void os_cmd_dnsblexempt(sourceinfo_t *si, int parc, char *parv[]);
|
|
static void os_cmd_dnsblscan(sourceinfo_t *si, int parc, char *parv[]);
|
|
static void write_dnsbl_exempt_db(database_handle_t *db);
|
|
static void db_h_ble(database_handle_t *db, const char *type);
|
|
static void lookup_blacklists(user_t *u);
|
|
|
|
command_t os_set_dnsblaction = { "DNSBLACTION", N_("Changes what happens to a user when they hit a DNSBL."), PRIV_USER_ADMIN, 1, os_cmd_set_dnsblaction, { .path = "contrib/set_dnsblaction" } };
|
|
command_t os_dnsblexempt = { "DNSBLEXEMPT", N_("Manage the list of IP's exempt from DNSBL checking."), PRIV_USER_ADMIN, 3, os_cmd_dnsblexempt, { .path = "contrib/dnsblexempt" } };
|
|
command_t os_dnsblscan = { "DNSBLSCAN", N_("Manually scan if a user is in a DNSBL."), PRIV_USER_ADMIN, 1, os_cmd_dnsblscan, { .path = "contrib/dnsblscan" } };
|
|
|
|
static inline mowgli_list_t *dnsbl_queries(user_t *u)
|
|
{
|
|
mowgli_list_t *l;
|
|
|
|
return_val_if_fail(u != NULL, NULL);
|
|
|
|
l = privatedata_get(u, "dnsbl:queries");
|
|
if (l != NULL)
|
|
return l;
|
|
|
|
l = mowgli_list_create();
|
|
privatedata_set(u, "dnsbl:queries", l);
|
|
|
|
return l;
|
|
}
|
|
|
|
static void os_cmd_set_dnsblaction(sourceinfo_t *si, int parc, char *parv[])
|
|
{
|
|
char *act = parv[0];
|
|
|
|
if (!act)
|
|
{
|
|
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DNSBLACTION");
|
|
command_fail(si, fault_needmoreparams, _("Syntax: SET DNSBLACTION <action>"));
|
|
return;
|
|
}
|
|
|
|
if (!strcasecmp("SNOOP", act) || !strcasecmp("KLINE", act) || !strcasecmp("NOTIFY", act))
|
|
{
|
|
action = sstrdup(act);
|
|
command_success_nodata(si, _("DNSBLACTION successfully set to \2%s\2"), act);
|
|
logcommand(si, CMDLOG_ADMIN, "SET:DNSBLACTION: \2%s\2", act);
|
|
return;
|
|
}
|
|
else if (!strcasecmp("NONE", act))
|
|
{
|
|
action = NULL;
|
|
command_success_nodata(si, _("DNSBLACTION successfully set to \2%s\2"), act);
|
|
logcommand(si, CMDLOG_ADMIN, "SET:DNSBLACTION: \2%s\2", act);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
command_fail(si, fault_badparams, _("Invalid action given."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void os_cmd_dnsblexempt(sourceinfo_t *si, int parc, char *parv[])
|
|
{
|
|
char *command = parv[0];
|
|
char *ip = parv[1];
|
|
char *reason = parv[2];
|
|
mowgli_node_t *n, *tn;
|
|
dnsbl_exempt_t *de;
|
|
|
|
if (!command)
|
|
{
|
|
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DNSBLEXEMPT");
|
|
command_fail(si, fault_needmoreparams, _("Syntax: DNSBLEXEMPT ADD|DEL|LIST [ip] [reason]"));
|
|
return;
|
|
}
|
|
|
|
if (!strcasecmp("ADD", command))
|
|
{
|
|
|
|
if (!ip || !reason)
|
|
{
|
|
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DNSBLEXEMPT");
|
|
command_fail(si, fault_needmoreparams, _("Syntax: DNSBLEXEMPT ADD <ip> <reason>"));
|
|
return;
|
|
}
|
|
|
|
MOWGLI_ITER_FOREACH(n, dnsbl_elist.head)
|
|
{
|
|
de = n->data;
|
|
|
|
if (!irccasecmp(de->ip, ip))
|
|
{
|
|
command_success_nodata(si, _("\2%s\2 has already been entered into the DNSBL exempts list."), ip);
|
|
return;
|
|
}
|
|
}
|
|
|
|
de = smalloc(sizeof(dnsbl_exempt_t));
|
|
de->exempt_ts = CURRTIME;;
|
|
de->creator = sstrdup(get_source_name(si));
|
|
de->reason = sstrdup(reason);
|
|
de->ip = sstrdup(ip);
|
|
mowgli_node_add(de, mowgli_node_create(), &dnsbl_elist);
|
|
|
|
command_success_nodata(si, _("You have added \2%s\2 to the DNSBL exempts list."), ip);
|
|
logcommand(si, CMDLOG_ADMIN, "DNSBL:EXEMPT:ADD: \2%s\2 \2%s\2", ip, reason);
|
|
}
|
|
else if (!strcasecmp("DEL", command))
|
|
{
|
|
|
|
if (!ip)
|
|
{
|
|
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DNSBLEXEMPT");
|
|
command_fail(si, fault_needmoreparams, _("Syntax: DNSBLEXEMPT DEL <ip>"));
|
|
return;
|
|
}
|
|
|
|
MOWGLI_ITER_FOREACH_SAFE(n, tn, dnsbl_elist.head)
|
|
{
|
|
de = n->data;
|
|
|
|
if (!irccasecmp(de->ip, ip))
|
|
{
|
|
logcommand(si, CMDLOG_SET, "DNSBL:EXEMPT:DEL: \2%s\2", de->ip);
|
|
command_success_nodata(si, _("DNSBL Exempt IP \2%s\2 has been deleted."), de->ip);
|
|
|
|
mowgli_node_delete(n, &dnsbl_elist);
|
|
|
|
free(de->creator);
|
|
free(de->reason);
|
|
free(de->ip);
|
|
free(de);
|
|
|
|
return;
|
|
}
|
|
}
|
|
command_success_nodata(si, _("IP \2%s\2 not found in DNSBL Exempt database."), ip);
|
|
}
|
|
else if (!strcasecmp("LIST", command))
|
|
{
|
|
char buf[BUFSIZE];
|
|
struct tm tm;
|
|
|
|
MOWGLI_ITER_FOREACH(n, dnsbl_elist.head)
|
|
{
|
|
de = n->data;
|
|
|
|
tm = *localtime(&de->exempt_ts);
|
|
strftime(buf, BUFSIZE, TIME_FORMAT, &tm);
|
|
command_success_nodata(si, "IP: \2%s\2 Reason: \2%s\2 (%s - %s)",
|
|
de->ip, de->reason, de->creator, buf);
|
|
}
|
|
command_success_nodata(si, "End of list.");
|
|
logcommand(si, CMDLOG_GET, "DNSBL:EXEMPT:LIST");
|
|
}
|
|
else
|
|
{
|
|
command_fail(si, fault_needmoreparams, STR_INVALID_PARAMS, "DNSBLEXEMPT");
|
|
command_fail(si, fault_needmoreparams, _("Syntax: DNSBLEXEMPT ADD|DEL|LIST [ip] [reason]"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void os_cmd_dnsblscan(sourceinfo_t *si, int parc, char *parv[])
|
|
{
|
|
char *user = parv[0];
|
|
user_t *u;
|
|
|
|
if (!user)
|
|
{
|
|
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DNSBLSCAN");
|
|
command_fail(si, fault_needmoreparams, _("Syntax: DNSBLSCAN <user>"));
|
|
return;
|
|
}
|
|
|
|
if ((u = user_find_named(user)))
|
|
{
|
|
lookup_blacklists(u);
|
|
logcommand(si, CMDLOG_ADMIN, "DNSBLSCAN: %s", user);
|
|
command_success_nodata(si, "%s has been scanned.", user);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
command_fail(si, fault_badparams, "User %s is not on the network, you can not scan them.", user);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* private interfaces */
|
|
static struct Blacklist *find_blacklist(char *name)
|
|
{
|
|
mowgli_node_t *n;
|
|
|
|
MOWGLI_ITER_FOREACH(n, blacklist_list.head)
|
|
{
|
|
struct Blacklist *blptr = (struct Blacklist *) n->data;
|
|
|
|
if (!strcasecmp(blptr->host, name))
|
|
return blptr;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void blacklist_dns_callback(void *vptr, dns_reply_t *reply)
|
|
{
|
|
struct BlacklistClient *blcptr = (struct BlacklistClient *) vptr;
|
|
int listed = 0;
|
|
mowgli_list_t *l;
|
|
|
|
if (blcptr == NULL)
|
|
return;
|
|
|
|
if (blcptr->u == NULL)
|
|
{
|
|
free(blcptr);
|
|
return;
|
|
}
|
|
|
|
if (reply != NULL)
|
|
{
|
|
/* only accept 127.x.y.z as a listing */
|
|
if (reply->addr.saddr.sa.sa_family == AF_INET &&
|
|
!memcmp(&((struct sockaddr_in *)&reply->addr)->sin_addr, "\177", 1))
|
|
listed++;
|
|
else if (blcptr->blacklist->lastwarning + 3600 < CURRTIME)
|
|
{
|
|
slog(LG_DEBUG,
|
|
"Garbage reply from blacklist %s",
|
|
blcptr->blacklist->host);
|
|
blcptr->blacklist->lastwarning = CURRTIME;
|
|
}
|
|
}
|
|
|
|
/* they have a blacklist entry for this client */
|
|
if (listed)
|
|
{
|
|
dnsbl_hit(blcptr->u, blcptr->blacklist);
|
|
return;
|
|
}
|
|
|
|
l = dnsbl_queries(blcptr->u);
|
|
mowgli_node_delete(&blcptr->node, l);
|
|
|
|
free(blcptr);
|
|
}
|
|
|
|
/* XXX: no IPv6 implementation, not to concerned right now though. */
|
|
static void initiate_blacklist_dnsquery(struct Blacklist *blptr, user_t *u)
|
|
{
|
|
struct BlacklistClient *blcptr = malloc(sizeof(struct BlacklistClient));
|
|
char buf[IRCD_RES_HOSTLEN + 1];
|
|
int ip[4];
|
|
mowgli_list_t *l;
|
|
|
|
blcptr->blacklist = blptr;
|
|
blcptr->u = u;
|
|
|
|
blcptr->dns_query.ptr = blcptr;
|
|
blcptr->dns_query.callback = blacklist_dns_callback;
|
|
|
|
/* A sscanf worked fine for chary for many years, it'll be fine here */
|
|
sscanf(u->ip, "%d.%d.%d.%d", &ip[3], &ip[2], &ip[1], &ip[0]);
|
|
|
|
/* becomes 2.0.0.127.torbl.ahbl.org or whatever */
|
|
snprintf(buf, sizeof buf, "%d.%d.%d.%d.%s", ip[0], ip[1], ip[2], ip[3], blptr->host);
|
|
|
|
gethost_byname_type(buf, &blcptr->dns_query, T_A);
|
|
|
|
l = dnsbl_queries(u);
|
|
mowgli_node_add(blcptr, &blcptr->node, l);
|
|
blptr->refcount++;
|
|
}
|
|
|
|
/* public interfaces */
|
|
static struct Blacklist *new_blacklist(char *name)
|
|
{
|
|
struct Blacklist *blptr;
|
|
|
|
if (name == NULL)
|
|
return NULL;
|
|
|
|
blptr = find_blacklist(name);
|
|
|
|
if (blptr == NULL)
|
|
{
|
|
blptr = malloc(sizeof(struct Blacklist));
|
|
mowgli_node_add(blptr, mowgli_node_create(), &blacklist_list);
|
|
}
|
|
|
|
mowgli_strlcpy(blptr->host, name, IRCD_RES_HOSTLEN + 1);
|
|
blptr->lastwarning = 0;
|
|
|
|
return blptr;
|
|
}
|
|
|
|
static void lookup_blacklists(user_t *u)
|
|
{
|
|
mowgli_node_t *n;
|
|
|
|
MOWGLI_ITER_FOREACH(n, blacklist_list.head)
|
|
{
|
|
struct Blacklist *blptr = (struct Blacklist *) n->data;
|
|
blptr->status = 0;
|
|
|
|
if (u == NULL)
|
|
return;
|
|
|
|
initiate_blacklist_dnsquery(blptr, u);
|
|
}
|
|
}
|
|
|
|
/* This appears to be unnecessary on Atheme and only causes crashes so #if 0
|
|
* it out, at least for now. --jdhore
|
|
*/
|
|
#if 0
|
|
static void abort_blacklist_queries(user_t *u)
|
|
{
|
|
mowgli_node_t *n, *tn;
|
|
mowgli_list_t *l;
|
|
struct BlacklistClient *blcptr;
|
|
|
|
if (u == NULL)
|
|
return;
|
|
|
|
l = dnsbl_queries(u);
|
|
|
|
MOWGLI_ITER_FOREACH_SAFE(n, tn, l->head)
|
|
{
|
|
blcptr = n->data;
|
|
mowgli_node_delete(&blcptr->node, l);
|
|
unref_blacklist(blcptr->blacklist);
|
|
delete_resolver_queries(&blcptr->dns_query);
|
|
free(blcptr);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void destroy_blacklists(void)
|
|
{
|
|
mowgli_node_t *n, *tn;
|
|
struct Blacklist *blptr;
|
|
|
|
MOWGLI_ITER_FOREACH_SAFE(n, tn, blacklist_list.head)
|
|
{
|
|
blptr = n->data;
|
|
blptr->hits = 0; /* keep it simple and consistent */
|
|
free(n->data);
|
|
mowgli_node_delete(n, &blacklist_list);
|
|
mowgli_node_free(n);
|
|
}
|
|
}
|
|
|
|
static int dnsbl_config_handler(mowgli_config_file_entry_t *ce)
|
|
{
|
|
mowgli_config_file_entry_t *cce;
|
|
|
|
MOWGLI_ITER_FOREACH(cce, ce->entries)
|
|
{
|
|
char *line = sstrdup(cce->varname);
|
|
new_blacklist(line);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void dnsbl_config_purge(void *unused)
|
|
{
|
|
destroy_blacklists();
|
|
}
|
|
|
|
static void check_dnsbls(hook_user_nick_t *data)
|
|
{
|
|
user_t *u = data->u;
|
|
mowgli_node_t *n;
|
|
|
|
if (!u)
|
|
return;
|
|
|
|
if (is_internal_client(u))
|
|
return;
|
|
|
|
if (!action)
|
|
return;
|
|
|
|
MOWGLI_ITER_FOREACH(n, dnsbl_elist.head)
|
|
{
|
|
dnsbl_exempt_t *de = n->data;
|
|
|
|
if (!irccasecmp(de->ip, u->ip))
|
|
return;
|
|
}
|
|
|
|
lookup_blacklists(u);
|
|
}
|
|
|
|
static void dnsbl_hit(user_t *u, struct Blacklist *blptr)
|
|
{
|
|
service_t *svs;
|
|
|
|
svs = service_find("operserv");
|
|
|
|
if (!strcasecmp("SNOOP", action))
|
|
{
|
|
slog(LG_INFO, "DNSBL: \2%s\2!%s@%s [%s] is listed in DNS Blacklist %s.", u->nick, u->user, u->host, u->gecos, blptr->host);
|
|
/* abort_blacklist_queries(u); */
|
|
return;
|
|
}
|
|
else if (!strcasecmp("NOTIFY", action))
|
|
{
|
|
slog(LG_INFO, "DNSBL: \2%s\2!%s@%s [%s] is listed in DNS Blacklist %s.", u->nick, u->user, u->host, u->gecos, blptr->host);
|
|
notice(svs->nick, u->nick, "Your IP address %s is listed in DNS Blacklist %s", u->ip, blptr->host);
|
|
/* abort_blacklist_queries(u); */
|
|
return;
|
|
}
|
|
else if (!strcasecmp("KLINE", action))
|
|
{
|
|
slog(LG_INFO, "DNSBL: k-lining \2%s\2!%s@%s [%s] who is listed in DNS Blacklist %s.", u->nick, u->user, u->host, u->gecos, blptr->host);
|
|
/* abort_blacklist_queries(u); */
|
|
notice(svs->nick, u->nick, "Your IP address %s is listed in DNS Blacklist %s", u->ip, blptr->host);
|
|
kline_sts("*", "*", u->host, 86400, "Banned (DNS Blacklist)");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void osinfo_hook(sourceinfo_t *si)
|
|
{
|
|
mowgli_node_t *n;
|
|
|
|
if (action)
|
|
command_success_nodata(si, "Action taken when a user is an a DNSBL: %s", action);
|
|
else
|
|
command_success_nodata(si, "Action taken when a user is an a DNSBL: %s", "None");
|
|
|
|
MOWGLI_ITER_FOREACH(n, blacklist_list.head)
|
|
{
|
|
struct Blacklist *blptr = (struct Blacklist *) n->data;
|
|
|
|
command_success_nodata(si, "Blacklist(s): %s", blptr->host);
|
|
}
|
|
}
|
|
|
|
static void write_dnsbl_exempt_db(database_handle_t *db)
|
|
{
|
|
mowgli_node_t *n;
|
|
|
|
MOWGLI_ITER_FOREACH(n, dnsbl_elist.head)
|
|
{
|
|
dnsbl_exempt_t *de = n->data;
|
|
|
|
db_start_row(db, "BW");
|
|
db_write_word(db, de->ip);
|
|
db_write_time(db, de->exempt_ts);
|
|
db_write_word(db, de->creator);
|
|
db_write_word(db, de->reason);
|
|
db_commit_row(db);
|
|
}
|
|
}
|
|
|
|
static void db_h_ble(database_handle_t *db, const char *type)
|
|
{
|
|
const char *ip = db_sread_word(db);
|
|
time_t exempt_ts = db_sread_time(db);
|
|
const char *creator = db_sread_word(db);
|
|
const char *reason = db_sread_word(db);
|
|
|
|
dnsbl_exempt_t *de = smalloc(sizeof(dnsbl_exempt_t));
|
|
|
|
de->ip = sstrdup(ip);
|
|
de->exempt_ts = exempt_ts;
|
|
de->creator = sstrdup(creator);
|
|
de->reason = sstrdup(reason);
|
|
|
|
mowgli_node_add(de, mowgli_node_create(), &dnsbl_elist);
|
|
}
|
|
|
|
void
|
|
_modinit(module_t *m)
|
|
{
|
|
MODULE_TRY_REQUEST_SYMBOL(m, os_set_cmdtree, "operserv/set", "os_set_cmdtree");
|
|
|
|
if (!module_find_published("backend/opensex"))
|
|
{
|
|
slog(LG_INFO, "Module %s requires use of the OpenSEX database backend, refusing to load.", m->name);
|
|
m->mflags = MODTYPE_FAIL;
|
|
return;
|
|
}
|
|
|
|
hook_add_db_write(write_dnsbl_exempt_db);
|
|
|
|
db_register_type_handler("BLE", db_h_ble);
|
|
|
|
service_named_bind_command("operserv", &os_dnsblexempt);
|
|
service_named_bind_command("operserv", &os_dnsblscan);
|
|
|
|
hook_add_event("config_purge");
|
|
hook_add_config_purge(dnsbl_config_purge);
|
|
|
|
hook_add_event("user_add");
|
|
hook_add_user_add(check_dnsbls);
|
|
|
|
hook_add_event("operserv_info");
|
|
hook_add_operserv_info(osinfo_hook);
|
|
|
|
add_dupstr_conf_item("dnsbl_action", &conf_gi_table, 0, &action, NULL);
|
|
add_conf_item("BLACKLISTS", &conf_gi_table, dnsbl_config_handler);
|
|
command_add(&os_set_dnsblaction, *os_set_cmdtree);
|
|
}
|
|
|
|
void
|
|
_moddeinit(module_unload_intent_t intent)
|
|
{
|
|
hook_del_db_write(write_dnsbl_exempt_db);
|
|
hook_del_user_add(check_dnsbls);
|
|
hook_del_config_purge(dnsbl_config_purge);
|
|
hook_del_operserv_info(osinfo_hook);
|
|
|
|
db_unregister_type_handler("BLE");
|
|
|
|
del_conf_item("dnsbl_action", &conf_gi_table);
|
|
del_conf_item("BLACKLISTS", &conf_gi_table);
|
|
command_delete(&os_set_dnsblaction, *os_set_cmdtree);
|
|
service_named_unbind_command("operserv", &os_dnsblexempt);
|
|
service_named_unbind_command("operserv", &os_dnsblscan);
|
|
}
|
|
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
|
|
* vim:ts=8
|
|
* vim:sw=8
|
|
* vim:noexpandtab
|
|
*/
|