chiark / gitweb /
56e7b9e9f007fed407dab376fc62f33e661c7c6e
[adns.git] / client / adh-query.c
1 /*
2  * adh-query.c
3  * - useful general-purpose resolver client program
4  *   make queries and print answers
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 adns_state ads;
31 struct outstanding_list outstanding;
32
33 static unsigned long idcounter;
34
35 void ensure_adns_init(void) {
36   adns_initflags initflags;
37   int r;
38   
39   if (ads) return;
40
41   if (signal(SIGPIPE,SIG_IGN) == SIG_ERR) sysfail("ignore SIGPIPE",errno);
42
43   initflags= adns_if_noautosys|adns_if_nosigpipe|ov_verbose;
44   if (!ov_env) initflags |= adns_if_noenv;
45
46   if (config_text) {
47     r= adns_init_strcfg(&ads, initflags, stderr, config_text);
48   } else {
49     r= adns_init(&ads, initflags, 0);
50   }
51   if (r) sysfail("adns_init",r);
52
53   if (ov_format == fmt_default)
54     ov_format= ov_asynch ? fmt_asynch : fmt_simple;
55 }
56
57 void type_info(adns_rrtype type, const char **typename_r,
58                const void *datap, char **data_r) {
59   static char buf[12];
60   adns_status st;
61   
62   st= adns_rr_info(type, typename_r, 0,0, datap,data_r);
63   if (st == adns_s_nomemory) sysfail("adns_rr_info failed",ENOMEM);
64   assert(!st);
65   if (typename_r && !*typename_r) {
66     sprintf(buf,"TYPE%d", (int)(type & adns_rrt_typemask));
67     *typename_r= buf;
68   }
69 }
70
71 static void prep_query(struct query_node **qun_r, int *quflags_r) {
72   struct query_node *qun;
73   char idbuf[20];
74   
75   if (ov_pipe && !ads) usageerr("-f/--pipe not consistent with domains on command line");
76   ensure_adns_init();
77   
78   qun= malloc(sizeof(*qun));
79   qun->pqfr= ov_pqfr;
80   if (ov_id) {
81     qun->id= xstrsave(ov_id);
82   } else {
83     sprintf(idbuf,"%lu",idcounter++);
84     idcounter &= 0x0fffffffflu;
85     qun->id= xstrsave(idbuf);
86   }
87
88   *quflags_r=
89     (ov_search ? adns_qf_search : 0) |
90     (ov_tcp ? adns_qf_usevc : 0) |
91     ((ov_pqfr.show_owner || ov_format == fmt_simple) ? adns_qf_owner : 0) |
92     (ov_qc_query ? adns_qf_quoteok_query : 0) |
93     (ov_qc_anshost ? adns_qf_quoteok_anshost : 0) |
94     (ov_qc_cname ? 0 : adns_qf_quoteok_cname) |
95     (ov_v6map ? adns_qf_ipv6_mapv4 : 0) |
96     ov_cname | ov_afflags;
97     
98   *qun_r= qun;
99 }
100
101 static void sockaddr_aton(const char *text, adns_rr_addr *a) {
102   int err;
103
104   a->len= sizeof(a->addr);
105   err= adns_text2addr(text,0,adns_qf_addrlit_scope_forbid,
106                       &a->addr.sa,&a->len);
107   if (err == EINVAL) usageerr("invalid IP address %s",text);
108   else if (err) sysfail("adns_text2addr",err);
109 }
110   
111 void of_ptr(const struct optioninfo *oi, const char *arg, const char *arg2) {
112   struct query_node *qun;
113   int quflags, r;
114   adns_rr_addr a;
115
116   sockaddr_aton(arg,&a);
117   prep_query(&qun,&quflags);
118   qun->owner= xstrsave(arg);
119   r= adns_submit_reverse(ads, &a.addr.sa,
120                          ov_type == adns_r_none ? adns_r_ptr : ov_type,
121                          quflags,
122                          qun,
123                          &qun->qu);
124   if (r) sysfail("adns_submit_reverse",r);
125
126   LIST_LINK_TAIL(outstanding,qun);
127 }
128
129 void of_reverse(const struct optioninfo *oi, const char *arg, const char *arg2) {
130   struct query_node *qun;
131   int quflags, r;
132   adns_rr_addr a;
133
134   sockaddr_aton(arg,&a);
135   prep_query(&qun,&quflags);
136   qun->owner= xmalloc(strlen(arg) + strlen(arg2) + 2);
137   sprintf(qun->owner, "%s %s", arg,arg2);
138   r= adns_submit_reverse_any(ads, &a.addr.sa,arg2,
139                              ov_type == adns_r_none ? adns_r_txt : ov_type,
140                              quflags,
141                              qun,
142                              &qun->qu);
143   if (r) sysfail("adns_submit_reverse",r);
144
145   LIST_LINK_TAIL(outstanding,qun);
146 }
147
148 void query_do(const char *domain) {
149   struct query_node *qun;
150   int quflags, r;
151
152   prep_query(&qun,&quflags);
153   qun->owner= xstrsave(domain);
154   r= adns_submit(ads, domain,
155                  ov_type == adns_r_none ? adns_r_addr : ov_type,
156                  quflags,
157                  qun,
158                  &qun->qu);
159   if (r) sysfail("adns_submit",r);
160
161   LIST_LINK_TAIL(outstanding,qun);
162 }
163
164 static void dequeue_query(struct query_node *qun) {
165   LIST_UNLINK(outstanding,qun);
166   free(qun->id);
167   free(qun->owner);
168   free(qun);
169 }
170
171 static void print_withspace(const char *str) {
172   if (printf("%s ", str) == EOF) outerr();
173 }
174
175 static void print_ttl(struct query_node *qun, adns_answer *answer) {
176   unsigned long ttl;
177   time_t now;
178   
179   switch (qun->pqfr.ttl) {
180   case tm_none:
181     return;
182   case tm_rel:
183     if (time(&now) == (time_t)-1) sysfail("get current time",errno);
184     ttl= answer->expires < now ? 0 : answer->expires - now;
185     break;
186   case tm_abs:
187     ttl= answer->expires;
188     break;
189   default:
190     abort();
191   }
192   if (printf("%lu ",ttl) == EOF) outerr();
193 }
194
195 static const char *owner_show(struct query_node *qun, adns_answer *answer) {
196   return answer->owner ? answer->owner : qun->owner;
197 }
198
199 static void print_owner_ttl(struct query_node *qun, adns_answer *answer) {
200   if (qun->pqfr.show_owner) print_withspace(owner_show(qun,answer));
201   print_ttl(qun,answer);
202 }
203
204 static void check_status(adns_status st) {
205   static const adns_status statuspoints[]= {
206     adns_s_ok,
207     adns_s_max_localfail, adns_s_max_remotefail, adns_s_max_tempfail,
208     adns_s_max_misconfig, adns_s_max_misquery
209   };
210
211   const adns_status *spp;
212   int minrcode;
213
214   for (minrcode=0, spp=statuspoints;
215        spp < statuspoints + (sizeof(statuspoints)/sizeof(statuspoints[0]));
216        spp++)
217     if (st > *spp) minrcode++;
218   if (rcode < minrcode) rcode= minrcode;
219 }
220
221 static void print_status(adns_status st, struct query_node *qun, adns_answer *answer) {
222   const char *statustypeabbrev, *statusabbrev, *statusstring;
223
224   statustypeabbrev= adns_errtypeabbrev(st);
225   statusabbrev= adns_errabbrev(st);
226   statusstring= adns_strerror(st);
227   assert(!strchr(statusstring,'"'));
228
229   if (printf("%s %d %s ", statustypeabbrev, st, statusabbrev)
230       == EOF) outerr();
231   print_owner_ttl(qun,answer);
232   if (qun->pqfr.show_cname)
233     print_withspace(answer->cname ? answer->cname : "$");
234   if (printf("\"%s\"\n", statusstring) == EOF) outerr();
235 }
236
237 static void print_dnsfail(adns_status st, struct query_node *qun, adns_answer *answer) {
238   int r;
239   const char *typename, *statusstring;
240   
241   if (ov_format == fmt_inline) {
242     if (fputs("; failed ",stdout) == EOF) outerr();
243     print_status(st,qun,answer);
244     return;
245   }
246   assert(ov_format == fmt_simple);
247   if (st == adns_s_nxdomain) {
248     r= fprintf(stderr,"%s does not exist\n", owner_show(qun,answer));
249   } else {
250     type_info(answer->type, &typename, 0,0);
251     if (st == adns_s_nodata) {
252       r= fprintf(stderr,"%s has no %s record\n", owner_show(qun,answer), typename);
253     } else {
254       statusstring= adns_strerror(st);
255       r= fprintf(stderr,"Error during DNS %s lookup for %s: %s\n",
256                  typename, owner_show(qun,answer), statusstring);
257     }
258   }
259   if (r == EOF) sysfail("write error message to stderr",errno);
260 }
261     
262 void query_done(struct query_node *qun, adns_answer *answer) {
263   adns_status st;
264   int rrn, nrrs;
265   const char *rrp, *realowner, *typename;
266   char *datastr;
267
268   st= answer->status;
269   nrrs= answer->nrrs;
270   if (ov_format == fmt_asynch) {
271     check_status(st);
272     if (printf("%s %d ", qun->id, nrrs) == EOF) outerr();
273     print_status(st,qun,answer);
274   } else {
275     if (qun->pqfr.show_cname && answer->cname) {
276       print_owner_ttl(qun,answer);
277       if (qun->pqfr.show_type) print_withspace("CNAME");
278       if (printf("%s\n", answer->cname) == EOF) outerr();
279     }
280     if (st) {
281       check_status(st);
282       print_dnsfail(st,qun,answer);
283     }
284   }
285   if (qun->pqfr.show_owner) {
286     realowner= answer->cname ? answer->cname : owner_show(qun,answer);
287     assert(realowner);
288   } else {
289     realowner= 0;
290   }
291   if (nrrs) {
292     for (rrn=0, rrp = answer->rrs.untyped;
293          rrn < nrrs;
294          rrn++, rrp += answer->rrsz) {
295       if (realowner) print_withspace(realowner);
296       print_ttl(qun,answer);
297       type_info(answer->type,&typename, rrp,&datastr);
298       if (qun->pqfr.show_type) print_withspace(typename);
299       if (printf("%s\n",datastr) == EOF) outerr();
300       free(datastr);
301     }
302   }
303   if (fflush(stdout)) outerr();
304   free(answer);
305   dequeue_query(qun);
306 }
307
308 void of_asynch_id(const struct optioninfo *oi, const char *arg, const char *arg2) {
309   free(ov_id);
310   ov_id= xstrsave(arg);
311 }
312
313 void of_cancel_id(const struct optioninfo *oi, const char *arg, const char *arg2) {
314   struct query_node *qun;
315
316   for (qun= outstanding.head;
317        qun && strcmp(qun->id,arg);
318        qun= qun->next);
319   if (!qun) return;
320   adns_cancel(qun->qu);
321   dequeue_query(qun);
322 }