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