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