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