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