2 dnsbl (rbl) checker application
4 Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru>
5 This file is part of UDNS library, an async DNS stub resolver.
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library, in file named COPYING.LGPL; if not,
19 write to the Free Software Foundation, Inc., 59 Temple Place,
20 Suite 330, Boston, MA 02111-1307 USA
31 # include <winsock2.h>
34 # include <sys/types.h>
35 # include <sys/socket.h>
36 # include <netinet/in.h>
47 static const char *version = "udns-rblcheck 0.4";
48 static char *progname;
50 static void error(int die, const char *fmt, ...) {
52 fprintf(stderr, "%s: ", progname);
53 va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
61 struct ipcheck *parent;
64 struct dns_rr_a4 *addr;
65 struct dns_rr_txt *txt;
72 struct rblookup *lookup;
75 #define notlisted ((void*)1)
77 static int nzones, nzalloc;
78 static const char **zones;
82 static int verbose = 1;
84 * <0 - only bare As/TXTs
86 * 1(default) - what is listed by RBL: result
87 * 2 - what is[not ]listed by RBL: result, name lookups
93 static void *ecalloc(int size, int cnt) {
94 void *t = calloc(size, cnt);
96 error(1, "out of memory");
100 static void addzone(const char *zone) {
101 if (nzones >= nzalloc) {
102 const char **zs = (const char**)ecalloc(sizeof(char*), (nzalloc += 16));
104 memcpy(zs, zones, nzones * sizeof(char*));
109 zones[nzones++] = zone;
112 static int addzonefile(const char *fname) {
113 FILE *f = fopen(fname, "r");
117 while(fgets(linebuf, sizeof(linebuf), f)) {
118 char *p = linebuf, *e;
119 while(*p == ' ' || *p == '\t') ++p;
120 if (*p == '#' || *p == '\n') continue;
122 while(*e && *e != ' ' && *e != '\t' && *e != '\n')
125 p = memcpy(ecalloc(e - p, 1), p, e - p); // strdup
132 static void dnserror(struct rblookup *ipl, const char *what) {
134 error(0, "unable to %s for %s (%s): %s",
135 what, dns_ntop(AF_INET, &ipl->key, buf, sizeof(buf)),
136 ipl->zone, dns_strerror(dns_status(0)));
140 static void display_result(struct ipcheck *ipc) {
142 struct rblookup *l, *le;
144 if (!ipc->naddr) return;
145 for (l = ipc->lookup, le = l + nzones * ipc->naddr; l < le; ++l) {
146 if (!l->addr) continue;
147 if (verbose < 2 && l->addr == notlisted) continue;
149 dns_ntop(AF_INET, &l->key, buf, sizeof(buf));
150 if (ipc->name) printf("%s[%s]", ipc->name, buf);
151 else printf("%s", buf);
153 if (l->addr == notlisted) {
154 printf(" is NOT listed by %s\n", l->zone);
157 else if (verbose >= 1)
158 printf(" is listed by %s: ", l->zone);
159 else if (verbose >= 0)
160 printf(" %s ", l->zone);
161 if (verbose >= 1 || !do_txt)
162 for (j = 0; j < l->addr->dnsa4_nrr; ++j)
163 printf("%s%s", j ? " " : "",
164 dns_ntop(AF_INET, &l->addr->dnsa4_addr[j], buf, sizeof(buf)));
167 for(j = 0; j < l->txt->dnstxt_nrr; ++j) {
168 unsigned char *t = l->txt->dnstxt_txt[j].txt;
169 unsigned char *e = t + l->txt->dnstxt_txt[j].len;
170 printf("%s\"", verbose > 0 ? "\n\t" : j ? " " : "");
172 if (*t < ' ' || *t >= 127) printf("\\x%02x", *t);
173 else if (*t == '\\' || *t == '"') printf("\\%c", *t);
182 printf("%s<no text available>", verbose > 0 ? "\n\t" : "");
189 static void txtcb(struct dns_ctx *ctx, struct dns_rr_txt *r, void *data) {
190 struct rblookup *ipl = data;
193 ++ipl->parent->listed;
195 else if (dns_status(ctx) != DNS_E_NXDOMAIN)
196 dnserror(ipl, "lookup DNSBL TXT record");
199 static void a4cb(struct dns_ctx *ctx, struct dns_rr_a4 *r, void *data) {
200 struct rblookup *ipl = data;
205 if (dns_submit_a4dnsbl_txt(0, &ipl->key, ipl->zone, txtcb, ipl))
207 dnserror(ipl, "submit DNSBL TXT record");
209 ++ipl->parent->listed;
211 else if (dns_status(ctx) != DNS_E_NXDOMAIN)
212 dnserror(ipl, "lookup DNSBL A record");
214 ipl->addr = notlisted;
218 submit_a_queries(struct ipcheck *ipc,
219 int naddr, const struct in_addr *addr) {
221 struct rblookup *rl = ecalloc(sizeof(*rl), nzones * naddr);
224 for(a = 0; a < naddr; ++a) {
225 for(z = 0; z < nzones; ++z) {
229 if (!dns_submit_a4dnsbl(0, &rl->key, rl->zone, a4cb, rl))
230 dnserror(rl, "submit DNSBL A query");
237 static void namecb(struct dns_ctx *ctx, struct dns_rr_a4 *rr, void *data) {
238 struct ipcheck *ipc = data;
240 submit_a_queries(ipc, rr->dnsa4_nrr, rr->dnsa4_addr);
244 error(0, "unable to lookup `%s': %s",
245 ipc->name, dns_strerror(dns_status(ctx)));
250 static int submit(struct ipcheck *ipc) {
252 if (dns_pton(AF_INET, ipc->name, &addr) > 0) {
253 submit_a_queries(ipc, 1, &addr);
256 else if (!dns_submit_a4(0, ipc->name, 0, namecb, ipc)) {
257 error(0, "unable to submit name query for %s: %s\n",
258 ipc->name, dns_strerror(dns_status(0)));
264 static void waitdns(struct ipcheck *ipc) {
268 int fd = dns_sock(NULL);
271 while((c = dns_timeouts(NULL, -1, now)) > 0) {
275 c = select(fd+1, &fds, NULL, NULL, &tv);
278 dns_ioevent(NULL, now);
279 if (stopfirst && ipc->listed)
284 int main(int argc, char **argv) {
287 char *nameserver = NULL;
290 if (!(progname = strrchr(argv[0], '/'))) progname = argv[0];
291 else argv[0] = ++progname;
293 while((c = getopt(argc, argv, "hqtvms:S:cn:")) != EOF) switch(c) {
294 case 's': ++zgiven; addzone(optarg); break;
297 if (addzonefile(optarg)) break;
298 error(1, "unable to read zonefile `%s'", optarg);
299 case 'c': ++zgiven; nzones = 0; break;
300 case 'q': --verbose; break;
301 case 'v': ++verbose; break;
302 case 't': do_txt = 1; break;
303 case 'n': nameserver = optarg; break;
304 case 'm': ++stopfirst; break;
306 printf("%s: %s (udns library version %s).\n",
307 progname, version, dns_version());
308 printf("Usage is: %s [options] address..\n", progname);
310 "Where options are:\n"
311 " -h - print this help and exit\n"
312 " -s service - add the service (DNSBL zone) to the serice list\n"
313 " -S service-file - add the DNSBL zone(s) read from the given file\n"
314 " -c - clear service list\n"
315 " -v - increase verbosity level (more -vs => more verbose)\n"
316 " -q - decrease verbosity level (opposite of -v)\n"
317 " -t - obtain and print TXT records if any\n"
318 " -m - stop checking after first address match in any list\n"
319 " -n ipaddr - use the given nameserver instead of the default\n"
320 "(if no -s or -S option is given, use $RBLCHECK_ZONES, ~/.rblcheckrc\n"
321 "or /etc/rblcheckrc in that order)\n"
325 error(1, "use `%s -h' for help", progname);
329 char *s = getenv("RBLCHECK_ZONES");
333 for(k = strtok(s, " \t"); k; k = strtok(NULL, " \t"))
337 else { /* probably worthless on windows? */
339 char *home = getenv("HOME");
340 if (!home) home = ".";
341 path = malloc(strlen(home) + 1 + sizeof(".rblcheckrc"));
342 sprintf(path, "%s/.rblcheckrc", home);
343 if (!addzonefile(path))
344 addzonefile("/etc/rblcheckrc");
349 error(1, "no service (zone) list specified (-s or -S option)");
357 if (dns_init(NULL, 0) < 0)
358 error(1, "unable to initialize DNS library: %s", strerror(errno));
360 dns_add_serv(NULL, NULL);
361 if (dns_add_serv(NULL, nameserver) < 0)
362 error(1, "wrong IP address for a nameserver: `%s'", nameserver);
364 if (dns_open(NULL) < 0)
365 error(1, "unable to initialize DNS library: %s", strerror(errno));
367 for (c = 0; c < argc; ++c) {
368 if (c && (verbose > 1 || (verbose == 1 && do_txt))) putchar('\n');
369 memset(&ipc, 0, sizeof(ipc));
373 display_result(&ipc);
374 if (stopfirst > 1 && listed) break;
377 return listed ? 100 : failures ? 2 : 0;