/* $Id: dnsrbl.c,v 1.15.2.2 2006/10/26 21:01:08 manu Exp $ */
/*
* Copyright (c) 2006 Emmanuel Dreyfus
* All rights reserved.
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Emmanuel Dreyfus
*
* THIS SOFTWARE IS PROVIDED ``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.
*/
#include "config.h"
#ifdef USE_DNSRBL
#ifdef HAVE_SYS_CDEFS_H
#include <sys/cdefs.h>
#ifdef __RCSID
__RCSID("$Id: dnsrbl.c,v 1.15.2.2 2006/10/26 21:01:08 manu Exp $");
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <errno.h>
#include <sysexits.h>
#ifdef HAVE_OLD_QUEUE_H
#include "queue.h"
#else
#include <sys/queue.h>
#endif
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <resolv.h>
#ifndef NS_MAXMSG
#define NS_MAXMSG 65535
#endif
#ifdef res_ninit
#define HAVE_RESN 1
#ifndef res_ndestroy
#define res_ndestroy(res) res_nclose(res)
#endif
#else
#define res_ninit(res) \
((_res.options & RES_INIT) == 0 && res_init())
#define res_nquery(res, req, class, type, ans, anslen) \
res_query(req, class, type, ans, anslen)
#define res_ndestroy(res)
#endif
#include "milter-greylist.h"
#include "pending.h"
#include "conf.h"
#include "dnsrbl.h"
/*
* locking is done through the same lock as acllist: both are static
* configuration, which are readen or changed at the same times.
*/
struct dnsrbllist dnsrbl_head;
void
dnsrbl_init(void) {
LIST_INIT(&dnsrbl_head);
return;
}
int
dnsrbl_check_source(sa, salen, source)
struct sockaddr *sa;
socklen_t salen;
struct dnsrbl_entry *source;
{
#ifdef HAVE_RESN
struct __res_state res;
#endif
sockaddr_t ss;
char req[NS_MAXDNAME + 1];
char *ans = NULL;
int anslen;
ns_msg handle;
ns_rr rr;
int qtype, i;
char *dnsrbl = source->d_domain;
struct sockaddr *blacklisted;
int retval = 0;
char *addr;
size_t len;
/* No IPv6 DNSRBL exists right now */
if (sa->sa_family != AF_INET)
return 0;
blacklisted = SA(&source->d_blacklisted);
switch (blacklisted->sa_family) {
case AF_INET:
qtype = T_A;
addr = (char *)SADDR4(blacklisted);
len = sizeof(*SADDR4(blacklisted));
break;
#ifdef AF_INET6
case AF_INET6:
qtype = T_AAAA;
addr = (char *)SADDR6(blacklisted);
len = sizeof(*SADDR6(blacklisted));
break;
#endif
default:
mg_log(LOG_ERR, "unexpected address family %d",
blacklisted->sa_family);
exit(EX_SOFTWARE);
break;
}
#ifdef HAVE_RESN
bzero(&res, sizeof(res));
#endif
if (res_ninit(&res) != 0) {
mg_log(LOG_ERR, "res_ninit failed: %s", strerror(errno));
return -1;
}
reverse_endian(SA(&ss), sa);
if ((iptostring(SA(&ss), salen, req, NS_MAXDNAME)) == NULL){
mg_log(LOG_ERR, "iptostring failed: %s", strerror(errno));
retval = -1;
goto end;
}
(void)mystrlcat(req, ".", NS_MAXDNAME);
(void)mystrlcat(req, dnsrbl, NS_MAXDNAME);
if ((ans = malloc(NS_MAXMSG + 1)) == NULL) {
mg_log(LOG_ERR, "malloc failed: %s", strerror(errno));
goto end;
}
anslen = res_nquery(&res, req, C_IN, qtype, ans, NS_MAXMSG + 1);
if (anslen == -1)
goto end;
if (ns_initparse(ans, anslen, &handle) < 0) {
mg_log(LOG_ERR, "ns_initparse failed: %s", strerror(errno));
retval = -1;
goto end;
}
for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) {
if ((ns_parserr(&handle, ns_s_an, i, &rr)) != 0) {
mg_log(LOG_ERR, "ns_parserr failed: %s",
strerror(errno));
retval = -1;
goto end;
}
switch (blacklisted->sa_family) {
case AF_INET:
if (rr.type != T_A)
continue;
break;
#ifdef AF_INET6
case AF_INET6:
if (rr.type != T_AAAA)
continue;
break;
#endif
default:
mg_log(LOG_ERR, "unexpected sa_family");
exit(EX_OSERR);
break;
}
if (rr.rdlength != len)
continue;
if (memcmp(addr, rr.rdata, len) == 0) {
retval = 1;
goto end;
}
}
end:
if (retval == 1 && conf.c_debug) {
char addrstr[NS_MAXDNAME + 1];
iptostring(sa, salen, addrstr, sizeof(addrstr));
mg_log(LOG_DEBUG, "Host %s exists in DNSRBL \"%s\"",
addrstr, source->d_name);
}
free(ans);
res_ndestroy(&res);
return retval;
}
/* XXX this code is probably broken with IPv6 */
void
reverse_endian(dst, src)
struct sockaddr *src;
struct sockaddr *dst;
{
int i, len;
char *src_start;
char *dst_start;
switch (src->sa_family) {
case AF_INET:
src_start = (char *)SADDR4(src);
dst_start = (char *)SADDR4(dst);
len = sizeof(*SADDR4(src));
break;
#ifdef AF_INET6
case AF_INET6:
src_start = (char *)SADDR6(src);
dst_start = (char *)SADDR6(dst);
len = sizeof(*SADDR6(src));
break;
#endif
default:
mg_log(LOG_ERR, "invalid address family %d", src->sa_family);
exit(EX_SOFTWARE);
break;
}
dst->sa_family = src->sa_family;
#ifdef HAVE_SA_LEN
dst->sa_len = src->sa_len;
#endif
for (i = 0; i < len; i++)
dst_start[len - 1 - i] = src_start[i];
return;
}
void
dnsrbl_source_add(name, domain, blacklisted) /* acllist must be write locked */
char *name;
char *domain;
struct sockaddr *blacklisted;
{
struct dnsrbl_entry *de;
socklen_t salen;
char addrstr[IPADDRSTRLEN];
if ((de = malloc(sizeof(*de))) == NULL) {
mg_log(LOG_ERR, "malloc failed: %s", strerror(errno));
exit(EX_OSERR);
}
switch(blacklisted->sa_family) {
case AF_INET:
salen = sizeof(struct sockaddr_in);
break;
#ifdef AF_INET6
case AF_INET6:
salen = sizeof(struct sockaddr_in6);
break;
#endif
default:
mg_log(LOG_ERR, "invalid address family %d",
blacklisted->sa_family);
exit(EX_SOFTWARE);
break;
}
strncpy(de->d_name, name, sizeof(de->d_name));
de->d_name[sizeof(de->d_name) - 1] = '\0';
strncpy(de->d_domain, domain, sizeof(de->d_domain));
de->d_domain[sizeof(de->d_domain) - 1] = '\0';
memcpy(&de->d_blacklisted, blacklisted, salen);
LIST_INSERT_HEAD(&dnsrbl_head, de, d_list);
if (conf.c_debug || conf.c_acldebug) {
if ((iptostring(SA(&de->d_blacklisted), salen, addrstr,
sizeof(addrstr))) == NULL) {
mg_log(LOG_ERR, "iptostring failed: %s",
strerror(errno));
exit(EX_SOFTWARE);
}
mg_log(LOG_DEBUG, "load DNSRBL \"%s\" \"%s\" %s",
de->d_name, de->d_domain, addrstr);
}
return;
}
struct dnsrbl_entry *
dnsrbl_byname(dnsrbl) /* acllist must be read locked */
char *dnsrbl;
{
struct dnsrbl_entry *de;
LIST_FOREACH(de, &dnsrbl_head, d_list) {
if (strcmp(de->d_name, dnsrbl) == 0)
break;
}
return de;
}
void
dnsrbl_clear(void) /* acllist must be write locked */
{
struct dnsrbl_entry *de;
while(!LIST_EMPTY(&dnsrbl_head)) {
de = LIST_FIRST(&dnsrbl_head);
LIST_REMOVE(de, d_list);
free(de);
}
dnsrbl_init();
return;
}
#endif /* USE_DNSRBL */
syntax highlighted by Code2HTML, v. 0.9.1