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