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 = 65536;
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 free(responsebuf);
323 return NULL;
324 }
325 else {
326 /* We managed a lookup, and our buffer was large enough. */
327 responselen = dns_len;
328 break;
329 }
330 }
331
332
333
334 /*
335 * initialize stuff
336 */
337 spfrr = SPF_dns_rr_new_init(spf_dns_server,
338 domain, rr_type, 0, NETDB_SUCCESS);
339 if (!spfrr) {
340 free(responsebuf);
341 return NULL;
342 }
343
344 err = ns_initparse(responsebuf, responselen, &ns_handle);
345
346 if (err < 0) { /* 0 or -1 */
347 if (spf_dns_server->debug)
348 SPF_debugf("ns_initparse failed: err = %d %s (%d)",
349 err, strerror(errno), errno);
350 free(responsebuf);
351 /* XXX Do we really want to return success with no data
352 * on parse failure? */
353 spfrr->herrno = NO_RECOVERY;
354 return spfrr;
355 }
356
357
358 if (spf_dns_server->debug > 1) {
359 SPF_debugf("msg id: %d", ns_msg_id(ns_handle));
360 SPF_debugf("ns_f_qr quest/resp: %d", ns_msg_getflag(ns_handle, ns_f_qr));
361 SPF_debugf("ns_f_opcode: %d", ns_msg_getflag(ns_handle, ns_f_opcode));
362 SPF_debugf("ns_f_aa auth ans: %d", ns_msg_getflag(ns_handle, ns_f_aa));
363 SPF_debugf("ns_f_tc truncated: %d", ns_msg_getflag(ns_handle, ns_f_tc));
364 SPF_debugf("ns_f_rd rec desire: %d", ns_msg_getflag(ns_handle, ns_f_rd));
365 SPF_debugf("ns_f_ra rec avail: %d", ns_msg_getflag(ns_handle, ns_f_ra));
366 SPF_debugf("ns_f_rcode: %d", ns_msg_getflag(ns_handle, ns_f_rcode));
367 }
368
369
370 /* FIXME the error handling from here on is suspect at best */
371 for (ns_sect = 0; ns_sect < num_ns_sect; ns_sect++) {
372 /* We pass this point if:
373 * - We are the 'answer' section.
374 * - We are debugging.
375 * Otherwise, we continue to the next section.
376 */
377 if (ns_sects[ns_sect].number != ns_s_an && spf_dns_server->debug <= 1)
378 continue;
379
380 nrec = ns_msg_count(ns_handle, ns_sects[ns_sect].number);
381
382 if (spf_dns_server->debug > 1)
383 SPF_debugf("%s: %d", ns_sects[ns_sect].name, nrec);
384
385 spfrr->num_rr = 0;
386 cnt = 0;
387 for (i = 0; i < nrec; i++) {
388 err = ns_parserr(&ns_handle, ns_sects[ns_sect].number, i, &rr);
389 if (err < 0) { /* 0 or -1 */
390 if (spf_dns_server->debug > 1)
391 SPF_debugf("ns_parserr failed: err = %d %s (%d)",
392 err, strerror(errno), errno);
393 free(responsebuf);
394 /* XXX Do we really want to return partial data
395 * on parse failures? */
396 spfrr->herrno = NO_RECOVERY;
397 return spfrr;
398 }
399
400 rdlen = ns_rr_rdlen(rr);
401 if (spf_dns_server->debug > 1)
402 SPF_debugf("name: %s type: %d class: %d ttl: %d rdlen: %lu",
403 ns_rr_name(rr), ns_rr_type(rr), ns_rr_class(rr),
404 ns_rr_ttl(rr), (unsigned long)rdlen);
405
406 if (rdlen <= 0)
407 continue;
408
409 rdata = ns_rr_rdata(rr);
410
411 if (spf_dns_server->debug > 1)
412 SPF_dns_resolv_debug(spf_dns_server, rr,
413 responsebuf, responselen, rdata, rdlen);
414
415 /* And now, if we aren't the answer section, we skip the section. */
416 if (ns_sects[ns_sect].number != ns_s_an)
417 continue;
418
419 /* Now, we are in the answer section. */
420 if (ns_rr_type(rr) != spfrr->rr_type && ns_rr_type(rr) != ns_t_cname) {
421 SPF_debugf("unexpected rr type: %d expected: %d",
422 ns_rr_type(rr), rr_type);
423 continue;
424 }
425
426 switch (ns_rr_type(rr)) {
427 case ns_t_a:
428 if (rdlen != 4) {
429 /* XXX Error handling. */
430 free(responsebuf);
431 return spfrr;
432 }
433 if (SPF_dns_rr_buf_realloc(spfrr, cnt,
434 sizeof(spfrr->rr[cnt]->a)) != SPF_E_SUCCESS) {
435 free(responsebuf);
436 /* XXX Do we really want to return partial data
437 * on out of memory conditions? */
438 return spfrr;
439 }
440 memcpy(&spfrr->rr[cnt]->a, rdata, sizeof(spfrr->rr[cnt]->a));
441 cnt++;
442 break;
443
444 case ns_t_aaaa:
445 if (rdlen != 16) {
446 /* XXX Error handling. */
447 free(responsebuf);
448 return spfrr;
449 }
450 if (SPF_dns_rr_buf_realloc(spfrr, cnt,
451 sizeof(spfrr->rr[cnt]->aaaa)) != SPF_E_SUCCESS) {
452 free(responsebuf);
453 /* XXX Do we really want to return partial data
454 * on out of memory conditions? */
455 return spfrr;
456 }
457 memcpy(&spfrr->rr[cnt]->aaaa, rdata, sizeof(spfrr->rr[cnt]->aaaa));
458 cnt++;
459 break;
460
461 case ns_t_ns:
462 break;
463
464 case ns_t_cname:
465 /* FIXME: are CNAMEs always sent with the real RR? */
466 break;
467
468 case ns_t_mx:
469 if (rdlen < NS_INT16SZ) {
470 /* XXX Error handling. */
471 free(responsebuf);
472 return spfrr;
473 }
474 err = ns_name_uncompress(responsebuf,
475 responsebuf + responselen,
476 rdata + NS_INT16SZ,
477 name_buf, sizeof(name_buf));
478 if (err < 0) { /* 0 or -1 */
479 if (spf_dns_server->debug > 1)
480 SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
481 err, strerror(errno), errno);
482 free(responsebuf);
483 /* XXX Do we really want to return partial data
484 * on parse error? */
485 return spfrr;
486 }
487
488 if (SPF_dns_rr_buf_realloc(spfrr, cnt,
489 strlen(name_buf) + 1 ) != SPF_E_SUCCESS) {
490 free(responsebuf);
491 /* XXX Do we really want to return partial data
492 * on out of memory conditions? */
493 return spfrr;
494 }
495 strcpy(spfrr->rr[cnt]->mx, name_buf);
496 cnt++;
497 break;
498
499 case ns_t_spf:
500 case ns_t_txt:
501 if (rdlen > 1) {
502 u_char *src, *dst;
503 size_t len;
504
505 /* Just rdlen is enough because there is at least one
506 * length byte, which we do not copy. */
507 if (SPF_dns_rr_buf_realloc(spfrr, cnt, rdlen) != SPF_E_SUCCESS) {
508 free(responsebuf);
509 /* XXX Do we really want to return partial data
510 * on out of memory conditions? */
511 return spfrr;
512 }
513
514 dst = (u_char *)spfrr->rr[cnt]->txt;
515 src = (u_char *)rdata;
516 len = 0;
517 while (rdlen > 0) {
518 /* Consume one byte into a length. */
519 len = *src;
520 src++;
521 rdlen--;
522
523 /* Avoid buffer overrun if len is junk. */
524 /* XXX don't we rather want to flag this as error? */
525 if (len > rdlen)
526 len = rdlen;
527 memcpy(dst, src, len);
528
529 /* Consume the data. */
530 src += len;
531 dst += len;
532 rdlen -= len;
533 }
534 *dst = '\0';
535 }
536 else {
537 if (SPF_dns_rr_buf_realloc(spfrr, cnt, 1) != SPF_E_SUCCESS) {
538 free(responsebuf);
539 /* XXX Do we really want to return partial data
540 * on out of memory conditions? */
541 return spfrr;
542 }
543 spfrr->rr[cnt]->txt[0] = '\0';
544 }
545
546 cnt++;
547 break;
548
549 case ns_t_ptr:
550 err = ns_name_uncompress(responsebuf,
551 responsebuf + responselen,
552 rdata,
553 name_buf, sizeof(name_buf));
554 if (err < 0) { /* 0 or -1 */
555 if (spf_dns_server->debug > 1)
556 SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
557 err, strerror(errno), errno);
558 free(responsebuf);
559 /* XXX Do we really want to return partial data
560 * on parse error? */
561 return spfrr;
562 }
563
564 if (SPF_dns_rr_buf_realloc(spfrr, cnt,
565 strlen(name_buf) + 1) != SPF_E_SUCCESS) {
566 free(responsebuf);
567 /* XXX Do we really want to return partial data
568 * on out of memory conditions? */
569 return spfrr;
570 }
571 strcpy(spfrr->rr[cnt]->ptr, name_buf);
572 cnt++;
573 break;
574
575 default:
576 break;
577 }
578 }
579
580 spfrr->num_rr = cnt;
581 }
582
583 if (spfrr->num_rr == 0)
584 spfrr->herrno = NO_DATA;
585
586 free(responsebuf);
587 return spfrr;
588}
589
590
591static void
592SPF_dns_resolv_free(SPF_dns_server_t *spf_dns_server)
593{
594 SPF_ASSERT_NOTNULL(spf_dns_server);
595
596#if ! HAVE_DECL_RES_NINIT
597 res_close();
598#endif
599
600 free(spf_dns_server);
601}
602
603SPF_dns_server_t *
604SPF_dns_resolv_new(SPF_dns_server_t *layer_below,
605 const char *name, int debug)
606{
607 SPF_dns_server_t *spf_dns_server;
608
609#if HAVE_DECL_RES_NINIT
610 pthread_once(&res_state_control, SPF_dns_resolv_init_key);
611#else
612 if (res_init() != 0) {
613 SPF_warning("Failed to call res_init()");
614 return NULL;
615 }
616#endif
617
618 spf_dns_server = malloc(sizeof(SPF_dns_server_t));
619 if (spf_dns_server == NULL)
620 return NULL;
621 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
622
623 if (name == NULL)
624 name = "resolv";
625
626 spf_dns_server->destroy = SPF_dns_resolv_free;
627 spf_dns_server->lookup = SPF_dns_resolv_lookup;
628 spf_dns_server->get_spf = NULL;
629 spf_dns_server->get_exp = NULL;
630 spf_dns_server->add_cache = NULL;
631 spf_dns_server->layer_below = layer_below;
632 spf_dns_server->name = name;
633 spf_dns_server->debug = debug;
634
635 return spf_dns_server;
636}
637
638#endif /* _WIN32 */
#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 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_E_SUCCESS
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 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
#define NULL
#define debug
#define SPF_h_errno
SPF_dns_server_t * SPF_dns_resolv_new(SPF_dns_server_t *layer_below, const char *name, int 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