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