chiark / gitweb /
src/internal.h: Hoist the qcontext definition to before typeinfo.
[adns.git] / src / general.c
1 /*
2  * general.c
3  * - diagnostic functions
4  * - vbuf handling
5  */
6 /*
7  *  This file is part of adns, which is
8  *    Copyright (C) 1997-2000,2003,2006  Ian Jackson
9  *    Copyright (C) 1999-2000,2003,2006  Tony Finch
10  *    Copyright (C) 1991 Massachusetts Institute of Technology
11  *  (See the file INSTALL for full details.)
12  *  
13  *  This program is free software; you can redistribute it and/or modify
14  *  it under the terms of the GNU General Public License as published by
15  *  the Free Software Foundation; either version 2, or (at your option)
16  *  any later version.
17  *  
18  *  This program is distributed in the hope that it will be useful,
19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *  GNU General Public License for more details.
22  *  
23  *  You should have received a copy of the GNU General Public License
24  *  along with this program; if not, write to the Free Software Foundation,
25  *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
26  */
27
28 #include <stdlib.h>
29 #include <unistd.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__vlprintf(adns_state ads, const char *fmt, va_list al) {
41   ads->logfn(ads,ads->logfndata,fmt,al);
42 }
43
44 void adns__lprintf(adns_state ads, const char *fmt, ...) {
45   va_list al;
46   va_start(al,fmt);
47   adns__vlprintf(ads,fmt,al);
48   va_end(al);
49 }
50
51 void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent,
52                  int serv, adns_query qu, const char *fmt, va_list al) {
53   const char *bef, *aft;
54   vbuf vb;
55   
56   if (!ads->logfn ||
57       (!(ads->iflags & adns_if_debug)
58        && (!prevent || (ads->iflags & prevent))))
59     return;
60
61   if (ads->iflags & adns_if_logpid) {
62     adns__lprintf(ads,"adns%s [%ld]: ",pfx,(long)getpid());
63   } else {
64     adns__lprintf(ads,"adns%s: ",pfx);
65   }
66
67   adns__vlprintf(ads,fmt,al);
68
69   bef= " (";
70   aft= "\n";
71
72   if (qu && qu->query_dgram) {
73     adns__vbuf_init(&vb);
74     adns__lprintf(ads,"%sQNAME=%s, QTYPE=%s",
75             bef,
76             adns__diag_domain(qu->ads,-1,0, &vb,
77                               qu->query_dgram,qu->query_dglen,DNS_HDRSIZE),
78             qu->typei ? qu->typei->rrtname : "<unknown>");
79     if (qu->typei && qu->typei->fmtname)
80       adns__lprintf(ads,"(%s)",qu->typei->fmtname);
81     bef=", "; aft=")\n";
82     adns__vbuf_free(&vb);
83   }
84   
85   if (serv>=0) {
86     adns__lprintf(ads,"%sNS=%s",bef,inet_ntoa(ads->servers[serv].addr));
87     bef=", "; aft=")\n";
88   }
89
90   adns__lprintf(ads,"%s",aft);
91 }
92
93 void adns__debug(adns_state ads, int serv, adns_query qu,
94                  const char *fmt, ...) {
95   va_list al;
96
97   va_start(al,fmt);
98   adns__vdiag(ads," debug",0,serv,qu,fmt,al);
99   va_end(al);
100 }
101
102 void adns__warn(adns_state ads, int serv, adns_query qu,
103                 const char *fmt, ...) {
104   va_list al;
105
106   va_start(al,fmt);
107   adns__vdiag(ads," warning",
108               adns_if_noerrprint|adns_if_noserverwarn, serv,qu,fmt,al);
109   va_end(al);
110 }
111
112 void adns__diag(adns_state ads, int serv, adns_query qu,
113                 const char *fmt, ...) {
114   va_list al;
115
116   va_start(al,fmt);
117   adns__vdiag(ads,"",adns_if_noerrprint,serv,qu,fmt,al);
118   va_end(al);
119 }
120
121 /* vbuf functions */
122
123 void adns__vbuf_init(vbuf *vb) {
124   vb->used= vb->avail= 0; vb->buf= 0;
125 }
126
127 int adns__vbuf_ensure(vbuf *vb, int want) {
128   void *nb;
129   
130   if (vb->avail >= want) return 1;
131   nb= realloc(vb->buf,want); if (!nb) return 0;
132   vb->buf= nb;
133   vb->avail= want;
134   return 1;
135 }
136   
137 void adns__vbuf_appendq(vbuf *vb, const byte *data, int len) {
138   memcpy(vb->buf+vb->used,data,len);
139   vb->used+= len;
140 }
141
142 int adns__vbuf_append(vbuf *vb, const byte *data, int len) {
143   int newlen;
144   void *nb;
145
146   newlen= vb->used+len;
147   if (vb->avail < newlen) {
148     if (newlen<20) newlen= 20;
149     newlen <<= 1;
150     nb= realloc(vb->buf,newlen);
151     if (!nb) { newlen= vb->used+len; nb= realloc(vb->buf,newlen); }
152     if (!nb) return 0;
153     vb->buf= nb;
154     vb->avail= newlen;
155   }
156   adns__vbuf_appendq(vb,data,len);
157   return 1;
158 }
159
160 int adns__vbuf_appendstr(vbuf *vb, const char *data) {
161   int l;
162   l= strlen(data);
163   return adns__vbuf_append(vb,data,l);
164 }
165
166 void adns__vbuf_free(vbuf *vb) {
167   free(vb->buf);
168   adns__vbuf_init(vb);
169 }
170
171 /* Additional diagnostic functions */
172
173 const char *adns__diag_domain(adns_state ads, int serv, adns_query qu,
174                               vbuf *vb, const byte *dgram,
175                               int dglen, int cbyte) {
176   adns_status st;
177
178   st= adns__parse_domain(ads,serv,qu,vb, pdf_quoteok,
179                          dgram,dglen,&cbyte,dglen);
180   if (st == adns_s_nomemory) {
181     return "<cannot report domain... out of memory>";
182   }
183   if (st) {
184     vb->used= 0;
185     if (!(adns__vbuf_appendstr(vb,"<bad format... ") &&
186           adns__vbuf_appendstr(vb,adns_strerror(st)) &&
187           adns__vbuf_appendstr(vb,">") &&
188           adns__vbuf_append(vb,"",1))) {
189       return "<cannot report bad format... out of memory>";
190     }
191   }
192   if (!vb->used) {
193     adns__vbuf_appendstr(vb,"<truncated ...>");
194     adns__vbuf_append(vb,"",1);
195   }
196   return vb->buf;
197 }
198
199 adns_status adns_rr_info(adns_rrtype type,
200                          const char **rrtname_r, const char **fmtname_r,
201                          int *len_r,
202                          const void *datap, char **data_r) {
203   const typeinfo *typei;
204   vbuf vb;
205   adns_status st;
206
207   typei= adns__findtype(type);
208   if (!typei) return adns_s_unknownrrtype;
209
210   if (rrtname_r) *rrtname_r= typei->rrtname;
211   if (fmtname_r) *fmtname_r= typei->fmtname;
212   if (len_r) *len_r= typei->rrsz;
213
214   if (!datap) return adns_s_ok;
215   
216   adns__vbuf_init(&vb);
217   st= typei->convstring(&vb,datap);
218   if (st) goto x_freevb;
219   if (!adns__vbuf_append(&vb,"",1)) { st= adns_s_nomemory; goto x_freevb; }
220   assert(strlen(vb.buf) == vb.used-1);
221   *data_r= realloc(vb.buf,vb.used);
222   if (!*data_r) *data_r= vb.buf;
223   return adns_s_ok;
224
225  x_freevb:
226   adns__vbuf_free(&vb);
227   return st;
228 }
229
230
231 #define SINFO(n,s) { adns_s_##n, #n, s }
232
233 static const struct sinfo {
234   adns_status st;
235   const char *abbrev;
236   const char *string;
237 } sinfos[]= {
238   SINFO( ok,                  "OK"                                           ),
239                                                                               
240   SINFO( nomemory,            "Out of memory"                                ),
241   SINFO( unknownrrtype,       "Query not implemented in DNS library"         ),
242   SINFO( systemfail,          "General resolver or system failure"           ),
243                                                                               
244   SINFO( timeout,             "DNS query timed out"                          ),
245   SINFO( allservfail,         "All nameservers failed"                       ),
246   SINFO( norecurse,           "Recursion denied by nameserver"               ),
247   SINFO( invalidresponse,     "Nameserver sent bad response"                 ),
248   SINFO( unknownformat,       "Nameserver used unknown format"               ),
249                                                                               
250   SINFO( rcodeservfail,       "Nameserver reports failure"                   ),
251   SINFO( rcodeformaterror,    "Query not understood by nameserver"           ),
252   SINFO( rcodenotimplemented, "Query not implemented by nameserver"          ),
253   SINFO( rcoderefused,        "Query refused by nameserver"                  ),
254   SINFO( rcodeunknown,        "Nameserver sent unknown response code"        ),
255                                                                               
256   SINFO( inconsistent,        "Inconsistent resource records in DNS"         ),
257   SINFO( prohibitedcname,     "DNS alias found where canonical name wanted"  ),
258   SINFO( answerdomaininvalid, "Found syntactically invalid domain name"      ),
259   SINFO( answerdomaintoolong, "Found overly-long domain name"                ),
260   SINFO( invaliddata,         "Found invalid DNS data"                       ),
261                                                                               
262   SINFO( querydomainwrong,    "Domain invalid for particular DNS query type" ),
263   SINFO( querydomaininvalid,  "Domain name is syntactically invalid"         ),
264   SINFO( querydomaintoolong,  "Domain name or component is too long"         ),
265                                                                               
266   SINFO( nxdomain,            "No such domain"                               ),
267   SINFO( nodata,              "No such data"                                 )
268 };
269
270 static int si_compar(const void *key, const void *elem) {
271   const adns_status *st= key;
272   const struct sinfo *si= elem;
273
274   return *st < si->st ? -1 : *st > si->st ? 1 : 0;
275 }
276
277 static const struct sinfo *findsinfo(adns_status st) {
278   return bsearch(&st,sinfos, sizeof(sinfos)/sizeof(*sinfos),
279                  sizeof(*sinfos), si_compar);
280 }
281
282 const char *adns_strerror(adns_status st) {
283   const struct sinfo *si;
284
285   si= findsinfo(st);
286   return si->string;
287 }
288
289 const char *adns_errabbrev(adns_status st) {
290   const struct sinfo *si;
291
292   si= findsinfo(st);
293   return si->abbrev;
294 }
295
296
297 #define STINFO(max) { adns_s_max_##max, #max }
298
299 static const struct stinfo {
300   adns_status stmax;
301   const char *abbrev;
302 } stinfos[]= {
303   { adns_s_ok, "ok" },
304   STINFO(  localfail   ),
305   STINFO(  remotefail  ),
306   STINFO(  tempfail    ),
307   STINFO(  misconfig   ),
308   STINFO(  misquery    ),
309   STINFO(  permfail    )
310 };
311
312 static int sti_compar(const void *key, const void *elem) {
313   const adns_status *st= key;
314   const struct stinfo *sti= elem;
315
316   adns_status here, min, max;
317
318   here= *st;
319   min= (sti==stinfos) ? 0 : sti[-1].stmax+1;
320   max= sti->stmax;
321   
322   return here < min  ? -1 : here > max ? 1 : 0;
323 }
324
325 const char *adns_errtypeabbrev(adns_status st) {
326   const struct stinfo *sti;
327
328   sti= bsearch(&st,stinfos, sizeof(stinfos)/sizeof(*stinfos),
329                sizeof(*stinfos), sti_compar);
330   return sti->abbrev;
331 }
332
333
334 void adns__isort(void *array, int nobjs, int sz, void *tempbuf,
335                  int (*needswap)(void *context, const void *a, const void *b),
336                  void *context) {
337   byte *data= array;
338   int i, place;
339
340   for (i=0; i<nobjs; i++) {
341     for (place= i;
342          place>0 && needswap(context, data + (place-1)*sz, data + i*sz);
343          place--);
344     if (place != i) {
345       memcpy(tempbuf, data + i*sz, sz);
346       memmove(data + (place+1)*sz, data + place*sz, (i-place)*sz);
347       memcpy(data + place*sz, tempbuf, sz);
348     }
349   }
350 }
351
352 /* SIGPIPE protection. */
353
354 void adns__sigpipe_protect(adns_state ads) {
355   sigset_t toblock;
356   struct sigaction sa;
357   int r;
358
359   if (ads->iflags & adns_if_nosigpipe) return;
360
361   sigfillset(&toblock);
362   sigdelset(&toblock,SIGPIPE);
363
364   sa.sa_handler= SIG_IGN;
365   sigfillset(&sa.sa_mask);
366   sa.sa_flags= 0;
367   
368   r= sigprocmask(SIG_SETMASK,&toblock,&ads->stdsigmask); assert(!r);
369   r= sigaction(SIGPIPE,&sa,&ads->stdsigpipe); assert(!r);
370 }
371
372 void adns__sigpipe_unprotect(adns_state ads) {
373   int r;
374
375   if (ads->iflags & adns_if_nosigpipe) return;
376
377   r= sigaction(SIGPIPE,&ads->stdsigpipe,0); assert(!r);
378   r= sigprocmask(SIG_SETMASK,&ads->stdsigmask,0); assert(!r);
379 }