chiark / gitweb /
FOUND on davenant
[adns] / client / adnsresfilter.c
CommitLineData
790cfd2b 1/*
2 * adnsresfilter.c
3 * - filter which does resolving, not part of the library
4 */
5/*
39f45e7e 6 * This file is part of adns, which is
7 * Copyright (C) 1997-2000,2003,2006 Ian Jackson
8 * Copyright (C) 1999-2000,2003,2006 Tony Finch
9 * Copyright (C) 1991 Massachusetts Institute of Technology
10 * (See the file INSTALL for full details.)
790cfd2b 11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2, or (at your option)
15 * any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <errno.h>
31#include <search.h>
32#include <assert.h>
33#include <ctype.h>
34
93b54ac1 35#include <sys/types.h>
36#include <unistd.h>
8a4e090e 37#include <fcntl.h>
83e1be2f 38
790cfd2b 39#include "config.h"
cc0f95d0 40#include "adns.h"
83e1be2f 41#include "dlist.h"
42#include "tvarith.h"
8a5e5147 43#include "client.h"
83e1be2f 44
ae0da6f3 45#ifdef ADNS_REGRESS_TEST
46# include "hredirect.h"
47#endif
48
83e1be2f 49struct outqueuenode {
50 struct outqueuenode *next, *back;
2db4f864 51 char *buffer, *textp;
83e1be2f 52 int textlen;
53 struct timeval printbefore;
54 struct treething *addr;
55};
56
a93e4dbf 57static int bracket, forever, address;
9463e0a6 58static unsigned long timeout= 1000;
83e1be2f 59static adns_rrtype rrt= adns_r_ptr;
ae0da6f3 60static adns_initflags initflags= 0;
61static const char *config_text;
83e1be2f 62
63static int outblocked, inputeof;
64static struct { struct outqueuenode *head, *tail; } outqueue;
65static int peroutqueuenode, outqueuelen;
66
67static struct sockaddr_in sa;
68static adns_state ads;
69
70static char addrtextbuf[14];
71static int cbyte, inbyte, inbuf;
72static unsigned char bytes[4];
73static struct timeval printbefore;
74
75struct treething {
76 unsigned char bytes[4];
77 adns_query qu;
78 adns_answer *ans;
79};
80
81static struct treething *newthing;
82static void *treeroot;
83
84static int nonblock(int fd, int isnonblock) {
85 int r;
86
87 r= fcntl(fd,F_GETFL);
88 if (r==-1) return -1;
89 r= fcntl(fd,F_SETFL, isnonblock ? r|O_NONBLOCK : r&~O_NONBLOCK);
90 if (r==-1) return -1;
91 return 0;
92}
93
8a5e5147 94void quitnow(int exitstatus) {
83e1be2f 95 nonblock(0,0);
96 nonblock(1,0);
97 exit(exitstatus);
98}
790cfd2b 99
100static void sysfail(const char *what) NONRETURNING;
101static void sysfail(const char *what) {
102 fprintf(stderr,"adnsresfilter: system call failed: %s: %s\n",what,strerror(errno));
8a5e5147 103 quitnow(2);
83e1be2f 104}
105
106static void *xmalloc(size_t sz) {
107 void *r;
108 r= malloc(sz); if (r) return r;
109 sysfail("malloc");
790cfd2b 110}
111
112static void outputerr(void) NONRETURNING;
113static void outputerr(void) { sysfail("write to stdout"); }
114
115static void usage(void) {
116 if (printf("usage: adnsresfilter [<options ...>]\n"
8a5e5147 117 " adnsresfilter -h|--help | --version\n"
3d89679a 118 "options: -t<milliseconds>|--timeout <milliseconds>\n"
119 " -w|--wait (always wait for queries to time out or fail)\n"
120 " -b|--brackets (require [...] around IP addresses)\n"
121 " -a|--address (always include [address] in output)\n"
122 " -u|--unchecked (do not forward map for checking)\n"
ae0da6f3 123 " --config <text> (use this instead of resolv.conf)\n"
124 " --debug (turn on adns resolver debugging)\n"
3d89679a 125 "Timeout is the maximum amount to delay any particular bit of output for.\n"
ae0da6f3 126 "Lookups will go on in the background. Default timeout = 1000 (ms).\n")
790cfd2b 127 == EOF) outputerr();
d024926c 128 if (fflush(stdout)) sysfail("flush stdout");
790cfd2b 129}
130
131static void usageerr(const char *why) NONRETURNING;
132static void usageerr(const char *why) {
133 fprintf(stderr,"adnsresfilter: bad usage: %s\n",why);
134 usage();
8a5e5147 135 quitnow(1);
790cfd2b 136}
137
138static void adnsfail(const char *what, int e) NONRETURNING;
139static void adnsfail(const char *what, int e) {
140 fprintf(stderr,"adnsresfilter: adns call failed: %s: %s\n",what,strerror(e));
8a5e5147 141 quitnow(2);
790cfd2b 142}
143
7ba81901 144static void settimeout(const char *arg) {
145 char *ep;
146 timeout= strtoul(arg,&ep,0);
147 if (*ep) usageerr("invalid timeout");
790cfd2b 148}
149
83e1be2f 150static void parseargs(const char *const *argv) {
790cfd2b 151 const char *arg;
83e1be2f 152 int c;
790cfd2b 153
154 while ((arg= *++argv)) {
155 if (arg[0] != '-') usageerr("no non-option arguments are allowed");
156 if (arg[1] == '-') {
ae0da6f3 157 if (!strcmp(arg,"--timeout")) {
158 if (!(arg= *++argv)) usageerr("--timeout needs a value");
159 settimeout(arg);
160 forever= 0;
790cfd2b 161 } else if (!strcmp(arg,"--wait")) {
162 forever= 1;
ae0da6f3 163 } else if (!strcmp(arg,"--brackets")) {
164 bracket= 1;
a93e4dbf 165 } else if (!strcmp(arg,"--address")) {
166 address= 1;
ae0da6f3 167 } else if (!strcmp(arg,"--unchecked")) {
168 rrt= adns_r_ptr_raw;
169 } else if (!strcmp(arg,"--config")) {
170 if (!(arg= *++argv)) usageerr("--config needs a value");
171 config_text= arg;
172 } else if (!strcmp(arg,"--debug")) {
173 initflags |= adns_if_debug;
790cfd2b 174 } else if (!strcmp(arg,"--help")) {
8a5e5147 175 usage(); quitnow(0);
176 } else if (!strcmp(arg,"--version")) {
177 VERSION_PRINT_QUIT("adnsresfilter"); quitnow(0);
790cfd2b 178 } else {
179 usageerr("unknown long option");
180 }
181 } else {
182 while ((c= *++arg)) {
183 switch (c) {
ae0da6f3 184 case 't':
185 if (*++arg) settimeout(arg);
186 else if ((arg= *++argv)) settimeout(arg);
187 else usageerr("-t needs a value");
188 forever= 0;
189 arg= "\0";
790cfd2b 190 break;
191 case 'w':
192 forever= 1;
193 break;
ae0da6f3 194 case 'b':
195 bracket= 1;
196 break;
a93e4dbf 197 case 'a':
198 address= 1;
199 break;
ae0da6f3 200 case 'u':
201 rrt= adns_r_ptr_raw;
202 break;
790cfd2b 203 case 'h':
7ba81901 204 usage();
8a5e5147 205 quitnow(0);
790cfd2b 206 default:
207 usageerr("unknown short option");
208 }
209 }
210 }
211 }
83e1be2f 212}
790cfd2b 213
83e1be2f 214static void queueoutchar(int c) {
215 struct outqueuenode *entry;
216
217 entry= outqueue.tail;
2db4f864 218 if (!entry || entry->addr ||
219 entry->textlen >= peroutqueuenode - (entry->textp - entry->buffer)) {
7ba81901 220 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
221 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
83e1be2f 222 entry= xmalloc(sizeof(*entry));
83e1be2f 223 entry->buffer= xmalloc(peroutqueuenode);
224 entry->textp= entry->buffer;
225 entry->textlen= 0;
226 entry->addr= 0;
227 LIST_LINK_TAIL(outqueue,entry);
228 outqueuelen++;
229 }
7ba81901 230 entry->textp[entry->textlen++]= c;
83e1be2f 231}
790cfd2b 232
83e1be2f 233static void queueoutstr(const char *str, int len) {
7ba81901 234 while (len-- > 0) queueoutchar(*str++);
83e1be2f 235}
790cfd2b 236
83e1be2f 237static void writestdout(struct outqueuenode *entry) {
238 int r;
239
240 while (entry->textlen) {
241 r= write(1, entry->textp, entry->textlen);
242 if (r < 0) {
243 if (errno == EINTR) continue;
244 if (errno == EAGAIN) { outblocked= 1; break; }
245 sysfail("write stdout");
246 }
7ba81901 247 assert(r <= entry->textlen);
83e1be2f 248 entry->textp += r;
249 entry->textlen -= r;
250 }
251 if (!entry->textlen) {
252 LIST_UNLINK(outqueue,entry);
253 free(entry->buffer);
254 free(entry);
255 outqueuelen--;
256 }
257}
258
259static void replacetextwithname(struct outqueuenode *entry) {
a93e4dbf 260 char *name, *newbuf;
261 int namelen, newlen;
262
263 name= entry->addr->ans->rrs.str[0];
264 namelen= strlen(name);
265 if (!address) {
266 free(entry->buffer);
267 entry->buffer= 0;
268 entry->textp= name;
269 entry->textlen= namelen;
270 } else {
271 newlen= entry->textlen + namelen + (bracket ? 0 : 2);
272 newbuf= xmalloc(newlen + 1);
273 sprintf(newbuf, bracket ? "%s%.*s" : "%s[%.*s]", name, entry->textlen, entry->textp);
274 free(entry->buffer);
275 entry->buffer= entry->textp= newbuf;
276 entry->textlen= newlen;
277 }
83e1be2f 278}
279
280static void checkadnsqueries(void) {
281 adns_query qu;
282 adns_answer *ans;
283 void *context;
284 struct treething *foundthing;
285 int r;
286
287 for (;;) {
288 qu= 0; context= 0; ans= 0;
289 r= adns_check(ads,&qu,&ans,&context);
290 if (r == ESRCH || r == EAGAIN) break;
291 assert(!r);
292 foundthing= context;
293 foundthing->ans= ans;
294 foundthing->qu= 0;
295 }
296}
297
298static void restartbuf(void) {
299 if (inbuf>0) queueoutstr(addrtextbuf,inbuf);
790cfd2b 300 inbuf= 0;
83e1be2f 301}
302
7ba81901 303static int comparer(const void *a, const void *b) {
304 return memcmp(a,b,4);
305}
306
83e1be2f 307static void procaddr(void) {
308 struct treething *foundthing;
309 void **searchfound;
310 struct outqueuenode *entry;
311 int r;
312
313 if (!newthing) {
314 newthing= xmalloc(sizeof(struct treething));
315 newthing->qu= 0;
316 newthing->ans= 0;
317 }
318
319 memcpy(newthing->bytes,bytes,4);
320 searchfound= tsearch(newthing,&treeroot,comparer);
321 if (!searchfound) sysfail("tsearch");
322 foundthing= *searchfound;
323
324 if (foundthing == newthing) {
7ba81901 325 newthing= 0;
83e1be2f 326 memcpy(&sa.sin_addr,bytes,4);
327 r= adns_submit_reverse(ads, (const struct sockaddr*)&sa,
328 rrt,0,foundthing,&foundthing->qu);
329 if (r) adnsfail("submit",r);
330 }
331 entry= xmalloc(sizeof(*entry));
332 entry->buffer= xmalloc(inbuf);
333 entry->textp= entry->buffer;
334 memcpy(entry->textp,addrtextbuf,inbuf);
335 entry->textlen= inbuf;
336 entry->addr= foundthing;
7ba81901 337 entry->printbefore= printbefore;
83e1be2f 338 LIST_LINK_TAIL(outqueue,entry);
339 outqueuelen++;
340 inbuf= 0;
341 cbyte= -1;
342}
343
344static void startaddr(void) {
345 bytes[cbyte=0]= 0;
346 inbyte= 0;
347}
348
349static void readstdin(void) {
350 char readbuf[512], *p;
351 int r, c, nbyte;
352
353 while ((r= read(0,readbuf,sizeof(readbuf))) <= 0) {
354 if (r == 0) { inputeof= 1; return; }
355 if (r == EAGAIN) return;
356 if (r != EINTR) sysfail("read stdin");
357 }
358 for (p=readbuf; r>0; r--,p++) {
7ba81901 359 c= *p;
790cfd2b 360 if (cbyte==-1 && bracket && c=='[') {
83e1be2f 361 addrtextbuf[inbuf++]= c;
790cfd2b 362 startaddr();
363 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
83e1be2f 364 queueoutchar(c);
790cfd2b 365 startaddr();
366 } else if (cbyte>=0 && inbyte<3 && c>='0' && c<='9' &&
367 (nbyte= bytes[cbyte]*10 + (c-'0')) <= 255) {
368 bytes[cbyte]= nbyte;
83e1be2f 369 addrtextbuf[inbuf++]= c;
790cfd2b 370 inbyte++;
371 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
372 bytes[++cbyte]= 0;
83e1be2f 373 addrtextbuf[inbuf++]= c;
790cfd2b 374 inbyte= 0;
375 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
83e1be2f 376 addrtextbuf[inbuf++]= c;
790cfd2b 377 procaddr();
378 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
379 procaddr();
83e1be2f 380 queueoutchar(c);
790cfd2b 381 startaddr();
382 } else {
383 restartbuf();
83e1be2f 384 queueoutchar(c);
790cfd2b 385 cbyte= -1;
386 if (!bracket && !isalnum(c)) startaddr();
387 }
388 }
83e1be2f 389}
390
391static void startup(void) {
392 int r;
393
83e1be2f 394 if (nonblock(0,1)) sysfail("set stdin to nonblocking mode");
395 if (nonblock(1,1)) sysfail("set stdout to nonblocking mode");
396 memset(&sa,0,sizeof(sa));
397 sa.sin_family= AF_INET;
ae0da6f3 398 if (config_text) {
399 r= adns_init_strcfg(&ads,initflags,stderr,config_text);
400 } else {
401 r= adns_init(&ads,initflags,0);
402 }
403 if (r) adnsfail("init",r);
83e1be2f 404 cbyte= -1;
405 inbyte= -1;
406 inbuf= 0;
407 if (!bracket) startaddr();
408}
409
410int main(int argc, const char *const *argv) {
411 int r, maxfd;
412 fd_set readfds, writefds, exceptfds;
413 struct outqueuenode *entry;
414 struct timeval *tv, tvbuf, now;
415
416 parseargs(argv);
417 startup();
418
419 while (!inputeof || outqueue.head) {
420 maxfd= 2;
421 tv= 0;
422 FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
423 if ((entry= outqueue.head) && !outblocked) {
424 if (!entry->addr) {
425 writestdout(entry);
426 continue;
7ba81901 427 }
428 if (entry->addr->ans) {
83e1be2f 429 if (entry->addr->ans->nrrs)
430 replacetextwithname(entry);
431 entry->addr= 0;
432 continue;
433 }
434 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
435 if (forever) {
436 tv= 0;
437 } else if (!timercmp(&now,&entry->printbefore,<)) {
438 entry->addr= 0;
439 continue;
440 } else {
7ba81901 441 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
442 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
83e1be2f 443 tvbuf.tv_sec += tvbuf.tv_usec / 1000000;
444 tvbuf.tv_usec %= 1000000;
445 tv= &tvbuf;
446 }
447 adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,
448 &tv,&tvbuf,&now);
449 }
450 if (outblocked) FD_SET(1,&writefds);
7ba81901 451 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
83e1be2f 452
453 r= select(maxfd,&readfds,&writefds,&exceptfds,tv);
454 if (r < 0) { if (r == EINTR) continue; else sysfail("select"); }
455
456 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
457 adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,&now);
458 checkadnsqueries();
459
460 if (FD_ISSET(0,&readfds)) {
461 if (!forever) {
462 printbefore= now;
463 timevaladd(&printbefore,timeout);
464 }
465 readstdin();
466 } else if (FD_ISSET(1,&writefds)) {
467 outblocked= 0;
468 }
469 }
83e1be2f 470 if (nonblock(0,0)) sysfail("un-nonblock stdin");
471 if (nonblock(1,0)) sysfail("un-nonblock stdout");
d024926c 472 adns_finish(ads);
790cfd2b 473 exit(0);
474}