chiark / gitweb /
src/: Lay the groundwork for variably-sized record structures.
[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 int adns__getrrsz_default(const typeinfo *typei, adns_rrtype type)
200   { return typei->fixed_rrsz; }
201
202 adns_status adns_rr_info(adns_rrtype type,
203                          const char **rrtname_r, const char **fmtname_r,
204                          int *len_r,
205                          const void *datap, char **data_r) {
206   const typeinfo *typei;
207   vbuf vb;
208   adns_status st;
209
210   typei= adns__findtype(type);
211   if (!typei) return adns_s_unknownrrtype;
212
213   if (rrtname_r) *rrtname_r= typei->rrtname;
214   if (fmtname_r) *fmtname_r= typei->fmtname;
215   if (len_r) *len_r= typei->getrrsz(typei, type);
216
217   if (!datap) return adns_s_ok;
218   
219   adns__vbuf_init(&vb);
220   st= typei->convstring(&vb,datap);
221   if (st) goto x_freevb;
222   if (!adns__vbuf_append(&vb,"",1)) { st= adns_s_nomemory; goto x_freevb; }
223   assert(strlen(vb.buf) == vb.used-1);
224   *data_r= realloc(vb.buf,vb.used);
225   if (!*data_r) *data_r= vb.buf;
226   return adns_s_ok;
227
228  x_freevb:
229   adns__vbuf_free(&vb);
230   return st;
231 }
232
233
234 #define SINFO(n,s) { adns_s_##n, #n, s }
235
236 static const struct sinfo {
237   adns_status st;
238   const char *abbrev;
239   const char *string;
240 } sinfos[]= {
241   SINFO( ok,                  "OK"                                           ),
242                                                                               
243   SINFO( nomemory,            "Out of memory"                                ),
244   SINFO( unknownrrtype,       "Query not implemented in DNS library"         ),
245   SINFO( systemfail,          "General resolver or system failure"           ),
246                                                                               
247   SINFO( timeout,             "DNS query timed out"                          ),
248   SINFO( allservfail,         "All nameservers failed"                       ),
249   SINFO( norecurse,           "Recursion denied by nameserver"               ),
250   SINFO( invalidresponse,     "Nameserver sent bad response"                 ),
251   SINFO( unknownformat,       "Nameserver used unknown format"               ),
252                                                                               
253   SINFO( rcodeservfail,       "Nameserver reports failure"                   ),
254   SINFO( rcodeformaterror,    "Query not understood by nameserver"           ),
255   SINFO( rcodenotimplemented, "Query not implemented by nameserver"          ),
256   SINFO( rcoderefused,        "Query refused by nameserver"                  ),
257   SINFO( rcodeunknown,        "Nameserver sent unknown response code"        ),
258                                                                               
259   SINFO( inconsistent,        "Inconsistent resource records in DNS"         ),
260   SINFO( prohibitedcname,     "DNS alias found where canonical name wanted"  ),
261   SINFO( answerdomaininvalid, "Found syntactically invalid domain name"      ),
262   SINFO( answerdomaintoolong, "Found overly-long domain name"                ),
263   SINFO( invaliddata,         "Found invalid DNS data"                       ),
264                                                                               
265   SINFO( querydomainwrong,    "Domain invalid for particular DNS query type" ),
266   SINFO( querydomaininvalid,  "Domain name is syntactically invalid"         ),
267   SINFO( querydomaintoolong,  "Domain name or component is too long"         ),
268                                                                               
269   SINFO( nxdomain,            "No such domain"                               ),
270   SINFO( nodata,              "No such data"                                 )
271 };
272
273 static int si_compar(const void *key, const void *elem) {
274   const adns_status *st= key;
275   const struct sinfo *si= elem;
276
277   return *st < si->st ? -1 : *st > si->st ? 1 : 0;
278 }
279
280 static const struct sinfo *findsinfo(adns_status st) {
281   return bsearch(&st,sinfos, sizeof(sinfos)/sizeof(*sinfos),
282                  sizeof(*sinfos), si_compar);
283 }
284
285 const char *adns_strerror(adns_status st) {
286   const struct sinfo *si;
287
288   si= findsinfo(st);
289   return si->string;
290 }
291
292 const char *adns_errabbrev(adns_status st) {
293   const struct sinfo *si;
294
295   si= findsinfo(st);
296   return si->abbrev;
297 }
298
299
300 #define STINFO(max) { adns_s_max_##max, #max }
301
302 static const struct stinfo {
303   adns_status stmax;
304   const char *abbrev;
305 } stinfos[]= {
306   { adns_s_ok, "ok" },
307   STINFO(  localfail   ),
308   STINFO(  remotefail  ),
309   STINFO(  tempfail    ),
310   STINFO(  misconfig   ),
311   STINFO(  misquery    ),
312   STINFO(  permfail    )
313 };
314
315 static int sti_compar(const void *key, const void *elem) {
316   const adns_status *st= key;
317   const struct stinfo *sti= elem;
318
319   adns_status here, min, max;
320
321   here= *st;
322   min= (sti==stinfos) ? 0 : sti[-1].stmax+1;
323   max= sti->stmax;
324   
325   return here < min  ? -1 : here > max ? 1 : 0;
326 }
327
328 const char *adns_errtypeabbrev(adns_status st) {
329   const struct stinfo *sti;
330
331   sti= bsearch(&st,stinfos, sizeof(stinfos)/sizeof(*stinfos),
332                sizeof(*stinfos), sti_compar);
333   return sti->abbrev;
334 }
335
336
337 void adns__isort(void *array, int nobjs, int sz, void *tempbuf,
338                  int (*needswap)(void *context, const void *a, const void *b),
339                  void *context) {
340   byte *data= array;
341   int i, place;
342
343   for (i=0; i<nobjs; i++) {
344     for (place= i;
345          place>0 && needswap(context, data + (place-1)*sz, data + i*sz);
346          place--);
347     if (place != i) {
348       memcpy(tempbuf, data + i*sz, sz);
349       memmove(data + (place+1)*sz, data + place*sz, (i-place)*sz);
350       memcpy(data + place*sz, tempbuf, sz);
351     }
352   }
353 }
354
355 /* SIGPIPE protection. */
356
357 void adns__sigpipe_protect(adns_state ads) {
358   sigset_t toblock;
359   struct sigaction sa;
360   int r;
361
362   if (ads->iflags & adns_if_nosigpipe) return;
363
364   sigfillset(&toblock);
365   sigdelset(&toblock,SIGPIPE);
366
367   sa.sa_handler= SIG_IGN;
368   sigfillset(&sa.sa_mask);
369   sa.sa_flags= 0;
370   
371   r= sigprocmask(SIG_SETMASK,&toblock,&ads->stdsigmask); assert(!r);
372   r= sigaction(SIGPIPE,&sa,&ads->stdsigpipe); assert(!r);
373 }
374
375 void adns__sigpipe_unprotect(adns_state ads) {
376   int r;
377
378   if (ads->iflags & adns_if_nosigpipe) return;
379
380   r= sigaction(SIGPIPE,&ads->stdsigpipe,0); assert(!r);
381   r= sigprocmask(SIG_SETMASK,&ads->stdsigmask,0); assert(!r);
382 }