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