+ -- Ian Jackson <ian@davenant.greenend.org.uk> Wed, 9 Aug 2000 16:59:28 +0100
[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
7 * Copyright (C) 1999 Ian Jackson <ian@davenant.greenend.org.uk>
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
2953d358 36#include <sys/fcntl.h>
37
592aa664 38#include "adns.h"
39#include "config.h"
2953d358 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
fd60df66 52static int bracket, forever, address;
51cc4d90 53static unsigned long timeout= 1000;
2953d358 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}
592aa664 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));
2953d358 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");
592aa664 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"
70da58ac 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")
592aa664 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();
2953d358 126 quit(1);
592aa664 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));
2953d358 132 quit(2);
592aa664 133}
134
41559f53 135static void settimeout(const char *arg) {
136 char *ep;
137 timeout= strtoul(arg,&ep,0);
138 if (*ep) usageerr("invalid timeout");
592aa664 139}
140
2953d358 141static void parseargs(const char *const *argv) {
592aa664 142 const char *arg;
2953d358 143 int c;
592aa664 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;
fd60df66 154 } else if (!strcmp(arg,"--address")) {
155 address= 1;
592aa664 156 } else if (!strcmp(arg,"--help")) {
2953d358 157 usage(); quit(0);
41559f53 158 } else if (!strcmp(arg,"--timeout")) {
159 if (!(arg= *++argv)) usageerr("--timeout needs a value");
160 settimeout(arg);
161 forever= 0;
592aa664 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;
fd60df66 177 case 'a':
178 address= 1;
179 break;
592aa664 180 case 'h':
41559f53 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;
592aa664 190 default:
191 usageerr("unknown short option");
192 }
193 }
194 }
195 }
2953d358 196}
592aa664 197
2953d358 198static void queueoutchar(int c) {
199 struct outqueuenode *entry;
200
201 entry= outqueue.tail;
41559f53 202 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
203 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
204 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
2953d358 205 entry= xmalloc(sizeof(*entry));
2953d358 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 }
41559f53 213 entry->textp[entry->textlen++]= c;
2953d358 214}
592aa664 215
2953d358 216static void queueoutstr(const char *str, int len) {
41559f53 217 while (len-- > 0) queueoutchar(*str++);
2953d358 218}
592aa664 219
2953d358 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 }
41559f53 230 assert(r <= entry->textlen);
2953d358 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) {
fd60df66 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 }
2953d358 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);
592aa664 283 inbuf= 0;
2953d358 284}
285
41559f53 286static int comparer(const void *a, const void *b) {
287 return memcmp(a,b,4);
288}
289
2953d358 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) {
41559f53 308 newthing= 0;
2953d358 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;
41559f53 320 entry->printbefore= printbefore;
2953d358 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++) {
41559f53 342 c= *p;
592aa664 343 if (cbyte==-1 && bracket && c=='[') {
2953d358 344 addrtextbuf[inbuf++]= c;
592aa664 345 startaddr();
346 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
2953d358 347 queueoutchar(c);
592aa664 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;
2953d358 352 addrtextbuf[inbuf++]= c;
592aa664 353 inbyte++;
354 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
355 bytes[++cbyte]= 0;
2953d358 356 addrtextbuf[inbuf++]= c;
592aa664 357 inbyte= 0;
358 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
2953d358 359 addrtextbuf[inbuf++]= c;
592aa664 360 procaddr();
361 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
362 procaddr();
2953d358 363 queueoutchar(c);
592aa664 364 startaddr();
365 } else {
366 restartbuf();
2953d358 367 queueoutchar(c);
592aa664 368 cbyte= -1;
369 if (!bracket && !isalnum(c)) startaddr();
370 }
371 }
2953d358 372}
373
374static void startup(void) {
375 int r;
376
2953d358 377 if (nonblock(0,1)) sysfail("set stdin to nonblocking mode");
378 if (nonblock(1,1)) sysfail("set stdout to nonblocking mode");
379 memset(&sa,0,sizeof(sa));
380 sa.sin_family= AF_INET;
381 r= adns_init(&ads,0,0); if (r) adnsfail("init",r);
382 cbyte= -1;
383 inbyte= -1;
384 inbuf= 0;
385 if (!bracket) startaddr();
386}
387
388int main(int argc, const char *const *argv) {
389 int r, maxfd;
390 fd_set readfds, writefds, exceptfds;
391 struct outqueuenode *entry;
392 struct timeval *tv, tvbuf, now;
393
394 parseargs(argv);
395 startup();
396
397 while (!inputeof || outqueue.head) {
398 maxfd= 2;
399 tv= 0;
400 FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
401 if ((entry= outqueue.head) && !outblocked) {
402 if (!entry->addr) {
403 writestdout(entry);
404 continue;
41559f53 405 }
406 if (entry->addr->ans) {
2953d358 407 if (entry->addr->ans->nrrs)
408 replacetextwithname(entry);
409 entry->addr= 0;
410 continue;
411 }
412 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
413 if (forever) {
414 tv= 0;
415 } else if (!timercmp(&now,&entry->printbefore,<)) {
416 entry->addr= 0;
417 continue;
418 } else {
41559f53 419 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
420 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
2953d358 421 tvbuf.tv_sec += tvbuf.tv_usec / 1000000;
422 tvbuf.tv_usec %= 1000000;
423 tv= &tvbuf;
424 }
425 adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,
426 &tv,&tvbuf,&now);
427 }
428 if (outblocked) FD_SET(1,&writefds);
41559f53 429 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
2953d358 430
431 r= select(maxfd,&readfds,&writefds,&exceptfds,tv);
432 if (r < 0) { if (r == EINTR) continue; else sysfail("select"); }
433
434 r= gettimeofday(&now,0); if (r) sysfail("gettimeofday");
435 adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,&now);
436 checkadnsqueries();
437
438 if (FD_ISSET(0,&readfds)) {
439 if (!forever) {
440 printbefore= now;
441 timevaladd(&printbefore,timeout);
442 }
443 readstdin();
444 } else if (FD_ISSET(1,&writefds)) {
445 outblocked= 0;
446 }
447 }
2953d358 448 if (nonblock(0,0)) sysfail("un-nonblock stdin");
449 if (nonblock(1,0)) sysfail("un-nonblock stdout");
41559f53 450 if (ferror(stdin) || fclose(stdin)) sysfail("read stdin");
451 if (fclose(stdout)) sysfail("close stdout");
592aa664 452 exit(0);
453}