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