]> git.mxchange.org Git - simgear.git/blob - 3rdparty/udns/rblcheck.c
Initial commit for a DNS service resolver
[simgear.git] / 3rdparty / udns / rblcheck.c
1 /* rblcheck.c
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_CONFIG_H
25 # include "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.4";
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     p = memcpy(ecalloc(e - p, 1), p, e - p); // strdup
126     addzone(p);
127   }
128   fclose(f);
129   return 1;
130 }
131
132 static void dnserror(struct rblookup *ipl, const char *what) {
133   char buf[4*4];
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)));
137   ++failures;
138 }
139
140 static void display_result(struct ipcheck *ipc) {
141   int j;
142   struct rblookup *l, *le;
143   char buf[4*4];
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;
148     if (verbose >= 0) {
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);
152     }
153     if (l->addr == notlisted) {
154       printf(" is NOT listed by %s\n", l->zone);
155       continue;
156     }
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)));
165     if (!do_txt) ;
166     else if (l->txt) {
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 ? " " : "");
171         while(t < e) {
172           if (*t < ' ' || *t >= 127) printf("\\x%02x", *t);
173           else if (*t == '\\' || *t == '"') printf("\\%c", *t);
174           else putchar(*t);
175           ++t;
176         }
177         putchar('"');
178       }
179       free(l->txt);
180     }
181     else
182       printf("%s<no text available>", verbose > 0 ? "\n\t" : "");
183     free(l->addr);
184     putchar('\n');
185   }
186   free(ipc->lookup);
187 }
188
189 static void txtcb(struct dns_ctx *ctx, struct dns_rr_txt *r, void *data) {
190   struct rblookup *ipl = data;
191   if (r) {
192     ipl->txt = r;
193     ++ipl->parent->listed;
194   }
195   else if (dns_status(ctx) != DNS_E_NXDOMAIN)
196     dnserror(ipl, "lookup DNSBL TXT record");
197 }
198
199 static void a4cb(struct dns_ctx *ctx, struct dns_rr_a4 *r, void *data) {
200   struct rblookup *ipl = data;
201   if (r) {
202     ipl->addr = r;
203     ++listed;
204     if (do_txt) {
205       if (dns_submit_a4dnsbl_txt(0, &ipl->key, ipl->zone, txtcb, ipl))
206         return;
207       dnserror(ipl, "submit DNSBL TXT record");
208     }
209     ++ipl->parent->listed;
210   }
211   else if (dns_status(ctx) != DNS_E_NXDOMAIN)
212     dnserror(ipl, "lookup DNSBL A record");
213   else
214     ipl->addr = notlisted;
215 }
216
217 static int
218 submit_a_queries(struct ipcheck *ipc,
219                  int naddr, const struct in_addr *addr) {
220   int z, a;
221   struct rblookup *rl = ecalloc(sizeof(*rl), nzones * naddr);
222   ipc->lookup = rl;
223   ipc->naddr = naddr;
224   for(a = 0; a < naddr; ++a) {
225     for(z = 0; z < nzones; ++z) {
226       rl->key = addr[a];
227       rl->zone = zones[z];
228       rl->parent = ipc;
229       if (!dns_submit_a4dnsbl(0, &rl->key, rl->zone, a4cb, rl))
230         dnserror(rl, "submit DNSBL A query");
231       ++rl;
232     }
233   }
234   return 0;
235 }
236
237 static void namecb(struct dns_ctx *ctx, struct dns_rr_a4 *rr, void *data) {
238   struct ipcheck *ipc = data;
239   if (rr) {
240     submit_a_queries(ipc, rr->dnsa4_nrr, rr->dnsa4_addr);
241     free(rr);
242   }
243   else {
244     error(0, "unable to lookup `%s': %s",
245           ipc->name, dns_strerror(dns_status(ctx)));
246     ++failures;
247   }
248 }
249
250 static int submit(struct ipcheck *ipc) {
251   struct in_addr addr;
252   if (dns_pton(AF_INET, ipc->name, &addr) > 0) {
253     submit_a_queries(ipc, 1, &addr);
254     ipc->name = NULL;
255   }
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)));
259     ++failures;
260   }
261   return 0;
262 }
263
264 static void waitdns(struct ipcheck *ipc) {
265   struct timeval tv;
266   fd_set fds;
267   int c;
268   int fd = dns_sock(NULL);
269   time_t now = 0;
270   FD_ZERO(&fds);
271   while((c = dns_timeouts(NULL, -1, now)) > 0) {
272     FD_SET(fd, &fds);
273     tv.tv_sec = c;
274     tv.tv_usec = 0;
275     c = select(fd+1, &fds, NULL, NULL, &tv);
276     now = time(NULL);
277     if (c > 0)
278       dns_ioevent(NULL, now);
279     if (stopfirst && ipc->listed)
280       break;
281   }
282 }
283
284 int main(int argc, char **argv) {
285   int c;
286   struct ipcheck ipc;
287   char *nameserver = NULL;
288   int zgiven = 0;
289
290   if (!(progname = strrchr(argv[0], '/'))) progname = argv[0];
291   else argv[0] = ++progname;
292
293   while((c = getopt(argc, argv, "hqtvms:S:cn:")) != EOF) switch(c) {
294   case 's': ++zgiven; addzone(optarg); break;
295   case 'S':
296     ++zgiven;
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;
305   case 'h':
306     printf("%s: %s (udns library version %s).\n",
307            progname, version, dns_version());
308     printf("Usage is: %s [options] address..\n", progname);
309     printf(
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"
322     );
323     return 0;
324   default:
325     error(1, "use `%s -h' for help", progname);
326   }
327
328   if (!zgiven) {
329     char *s = getenv("RBLCHECK_ZONES");
330     if (s) {
331       char *k;
332       s = strdup(s);
333       for(k = strtok(s, " \t"); k; k = strtok(NULL, " \t"))
334         addzone(k);
335       free(s);
336     }
337     else {      /* probably worthless on windows? */
338       char *path;
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");
345       free(path);
346     }
347   }
348   if (!nzones)
349     error(1, "no service (zone) list specified (-s or -S option)");
350
351   argv += optind;
352   argc -= optind;
353
354   if (!argc)
355     return 0;
356
357   if (dns_init(NULL, 0) < 0)
358     error(1, "unable to initialize DNS library: %s", strerror(errno));
359   if (nameserver) {
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);
363   }
364   if (dns_open(NULL) < 0)
365     error(1, "unable to initialize DNS library: %s", strerror(errno));
366
367   for (c = 0; c < argc; ++c) {
368     if (c && (verbose > 1 || (verbose == 1 && do_txt))) putchar('\n');
369     memset(&ipc, 0, sizeof(ipc));
370     ipc.name = argv[c];
371     submit(&ipc);
372     waitdns(&ipc);
373     display_result(&ipc);
374     if (stopfirst > 1 && listed) break;
375   }
376
377   return listed ? 100 : failures ? 2 : 0;
378 }