libspf2 1.2.11
spf_dns_resolv.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of either:
4 *
5 * a) The GNU Lesser General Public License as published by the Free
6 * Software Foundation; either version 2.1, or (at your option) any
7 * later version,
8 *
9 * OR
10 *
11 * b) The two-clause BSD license.
12 *
13 * These licenses can be found with the distribution in the file LICENSES
14 */
15
29#ifndef _WIN32
30
31#include "spf_sys_config.h"
32
33#ifdef HAVE_ERRNO_H
34#include <errno.h>
35#endif
36
37#ifdef STDC_HEADERS
38# include <stdio.h> /* stdin / stdout */
39# include <stdlib.h> /* malloc / free */
40#endif
41
42#ifdef HAVE_STRING_H
43# include <string.h> /* strstr / strdup */
44#else
45# ifdef HAVE_STRINGS_H
46# include <strings.h> /* strstr / strdup */
47# endif
48#endif
49
50#ifdef HAVE_RESOLV_H
51# include <resolv.h> /* dn_skipname */
52#endif
53#ifdef HAVE_NETDB_H
54# include <netdb.h>
55#endif
56
57#ifdef HAVE_PTHREAD_H
58# include <pthread.h>
59#endif
60
61#include "spf.h"
62#include "spf_dns.h"
63#include "spf_internal.h"
64#include "spf_dns_internal.h"
65#include "spf_dns_resolv.h"
66
72static const struct res_sym ns_sects[] = {
73 { ns_s_qd, "QUESTION", "Question" },
74 { ns_s_an, "ANSWER", "Answer" },
75 { ns_s_ns, "AUTHORITY", "Authority" },
76 { ns_s_ar, "ADDITIONAL", "Additional" },
77};
78
79static const int num_ns_sect = sizeof(ns_sects) / sizeof(*ns_sects);
80
81
82#if HAVE_DECL_RES_NINIT
83# define SPF_h_errno res_state->res_h_errno
84#else
85# define SPF_h_errno h_errno
86#endif
87
88#if HAVE_DECL_RES_NINIT
89static pthread_once_t res_state_control = PTHREAD_ONCE_INIT;
90static pthread_key_t res_state_key;
91
92static void
93SPF_dns_resolv_thread_term(void *arg)
94{
95#if HAVE_DECL_RES_NDESTROY
96 res_ndestroy( (struct __res_state *)arg );
97#else
98 res_nclose( (struct __res_state *)arg );
99#endif
100 free(arg);
101}
102
103static void
104SPF_dns_resolv_init_key(void)
105{
106 pthread_key_create(&res_state_key, SPF_dns_resolv_thread_term);
107}
108#endif
109
111static void
112SPF_dns_resolv_debug(SPF_dns_server_t *spf_dns_server, ns_rr rr,
113 const u_char *responsebuf, size_t responselen,
114 const u_char *rdata, size_t rdlen)
115{
116 char ip4_buf[ INET_ADDRSTRLEN ];
117 char ip6_buf[ INET6_ADDRSTRLEN ];
118 char name_buf[ NS_MAXDNAME ];
119 int prio;
120 int err;
121
122 switch (ns_rr_type(rr)) {
123 case ns_t_a:
124 if (rdlen != 4)
125 SPF_debugf("A: wrong rdlen %lu", (unsigned long)rdlen);
126 else
127 SPF_debugf("A: %s",
128 inet_ntop(AF_INET, rdata,
129 ip4_buf, sizeof(ip4_buf)));
130 break;
131
132 case ns_t_aaaa:
133 if (rdlen != 16)
134 SPF_debugf("AAAA: wrong rdlen %lu", (unsigned long)rdlen);
135 else
136 SPF_debugf("AAAA: %s",
137 inet_ntop(AF_INET6, rdata,
138 ip6_buf, sizeof(ip6_buf)));
139 break;
140
141 case ns_t_ns:
142 err = ns_name_uncompress(responsebuf,
143 responsebuf + responselen,
144 rdata,
145 name_buf, sizeof(name_buf));
146 if (err < 0) /* 0 or -1 */
147 SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
148 err, strerror(errno), errno);
149 else
150 SPF_debugf("NS: %s", name_buf);
151 break;
152
153 case ns_t_cname:
154 err = ns_name_uncompress(responsebuf,
155 responsebuf + responselen,
156 rdata,
157 name_buf, sizeof(name_buf));
158 if ( err < 0 ) /* 0 or -1 */
159 SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
160 err, strerror(errno), errno );
161 else
162 SPF_debugf("CNAME: %s", name_buf);
163 break;
164
165 case ns_t_mx:
166 if (rdlen < NS_INT16SZ) {
167 SPF_debugf("MX: rdlen too short: %lu", (unsigned long)rdlen);
168 break;
169 }
170 prio = ns_get16(rdata);
171 err = ns_name_uncompress(responsebuf,
172 responsebuf + responselen,
173 rdata + NS_INT16SZ,
174 name_buf, sizeof(name_buf));
175 if (err < 0) /* 0 or -1 */
176 SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
177 err, strerror(errno), errno);
178 else
179 SPF_debugf("MX: %d %s", prio, name_buf);
180 break;
181
182 case ns_t_spf:
183 case ns_t_txt:
184 if (rdlen < 1) {
185 SPF_debugf(ns_rr_type(rr) == ns_t_txt ? "TXT" : "SPF" ": rdlen too short: %lu", (unsigned long)rdlen);
186 break;
187 }
188 /* XXX I think this is wrong/unsafe. Shevek. */
189 /* XXX doesn't parse the different TXT "sections" */
190 SPF_debugf(ns_rr_type(rr) == ns_t_txt ? "TXT" : "SPF" ": (%lu) \"%.*s\"",
191 (unsigned long)rdlen, (int)rdlen - 1, rdata + 1);
192 break;
193
194 case ns_t_ptr:
195 err = ns_name_uncompress(responsebuf,
196 responsebuf + responselen,
197 rdata,
198 name_buf, sizeof(name_buf));
199 if (err < 0) /* 0 or -1 */
200 SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
201 err, strerror(errno), errno);
202 else
203 SPF_debugf("PTR: %s", name_buf);
204 break;
205
206 default:
207 SPF_debugf("not parsed: type: %d", ns_rr_type(rr));
208 break;
209 }
210
211}
212
218static SPF_dns_rr_t *
219SPF_dns_resolv_lookup(SPF_dns_server_t *spf_dns_server,
220 const char *domain, ns_type rr_type, int should_cache)
221{
222 SPF_dns_rr_t *spfrr;
223
224 int err;
225 int i;
226 int nrec;
227 int cnt;
228
229 u_char *responsebuf;
230 size_t responselen;
231
232 ns_msg ns_handle;
233 ns_rr rr;
234
235 int ns_sect;
236 // int num_ns_sect = sizeof( ns_sects ) / sizeof( *ns_sects );
237
238 char name_buf[ NS_MAXDNAME ];
239
240 size_t rdlen;
241 const u_char *rdata;
242
243#if HAVE_DECL_RES_NINIT
244 void *res_spec;
245 struct __res_state *res_state;
246#endif
247
248 SPF_ASSERT_NOTNULL(spf_dns_server);
249
250#if HAVE_DECL_RES_NINIT
252 res_spec = pthread_getspecific(res_state_key);
253 if (res_spec == NULL) {
254 res_state = (struct __res_state *)
255 malloc(sizeof(struct __res_state));
256 /* XXX The interface doesn't allow to communicate back failure
257 * to allocate memory, but SPF_errorf aborts anyway. */
258 if (! res_state)
259 SPF_errorf("Failed to allocate %lu bytes for res_state",
260 (unsigned long)sizeof(struct __res_state));
261 memset(res_state, 0, sizeof(struct __res_state));
262 if (res_ninit(res_state) != 0)
263 SPF_error("Failed to call res_ninit()");
264 pthread_setspecific(res_state_key, (void *)res_state);
265 }
266 else {
267 res_state = (struct __res_state *)res_spec;
268 }
269#endif
270
271 responselen = 2048;
272 responsebuf = (u_char *)malloc(responselen);
273 if (! responsebuf)
274 return NULL; /* NULL always means OOM from DNS lookup. */
275 memset(responsebuf, 0, responselen);
276
277 /*
278 * Retry the lookup until our response buffer is big enough.
279 *
280 * This loop repeats until either we fail a lookup or we succeed.
281 * The size of the response buffer is monotonic increasing, so eventually we
282 * must either succeed, or we try to malloc more RAM than we can.
283 *
284 * The Linux man pages do not describe res_nquery adequately. Solaris says:
285 *
286 * The res_nquery() and res_query() routines return a length that may be bigger
287 * than anslen. In that case, retry the query with a larger buf. The answer to the
288 * second query may be larger still], so it is recommended that you supply a buf
289 * larger than the answer returned by the previous query. answer must be large
290 * enough to receive a maximum UDP response from the server or parts of the answer
291 * will be silently discarded. The default maximum UDP response size is 512 bytes.
292 */
293 for (;;) {
294 int dns_len;
295
296#if HAVE_DECL_RES_NINIT
297 /* Resolve the name. */
298 dns_len = res_nquery(res_state, domain, ns_c_in, rr_type,
299 responsebuf, responselen);
300#else
301 dns_len = res_query(domain, ns_c_in, rr_type,
302 responsebuf, responselen);
303#endif
304
305 if (dns_len < 0) {
306 /* We failed to perform a lookup. */
307 /* This block returns unconditionally. */
308 free(responsebuf);
309 if (spf_dns_server->debug)
310 SPF_debugf("query failed: err = %d %s (%d): %s",
311 dns_len, hstrerror(SPF_h_errno), SPF_h_errno,
312 domain);
313 if ((SPF_h_errno == HOST_NOT_FOUND) &&
314 (spf_dns_server->layer_below != NULL)) {
315 return SPF_dns_lookup(spf_dns_server->layer_below,
316 domain, rr_type, should_cache);
317 }
318 return SPF_dns_rr_new_init(spf_dns_server,
319 domain, rr_type, 0, SPF_h_errno);
320 }
321 else if (dns_len > responselen) {
322 void *tmp;
323 /* We managed a lookup but our buffer was too small. */
324 responselen = dns_len + (dns_len >> 1);
325#if 0
326 /* Sanity-trap - we should never hit this. */
327 if (responselen > 1048576) { /* One megabyte. */
328 free(responsebuf);
329 return SPF_dns_rr_new_init(spf_dns_server,
330 domain, rr_type, 0, SPF_h_errno);
331 }
332#endif
333 tmp = realloc(responsebuf, responselen);
334 if (!tmp) {
335 free(responsebuf);
336 return NULL;
337 }
338 responsebuf = tmp;
339 }
340 else {
341 /* We managed a lookup, and our buffer was large enough. */
342 responselen = dns_len;
343 break;
344 }
345 }
346
347
348
349 /*
350 * initialize stuff
351 */
352 spfrr = SPF_dns_rr_new_init(spf_dns_server,
353 domain, rr_type, 0, NETDB_SUCCESS);
354 if (!spfrr) {
355 free(responsebuf);
356 return NULL;
357 }
358
359 err = ns_initparse(responsebuf, responselen, &ns_handle);
360
361 if (err < 0) { /* 0 or -1 */
362 if (spf_dns_server->debug)
363 SPF_debugf("ns_initparse failed: err = %d %s (%d)",
364 err, strerror(errno), errno);
365 free(responsebuf);
366 /* XXX Do we really want to return success with no data
367 * on parse failure? */
368 spfrr->herrno = NO_RECOVERY;
369 return spfrr;
370 }
371
372
373 if (spf_dns_server->debug > 1) {
374 SPF_debugf("msg id: %d", ns_msg_id(ns_handle));
375 SPF_debugf("ns_f_qr quest/resp: %d", ns_msg_getflag(ns_handle, ns_f_qr));
376 SPF_debugf("ns_f_opcode: %d", ns_msg_getflag(ns_handle, ns_f_opcode));
377 SPF_debugf("ns_f_aa auth ans: %d", ns_msg_getflag(ns_handle, ns_f_aa));
378 SPF_debugf("ns_f_tc truncated: %d", ns_msg_getflag(ns_handle, ns_f_tc));
379 SPF_debugf("ns_f_rd rec desire: %d", ns_msg_getflag(ns_handle, ns_f_rd));
380 SPF_debugf("ns_f_ra rec avail: %d", ns_msg_getflag(ns_handle, ns_f_ra));
381 SPF_debugf("ns_f_rcode: %d", ns_msg_getflag(ns_handle, ns_f_rcode));
382 }
383
384
385 /* FIXME the error handling from here on is suspect at best */
386 for (ns_sect = 0; ns_sect < num_ns_sect; ns_sect++) {
387 /* We pass this point if:
388 * - We are the 'answer' section.
389 * - We are debugging.
390 * Otherwise, we continue to the next section.
391 */
392 if (ns_sects[ns_sect].number != ns_s_an && spf_dns_server->debug <= 1)
393 continue;
394
395 nrec = ns_msg_count(ns_handle, ns_sects[ns_sect].number);
396
397 if (spf_dns_server->debug > 1)
398 SPF_debugf("%s: %d", ns_sects[ns_sect].name, nrec);
399
400 spfrr->num_rr = 0;
401 cnt = 0;
402 for (i = 0; i < nrec; i++) {
403 err = ns_parserr(&ns_handle, ns_sects[ns_sect].number, i, &rr);
404 if (err < 0) { /* 0 or -1 */
405 if (spf_dns_server->debug > 1)
406 SPF_debugf("ns_parserr failed: err = %d %s (%d)",
407 err, strerror(errno), errno);
408 free(responsebuf);
409 /* XXX Do we really want to return partial data
410 * on parse failures? */
411 spfrr->herrno = NO_RECOVERY;
412 return spfrr;
413 }
414
415 rdlen = ns_rr_rdlen(rr);
416 if (spf_dns_server->debug > 1)
417 SPF_debugf("name: %s type: %d class: %d ttl: %d rdlen: %lu",
418 ns_rr_name(rr), ns_rr_type(rr), ns_rr_class(rr),
419 ns_rr_ttl(rr), (unsigned long)rdlen);
420
421 if (rdlen <= 0)
422 continue;
423
424 rdata = ns_rr_rdata(rr);
425
426 if (spf_dns_server->debug > 1)
427 SPF_dns_resolv_debug(spf_dns_server, rr,
428 responsebuf, responselen, rdata, rdlen);
429
430 /* And now, if we aren't the answer section, we skip the section. */
431 if (ns_sects[ns_sect].number != ns_s_an)
432 continue;
433
434 /* Now, we are in the answer section. */
435 if (ns_rr_type(rr) != spfrr->rr_type && ns_rr_type(rr) != ns_t_cname) {
436 SPF_debugf("unexpected rr type: %d expected: %d",
437 ns_rr_type(rr), rr_type);
438 continue;
439 }
440
441 switch (ns_rr_type(rr)) {
442 case ns_t_a:
443 if (rdlen != 4) {
444 /* XXX Error handling. */
445 free(responsebuf);
446 return spfrr;
447 }
448 if (SPF_dns_rr_buf_realloc(spfrr, cnt,
449 sizeof(spfrr->rr[cnt]->a)) != SPF_E_SUCCESS) {
450 free(responsebuf);
451 /* XXX Do we really want to return partial data
452 * on out of memory conditions? */
453 return spfrr;
454 }
455 memcpy(&spfrr->rr[cnt]->a, rdata, sizeof(spfrr->rr[cnt]->a));
456 cnt++;
457 break;
458
459 case ns_t_aaaa:
460 if (rdlen != 16) {
461 /* XXX Error handling. */
462 free(responsebuf);
463 return spfrr;
464 }
465 if (SPF_dns_rr_buf_realloc(spfrr, cnt,
466 sizeof(spfrr->rr[cnt]->aaaa)) != SPF_E_SUCCESS) {
467 free(responsebuf);
468 /* XXX Do we really want to return partial data
469 * on out of memory conditions? */
470 return spfrr;
471 }
472 memcpy(&spfrr->rr[cnt]->aaaa, rdata, sizeof(spfrr->rr[cnt]->aaaa));
473 cnt++;
474 break;
475
476 case ns_t_ns:
477 break;
478
479 case ns_t_cname:
480 /* FIXME: are CNAMEs always sent with the real RR? */
481 break;
482
483 case ns_t_mx:
484 if (rdlen < NS_INT16SZ) {
485 /* XXX Error handling. */
486 free(responsebuf);
487 return spfrr;
488 }
489 err = ns_name_uncompress(responsebuf,
490 responsebuf + responselen,
491 rdata + NS_INT16SZ,
492 name_buf, sizeof(name_buf));
493 if (err < 0) { /* 0 or -1 */
494 if (spf_dns_server->debug > 1)
495 SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
496 err, strerror(errno), errno);
497 free(responsebuf);
498 /* XXX Do we really want to return partial data
499 * on parse error? */
500 return spfrr;
501 }
502
503 if (SPF_dns_rr_buf_realloc(spfrr, cnt,
504 strlen(name_buf) + 1 ) != SPF_E_SUCCESS) {
505 free(responsebuf);
506 /* XXX Do we really want to return partial data
507 * on out of memory conditions? */
508 return spfrr;
509 }
510 strcpy(spfrr->rr[cnt]->mx, name_buf);
511 cnt++;
512 break;
513
514 case ns_t_spf:
515 case ns_t_txt:
516 if (rdlen > 1) {
517 u_char *src, *dst;
518 size_t len;
519
520 /* Just rdlen is enough because there is at least one
521 * length byte, which we do not copy. */
522 if (SPF_dns_rr_buf_realloc(spfrr, cnt, rdlen) != SPF_E_SUCCESS) {
523 free(responsebuf);
524 /* XXX Do we really want to return partial data
525 * on out of memory conditions? */
526 return spfrr;
527 }
528
529 dst = (u_char *)spfrr->rr[cnt]->txt;
530 src = (u_char *)rdata;
531 len = 0;
532 while (rdlen > 0) {
533 /* Consume one byte into a length. */
534 len = *src;
535 src++;
536 rdlen--;
537
538 /* Avoid buffer overrun if len is junk. */
539 /* XXX don't we rather want to flag this as error? */
540 if (len > rdlen)
541 len = rdlen;
542 memcpy(dst, src, len);
543
544 /* Consume the data. */
545 src += len;
546 dst += len;
547 rdlen -= len;
548 }
549 *dst = '\0';
550 }
551 else {
552 if (SPF_dns_rr_buf_realloc(spfrr, cnt, 1) != SPF_E_SUCCESS) {
553 free(responsebuf);
554 /* XXX Do we really want to return partial data
555 * on out of memory conditions? */
556 return spfrr;
557 }
558 spfrr->rr[cnt]->txt[0] = '\0';
559 }
560
561 cnt++;
562 break;
563
564 case ns_t_ptr:
565 err = ns_name_uncompress(responsebuf,
566 responsebuf + responselen,
567 rdata,
568 name_buf, sizeof(name_buf));
569 if (err < 0) { /* 0 or -1 */
570 if (spf_dns_server->debug > 1)
571 SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
572 err, strerror(errno), errno);
573 free(responsebuf);
574 /* XXX Do we really want to return partial data
575 * on parse error? */
576 return spfrr;
577 }
578
579 if (SPF_dns_rr_buf_realloc(spfrr, cnt,
580 strlen(name_buf) + 1) != SPF_E_SUCCESS) {
581 free(responsebuf);
582 /* XXX Do we really want to return partial data
583 * on out of memory conditions? */
584 return spfrr;
585 }
586 strcpy(spfrr->rr[cnt]->ptr, name_buf);
587 cnt++;
588 break;
589
590 default:
591 break;
592 }
593 }
594
595 spfrr->num_rr = cnt;
596 }
597
598 if (spfrr->num_rr == 0)
599 spfrr->herrno = NO_DATA;
600
601 free(responsebuf);
602 return spfrr;
603}
604
605
606static void
607SPF_dns_resolv_free(SPF_dns_server_t *spf_dns_server)
608{
609 SPF_ASSERT_NOTNULL(spf_dns_server);
610
611#if ! HAVE_DECL_RES_NINIT
612 res_close();
613#endif
614
615 free(spf_dns_server);
616}
617
618SPF_dns_server_t *
619SPF_dns_resolv_new(SPF_dns_server_t *layer_below,
620 const char *name, int debug)
621{
622 SPF_dns_server_t *spf_dns_server;
623
624#if HAVE_DECL_RES_NINIT
625 pthread_once(&res_state_control, SPF_dns_resolv_init_key);
626#else
627 if (res_init() != 0) {
628 SPF_warning("Failed to call res_init()");
629 return NULL;
630 }
631#endif
632
633 spf_dns_server = malloc(sizeof(SPF_dns_server_t));
634 if (spf_dns_server == NULL)
635 return NULL;
636 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
637
638 if (name == NULL)
639 name = "resolv";
640
641 spf_dns_server->destroy = SPF_dns_resolv_free;
642 spf_dns_server->lookup = SPF_dns_resolv_lookup;
643 spf_dns_server->get_spf = NULL;
644 spf_dns_server->get_exp = NULL;
645 spf_dns_server->add_cache = NULL;
646 spf_dns_server->layer_below = layer_below;
647 spf_dns_server->name = name;
648 spf_dns_server->debug = debug;
649
650 return spf_dns_server;
651}
652
653#endif /* _WIN32 */
#define ns_t_spf
Definition spf_dns.h:89
#define ns_t_mx
Definition spf_dns.h:79
#define ns_t_aaaa
Definition spf_dns.h:81
#define ns_t_a
Definition spf_dns.h:75
#define ns_t_ptr
Definition spf_dns.h:78
#define NO_DATA
Definition spf_dns.h:106
#define NO_RECOVERY
Definition spf_dns.h:105
#define HOST_NOT_FOUND
Definition spf_dns.h:103
#define NETDB_SUCCESS
Definition spf_dns.h:102
#define ns_t_txt
Definition spf_dns.h:80
SPF_dns_rr_t * SPF_dns_lookup(SPF_dns_server_t *spf_dns_server, const char *domain, ns_type rr_type, int should_cache)
Definition spf_dns.c:133
#define ns_t_ns
Definition spf_dns.h:76
#define ns_t_cname
Definition spf_dns.h:77
SPF_dns_rr_t * SPF_dns_rr_new_init(SPF_dns_server_t *spf_dns_server, const char *domain, ns_type rr_type, int ttl, SPF_dns_stat_t herrno)
Definition spf_dns_rr.c:61
SPF_errcode_t SPF_dns_rr_buf_realloc(SPF_dns_rr_t *spfrr, int idx, size_t len)
Definition spf_dns_rr.c:134
#define NULL
#define SPF_errorf
Definition spf_log.h:77
#define SPF_ASSERT_NOTNULL(x)
Definition spf_log.h:118
#define SPF_warning(errmsg)
Definition spf_log.h:45
#define SPF_error(errmsg)
Definition spf_log.h:40
#define SPF_debugf
Definition spf_log.h:80
@ SPF_E_SUCCESS
#define ns_name_uncompress
#define ns_parserr
#define ns_rr_ttl(rr)
ns_type
#define ns_initparse
ns_sect
@ ns_s_qd
@ ns_s_ar
@ ns_s_ns
@ ns_s_an
#define ns_rr_rdata(rr)
#define NS_MAXDNAME
#define NS_INT16SZ
#define ns_rr_name(rr)
#define ns_rr_rdlen(rr)
#define ns_rr_class(rr)
#define ns_msg_id(handle)
#define ns_rr_type(rr)
#define ns_msg_getflag
@ ns_f_tc
@ ns_f_qr
@ ns_f_rd
@ ns_f_rcode
@ ns_f_ra
@ ns_f_opcode
@ ns_f_aa
#define ns_get16
@ ns_c_in
#define ns_msg_count(handle, section)
#define SPF_h_errno
SPF_dns_server_t * SPF_dns_resolv_new(SPF_dns_server_t *layer_below, const char *name, int debug)
#define debug
struct in_addr a
Definition spf_dns_rr.h:33
struct in6_addr aaaa
Definition spf_dns_rr.h:37
SPF_dns_rr_data_t ** rr
Definition spf_dns_rr.h:60
ns_type rr_type
Definition spf_dns_rr.h:56
SPF_dns_stat_t herrno
Definition spf_dns_rr.h:66