chiark / gitweb /
+ * Give ESRCH, not EAGAIN, if _check called with no queries outstanding.
[adns.git] / src / general.c
1 /*
2  * general.c
3  * - diagnostic functions
4  * - vbuf handling
5  */
6 /*
7  *  This file is
8  *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
9  *
10  *  It is part of adns, which is
11  *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
12  *    Copyright (C) 1999 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 <stdlib.h>
30
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35
36 #include "internal.h"
37
38 /* Core diagnostic functions */
39
40 void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent,
41                  int serv, adns_query qu, const char *fmt, va_list al) {
42   const char *bef, *aft;
43   vbuf vb;
44   
45   if (!ads->diagfile ||
46       (!(ads->iflags & adns_if_debug) && (!prevent || (ads->iflags & prevent))))
47     return;
48
49   fprintf(ads->diagfile,"adns%s: ",pfx);
50
51   vfprintf(ads->diagfile,fmt,al);
52
53   bef= " (";
54   aft= "\n";
55
56   if (qu && qu->query_dgram) {
57     adns__vbuf_init(&vb);
58     fprintf(ads->diagfile,"%sQNAME=%s, QTYPE=%s",
59             bef,
60             adns__diag_domain(qu->ads,-1,0, &vb,
61                               qu->query_dgram,qu->query_dglen,DNS_HDRSIZE),
62             qu->typei ? qu->typei->rrtname : "<unknown>");
63     if (qu->typei && qu->typei->fmtname)
64       fprintf(ads->diagfile,"(%s)",qu->typei->fmtname);
65     bef=", "; aft=")\n";
66   }
67   
68   if (serv>=0) {
69     fprintf(ads->diagfile,"%sNS=%s",bef,inet_ntoa(ads->servers[serv].addr));
70     bef=", "; aft=")\n";
71   }
72
73   fputs(aft,ads->diagfile);
74 }
75
76 void adns__debug(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
77   va_list al;
78
79   va_start(al,fmt);
80   adns__vdiag(ads," debug",0,serv,qu,fmt,al);
81   va_end(al);
82 }
83
84 void adns__warn(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
85   va_list al;
86
87   va_start(al,fmt);
88   adns__vdiag(ads," warning",adns_if_noerrprint|adns_if_noserverwarn,serv,qu,fmt,al);
89   va_end(al);
90 }
91
92 void adns__diag(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
93   va_list al;
94
95   va_start(al,fmt);
96   adns__vdiag(ads,"",adns_if_noerrprint,serv,qu,fmt,al);
97   va_end(al);
98 }
99
100 /* vbuf functions */
101
102 void adns__vbuf_init(vbuf *vb) {
103   vb->used= vb->avail= 0; vb->buf= 0;
104 }
105
106 int adns__vbuf_ensure(vbuf *vb, int want) {
107   void *nb;
108   
109   if (vb->avail >= want) return 1;
110   nb= realloc(vb->buf,want); if (!nb) return 0;
111   vb->buf= nb;
112   vb->avail= want;
113   return 1;
114 }
115   
116 void adns__vbuf_appendq(vbuf *vb, const byte *data, int len) {
117   memcpy(vb->buf+vb->used,data,len);
118   vb->used+= len;
119 }
120
121 int adns__vbuf_append(vbuf *vb, const byte *data, int len) {
122   int newlen;
123   void *nb;
124
125   newlen= vb->used+len;
126   if (vb->avail < newlen) {
127     if (newlen<20) newlen= 20;
128     newlen <<= 1;
129     nb= realloc(vb->buf,newlen);
130     if (!nb) { newlen= vb->used+len; nb= realloc(vb->buf,newlen); }
131     if (!nb) return 0;
132     vb->buf= nb;
133     vb->avail= newlen;
134   }
135   adns__vbuf_appendq(vb,data,len);
136   return 1;
137 }
138
139 int adns__vbuf_appendstr(vbuf *vb, const char *data) {
140   int l;
141   l= strlen(data);
142   return adns__vbuf_append(vb,data,l);
143 }
144
145 void adns__vbuf_free(vbuf *vb) {
146   free(vb->buf);
147   adns__vbuf_init(vb);
148 }
149
150 /* Additional diagnostic functions */
151
152 const char *adns__diag_domain(adns_state ads, int serv, adns_query qu,
153                               vbuf *vb, const byte *dgram, int dglen, int cbyte) {
154   adns_status st;
155
156   st= adns__parse_domain(ads,serv,qu,vb, pdf_quoteok, dgram,dglen,&cbyte,dglen);
157   if (st == adns_s_nomemory) {
158     return "<cannot report domain... out of memory>";
159   }
160   if (st) {
161     vb->used= 0;
162     if (!(adns__vbuf_appendstr(vb,"<bad format... ") &&
163           adns__vbuf_appendstr(vb,adns_strerror(st)) &&
164           adns__vbuf_appendstr(vb,">") &&
165           adns__vbuf_append(vb,"",1))) {
166       return "<cannot report bad format... out of memory>";
167     }
168   }
169   if (!vb->used) {
170     adns__vbuf_appendstr(vb,"<truncated ...>");
171     adns__vbuf_append(vb,"",1);
172   }
173   return vb->buf;
174 }
175
176 adns_status adns_rr_info(adns_rrtype type,
177                          const char **rrtname_r, const char **fmtname_r,
178                          int *len_r,
179                          const void *datap, char **data_r) {
180   const typeinfo *typei;
181   vbuf vb;
182   adns_status st;
183
184   typei= adns__findtype(type);
185   if (!typei) return adns_s_unknownrrtype;
186
187   if (rrtname_r) *rrtname_r= typei->rrtname;
188   if (fmtname_r) *fmtname_r= typei->fmtname;
189   if (len_r) *len_r= typei->rrsz;
190
191   if (!datap) return adns_s_ok;
192   
193   adns__vbuf_init(&vb);
194   st= typei->convstring(&vb,datap);
195   if (st) goto x_freevb;
196   if (!adns__vbuf_append(&vb,"",1)) { st= adns_s_nomemory; goto x_freevb; }
197   assert(strlen(vb.buf) == vb.used-1);
198   *data_r= realloc(vb.buf,vb.used);
199   if (!*data_r) *data_r= vb.buf;
200   return adns_s_ok;
201
202  x_freevb:
203   adns__vbuf_free(&vb);
204   return st;
205 }
206
207 #define SINFO(n,s) { adns_s_##n, #n, s }
208
209 static const struct sinfo {
210   adns_status st;
211   const char *abbrev;
212   const char *string;
213 } sinfos[]= {
214   SINFO(  ok,                  "OK"                                            ),
215
216   SINFO(  nomemory,            "Out of memory"                                 ),
217   SINFO(  unknownrrtype,       "Query not implemented in DNS library"          ),
218   SINFO(  systemfail,          "General resolver or system failure"            ),
219
220   SINFO(  timeout,             "DNS query timed out"                           ),
221   SINFO(  allservfail,         "All nameservers failed"                        ),
222   SINFO(  norecurse,           "Recursion denied by nameserver"                ),
223   SINFO(  invalidresponse,     "Nameserver sent bad response"                  ),
224   SINFO(  unknownformat,       "Nameserver used unknown format"                ),
225
226   SINFO(  rcodeservfail,       "Nameserver reports failure"                    ),
227   SINFO(  rcodeformaterror,    "Query not understood by nameserver"            ),
228   SINFO(  rcodenotimplemented, "Query not implemented by nameserver"           ),
229   SINFO(  rcoderefused,        "Query refused by nameserver"                   ),
230   SINFO(  rcodeunknown,        "Nameserver sent unknown response code"         ),
231   
232   SINFO(  inconsistent,        "Inconsistent resource records in DNS"          ),
233   SINFO(  prohibitedcname,     "DNS alias found where canonical name wanted"   ),
234   SINFO(  answerdomaininvalid, "Found syntactically invalid domain name"       ),
235   SINFO(  answerdomaintoolong, "Found overly-long domain name"                 ),
236   SINFO(  invaliddata,         "Found invalid DNS data"                        ),
237
238   SINFO(  querydomainwrong,    "Domain invalid for particular DNS query type"  ),
239   SINFO(  querydomaininvalid,  "Domain name is syntactically invalid"          ),
240   SINFO(  querydomaintoolong,  "Domain name is too long"                       ),
241
242   SINFO(  nxdomain,            "No such domain"                                ),
243   SINFO(  nodata,              "No such data"                                  ),
244 };
245
246 static int si_compar(const void *key, const void *elem) {
247   const adns_status *st= key;
248   const struct sinfo *si= elem;
249
250   return *st < si->st ? -1 : *st > si->st ? 1 : 0;
251 }
252
253 static const struct sinfo *findsinfo(adns_status st) {
254   return bsearch(&st,sinfos,sizeof(sinfos)/sizeof(*sinfos),sizeof(*sinfos),si_compar);
255 }
256
257 const char *adns_strerror(adns_status st) {
258   const struct sinfo *si;
259
260   si= findsinfo(st);
261   return si->string;
262 }
263
264 const char *adns_errabbrev(adns_status st) {
265   const struct sinfo *si;
266
267   si= findsinfo(st);
268   return si->abbrev;
269 }
270
271 void adns__isort(void *array, int nobjs, int sz, void *tempbuf,
272                  int (*needswap)(void *context, const void *a, const void *b),
273                  void *context) {
274   byte *data= array;
275   int i, place;
276
277   for (i=0; i<nobjs; i++) {
278     for (place= i;
279          place>0 && needswap(context, data + (place-1)*sz, data + i*sz);
280          place--);
281     if (place != i) {
282       memcpy(tempbuf, data + i*sz, sz);
283       memmove(data + (place+1)*sz, data + place*sz, (i-place)*sz);
284       memcpy(data + place*sz, tempbuf, sz);
285     }
286   }
287 }
288
289 /* SIGPIPE protection. */
290
291 void adns__sigpipe_protect(adns_state ads) {
292   sigset_t toblock;
293   struct sigaction sa;
294   int r;
295
296   if (ads->iflags & adns_if_nosigpipe) return;
297
298   sigfillset(&toblock);
299   sigdelset(&toblock,SIGPIPE);
300
301   sa.sa_handler= SIG_IGN;
302   sigfillset(&sa.sa_mask);
303   sa.sa_flags= 0;
304   
305   r= sigprocmask(SIG_SETMASK,&toblock,&ads->stdsigmask); assert(!r);
306   r= sigaction(SIGPIPE,&sa,&ads->stdsigpipe); assert(!r);
307 }
308
309 void adns__sigpipe_unprotect(adns_state ads) {
310   int r;
311
312   if (ads->iflags & adns_if_nosigpipe) return;
313
314   r= sigaction(SIGPIPE,&ads->stdsigpipe,0); assert(!r);
315   r= sigprocmask(SIG_SETMASK,&ads->stdsigmask,0); assert(!r);
316 }