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