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