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