Urgency medium
[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>
bef232ae 11 * Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
592aa664 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();
67a57aae 130 if (fflush(stdout)) sysfail("flush stdout");
592aa664 131}
132
133static void usageerr(const char *why) NONRETURNING;
134static void usageerr(const char *why) {
135 fprintf(stderr,"adnsresfilter: bad usage: %s\n",why);
136 usage();
2953d358 137 quit(1);
592aa664 138}
139
140static void adnsfail(const char *what, int e) NONRETURNING;
141static void adnsfail(const char *what, int e) {
142 fprintf(stderr,"adnsresfilter: adns call failed: %s: %s\n",what,strerror(e));
2953d358 143 quit(2);
592aa664 144}
145
41559f53 146static void settimeout(const char *arg) {
147 char *ep;
148 timeout= strtoul(arg,&ep,0);
149 if (*ep) usageerr("invalid timeout");
592aa664 150}
151
2953d358 152static void parseargs(const char *const *argv) {
592aa664 153 const char *arg;
2953d358 154 int c;
592aa664 155
156 while ((arg= *++argv)) {
157 if (arg[0] != '-') usageerr("no non-option arguments are allowed");
158 if (arg[1] == '-') {
98e3fa81 159 if (!strcmp(arg,"--timeout")) {
160 if (!(arg= *++argv)) usageerr("--timeout needs a value");
161 settimeout(arg);
162 forever= 0;
592aa664 163 } else if (!strcmp(arg,"--wait")) {
164 forever= 1;
98e3fa81 165 } else if (!strcmp(arg,"--brackets")) {
166 bracket= 1;
fd60df66 167 } else if (!strcmp(arg,"--address")) {
168 address= 1;
98e3fa81 169 } else if (!strcmp(arg,"--unchecked")) {
170 rrt= adns_r_ptr_raw;
171 } else if (!strcmp(arg,"--config")) {
172 if (!(arg= *++argv)) usageerr("--config needs a value");
173 config_text= arg;
174 } else if (!strcmp(arg,"--debug")) {
175 initflags |= adns_if_debug;
592aa664 176 } else if (!strcmp(arg,"--help")) {
2953d358 177 usage(); quit(0);
592aa664 178 } else {
179 usageerr("unknown long option");
180 }
181 } else {
182 while ((c= *++arg)) {
183 switch (c) {
98e3fa81 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";
592aa664 190 break;
191 case 'w':
192 forever= 1;
193 break;
98e3fa81 194 case 'b':
195 bracket= 1;
196 break;
fd60df66 197 case 'a':
198 address= 1;
199 break;
98e3fa81 200 case 'u':
201 rrt= adns_r_ptr_raw;
202 break;
592aa664 203 case 'h':
41559f53 204 usage();
205 quit(0);
592aa664 206 default:
207 usageerr("unknown short option");
208 }
209 }
210 }
211 }
2953d358 212}
592aa664 213
2953d358 214static void queueoutchar(int c) {
215 struct outqueuenode *entry;
216
217 entry= outqueue.tail;
41559f53 218 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
219 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
220 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
2953d358 221 entry= xmalloc(sizeof(*entry));
2953d358 222 entry->buffer= xmalloc(peroutqueuenode);
223 entry->textp= entry->buffer;
224 entry->textlen= 0;
225 entry->addr= 0;
226 LIST_LINK_TAIL(outqueue,entry);
227 outqueuelen++;
228 }
41559f53 229 entry->textp[entry->textlen++]= c;
2953d358 230}
592aa664 231
2953d358 232static void queueoutstr(const char *str, int len) {
41559f53 233 while (len-- > 0) queueoutchar(*str++);
2953d358 234}
592aa664 235
2953d358 236static void writestdout(struct outqueuenode *entry) {
237 int r;
238
239 while (entry->textlen) {
240 r= write(1, entry->textp, entry->textlen);
241 if (r < 0) {
242 if (errno == EINTR) continue;
243 if (errno == EAGAIN) { outblocked= 1; break; }
244 sysfail("write stdout");
245 }
41559f53 246 assert(r <= entry->textlen);
2953d358 247 entry->textp += r;
248 entry->textlen -= r;
249 }
250 if (!entry->textlen) {
251 LIST_UNLINK(outqueue,entry);
252 free(entry->buffer);
253 free(entry);
254 outqueuelen--;
255 }
256}
257
258static void replacetextwithname(struct outqueuenode *entry) {
fd60df66 259 char *name, *newbuf;
260 int namelen, newlen;
261
262 name= entry->addr->ans->rrs.str[0];
263 namelen= strlen(name);
264 if (!address) {
265 free(entry->buffer);
266 entry->buffer= 0;
267 entry->textp= name;
268 entry->textlen= namelen;
269 } else {
270 newlen= entry->textlen + namelen + (bracket ? 0 : 2);
271 newbuf= xmalloc(newlen + 1);
272 sprintf(newbuf, bracket ? "%s%.*s" : "%s[%.*s]", name, entry->textlen, entry->textp);
273 free(entry->buffer);
274 entry->buffer= entry->textp= newbuf;
275 entry->textlen= newlen;
276 }
2953d358 277}
278
279static void checkadnsqueries(void) {
280 adns_query qu;
281 adns_answer *ans;
282 void *context;
283 struct treething *foundthing;
284 int r;
285
286 for (;;) {
287 qu= 0; context= 0; ans= 0;
288 r= adns_check(ads,&qu,&ans,&context);
289 if (r == ESRCH || r == EAGAIN) break;
290 assert(!r);
291 foundthing= context;
292 foundthing->ans= ans;
293 foundthing->qu= 0;
294 }
295}
296
297static void restartbuf(void) {
298 if (inbuf>0) queueoutstr(addrtextbuf,inbuf);
592aa664 299 inbuf= 0;
2953d358 300}
301
41559f53 302static int comparer(const void *a, const void *b) {
303 return memcmp(a,b,4);
304}
305
2953d358 306static void procaddr(void) {
307 struct treething *foundthing;
308 void **searchfound;
309 struct outqueuenode *entry;
310 int r;
311
312 if (!newthing) {
313 newthing= xmalloc(sizeof(struct treething));
314 newthing->qu= 0;
315 newthing->ans= 0;
316 }
317
318 memcpy(newthing->bytes,bytes,4);
319 searchfound= tsearch(newthing,&treeroot,comparer);
320 if (!searchfound) sysfail("tsearch");
321 foundthing= *searchfound;
322
323 if (foundthing == newthing) {
41559f53 324 newthing= 0;
2953d358 325 memcpy(&sa.sin_addr,bytes,4);
326 r= adns_submit_reverse(ads, (const struct sockaddr*)&sa,
327 rrt,0,foundthing,&foundthing->qu);
328 if (r) adnsfail("submit",r);
329 }
330 entry= xmalloc(sizeof(*entry));
331 entry->buffer= xmalloc(inbuf);
332 entry->textp= entry->buffer;
333 memcpy(entry->textp,addrtextbuf,inbuf);
334 entry->textlen= inbuf;
335 entry->addr= foundthing;
41559f53 336 entry->printbefore= printbefore;
2953d358 337 LIST_LINK_TAIL(outqueue,entry);
338 outqueuelen++;
339 inbuf= 0;
340 cbyte= -1;
341}
342
343static void startaddr(void) {
344 bytes[cbyte=0]= 0;
345 inbyte= 0;
346}
347
348static void readstdin(void) {
349 char readbuf[512], *p;
350 int r, c, nbyte;
351
352 while ((r= read(0,readbuf,sizeof(readbuf))) <= 0) {
353 if (r == 0) { inputeof= 1; return; }
354 if (r == EAGAIN) return;
355 if (r != EINTR) sysfail("read stdin");
356 }
357 for (p=readbuf; r>0; r--,p++) {
41559f53 358 c= *p;
592aa664 359 if (cbyte==-1 && bracket && c=='[') {
2953d358 360 addrtextbuf[inbuf++]= c;
592aa664 361 startaddr();
362 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
2953d358 363 queueoutchar(c);
592aa664 364 startaddr();
365 } else if (cbyte>=0 && inbyte<3 && c>='0' && c<='9' &&
366 (nbyte= bytes[cbyte]*10 + (c-'0')) <= 255) {
367 bytes[cbyte]= nbyte;
2953d358 368 addrtextbuf[inbuf++]= c;
592aa664 369 inbyte++;
370 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
371 bytes[++cbyte]= 0;
2953d358 372 addrtextbuf[inbuf++]= c;
592aa664 373 inbyte= 0;
374 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
2953d358 375 addrtextbuf[inbuf++]= c;
592aa664 376 procaddr();
377 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
378 procaddr();
2953d358 379 queueoutchar(c);
592aa664 380 startaddr();
381 } else {
382 restartbuf();
2953d358 383 queueoutchar(c);
592aa664 384 cbyte= -1;
385 if (!bracket && !isalnum(c)) startaddr();
386 }
387 }
2953d358 388}
389
390static void startup(void) {
391 int r;
392
2953d358 393 if (nonblock(0,1)) sysfail("set stdin to nonblocking mode");
394 if (nonblock(1,1)) sysfail("set stdout to nonblocking mode");
395 memset(&sa,0,sizeof(sa));
396 sa.sin_family= AF_INET;
98e3fa81 397 if (config_text) {
398 r= adns_init_strcfg(&ads,initflags,stderr,config_text);
399 } else {
400 r= adns_init(&ads,initflags,0);
401 }
402 if (r) adnsfail("init",r);
2953d358 403 cbyte= -1;
404 inbyte= -1;
405 inbuf= 0;
406 if (!bracket) startaddr();
407}
408
409int main(int argc, const char *const *argv) {
410 int r, maxfd;
411 fd_set readfds, writefds, exceptfds;
412 struct outqueuenode *entry;
413 struct timeval *tv, tvbuf, now;
414
415 parseargs(argv);
416 startup();
417
418 while (!inputeof || outqueue.head) {
419 maxfd= 2;
420 tv= 0;
421 FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
422 if ((entry= outqueue.head) && !outblocked) {
423 if (!entry->addr) {
424 writestdout(entry);
425 continue;
41559f53 426 }
427 if (entry->addr->ans) {
2953d358 428 if (entry->addr->ans->nrrs)
429 replacetextwithname(entry);
430 entry->addr= 0;
431 continue;
432 }
433 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
434 if (forever) {
435 tv= 0;
436 } else if (!timercmp(&now,&entry->printbefore,<)) {
437 entry->addr= 0;
438 continue;
439 } else {
41559f53 440 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
441 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
2953d358 442 tvbuf.tv_sec += tvbuf.tv_usec / 1000000;
443 tvbuf.tv_usec %= 1000000;
444 tv= &tvbuf;
445 }
446 adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,
447 &tv,&tvbuf,&now);
448 }
449 if (outblocked) FD_SET(1,&writefds);
41559f53 450 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
2953d358 451
452 r= select(maxfd,&readfds,&writefds,&exceptfds,tv);
453 if (r < 0) { if (r == EINTR) continue; else sysfail("select"); }
454
455 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
456 adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,&now);
457 checkadnsqueries();
458
459 if (FD_ISSET(0,&readfds)) {
460 if (!forever) {
461 printbefore= now;
462 timevaladd(&printbefore,timeout);
463 }
464 readstdin();
465 } else if (FD_ISSET(1,&writefds)) {
466 outblocked= 0;
467 }
468 }
2953d358 469 if (nonblock(0,0)) sysfail("un-nonblock stdin");
470 if (nonblock(1,0)) sysfail("un-nonblock stdout");
67a57aae 471 adns_finish(ads);
592aa664 472 exit(0);
473}