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