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