Licensing: Update copyright dates for Ian Jackson
[adns] / client / adnsresfilter.c
CommitLineData
592aa664 1/*
2 * adnsresfilter.c
3 * - filter which does resolving, not part of the library
4 */
5/*
ae8cc977 6 * This file is part of adns, which is
26e1c3d6 7 * Copyright (C) 1997-2000,2003,2006,2014 Ian Jackson
ae8cc977 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.)
592aa664 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
7f8bbe29 14 * the Free Software Foundation; either version 3, or (at your option)
592aa664 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
8c09a4c6 23 * along with this program; if not, write to the Free Software Foundation.
592aa664 24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <errno.h>
30#include <search.h>
31#include <assert.h>
32#include <ctype.h>
33
bbede9a3 34#include <sys/types.h>
35#include <unistd.h>
0a4fd9ed 36#include <fcntl.h>
2953d358 37
592aa664 38#include "config.h"
5a0be244 39#include "adns.h"
2953d358 40#include "dlist.h"
41#include "tvarith.h"
aa3ffb57 42#include "client.h"
2953d358 43
98e3fa81 44#ifdef ADNS_REGRESS_TEST
45# include "hredirect.h"
46#endif
47
2953d358 48struct outqueuenode {
49 struct outqueuenode *next, *back;
e22eb8c7 50 char *buffer, *textp;
2953d358 51 int textlen;
52 struct timeval printbefore;
53 struct treething *addr;
54};
55
fd60df66 56static int bracket, forever, address;
51cc4d90 57static unsigned long timeout= 1000;
2953d358 58static adns_rrtype rrt= adns_r_ptr;
98e3fa81 59static adns_initflags initflags= 0;
60static const char *config_text;
2953d358 61
62static int outblocked, inputeof;
63static struct { struct outqueuenode *head, *tail; } outqueue;
64static int peroutqueuenode, outqueuelen;
65
66static struct sockaddr_in sa;
67static adns_state ads;
68
69static char addrtextbuf[14];
70static int cbyte, inbyte, inbuf;
71static unsigned char bytes[4];
72static struct timeval printbefore;
73
74struct treething {
75 unsigned char bytes[4];
76 adns_query qu;
77 adns_answer *ans;
78};
79
80static struct treething *newthing;
81static void *treeroot;
82
83static int nonblock(int fd, int isnonblock) {
84 int r;
85
86 r= fcntl(fd,F_GETFL);
87 if (r==-1) return -1;
88 r= fcntl(fd,F_SETFL, isnonblock ? r|O_NONBLOCK : r&~O_NONBLOCK);
89 if (r==-1) return -1;
90 return 0;
91}
92
aa3ffb57 93void quitnow(int exitstatus) {
2953d358 94 nonblock(0,0);
95 nonblock(1,0);
96 exit(exitstatus);
97}
592aa664 98
99static void sysfail(const char *what) NONRETURNING;
100static void sysfail(const char *what) {
101 fprintf(stderr,"adnsresfilter: system call failed: %s: %s\n",what,strerror(errno));
aa3ffb57 102 quitnow(2);
2953d358 103}
104
105static void *xmalloc(size_t sz) {
106 void *r;
107 r= malloc(sz); if (r) return r;
108 sysfail("malloc");
592aa664 109}
110
111static void outputerr(void) NONRETURNING;
112static void outputerr(void) { sysfail("write to stdout"); }
113
114static void usage(void) {
115 if (printf("usage: adnsresfilter [<options ...>]\n"
aa3ffb57 116 " adnsresfilter -h|--help | --version\n"
70da58ac 117 "options: -t<milliseconds>|--timeout <milliseconds>\n"
118 " -w|--wait (always wait for queries to time out or fail)\n"
119 " -b|--brackets (require [...] around IP addresses)\n"
120 " -a|--address (always include [address] in output)\n"
121 " -u|--unchecked (do not forward map for checking)\n"
98e3fa81 122 " --config <text> (use this instead of resolv.conf)\n"
123 " --debug (turn on adns resolver debugging)\n"
70da58ac 124 "Timeout is the maximum amount to delay any particular bit of output for.\n"
98e3fa81 125 "Lookups will go on in the background. Default timeout = 1000 (ms).\n")
592aa664 126 == EOF) outputerr();
67a57aae 127 if (fflush(stdout)) sysfail("flush stdout");
592aa664 128}
129
130static void usageerr(const char *why) NONRETURNING;
131static void usageerr(const char *why) {
132 fprintf(stderr,"adnsresfilter: bad usage: %s\n",why);
133 usage();
aa3ffb57 134 quitnow(1);
592aa664 135}
136
137static void adnsfail(const char *what, int e) NONRETURNING;
138static void adnsfail(const char *what, int e) {
139 fprintf(stderr,"adnsresfilter: adns call failed: %s: %s\n",what,strerror(e));
aa3ffb57 140 quitnow(2);
592aa664 141}
142
41559f53 143static void settimeout(const char *arg) {
144 char *ep;
145 timeout= strtoul(arg,&ep,0);
146 if (*ep) usageerr("invalid timeout");
592aa664 147}
148
2953d358 149static void parseargs(const char *const *argv) {
592aa664 150 const char *arg;
2953d358 151 int c;
592aa664 152
153 while ((arg= *++argv)) {
154 if (arg[0] != '-') usageerr("no non-option arguments are allowed");
155 if (arg[1] == '-') {
98e3fa81 156 if (!strcmp(arg,"--timeout")) {
157 if (!(arg= *++argv)) usageerr("--timeout needs a value");
158 settimeout(arg);
159 forever= 0;
592aa664 160 } else if (!strcmp(arg,"--wait")) {
161 forever= 1;
98e3fa81 162 } else if (!strcmp(arg,"--brackets")) {
163 bracket= 1;
fd60df66 164 } else if (!strcmp(arg,"--address")) {
165 address= 1;
98e3fa81 166 } else if (!strcmp(arg,"--unchecked")) {
167 rrt= adns_r_ptr_raw;
168 } else if (!strcmp(arg,"--config")) {
169 if (!(arg= *++argv)) usageerr("--config needs a value");
170 config_text= arg;
171 } else if (!strcmp(arg,"--debug")) {
172 initflags |= adns_if_debug;
592aa664 173 } else if (!strcmp(arg,"--help")) {
aa3ffb57 174 usage(); quitnow(0);
175 } else if (!strcmp(arg,"--version")) {
176 VERSION_PRINT_QUIT("adnsresfilter"); quitnow(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();
aa3ffb57 204 quitnow(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;
e22eb8c7 217 if (!entry || entry->addr ||
218 entry->textlen >= peroutqueuenode - (entry->textp - entry->buffer)) {
41559f53 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}