/************************************************************************
* IRC - Internet Relay Chat, iauth/mod_dnsbl.c
* Copyright (C) 2003 erra@RusNet
* based on iauth/mod_socks.c
* Copyright (C) 1998 Christophe Kalt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: mod_dnsbl.c,v 1.7 2004/05/24 13:04:13 gvs Exp $";
#endif
#include "os.h"
#include "a_defines.h"
#define MOD_DNSBL_C
#include "a_externs.h"
#undef MOD_DNSBL_C
/****************************** PRIVATE *************************************/
#define CACHETIME 30
struct hostlog
{
struct hostlog *next;
char ip[HOSTLEN+1];
u_char state; /* 0 = not found, 1 = found, 2 = timeout */
time_t expire;
};
#define OPT_LOG 0x001
#define OPT_DENY 0x002
#define OPT_PARANOID 0x004
#define OK 0
#define DNSBL_FOUND 1
#define DNSBL_FAILED 2
struct dnsbl_list
{
char *host;
struct dnsbl_list *next;
};
struct dnsbl_private
{
struct hostlog *cache;
u_int lifetime;
u_char options;
/* stats */
u_int chitc, chito, chitn, cmiss, cnow, cmax;
u_int found, failed, good, total, rejects;
struct dnsbl_list *host_list;
};
/*
* dnsbl_succeed
*
* Found a host in DNSBL. Deal with it.
*/
static void
dnsbl_succeed(cl, listname, result)
int cl;
char *listname, *result;
{
struct dnsbl_private *mydata = cldata[cl].instance->data;
if (mydata->options & OPT_PARANOID || (mydata->options & OPT_DENY &&
result[0] == '\177' && result[1] == '\0' &&
result[2] == '\0' /* && result[3] == '\2' */) )
{
cldata[cl].state |= A_DENY;
sendto_ircd("K %d %s %u :found in DNSBL (%s)", cl,
cldata[cl].itsip, cldata[cl].itsport, listname);
mydata->rejects++;
}
if (mydata->options & OPT_LOG)
sendto_log(ALOG_FLOG, LOG_INFO, "%s: found: %s[%s]",
listname, cldata[cl].host, cldata[cl].itsip);
}
/*
* dnsbl_add_cache
*
* Add an entry to the cache.
*/
static void
dnsbl_add_cache(cl, state)
int cl, state;
{
struct dnsbl_private *mydata = cldata[cl].instance->data;
struct hostlog *next;
if (state == DNSBL_FOUND)
mydata->found ++;
else if (state == DNSBL_FAILED)
mydata->failed ++;
else /* state == OK */
mydata->good ++;
if (mydata->lifetime == 0)
return;
mydata->cnow ++;
if (mydata->cnow > mydata->cmax)
mydata->cmax = mydata->cnow;
next = mydata->cache;
mydata->cache = (struct hostlog *)malloc(sizeof(struct hostlog));
mydata->cache->expire = time(NULL) + mydata->lifetime;
strcpy(mydata->cache->ip, cldata[cl].itsip);
mydata->cache->state = state;
mydata->cache->next = next;
DebugLog((ALOG_DNSBLC, 0, "dnsbl_add_cache(%d): new cache %s, result=%d",
cl, mydata->cache->ip, state));
}
/*
* dnsbl_check_cache
*
* Check cache for an entry.
*/
static int
dnsbl_check_cache(cl)
{
struct dnsbl_private *mydata = cldata[cl].instance->data;
struct hostlog **last, *pl;
time_t now = time(NULL);
if (!mydata || mydata->lifetime == 0)
return 0;
DebugLog((ALOG_DNSBLC, 0, "dnsbl_check_cache(%d): Checking cache for %s",
cl, cldata[cl].itsip));
last = &(mydata->cache);
while (pl = *last)
{
DebugLog((ALOG_DNSBLC, 0, "dnsbl_check_cache(%d): cache %s",
cl, pl->ip));
if (pl->expire < now)
{
DebugLog((ALOG_DNSBLC, 0,
"dnsbl_check_cache(%d): free %s (%d < %d)",
cl, pl->ip, pl->expire, now));
*last = pl->next;
free(pl);
mydata->cnow --;
continue;
}
if (!strcasecmp(pl->ip, cldata[cl].itsip))
{
DebugLog((ALOG_DNSBLC, 0,
"dnsbl_check_cache(%d): match (%u)",
cl, pl->state));
pl->expire = now + mydata->lifetime; /* dubious */
if (pl->state == DNSBL_FOUND)
{
dnsbl_succeed(cl, "cached", "\177\0\0");
mydata->chito ++;
}
else if (pl->state == OK)
mydata->chitn ++;
else
mydata->chitc ++;
return -1;
}
last = &(pl->next);
}
mydata->cmiss ++;
return 0;
}
/******************************** PUBLIC ************************************/
/*
* dnsbl_init
*
* This procedure is called when a particular module is loaded.
* Returns NULL if everything went fine,
* an error message otherwise.
*/
char *
dnsbl_init(self)
AnInstance *self;
{
struct dnsbl_private *mydata;
struct dnsbl_list *l;
char tmpbuf[255], cbuf[32], *s;
static char txtbuf[255];
if (self->opt == NULL)
return "Aie! no option(s): nothing to be done!";
mydata = (struct dnsbl_private *) malloc(sizeof(struct dnsbl_private));
bzero((char *) mydata, sizeof(struct dnsbl_private));
self->data = mydata;
mydata->cache = NULL;
mydata->host_list = NULL;
mydata->lifetime = CACHETIME;
tmpbuf[0] = txtbuf[0] = '\0';
if (strstr(self->opt, "log"))
{
mydata->options |= OPT_LOG;
strcat(tmpbuf, ",log");
strcat(txtbuf, ", Log");
}
if (strstr(self->opt, "reject"))
{
mydata->options |= OPT_DENY;
strcat(tmpbuf, ",reject");
strcat(txtbuf, ", Reject");
}
if (strstr(self->opt, "paranoid"))
{
mydata->options |= OPT_PARANOID;
strcat(tmpbuf, ",paranoid");
strcat(txtbuf, ", Paranoid");
}
if (mydata->options == 0)
{
DebugLog((ALOG_DNSBL, 0, "dnsbl_init: Aiee! no option(s)"));
return "Aiee! no option(s)";
}
if (s = strstr(self->opt, "list"))
{
char *ch = index(s, '=');
if (ch)
{
char *x = ch, *t;
for (t = index(++ch, ','); t || ch == x + 1;
t = index(ch, ','))
{
for (; isspace(*ch); ch++) ;
if (t)
*t = '\0';
l = (struct dnsbl_list *)
malloc(sizeof(struct dnsbl_list));
l->host = strdup(ch);
l->next = mydata->host_list;
sendto_log(ALOG_DMISC, LOG_NOTICE,
"dnsbl_init: Added %s as dnsbl", ch);
mydata->host_list = l;
if (t)
{
*t = ',';
x = t;
ch = ++t;
}
else
x = ch; /* need it to end the cycle */
}
}
}
if (mydata->host_list == NULL)
{
DebugLog((ALOG_DNSBL, 0, "dnsbl_init: Aiee! No DNSBL host: "
"nothing to be done!"));
return "Aiee! No DNSBL host: nothing to be done!";
}
if (strstr(self->opt, "cache"))
{
char *ch = index(self->opt, '=');
if (ch)
mydata->lifetime = atoi(++ch);
}
sprintf(cbuf, ",cache=%d", mydata->lifetime);
strcat(tmpbuf, cbuf);
sprintf(cbuf, ", Cache %d (min)", mydata->lifetime);
strcat(txtbuf, cbuf);
mydata->lifetime *= 60;
strcat(tmpbuf, ",list=");
strcat(txtbuf, ", List(s): ");
l = mydata->host_list;
strcat(tmpbuf, l->host);
strcat(txtbuf, l->host);
for (l = l->next; l; l = l->next)
{
strcat(tmpbuf, ",");
strcat(txtbuf, ", ");
strcat(tmpbuf, l->host);
strcat(txtbuf, l->host);
}
self->popt = strdup(tmpbuf + 1);
return txtbuf + 2;
}
/*
* dnsbl_release
*
* This procedure is called when a particular module is unloaded.
*/
void
dnsbl_release(self)
AnInstance *self;
{
struct dnsbl_private *mydata = self->data;
struct dnsbl_list *l, *n;
for (l = mydata->host_list; l; l = n)
{
free(l->host);
n = l->next;
free(l);
}
free(mydata);
free(self->popt);
}
/*
* dnsbl_stats
*
* This procedure is called regularly to update statistics sent to ircd.
*/
void
dnsbl_stats(self)
AnInstance *self;
{
struct dnsbl_private *mydata = self->data;
sendto_ircd("S dnsbl verified %u rejected %u",
mydata->total, mydata->rejects);
}
/*
* dnsbl_start
*
* This procedure is called to start the host check procedure.
* Returns 0 if everything went fine,
* -1 otherwise (nothing to be done, or failure)
*
* It is responsible for sending error messages where appropriate.
* In case of failure, it's responsible for cleaning up (e.g. dnsbl_clean
* will NOT be called)
*/
int
dnsbl_start(cl)
u_int cl;
{
struct dnsbl_private *mydata = cldata[cl].instance->data;
struct dnsbl_list *l, *m;
char *req;
char *ip, *c1, *c2, *c3;
int ipl;
struct hostent *hst = NULL;
if (cldata[cl].state & A_DENY)
{
/* no point of doing anything */
DebugLog((ALOG_DNSBL, 0,
"dnsbl_start(%d): A_DENY alredy set ", cl));
return -1;
}
if (strchr(cldata[cl].itsip,':')) {
DebugLog((ALOG_DNSBL, 0,
"dnsbl_start(%d): %s is IPv6, skipping ", cl, cldata[cl].itsip));
return -1;
}
if (dnsbl_check_cache(cl))
return -1;
ip = strdup(cldata[cl].itsip);
ipl = strlen(ip);
DebugLog((ALOG_DNSBL, 0, "dnsbl_start(%d): checking %s", cl, ip));
strtok(ip, ".");
c3 = strtok(NULL, ".");
c2 = strtok(NULL, ".");
c1 = strtok(NULL, ".");
for (l = mydata->host_list; l && !hst; l = l->next)
{
int len;
req = malloc(strlen(l->host) + ipl + 4);
len = sprintf( req, "%s.%s.%s.%s.%s", c1, c2, c3, ip, l->host);
DebugLog((ALOG_DNSBL, 0,
"dnsbl_start(%d): gethostbyname() for %s",
cl, req ));
if (req[len] != '.') { /* wrap around buggy DNS servers */
req[len++] = '.';
req[len] = '\0';
}
hst = gethostbyname(req);
m = l;
free(req);
}
mydata->total++;
if (hst)
{
int c = mydata->rejects;
dnsbl_succeed(cl, m->host, hst->h_addr_list[0]);
dnsbl_add_cache(cl, (c == mydata->rejects) ?
DNSBL_FAILED : DNSBL_FOUND);
/* cache failure when not actually rejected, otherwise
* client will be shot at the 2nd try -erra
*/
}
else
{
DebugLog((ALOG_DNSBL, 0,
"dnsbl_start(%d): gethostbyname() reported %s",
cl, hstrerror(h_errno) ));
dnsbl_add_cache(cl, DNSBL_FAILED);
}
free(ip);
return -1;
}
/*
* dnsbl_work
*
* This procedure is called whenever there's new data in the buffer.
* Returns 0 if everything went fine, and there is more work to be done,
* Returns -1 if the module has finished its work (and cleaned up).
*
* It is responsible for sending error messages where appropriate.
*/
int
dnsbl_work(cl)
u_int cl;
{
/*
** There' nothing to do here
*/
DebugLog((ALOG_DNSBL, 0,
"dnsbl_work(%d) invoked but why?", cl ));
}
/*
* dnsbl_clean
*
* This procedure is called whenever the module should interrupt its work.
* It is responsible for cleaning up any allocated data, and in particular
* closing file descriptors.
*/
void
dnsbl_clean(cl)
u_int cl;
{
DebugLog((ALOG_DNSBL, 0, "dnsbl_clean(%d): cleaning up", cl));
/*
** only one of rfd and wfd may be set at the same time,
** in any case, they would be the same fd, so only close() once
*/
}
/*
* dnsbl_timeout
*
* This procedure is called whenever the timeout set by the module is
* reached.
*
* Returns 0 if things are okay, -1 if check was aborted.
*/
int
dnsbl_timeout(cl)
u_int cl;
{
DebugLog((ALOG_DNSBL, 0, "dnsbl_timeout(%d): calling dnsbl_clean ", cl));
dnsbl_clean(cl);
return -1;
}
aModule Module_dnsbl =
{ "dnsbl", dnsbl_init, dnsbl_release, dnsbl_stats,
dnsbl_start, dnsbl_work, dnsbl_timeout, dnsbl_clean };
syntax highlighted by Code2HTML, v. 0.9.1