+ * Fix <sys/fcntl.h> in client/adnsresfilter.c to <fcntl.h>.
[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 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 <fcntl.h>
37
38 #include "adns.h"
39 #include "config.h"
40 #include "dlist.h"
41 #include "tvarith.h"
42
43 struct 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
52 static int bracket, forever, address;
53 static unsigned long timeout= 1000;
54 static adns_rrtype rrt= adns_r_ptr;
55
56 static int outblocked, inputeof;
57 static struct { struct outqueuenode *head, *tail; } outqueue;
58 static int peroutqueuenode, outqueuelen;
59
60 static struct sockaddr_in sa;
61 static adns_state ads;
62
63 static char addrtextbuf[14];
64 static int cbyte, inbyte, inbuf;
65 static unsigned char bytes[4];
66 static struct timeval printbefore;
67
68 struct treething {
69 unsigned char bytes[4];
70 adns_query qu;
71 adns_answer *ans;
72 };
73
74 static struct treething *newthing;
75 static void *treeroot;
76
77 static 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
87 static void quit(int exitstatus) NONRETURNING;
88 static void quit(int exitstatus) {
89 nonblock(0,0);
90 nonblock(1,0);
91 exit(exitstatus);
92 }
93
94 static void sysfail(const char *what) NONRETURNING;
95 static void sysfail(const char *what) {
96 fprintf(stderr,"adnsresfilter: system call failed: %s: %s\n",what,strerror(errno));
97 quit(2);
98 }
99
100 static void *xmalloc(size_t sz) {
101 void *r;
102 r= malloc(sz); if (r) return r;
103 sysfail("malloc");
104 }
105
106 static void outputerr(void) NONRETURNING;
107 static void outputerr(void) { sysfail("write to stdout"); }
108
109 static void usage(void) {
110 if (printf("usage: adnsresfilter [<options ...>]\n"
111 " adnsresfilter -h|--help\n"
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")
119 == EOF) outputerr();
120 }
121
122 static void usageerr(const char *why) NONRETURNING;
123 static void usageerr(const char *why) {
124 fprintf(stderr,"adnsresfilter: bad usage: %s\n",why);
125 usage();
126 quit(1);
127 }
128
129 static void adnsfail(const char *what, int e) NONRETURNING;
130 static void adnsfail(const char *what, int e) {
131 fprintf(stderr,"adnsresfilter: adns call failed: %s: %s\n",what,strerror(e));
132 quit(2);
133 }
134
135 static void settimeout(const char *arg) {
136 char *ep;
137 timeout= strtoul(arg,&ep,0);
138 if (*ep) usageerr("invalid timeout");
139 }
140
141 static void parseargs(const char *const *argv) {
142 const char *arg;
143 int c;
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;
154 } else if (!strcmp(arg,"--address")) {
155 address= 1;
156 } else if (!strcmp(arg,"--help")) {
157 usage(); quit(0);
158 } else if (!strcmp(arg,"--timeout")) {
159 if (!(arg= *++argv)) usageerr("--timeout needs a value");
160 settimeout(arg);
161 forever= 0;
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;
177 case 'a':
178 address= 1;
179 break;
180 case 'h':
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;
190 default:
191 usageerr("unknown short option");
192 }
193 }
194 }
195 }
196 }
197
198 static void queueoutchar(int c) {
199 struct outqueuenode *entry;
200
201 entry= outqueue.tail;
202 if (!entry || entry->addr || entry->textlen >= peroutqueuenode) {
203 peroutqueuenode= !peroutqueuenode || !entry || entry->addr ? 128 :
204 peroutqueuenode >= 1024 ? 4096 : peroutqueuenode<<2;
205 entry= xmalloc(sizeof(*entry));
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 }
213 entry->textp[entry->textlen++]= c;
214 }
215
216 static void queueoutstr(const char *str, int len) {
217 while (len-- > 0) queueoutchar(*str++);
218 }
219
220 static 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 }
230 assert(r <= entry->textlen);
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
242 static void replacetextwithname(struct outqueuenode *entry) {
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 }
261 }
262
263 static 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
281 static void restartbuf(void) {
282 if (inbuf>0) queueoutstr(addrtextbuf,inbuf);
283 inbuf= 0;
284 }
285
286 static int comparer(const void *a, const void *b) {
287 return memcmp(a,b,4);
288 }
289
290 static 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) {
308 newthing= 0;
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;
320 entry->printbefore= printbefore;
321 LIST_LINK_TAIL(outqueue,entry);
322 outqueuelen++;
323 inbuf= 0;
324 cbyte= -1;
325 }
326
327 static void startaddr(void) {
328 bytes[cbyte=0]= 0;
329 inbyte= 0;
330 }
331
332 static 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++) {
342 c= *p;
343 if (cbyte==-1 && bracket && c=='[') {
344 addrtextbuf[inbuf++]= c;
345 startaddr();
346 } else if (cbyte==-1 && !bracket && !isalnum(c)) {
347 queueoutchar(c);
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;
352 addrtextbuf[inbuf++]= c;
353 inbyte++;
354 } else if (cbyte>=0 && cbyte<3 && inbyte>0 && c=='.') {
355 bytes[++cbyte]= 0;
356 addrtextbuf[inbuf++]= c;
357 inbyte= 0;
358 } else if (cbyte==3 && inbyte>0 && bracket && c==']') {
359 addrtextbuf[inbuf++]= c;
360 procaddr();
361 } else if (cbyte==3 && inbyte>0 && !bracket && !isalnum(c)) {
362 procaddr();
363 queueoutchar(c);
364 startaddr();
365 } else {
366 restartbuf();
367 queueoutchar(c);
368 cbyte= -1;
369 if (!bracket && !isalnum(c)) startaddr();
370 }
371 }
372 }
373
374 static void startup(void) {
375 int r;
376
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
388 int 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;
405 }
406 if (entry->addr->ans) {
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 {
419 tvbuf.tv_sec= entry->printbefore.tv_sec - now.tv_sec - 1;
420 tvbuf.tv_usec= entry->printbefore.tv_usec - now.tv_usec + 1000000;
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);
429 if (!inputeof && outqueuelen<1024) FD_SET(0,&readfds);
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 }
448 if (nonblock(0,0)) sysfail("un-nonblock stdin");
449 if (nonblock(1,0)) sysfail("un-nonblock stdout");
450 if (ferror(stdin) || fclose(stdin)) sysfail("read stdin");
451 if (fclose(stdout)) sysfail("close stdout");
452 exit(0);
453 }