root/src/udns/rblcheck.c

Revision 5a9b91bc0a14543e0995eba973fbeacc05aaee8b, 9.9 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

svn merge -r 327:330 https://labs.omniti.com/reconnoiter/branches/dev/udns .

closes #38

  • Property mode set to 100644
Line 
1 /* $Id: rblcheck.c,v 1.14 2007/01/10 02:52:51 mjt Exp $
2    dnsbl (rbl) checker application
3
4    Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
5    This file is part of UDNS library, an async DNS stub resolver.
6
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.
11
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.
16
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
21
22  */
23
24 #ifdef HAVE_UDNS_CONFIG_H
25 # include "udns_config.h"
26 #endif
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #ifdef WINDOWS
31 # include <winsock2.h>
32 #else
33 # include <unistd.h>
34 # include <sys/types.h>
35 # include <sys/socket.h>
36 # include <netinet/in.h>
37 #endif
38 #include <time.h>
39 #include <errno.h>
40 #include <stdarg.h>
41 #include "udns.h"
42
43 #ifndef HAVE_GETOPT
44 # include "getopt.c"
45 #endif
46
47 static const char *version = "udns-rblcheck 0.2";
48 static char *progname;
49
50 static void error(int die, const char *fmt, ...) {
51   va_list ap;
52   fprintf(stderr, "%s: ", progname);
53   va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
54   putc('\n', stderr);
55   fflush(stderr);
56   if (die)
57     exit(1);
58 }
59
60 struct rblookup {
61   struct ipcheck *parent;
62   struct in_addr key;
63   const char *zone;
64   struct dns_rr_a4  *addr;
65   struct dns_rr_txt *txt;
66 };
67
68 struct ipcheck {
69   const char *name;
70   int naddr;
71   int listed;
72   struct rblookup *lookup;
73 };
74
75 #define notlisted ((void*)1)
76
77 static int nzones, nzalloc;
78 static const char **zones;
79
80 static int do_txt;
81 static int stopfirst;
82 static int verbose = 1;
83 /* verbosity level:
84  * <0 - only bare As/TXTs
85  * 0 - what RBL result
86  * 1(default) - what is listed by RBL: result
87  * 2          - what is[not ]listed by RBL: result, name lookups
88  */
89
90 static int listed;
91 static int failures;
92
93 static void *ecalloc(int size, int cnt) {
94   void *t = calloc(size, cnt);
95   if (!t)
96     error(1, "out of memory");
97   return t;
98 }
99
100 static void addzone(const char *zone) {
101   if (nzones >= nzalloc) {
102     const char **zs = (const char**)ecalloc(sizeof(char*), (nzalloc += 16));
103     if (zones) {
104       memcpy(zs, zones, nzones * sizeof(char*));
105       free(zones);
106     }
107     zones = zs;
108   }
109   zones[nzones++] = zone;
110 }
111
112 static int addzonefile(const char *fname) {
113   FILE *f = fopen(fname, "r");
114   char linebuf[2048];
115   if (!f)
116     return 0;
117   while(fgets(linebuf, sizeof(linebuf), f)) {
118     char *p = linebuf, *e;
119     while(*p == ' ' || *p == '\t') ++p;
120     if (*p == '#' || *p == '\n') continue;
121     e = p;
122     while(*e && *e != ' ' && *e != '\t' && *e != '\n')
123       ++e;
124     *e = '\0';
125     addzone(p);
126   }
127   fclose(f);
128   return 1;
129 }
130
131 static void dnserror(struct rblookup *ipl, const char *what) {
132   char buf[4*4];
133   error(0, "unable to %s for %s (%s): %s",
134           what, dns_ntop(AF_INET, &ipl->key, buf, sizeof(buf)),
135           ipl->zone, dns_strerror(dns_status(0)));
136   ++failures;
137 }
138
139 static void display_result(struct ipcheck *ipc) {
140   int j;
141   struct rblookup *l, *le;
142   char buf[4*4];
143   if (!ipc->naddr) return;
144   for (l = ipc->lookup, le = l + nzones * ipc->naddr; l < le; ++l) {
145     if (!l->addr) continue;
146     if (verbose < 2 && l->addr == notlisted) continue;
147     if (verbose >= 0) {
148       dns_ntop(AF_INET, &l->key, buf, sizeof(buf));
149       if (ipc->name) printf("%s[%s]", ipc->name, buf);
150       else printf("%s", buf);
151     }
152     if (l->addr == notlisted) {
153       printf(" is NOT listed by %s\n", l->zone);
154       continue;
155     }
156     else if (verbose >= 1)
157       printf(" is listed by %s: ", l->zone);
158     else if (verbose >= 0)
159       printf(" %s ", l->zone);
160     if (verbose >= 1 || !do_txt)
161       for (j = 0; j < l->addr->dnsa4_nrr; ++j)
162         printf("%s%s", j ? " " : "",
163                dns_ntop(AF_INET, &l->addr->dnsa4_addr[j], buf, sizeof(buf)));
164     if (!do_txt) ;
165     else if (l->txt) {
166       for(j = 0; j < l->txt->dnstxt_nrr; ++j) {
167         unsigned char *t = l->txt->dnstxt_txt[j].txt;
168         unsigned char *e = t + l->txt->dnstxt_txt[j].len;
169         printf("%s\"", verbose > 0 ? "\n\t" : j ? " " : "");
170         while(t < e) {
171           if (*t < ' ' || *t >= 127) printf("\\x%02x", *t);
172           else if (*t == '\\' || *t == '"') printf("\\%c", *t);
173           else putchar(*t);
174           ++t;
175         }
176         putchar('"');
177       }
178       free(l->txt);
179     }
180     else
181       printf("%s<no text available>", verbose > 0 ? "\n\t" : "");
182     free(l->addr);
183     putchar('\n');
184   }
185   free(ipc->lookup);
186 }
187
188 static void txtcb(struct dns_ctx *ctx, struct dns_rr_txt *r, void *data) {
189   struct rblookup *ipl = data;
190   if (r) {
191     ipl->txt = r;
192     ++ipl->parent->listed;
193   }
194   else if (dns_status(ctx) != DNS_E_NXDOMAIN)
195     dnserror(ipl, "lookup DNSBL TXT record");
196 }
197
198 static void a4cb(struct dns_ctx *ctx, struct dns_rr_a4 *r, void *data) {
199   struct rblookup *ipl = data;
200   if (r) {
201     ipl->addr = r;
202     ++listed;
203     if (do_txt) {
204       if (dns_submit_a4dnsbl_txt(0, &ipl->key, ipl->zone, txtcb, ipl))
205         return;
206       dnserror(ipl, "submit DNSBL TXT record");
207     }
208     ++ipl->parent->listed;
209   }
210   else if (dns_status(ctx) != DNS_E_NXDOMAIN)
211     dnserror(ipl, "lookup DNSBL A record");
212   else
213     ipl->addr = notlisted;
214 }
215
216 static int
217 submit_a_queries(struct ipcheck *ipc,
218                  int naddr, const struct in_addr *addr) {
219   int z, a;
220   struct rblookup *rl = ecalloc(sizeof(*rl), nzones * naddr);
221   ipc->lookup = rl;
222   ipc->naddr = naddr;
223   for(a = 0; a < naddr; ++a) {
224     for(z = 0; z < nzones; ++z) {
225       rl->key = addr[a];
226       rl->zone = zones[z];
227       rl->parent = ipc;
228       if (!dns_submit_a4dnsbl(0, &rl->key, rl->zone, a4cb, rl))
229         dnserror(rl, "submit DNSBL A query");
230       ++rl;
231     }
232   }
233   return 0;
234 }
235
236 static void namecb(struct dns_ctx *ctx, struct dns_rr_a4 *rr, void *data) {
237   struct ipcheck *ipc = data;
238   if (rr) {
239     submit_a_queries(ipc, rr->dnsa4_nrr, rr->dnsa4_addr);
240     free(rr);
241   }
242   else {
243     error(0, "unable to lookup `%s': %s",
244           ipc->name, dns_strerror(dns_status(ctx)));
245     ++failures;
246   }
247 }
248
249 static int submit(struct ipcheck *ipc) {
250   struct in_addr addr;
251   if (dns_pton(AF_INET, ipc->name, &addr) > 0) {
252     submit_a_queries(ipc, 1, &addr);
253     ipc->name = NULL;
254   }
255   else if (!dns_submit_a4(0, ipc->name, 0, namecb, ipc)) {
256     error(0, "unable to submit name query for %s: %s\n",
257           ipc->name, dns_strerror(dns_status(0)));
258     ++failures;
259   }
260   return 0;
261 }
262
263 static void waitdns(struct ipcheck *ipc) {
264   struct timeval tv;
265   fd_set fds;
266   int c;
267   int fd = dns_sock(NULL);
268   time_t now = 0;
269   FD_ZERO(&fds);
270   while((c = dns_timeouts(NULL, -1, now)) > 0) {
271     FD_SET(fd, &fds);
272     tv.tv_sec = c;
273     tv.tv_usec = 0;
274     c = select(fd+1, &fds, NULL, NULL, &tv);
275     now = time(NULL);
276     if (c > 0)
277       dns_ioevent(NULL, now);
278     if (stopfirst && ipc->listed)
279       break;
280   }
281 }
282
283 int main(int argc, char **argv) {
284   int c;
285   struct ipcheck ipc;
286   char *nameserver = NULL;
287   int zgiven = 0;
288
289   if (!(progname = strrchr(argv[0], '/'))) progname = argv[0];
290   else argv[0] = ++progname;
291
292   while((c = getopt(argc, argv, "hqtvms:S:cn:")) != EOF) switch(c) {
293   case 's': ++zgiven; addzone(optarg); break;
294   case 'S':
295     ++zgiven;
296     if (addzonefile(optarg)) break;
297     error(1, "unable to read zonefile `%s'", optarg);
298   case 'c': ++zgiven; nzones = 0; break;
299   case 'q': --verbose; break;
300   case 'v': ++verbose; break;
301   case 't': do_txt = 1; break;
302   case 'n': nameserver = optarg; break;
303   case 'm': ++stopfirst; break;
304   case 'h':
305     printf("%s: %s (udns library version %s).\n",
306            progname, version, dns_version());
307     printf("Usage is: %s [options] address..\n", progname);
308     printf(
309 "Where options are:\n"
310 " -h - print this help and exit\n"
311 " -s service - add the service (DNSBL zone) to the serice list\n"
312 " -S service-file - add the DNSBL zone(s) read from the given file\n"
313 " -c - clear service list\n"
314 " -v - increase verbosity level (more -vs => more verbose)\n"
315 " -q - decrease verbosity level (opposite of -v)\n"
316 " -t - obtain and print TXT records if any\n"
317 " -m - stop checking after first address match in any list\n"
318 " -n ipaddr - use the given nameserver instead of the default\n"
319 "(if no -s or -S option is given, use $RBLCHECK_ZONES, ~/.rblcheckrc\n"
320 "or /etc/rblcheckrc in that order)\n"
321     );
322     return 0;
323   default:
324     error(1, "use `%s -h' for help", progname);
325   }
326
327   if (!zgiven) {
328     char *s = getenv("RBLCHECK_ZONES");
329     if (s) {
330       char *k;
331       s = strdup(s);
332       for(k = strtok(s, " \t"); k; k = strtok(NULL, " \t"))
333         addzone(k);
334       free(s);
335     }
336     else {      /* probably worthless on windows? */
337       char *path;
338       char *home = getenv("HOME");
339       if (!home) home = ".";
340       path = malloc(strlen(home) + 1 + sizeof(".rblcheckrc"));
341       sprintf(path, "%s/.rblcheckrc", home);
342       if (!addzonefile(path))
343         addzonefile("/etc/rblcheckrc");
344       free(path);
345     }
346   }
347   if (!nzones)
348     error(1, "no service (zone) list specified (-s or -S option)");
349
350   argv += optind;
351   argc -= optind;
352
353   if (!argc)
354     return 0;
355
356   if (dns_init(NULL, 0) < 0)
357     error(1, "unable to initialize DNS library: %s", strerror(errno));
358   if (nameserver) {
359     dns_add_serv(NULL, NULL);
360     if (dns_add_serv(NULL, nameserver) < 0)
361       error(1, "wrong IP address for a nameserver: `%s'", nameserver);
362   }
363   if (dns_open(NULL) < 0)
364     error(1, "unable to initialize DNS library: %s", strerror(errno));
365
366   for (c = 0; c < argc; ++c) {
367     if (c && (verbose > 1 || (verbose == 1 && do_txt))) putchar('\n');
368     memset(&ipc, 0, sizeof(ipc));
369     ipc.name = argv[c];
370     submit(&ipc);
371     waitdns(&ipc);
372     display_result(&ipc);
373     if (stopfirst > 1 && listed) break;
374   }
375
376   return listed ? 100 : failures ? 2 : 0;
377 }
Note: See TracBrowser for help on using the browser.