[rbldnsd] dynamically loadable hook [was: Problems configuring BIND
9 with rbldnsd]
Michael Tokarev
mjt at tls.msk.ru
Wed Sep 6 02:39:55 MSD 2006
Michael Tokarev wrote:
> Chris Gabe wrote:
>> We would like to use rbldnsd to help scrape urls for our zone file
>> providers. Our rbldnsd implementation services several DNSBLs and url
>> block lists at once. It is problematic to use a log of *all* the
>> queries to obtain the list in question. It would be better if rbldnsd
>> provided the following capabilities:
>>
>> - log each query that goes to one of a specified set of block lists,
>> which is not present in any of them. We don't want to include those
>> already in any of the lists; but queries will go to specific lists. We
>> also want to exclude queries to those lists that are not appropriate,
>> like ip4sets.
>>
>> - support very frequent updates, required to fight quick DNS games by
>> spammers. E.g. log to a separate file each minute. wrap after, say, 60
>> minutes to the first file.
>> Or some equivalent that lets us efficiently process the output every
>> minute, i.e. sort | uniq, and submit it to the url block list server for
>> examination and potential inclusion.
>> We could get around this by keeping track of the position in the syslog,
>> I suppose. Or newsyslog every minute (blech)
>
> Oh well.
>
> The issue with all this is - everyone want different things wrt logging,
> statistics and the like. For example, NJABL wants to log queries coming
> from certain IP addresses, logging only the query IP which is not yet
> listed. Spamhaus wanted to see ALL realtime statistics per client IP.
> And so on.
>
> So it's impossible to implement it all in generic way, the only way I
> see is to use some programming locally.
>
> Rbldnsd has compile-time support for user-defined hooks in certain
> places. See rbldnsd_hooks.h and rbldnsd_hooks.c for examples.
> There's no documentation about how to use it all, and I never tried
> to actually implement a real example, don't even know if it will
> work. But I see no other way elegant way.
>
> Maybe some dynamically-loaded object implementing similar hooks,
> but this places unnecessary overhead for all the rest of users
> (and believe me, high load is quite common for public DNSBL
> mirrors, so every extra instruction counts).
Ok, I prototyped something. See the attached diff (against 0.996a).
Note for now it only works with gcc (-rdynamic), and only on systems
that use dlopen() in libdl to load dynamic objects.
Also attached is an example (dummy) program - just a quick hack,
printf()s should not be there. To use, in rbldnsd source dir:
gcc -o hooks.so hooks.c
./rbldnsd .. -x hooks.so -X hook-arg ...
/mjt
-------------- next part --------------
Index: Makefile.in
===================================================================
RCS file: /ws/CVS/rbldnsd/Makefile.in,v
retrieving revision 1.18
diff -u -p -r1.18 Makefile.in
--- Makefile.in 21 Dec 2005 21:42:06 -0000 1.18
+++ Makefile.in 5 Sep 2006 22:19:34 -0000
@@ -60,8 +60,8 @@ LIB_GSRC = $(LIBDNS_GSRC) $(LIBIP4_GSRC)
RBLDNSD_SRCS = rbldnsd.c rbldnsd_zones.c rbldnsd_packet.c \
rbldnsd_ip4set.c rbldnsd_ip4tset.c rbldnsd_ip4trie.c rbldnsd_dnset.c \
rbldnsd_generic.c rbldnsd_combined.c rbldnsd_acl.c \
- rbldnsd_hooks.c rbldnsd_util.c
-RBLDNSD_HDRS = rbldnsd.h rbldnsd_hooks.h
+ rbldnsd_util.c
+RBLDNSD_HDRS = rbldnsd.h
RBLDNSD_OBJS = $(RBLDNSD_SRCS:.c=.o) lib$(NAME).a
MISC = configure configure.lib \
Index: configure
===================================================================
RCS file: /ws/CVS/rbldnsd/configure,v
retrieving revision 1.14
diff -u -p -r1.14 configure
--- configure 12 Jun 2006 21:14:48 -0000 1.14
+++ configure 5 Sep 2006 22:19:34 -0000
@@ -13,7 +13,7 @@ else
exit 1
fi
-options="ipv6 stats master_dump zlib"
+options="ipv6 stats master_dump zlib dso"
for opt in $options; do
eval enable_$opt=
@@ -26,7 +26,7 @@ fi
enable() {
opt=`echo "$1" | sed 's/^--[^-]*-//'`
case "$opt" in
- ipv6|stats|master_dump|zlib) ;;
+ ipv6|stats|master_dump|zlib|dso) ;;
master-dump) opt=master_dump ;;
*) echo "configure: unrecognized option \`$1'" >&2; exit 1;;
esac
@@ -246,8 +246,7 @@ fi
if [ n = "$enable_zlib" ]; then
echo "#define NO_ZLIB 1 /* option disabled */" >>confdef.h
-else
- if ac_link_v "for zlib support" -lz <<EOF
+elif ac_link_v "for zlib support" -lz <<EOF
#include <sys/types.h>
#include <stdio.h>
#include <zlib.h>
@@ -260,14 +259,31 @@ int main() {
return 0;
}
EOF
- then
- LIBS="$LIBS -lz"
- else
- if [ "$enable_zlib" ]; then
- ac_fatal "zlib support is requested but not found/available"
- fi
- echo "#define NO_ZLIB" >>confdef.h
- fi
+then
+ LIBS="$LIBS -lz"
+elif [ "$enable_zlib" ]; then
+ ac_fatal "zlib support is requested but not found/available"
+else
+ echo "#define NO_ZLIB" >>confdef.h
+fi
+
+if [ n = "$enable_dso" ]; then
+ echo "#define NO_DSO 1 /* option disabled */" >>confdef.h
+elif ac_link_v "for dlopen() in -dl with -rdynamic" -ldl -rdynamic <<EOF
+#include <dlfcn.h>
+int main() {
+ void *handle, *func;
+ handle = dlopen("testfile", RTLD_NOW);
+ func = dlsym(handle, "function");
+ return 0;
+}
+EOF
+then
+ LIBS="$LIBS -ldl -rdynamic"
+elif [ "$enable_dso" ]; then
+ ac_fatal "dso support requires dlopen() in -ldl"
+else
+ echo "#define NO_DSO 1 /* not available */" >> confdef.h
fi
if [ n = "$enable_stats" ]; then
Index: rbldnsd.c
===================================================================
RCS file: /ws/CVS/rbldnsd/rbldnsd.c,v
retrieving revision 1.113
diff -u -p -r1.113 rbldnsd.c
--- rbldnsd.c 27 Jul 2006 09:37:49 -0000 1.113
+++ rbldnsd.c 5 Sep 2006 22:19:35 -0000
@@ -1,4 +1,4 @@
-/* $Id: rbldnsd.c,v 1.113 2006/07/27 09:37:49 mjt Exp $
+/* $Id: rbldnsd.c,v 1.114 2006/09/05 17:30:00 mjt Exp $
* rbldnsd: main program
*/
@@ -23,7 +23,6 @@
#include <fcntl.h>
#include <sys/wait.h>
#include "rbldnsd.h"
-#include "rbldnsd_hooks.h"
#ifndef NO_SELECT_H
# include <sys/select.h>
@@ -47,6 +46,9 @@
# define STATS_IPC_IOVEC 1
# endif
#endif
+#ifndef NO_DSO
+# include <dlfcn.h>
+#endif
#ifndef NI_MAXHOST
# define NI_MAXHOST 1025
@@ -109,6 +111,10 @@ static int fork_on_reload;
#if STATS_IPC_IOVEC
static struct iovec *stats_iov;
#endif
+#ifndef NO_DSO
+int (*hook_reload_check)(), (*hook_reload)();
+int (*hook_query_access)(), (*hook_query_result)();
+#endif
/* a list of zonetypes. */
const struct dstype *ds_types[] = {
@@ -138,7 +144,7 @@ static int satoi(const char *s) {
static void NORETURN usage(int exitcode) {
const struct dstype **dstp;
printf(
-"%s: rbl dns daemon version %s%s\n"
+"%s: rbl dns daemon version %s\n"
"Usage is: %s options zonespec...\n"
"where options are:\n"
" -u user[:group] - run as this user:group (rbldns)\n"
@@ -174,14 +180,15 @@ static void NORETURN usage(int exitcode)
#ifndef NO_ZLIB
" -C - disable on-the-fly decompression of dataset files\n"
#endif
-#ifdef do_hook_getopt
-" -H local_hook_options - process custom options (for custom builds)\n"
+#ifndef NO_DZO
+" -x extension - load given extension module (.so file)\n"
+" -X extarg - pass extarg to extension init routine\n"
#endif
" -d - dump all zones in BIND format to standard output and exit\n"
"each zone specified using `name:type:file,file...'\n"
"syntax, repeated names constitute the same zone.\n"
"Available dataset types:\n"
-, progname, version, hook_info, progname);
+, progname, version, progname);
for(dstp = ds_types; *dstp; ++dstp)
printf(" %s - %s\n", (*dstp)->dst_name, (*dstp)->dst_descr);
exit(exitcode);
@@ -349,6 +356,11 @@ static void init(int argc, char **argv)
int nodaemon = 0, quickstart = 0, dump = 0, nover = 0, forkon = 0;
int family = AF_UNSPEC;
int cfd = -1;
+ const struct zone *z;
+#ifndef NO_DSO
+ char *ext = NULL, *extarg = NULL;
+ int (*extinit)(const char *arg, struct zone *zonelist) = NULL;
+#endif
if ((progname = strrchr(argv[0], '/')) != NULL)
argv[0] = ++progname;
@@ -357,7 +369,7 @@ static void init(int argc, char **argv)
if (argc <= 1) usage(1);
- while((c = getopt(argc, argv, "u:r:b:w:t:c:p:nel:qs:h46dvaAfCH:")) != EOF)
+ while((c = getopt(argc, argv, "u:r:b:w:t:c:p:nel:qs:h46dvaAfCx:X:")) != EOF)
switch(c) {
case 'u': user = optarg; break;
case 'r': rootdir = optarg; break;
@@ -440,13 +452,13 @@ break;
case 'A': lazy = 0; break;
case 'f': forkon = 1; break;
case 'C': nouncompress = 1; break;
- case 'H':
-#ifdef do_hook_getopt
- if (hook_getopt(optarg) != 0)
- error(0, "error processing custom option `%s'", optarg);
- break;
+#ifndef NO_DSO
+ case 'x': ext = optarg; break;
+ case 'X': extarg = optarg; break;
#else
- error(0, "no custom option processing is compiled in");
+ case 'x':
+ case 'X':
+ error(0, "extension support is not compiled in");
#endif
case 'h': usage(0);
default: error(0, "type `%.50s -h' for help", progname);
@@ -458,7 +470,6 @@ break;
#ifndef NO_MASTER_DUMP
if (dump) {
- struct zone *z;
time_t now;
logto = LOGTO_STDERR;
for(c = 0; c < argc; ++c)
@@ -506,6 +517,17 @@ break;
initsockets(bindaddr, nba, family);
+#ifndef NO_DSO
+ if (ext) {
+ void *handle = dlopen(ext, RTLD_NOW);
+ if (!handle)
+ error(0, "unable to load extension `%s': %s", ext, dlerror());
+ extinit = dlsym(handle, "rbldnsd_extension_init");
+ if (!extinit)
+ error(0, "unable to find extension init routine in `%s'", ext);
+ }
+#endif
+
if (!user && !(uid = getuid()))
user = "rbldns";
@@ -560,25 +582,25 @@ break;
for(c = 0; c < argc; ++c)
zonelist = addzone(zonelist, argv[c]);
init_zones_caches(zonelist);
-#ifdef do_hook_init
- if (hook_init(zonelist) != 0) error(0, "error processing init hook");
+
+#ifndef NO_DSO
+ if (extinit && extinit(extarg, zonelist) != 0)
+ error(0, "unable to iniitialize extension `%s'", ext);
#endif
if (!quickstart && !do_reload(0))
error(0, "zone loading errors, aborting");
- { const struct zone *z;
- for(c = 0, z = zonelist; z; z = z->z_next)
- ++c;
- numzones = c;
- }
+ /* count number of zones */
+ for(c = 0, z = zonelist; z; z = z->z_next)
+ ++c;
+ numzones = c;
+
#if STATS_IPC_IOVEC
stats_iov = (struct iovec *)emalloc(numzones * sizeof(struct iovec));
- { struct zone *z;
- for(c = 0, z = zonelist; z; z = z->z_next, ++c) {
- stats_iov[c].iov_base = (char*)&z->z_stats;
- stats_iov[c].iov_len = sizeof(z->z_stats);
- }
+ for(c = 0, z = zonelist; z; z = z->z_next, ++c) {
+ stats_iov[c].iov_base = (char*)&z->z_stats;
+ stats_iov[c].iov_len = sizeof(z->z_stats);
}
#endif
dslog(LOG_INFO, 0, "rbldnsd version %s started (%d socket(s), %d zone(s))",
@@ -820,7 +842,7 @@ static int do_reload(int do_fork) {
#endif /* NO_TIMES */
ds = nextdataset2reload(NULL);
- if (!ds) {
+ if (!ds && call_hook(reload_check, (zonelist)) == 0) {
check_expires();
return 1; /* nothing to reload */
}
@@ -869,10 +891,11 @@ static int do_reload(int do_fork) {
#endif /* NO_TIMES */
r = 1;
- do {
+ while(ds) {
if (!loaddataset(ds))
r = 0;
- } while ((ds = nextdataset2reload(ds)) != NULL);
+ ds = nextdataset2reload(ds);
+ }
for (zone = zonelist; zone; zone = zone->z_next) {
time_t stamp = 0;
@@ -911,9 +934,8 @@ static int do_reload(int do_fork) {
"NS or SOA RRs are too long, will be ignored");
}
-#ifdef do_hook_reload
- hook_reload(zonelist);
-#endif
+ if (call_hook(reload, (zonelist)) != 0)
+ r = 0;
ip = ssprintf(ibuf, sizeof(ibuf), "zones reloaded");
#ifndef NO_TIMES
Index: rbldnsd.h
===================================================================
RCS file: /ws/CVS/rbldnsd/rbldnsd.h,v
retrieving revision 1.98
diff -u -p -r1.98 rbldnsd.h
--- rbldnsd.h 22 Jul 2006 07:03:51 -0000 1.98
+++ rbldnsd.h 5 Sep 2006 22:19:35 -0000
@@ -271,6 +271,9 @@ struct zone { /* zone, list of zones */
struct dnsstats z_stats; /* statistic counters */
struct dnsstats z_pstats; /* for stats monitoring: prev values */
#endif
+#ifndef NO_DSO
+ void *z_hookdata; /* data ptr for hooks */
+#endif
struct zone *z_next; /* next in list */
};
@@ -431,3 +434,38 @@ const char *ip4trie_lookup(const struct
struct ip4trie_node *
ip4trie_addnode(struct ip4trie *trie, ip4addr_t prefix, unsigned bits,
struct mempool *mp);
+
+/* hooks from a DSO extensions */
+#ifndef NO_DSO
+
+/* prototype for init routine */
+int rbldnsd_extension_init(char *arg, struct zone *zonelist);
+
+/* return true/false depending whenever hook needs to be reloaded */
+extern int (*hook_reload_check)(const struct zone *zonelist);
+
+/* perform actual reload, after all zones has been reloaded. */
+extern int (*hook_reload)(struct zone *zonelist);
+
+/* check whenever this query is allowed for this client:
+ * * 0 = ok, <0 = drop the packet, >0 = refuse */
+extern int (*hook_query_access)
+ (const struct sockaddr *requestor,
+ const struct zone *zone,
+ const struct dnsqinfo *qinfo);
+
+/* notice result of the OK query */
+extern int (*hook_query_result)
+ (const struct sockaddr *requestor,
+ const struct zone *zone,
+ const struct dnsqinfo *qinfo,
+ int positive);
+
+#define call_hook(name, args) \
+ (hook_##name ? hook_##name args : 0)
+
+#else /* dummy "functions" */
+
+#define call_hook(name, args) 0
+
+#endif
Index: rbldnsd_packet.c
===================================================================
RCS file: /ws/CVS/rbldnsd/rbldnsd_packet.c,v
retrieving revision 1.97
diff -u -p -r1.97 rbldnsd_packet.c
--- rbldnsd_packet.c 19 Dec 2005 14:36:21 -0000 1.97
+++ rbldnsd_packet.c 5 Sep 2006 22:19:36 -0000
@@ -11,7 +11,6 @@
#include <netdb.h>
#include <syslog.h>
#include "rbldnsd.h"
-#include "rbldnsd_hooks.h"
#ifndef NO_IPv6
# ifndef NI_MAXHOST
@@ -362,12 +361,10 @@ int replypacket(struct dnspacket *pkt, u
if (qi.qi_tflag & NSQUERY_REFUSE)
refuse(DNS_R_REFUSED);
-#ifdef do_hook_query_access
- if ((found = hook_query_access(zone, NULL, &qi))) {
+ if ((found = call_hook(query_access, (pkt->p_peer, zone, &qi)))) {
if (found < 0) return 0;
refuse(DNS_R_REFUSED);
}
-#endif
if (qi.qi_dnlab == 0) { /* query to base zone: SOA and NS */
@@ -414,9 +411,7 @@ int replypacket(struct dnspacket *pkt, u
addrr_soa(pkt, zone, 1); /* add SOA if any to AUTHORITY */
h[p_f2] = DNS_R_NXDOMAIN;
do_stats(zone->z_stats.q_nxd += 1);
-#ifdef do_hook_query_result
- hook_query_result(zone, NULL, &qi, 0);
-#endif
+ (void)call_hook(query_result, (pkt->p_peer, zone, &qi, 0));
}
else {
if (!h[p_ancnt2]) { /* positive reply, no answers */
@@ -427,9 +422,7 @@ int replypacket(struct dnspacket *pkt, u
!lazy)
addrr_ns(pkt, zone, 1); /* add nameserver records to positive reply */
do_stats(zone->z_stats.q_ok += 1);
-#ifdef do_hook_query_result
- hook_query_result(zone, NULL, &qi, 1);
-#endif
+ (void)call_hook(query_result, (pkt->p_peer, zone, &qi, 1));
}
if (rlen() > DNS_MAXPACKET) { /* add OPT record for long replies */
/* as per parsequery(), we always have 11 bytes for minimal OPT record at
-------------- next part --------------
A non-text attachment was scrubbed...
Name: hooks.c
Type: text/x-csrc
Size: 1895 bytes
Desc: not available
Url : http://www.corpit.ru/pipermail/rbldnsd/attachments/20060906/fc0817d5/hooks.c
More information about the rbldnsd
mailing list