+ * Fix adnsresfilter usage message to include correct default timeout.
[adns] / client / adnsresfilter.c
1 /*
2 * adnsresfilter.c
3 * - filter which does resolving, not part of the library
4 */
5 /*
6 * This file is
7 * Copyright (C) 1999-2000 Ian Jackson <ian@davenant.greenend.org.uk>
8 *
9 * It is part of adns, which is
10 * Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
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
36 #include <sys/types.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39
40 #include "config.h"
41 #include "adns.h"
42 #include "dlist.h"
43 #include "tvarith.h"
44
45 #ifdef ADNS_REGRESS_TEST
46 # include "hredirect.h"
47 #endif
48
49 struct 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
58 static int bracket, forever, address;
59 static unsigned long timeout= 1000;
60 static adns_rrtype rrt= adns_r_ptr;
61 static adns_initflags initflags= 0;
62 static const char *config_text;
63
64 static int outblocked, inputeof;
65 static struct { struct outqueuenode *head, *tail; } outqueue;
66 static int peroutqueuenode, outqueuelen;
67
68 static struct sockaddr_in sa;
69 static adns_state ads;
70
71 static char addrtextbuf[14];
72 static int cbyte, inbyte, inbuf;
73 static unsigned char bytes[4];
74 static struct timeval printbefore;
75
76 struct treething {
77 unsigned char bytes[4];
78 adns_query qu;
79 adns_answer *ans;
80 };
81
82 static struct treething *newthing;
83 static void *treeroot;
84
85 static 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
95 static void quit(int exitstatus) NONRETURNING;
96 static void quit(int exitstatus) {
97 nonblock(0,0);
98 nonblock(1,0);
99 exit(exitstatus);
100 }
101
102 static void sysfail(const char *what) NONRETURNING;
103 static void sysfail(const char *what) {
104 fprintf(stderr,"adnsresfilter: system call failed: %s: %s\n",what,strerror(errno));
105 quit(2);
106 }
107
108 static void *xmalloc(size_t sz) {
109 void *r;
110 r= malloc(sz); if (r) return r;
111 sysfail("malloc");
112 }
113
114 static void outputerr(void) NONRETURNING;
115 static void outputerr(void) { sysfail("write to stdout"); }
116
117 static void usage(void) {
118 if (printf("usage: adnsresfilter [<options ...>]\n"
119 " adnsresfilter -h|--help\n"
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"
125 " --config <text> (use this instead of resolv.conf)\n"
126 " --debug (turn on adns resolver debugging)\n"
127 "Timeout is the maximum amount to delay any particular bit of output for.\n"
128 "Lookups will go on in the background. Default timeout = 1000 (ms).\n")
129 == EOF) outputerr();
130 }
131
132 static void usageerr(const char *why) NONRETURNING;
133 static void usageerr(const char *why) {
134 fprintf(stderr,"adnsresfilter: bad usage: %s\n",why);
135 usage();
136 quit(1);
137 }
138
139 static void adnsfail(const char *what, int e) NONRETURNING;
140 static void adnsfail(const char *what, int e) {
141 fprintf(stderr,"adnsresfilter: adns call failed: %s: %s\n",what,strerror(e));
142 quit(2);
143 }
144
145 static void settimeout(const char *arg) {
146 char *ep;
147 timeout= strtoul(arg,&ep,0);
148 if (*ep) usageerr("invalid timeout");
149 }
150
151 static void parseargs(const char *const *argv) {
152 const char *arg;
153 int c;
154
155 while ((arg= *++argv)) {
156 if (arg[0] != '-') usageerr("no non-option arguments are allowed");
157 if (arg[1] == '-') {
158 if (!strcmp(arg,"--timeout")) {
159 if (!(arg= *++argv)) usageerr("--timeout needs a value");
160 settimeout(arg);
161 forever= 0;
162 } else if (!strcmp(arg,"--wait")) {
163 forever= 1;
164 } else if (!strcmp(arg,"--brackets")) {
165 bracket= 1;
166 } else if (!strcmp(arg,"--address")) {
167 address= 1;
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;
175 } else if (!strcmp(arg,"--help")) {
176 usage(); quit(0);
177 } else {
178 usageerr("unknown long option");
179 }
180 } else {
181 while ((c= *++arg)) {
182 switch (c) {
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";
189 break;
190 case 'w':
191 forever= 1;
192 break;
193 case 'b':
194 bracket= 1;
195 break;
196 case 'a':
197 address= 1;
198 break;
199 case 'u':
200 rrt= adns_r_ptr_raw;
201 break;
202 case 'h':
203 usage();
204 quit(0);
205 default:
206 usageerr("unknown short option");
207 }
208 }
209 }
210 }
211 }
212
213 static void queueoutchar(int c) {
214 struct outqueuenode *entry;
215
216 entry= outqueue.tail;
217 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
218 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
219 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
220 entry= xmalloc(sizeof(*entry));
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 }
228 entry->textp[entry->textlen++]= c;
229 }
230
231 static void queueoutstr(const char *str, int len) {
232 while (len-- > 0) queueoutchar(*str++);
233 }
234
235 static 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 }
245 assert(r <= entry->textlen);
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
257 static void replacetextwithname(struct outqueuenode *entry) {
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 }
276 }
277
278 static 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
296 static void restartbuf(void) {
297 if (inbuf>0) queueoutstr(addrtextbuf,inbuf);
298 inbuf= 0;
299 }
300
301 static int comparer(const void *a, const void *b) {
302 return memcmp(a,b,4);
303 }
304
305 static 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) {
323 newthing= 0;
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;
335 entry->printbefore= printbefore;
336 LIST_LINK_TAIL(outqueue,entry);
337 outqueuelen++;
338 inbuf= 0;
339 cbyte= -1;
340 }
341
342 static void startaddr(void) {
343 bytes[cbyte=0]= 0;
344 inbyte= 0;
345 }
346
347 static 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++) {
357 c= *p;
358 if (cbyte==-1 && bracket && c=='[') {
359 addrtextbuf[inbuf++]= c;
360 startaddr();
361 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
362 queueoutchar(c);
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;
367 addrtextbuf[inbuf++]= c;
368 inbyte++;
369 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
370 bytes[++cbyte]= 0;
371 addrtextbuf[inbuf++]= c;
372 inbyte= 0;
373 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
374 addrtextbuf[inbuf++]= c;
375 procaddr();
376 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
377 procaddr();
378 queueoutchar(c);
379 startaddr();
380 } else {
381 restartbuf();
382 queueoutchar(c);
383 cbyte= -1;
384 if (!bracket && !isalnum(c)) startaddr();
385 }
386 }
387 }
388
389 static void startup(void) {
390 int r;
391
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;
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);
402 cbyte= -1;
403 inbyte= -1;
404 inbuf= 0;
405 if (!bracket) startaddr();
406 }
407
408 int 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;
425 }
426 if (entry->addr->ans) {
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 {
439 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
440 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
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);
449 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
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 }
468 if (nonblock(0,0)) sysfail("un-nonblock stdin");
469 if (nonblock(1,0)) sysfail("un-nonblock stdout");
470 if (ferror(stdin) || fclose(stdin)) sysfail("read stdin");
471 if (fclose(stdout)) sysfail("close stdout");
472 exit(0);
473 }