chiark / gitweb /
6d4a1b841ce45d240af3e223ed10d04ecc203f1f
[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 Copyright (C) 1997-1999 Ian Jackson
8  *  
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2, or (at your option)
12  *  any later version.
13  *  
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *  
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software Foundation,
21  *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
22  */
23
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30
31 #include "internal.h"
32
33 /* Core diagnostic functions */
34
35 void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent,
36                  int serv, adns_query qu, const char *fmt, va_list al) {
37   const char *bef, *aft;
38   vbuf vb;
39   
40   if (!ads->diagfile ||
41       (!(ads->iflags & adns_if_debug) && (!prevent || (ads->iflags & prevent))))
42     return;
43
44   fprintf(ads->diagfile,"adns%s: ",pfx);
45
46   vfprintf(ads->diagfile,fmt,al);
47
48   bef= " (";
49   aft= "\n";
50
51   if (qu && qu->query_dgram) {
52     adns__vbuf_init(&vb);
53     fprintf(ads->diagfile,"%sQNAME=%s, QTYPE=%s",
54             bef,
55             adns__diag_domain(qu->ads,-1,0, &vb,
56                               qu->query_dgram,qu->query_dglen,DNS_HDRSIZE),
57             qu->typei ? qu->typei->rrtname : "<unknown>");
58     if (qu->typei && qu->typei->fmtname)
59       fprintf(ads->diagfile,"(%s)",qu->typei->fmtname);
60     bef=", "; aft=")\n";
61   }
62   
63   if (serv>=0) {
64     fprintf(ads->diagfile,"%sNS=%s",bef,inet_ntoa(ads->servers[serv].addr));
65     bef=", "; aft=")\n";
66   }
67
68   fputs(aft,ads->diagfile);
69 }
70
71 void adns__debug(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
72   va_list al;
73
74   va_start(al,fmt);
75   adns__vdiag(ads," debug",0,serv,qu,fmt,al);
76   va_end(al);
77 }
78
79 void adns__warn(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
80   va_list al;
81
82   va_start(al,fmt);
83   adns__vdiag(ads," warning",adns_if_noerrprint|adns_if_noserverwarn,serv,qu,fmt,al);
84   va_end(al);
85 }
86
87 void adns__diag(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
88   va_list al;
89
90   va_start(al,fmt);
91   adns__vdiag(ads,"",adns_if_noerrprint,serv,qu,fmt,al);
92   va_end(al);
93 }
94
95 /* vbuf functions */
96
97 void adns__vbuf_init(vbuf *vb) {
98   vb->used= vb->avail= 0; vb->buf= 0;
99 }
100
101 int adns__vbuf_ensure(vbuf *vb, int want) {
102   void *nb;
103   
104   if (vb->avail >= want) return 1;
105   nb= realloc(vb->buf,want); if (!nb) return 0;
106   vb->buf= nb;
107   vb->avail= want;
108   return 1;
109 }
110   
111 void adns__vbuf_appendq(vbuf *vb, const byte *data, int len) {
112   memcpy(vb->buf+vb->used,data,len);
113   vb->used+= len;
114 }
115
116 int adns__vbuf_append(vbuf *vb, const byte *data, int len) {
117   int newlen;
118   void *nb;
119
120   newlen= vb->used+len;
121   if (vb->avail < newlen) {
122     if (newlen<20) newlen= 20;
123     newlen <<= 1;
124     nb= realloc(vb->buf,newlen);
125     if (!nb) { newlen= vb->used+len; nb= realloc(vb->buf,newlen); }
126     if (!nb) return 0;
127     vb->buf= nb;
128     vb->avail= newlen;
129   }
130   adns__vbuf_appendq(vb,data,len);
131   return 1;
132 }
133
134 int adns__vbuf_appendstr(vbuf *vb, const char *data) {
135   int l;
136   l= strlen(data);
137   return adns__vbuf_append(vb,data,l);
138 }
139
140 void adns__vbuf_free(vbuf *vb) {
141   free(vb->buf);
142   adns__vbuf_init(vb);
143 }
144
145 /* Additional diagnostic functions */
146
147 const char *adns__diag_domain(adns_state ads, int serv, adns_query qu,
148                               vbuf *vb, const byte *dgram, int dglen, int cbyte) {
149   adns_status st;
150
151   st= adns__parse_domain(ads,serv,qu,vb, pdf_quoteok, dgram,dglen,&cbyte,dglen);
152   if (st == adns_s_nomemory) {
153     return "<cannot report domain... out of memory>";
154   }
155   if (st) {
156     vb->used= 0;
157     if (!(adns__vbuf_appendstr(vb,"<bad format... ") &&
158           adns__vbuf_appendstr(vb,adns_strerror(st)) &&
159           adns__vbuf_appendstr(vb,">") &&
160           adns__vbuf_append(vb,"",1))) {
161       return "<cannot report bad format... out of memory>";
162     }
163   }
164   if (!vb->used) {
165     adns__vbuf_appendstr(vb,"<truncated ...>");
166     adns__vbuf_append(vb,"",1);
167   }
168   return vb->buf;
169 }
170
171 adns_status adns_rr_info(adns_rrtype type,
172                          const char **rrtname_r, const char **fmtname_r,
173                          int *len_r,
174                          const void *datap, char **data_r) {
175   const typeinfo *typei;
176   vbuf vb;
177   adns_status st;
178
179   typei= adns__findtype(type);
180   if (!typei) return adns_s_unknownrrtype;
181
182   if (rrtname_r) *rrtname_r= typei->rrtname;
183   if (fmtname_r) *fmtname_r= typei->fmtname;
184   if (len_r) *len_r= typei->rrsz;
185
186   if (!datap) return adns_s_ok;
187   
188   adns__vbuf_init(&vb);
189   st= typei->convstring(&vb,datap);
190   if (st) goto x_freevb;
191   if (!adns__vbuf_append(&vb,"",1)) { st= adns_s_nomemory; goto x_freevb; }
192   assert(strlen(vb.buf) == vb.used-1);
193   *data_r= realloc(vb.buf,vb.used);
194   if (!*data_r) *data_r= vb.buf;
195   return adns_s_ok;
196
197  x_freevb:
198   adns__vbuf_free(&vb);
199   return st;
200 }
201
202 #define SINFO(n,s) { adns_s_##n, s }
203
204 static const struct sinfo {
205   adns_status st;
206   const char *string;
207 } sinfos[]= {
208   SINFO(  ok,                  "OK"                                            ),
209
210   SINFO(  nomemory,            "Out of memory"                                 ),
211   SINFO(  unknownrrtype,       "Query not implemented in DNS library"          ),
212
213   SINFO(  timeout,             "DNS query timed out"                           ),
214   SINFO(  allservfail,         "All nameservers failed"                        ),
215   SINFO(  norecurse,           "Recursion denied by nameserver"                ),
216   SINFO(  invalidresponse,     "Nameserver sent bad response"                  ),
217   SINFO(  unknownformat,       "Nameserver used unknown format"                ),
218
219   SINFO(  rcodeservfail,       "Nameserver reports failure"                    ),
220   SINFO(  rcodeformaterror,    "Query not understood by nameserver"            ),
221   SINFO(  rcodenotimplemented, "Query not implemented by nameserver"           ),
222   SINFO(  rcoderefused,        "Query refused by nameserver"                   ),
223   SINFO(  rcodeunknown,        "Nameserver sent unknown response code"         ),
224   
225   SINFO(  inconsistent,        "Inconsistent resource records in DNS"          ),
226   SINFO(  prohibitedcname,     "DNS data refers to an alias"                   ),
227   SINFO(  answerdomaininvalid, "Found syntactically invalid domain name"       ),
228   SINFO(  answerdomaintoolong, "Found overly-long domain name"                 ),
229   SINFO(  invaliddata,         "Found invalid DNS data"                        ),
230
231   SINFO(  querydomainwrong,    "Domain invalid for particular DNS query type"  ),
232   SINFO(  querydomaininvalid,  "Domain name is syntactically invalid"          ),
233   SINFO(  querydomaintoolong,  "Domain name is too long"                       ),
234
235   SINFO(  nxdomain,            "No such domain"                                ),
236   SINFO(  nodata,              "No such data"                                  ),
237 };
238
239 static int si_compar(const void *key, const void *elem) {
240   const adns_status *st= key;
241   const struct sinfo *si= elem;
242
243   return *st < si->st ? -1 : *st > si->st ? 1 : 0;
244 }
245
246 const char *adns_strerror(adns_status st) {
247   static char buf[100];
248
249   const struct sinfo *si;
250
251   si= bsearch(&st,sinfos,sizeof(sinfos)/sizeof(*si),sizeof(*si),si_compar);
252   if (si) return si->string;
253   
254   snprintf(buf,sizeof(buf),"code %d",st);
255   return buf;
256 }
257
258 void adns__isort(void *array, int nobjs, int sz, void *tempbuf,
259                  int (*needswap)(void *context, const void *a, const void *b),
260                  void *context) {
261   byte *data= array;
262   int i, place;
263
264   for (i=0; i<nobjs; i++) {
265     for (place= i;
266          place>0 && needswap(context, data + (place-1)*sz, data + i*sz);
267          place--);
268     if (place != i) {
269       memcpy(tempbuf, data + i*sz, sz);
270       memmove(data + (place+1)*sz, data + place*sz, (i-place)*sz);
271       memcpy(data + place*sz, tempbuf, sz);
272     }
273   }
274 }
275
276 /* SIGPIPE protection. */
277
278 void adns__sigpipe_protect(adns_state ads) {
279   sigset_t toblock;
280   struct sigaction sa;
281   int r;
282
283   if (ads->iflags & adns_if_nosigpipe) return;
284
285   sigfillset(&toblock);
286   sigdelset(&toblock,SIGPIPE);
287
288   sa.sa_handler= SIG_IGN;
289   sigfillset(&sa.sa_mask);
290   sa.sa_flags= 0;
291   
292   r= sigprocmask(SIG_SETMASK,&toblock,&ads->stdsigmask); assert(!r);
293   r= sigaction(SIGPIPE,&sa,&ads->stdsigpipe); assert(!r);
294 }
295
296 void adns__sigpipe_unprotect(adns_state ads) {
297   int r;
298
299   if (ads->iflags & adns_if_nosigpipe) return;
300
301   r= sigaction(SIGPIPE,&ads->stdsigpipe,0); assert(!r);
302   r= sigprocmask(SIG_SETMASK,&ads->stdsigmask,0); assert(!r);
303 }