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