chiark / gitweb /
srv processing written (except for sorting); now to be debugged
[adns.git] / client / adh-main.c
1 /*
2  * adh-main.c
3  * - useful general-purpose resolver client program
4  *   main program and useful subroutines
5  */
6 /*
7  *  This file is
8  *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
9  *
10  *  It is part of adns, which is
11  *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
12  *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
13  *  
14  *  This program is free software; you can redistribute it and/or modify
15  *  it under the terms of the GNU General Public License as published by
16  *  the Free Software Foundation; either version 2, or (at your option)
17  *  any later version.
18  *  
19  *  This program is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *  
24  *  You should have received a copy of the GNU General Public License
25  *  along with this program; if not, write to the Free Software Foundation,
26  *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
27  */
28
29 #include "adnshost.h"
30
31 int rcode;
32 const char *config_text;
33
34 static int used, avail;
35 static char *buf;
36
37 void quitnow(int rc) {
38   if (ads) adns_finish(ads);
39   free(buf);
40   free(ov_id);
41   exit(rc);
42 }
43
44 void sysfail(const char *what, int errnoval) {
45   fprintf(stderr,"adnshost failed: %s: %s\n",what,strerror(errnoval));
46   quitnow(10);
47 }
48
49 void usageerr(const char *fmt, ...) {
50   va_list al;
51   fputs("adnshost usage error: ",stderr);
52   va_start(al,fmt);
53   vfprintf(stderr,fmt,al);
54   va_end(al);
55   putc('\n',stderr);
56   quitnow(11);
57 }
58
59 void outerr(void) {
60   sysfail("write to stdout",errno);
61 }
62
63 void *xmalloc(size_t sz) {
64   void *p;
65
66   p= malloc(sz); if (!p) sysfail("malloc",sz);
67   return p;
68 }
69
70 char *xstrsave(const char *str) {
71   char *p;
72   
73   p= xmalloc(strlen(str)+1);
74   strcpy(p,str);
75   return p;
76 }
77
78 void of_config(const struct optioninfo *oi, const char *arg, const char *arg2) {
79   config_text= arg;
80 }
81
82 void of_type(const struct optioninfo *oi, const char *arg, const char *arg2) {
83   static const struct typename {
84     adns_rrtype type;
85     const char *desc;
86   } typenames[]= {
87     /* enhanced versions */
88     { adns_r_ns,     "ns"     },
89     { adns_r_soa,    "soa"    },
90     { adns_r_ptr,    "ptr"    },
91     { adns_r_mx,     "mx"     },
92     { adns_r_rp,     "rp"     },
93     { adns_r_srv,    "srv"    },
94     { adns_r_addr,   "addr"   },
95     
96     /* types with only one version */
97     { adns_r_cname,  "cname"  },
98     { adns_r_hinfo,  "hinfo"  },
99     { adns_r_txt,    "txt"    },
100     
101     /* raw versions */
102     { adns_r_a,        "a"    },
103     { adns_r_ns_raw,   "ns-"  },
104     { adns_r_soa_raw,  "soa-" },
105     { adns_r_ptr_raw,  "ptr-" },
106     { adns_r_mx_raw,   "mx-"  },
107     { adns_r_rp_raw,   "rp-"  },
108     { adns_r_srv_raw,  "srv-" },
109
110     { adns_r_none, 0 }
111   };
112
113   const struct typename *tnp;
114
115   for (tnp=typenames;
116        tnp->type && strcmp(arg,tnp->desc);
117        tnp++);
118   if (!tnp->type) usageerr("unknown RR type %s",arg);
119   ov_type= tnp->type;
120 }
121
122 static void process_optarg(const char *arg,
123                            const char *const **argv_p,
124                            const char *value) {
125   const struct optioninfo *oip;
126   const char *arg2;
127   int invert;
128
129   if (arg[0] == '-' || arg[0] == '+') {
130     if (arg[0] == '-' && arg[1] == '-') {
131       if (!strncmp(arg,"--no-",5)) {
132         invert= 1;
133         oip= opt_findl(arg+5);
134       } else {
135         invert= 0;
136         oip= opt_findl(arg+2);
137       }
138       if (oip->type == ot_funcarg) {
139         arg= argv_p ? *++(*argv_p) : value;
140         if (!arg) usageerr("option --%s requires a value argument",oip->lopt);
141         arg2= 0;
142       } else if (oip->type == ot_funcarg2) {
143         assert(argv_p);
144         arg= *++(*argv_p);
145         arg2= arg ? *++(*argv_p) : 0;
146         if (!arg || !arg2)
147           usageerr("option --%s requires two more arguments", oip->lopt);
148       } else {
149         if (value) usageerr("option --%s does not take a value",oip->lopt);
150         arg= 0;
151         arg2= 0;
152       }
153       opt_do(oip,invert,arg,arg2);
154     } else if (arg[0] == '-' && arg[1] == 0) {
155       arg= argv_p ? *++(*argv_p) : value;
156       if (!arg) usageerr("option `-' must be followed by a domain");
157       query_do(arg);
158     } else { /* arg[1] != '-', != '\0' */
159       invert= (arg[0] == '+');
160       ++arg;
161       while (*arg) {
162         oip= opt_finds(&arg);
163         if (oip->type == ot_funcarg) {
164           if (!*arg) {
165             arg= argv_p ? *++(*argv_p) : value;
166             if (!arg) usageerr("option -%s requires a value argument",oip->sopt);
167           } else {
168             if (value) usageerr("two values for option -%s given !",oip->sopt);
169           }
170           opt_do(oip,invert,arg,0);
171           arg= "";
172         } else {
173           if (value) usageerr("option -%s does not take a value",oip->sopt);
174           opt_do(oip,invert,0,0);
175         }
176       }
177     }
178   } else { /* arg[0] != '-' */
179     query_do(arg);
180   }
181 }
182     
183 static void read_stdin(void) {
184   int anydone, r;
185   char *newline, *space;
186
187   anydone= 0;
188   while (!anydone || used) {
189     while (!(newline= memchr(buf,'\n',used))) {
190       if (used == avail) {
191         avail += 20; avail <<= 1;
192         buf= realloc(buf,avail);
193         if (!buf) sysfail("realloc stdin buffer",errno);
194       }
195       do {
196         r= read(0,buf+used,avail-used);
197       } while (r < 0 && errno == EINTR);
198       if (r == 0) {
199         if (used) {
200           /* fake up final newline */
201           buf[used++]= '\n';
202           r= 1;
203         } else {
204           ov_pipe= 0;
205           return;
206         }
207       }
208       if (r < 0) sysfail("read stdin",errno);
209       used += r;
210     }
211     *newline++= 0;
212     space= strchr(buf,' ');
213     if (space) *space++= 0;
214     process_optarg(buf,0,space);
215     used -= (newline-buf);
216     memmove(buf,newline,used);
217     anydone= 1;
218   }
219 }
220
221 int main(int argc, const char *const *argv) {
222   struct timeval *tv, tvbuf;
223   adns_query qu;
224   void *qun_v;
225   adns_answer *answer;
226   int r, maxfd;
227   fd_set readfds, writefds, exceptfds;
228   const char *arg;
229   
230   while ((arg= *++argv)) process_optarg(arg,&argv,0);
231
232   if (!ov_pipe && !ads) usageerr("no domains given, and -f/--pipe not used; try --help");
233
234   ensure_adns_init();
235
236   for (;;) {
237     for (;;) {
238       qu= ov_asynch ? 0 : outstanding.head ? outstanding.head->qu : 0;
239       r= adns_check(ads,&qu,&answer,&qun_v);
240       if (r == EAGAIN) break;
241       if (r == ESRCH) { if (!ov_pipe) goto x_quit; else break; }
242       assert(!r);
243       query_done(qun_v,answer);
244     }
245     maxfd= 0;
246     FD_ZERO(&readfds);
247     FD_ZERO(&writefds);
248     FD_ZERO(&exceptfds);
249     if (ov_pipe) {
250       maxfd= 1;
251       FD_SET(0,&readfds);
252     }
253     tv= 0;
254     adns_beforeselect(ads, &maxfd, &readfds,&writefds,&exceptfds, &tv,&tvbuf,0);
255     r= select(maxfd, &readfds,&writefds,&exceptfds, tv);
256     if (r == -1) {
257       if (errno == EINTR) continue;
258       sysfail("select",errno);
259     }
260     adns_afterselect(ads, maxfd, &readfds,&writefds,&exceptfds, 0);
261     if (ov_pipe && FD_ISSET(0,&readfds)) read_stdin();
262   }
263 x_quit:
264   if (fclose(stdout)) outerr();
265   quitnow(rcode);
266 }