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
16 /**
17 * @file
18 * @brief A DNS resolver which uses libresolv/libbind to query a DNS server.
19 *
20 * If we have a res_ninit then we make a thread-local resolver
21 * state, which we use to perform all resolutions.
22 *
23 * If we do not have res_ninit, then we do a res_init() at
24 * server-create time, and a res_close() at server-close time, and
25 * we are NOT thread-safe. I think we don't actually have to call
26 * res_init(), but we do anyway.
27 */
28
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
67 /**
68 * @file
69 * Audited, 2008-09-15, Shevek.
70 */
71
72 static 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
79 static 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
89 static pthread_once_t res_state_control = PTHREAD_ONCE_INIT;
90 static pthread_key_t res_state_key;
91
92 static void
SPF_dns_resolv_thread_term(void * arg)93 SPF_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
103 static void
SPF_dns_resolv_init_key(void)104 SPF_dns_resolv_init_key(void)
105 {
106 pthread_key_create(&res_state_key, SPF_dns_resolv_thread_term);
107 }
108 #endif
109
110 /** XXX ns_rr is 1048 bytes, pass a pointer. */
111 static void
SPF_dns_resolv_debug(SPF_dns_server_t * spf_dns_server,ns_rr rr,const u_char * responsebuf,size_t responselen,const u_char * rdata,size_t rdlen)112 SPF_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_txt:
183 if (rdlen < 1) {
184 SPF_debugf("TXT: rdlen too short: %lu", (unsigned long)rdlen);
185 break;
186 }
187 /* XXX I think this is wrong/unsafe. Shevek. */
188 /* XXX doesn't parse the different TXT "sections" */
189 SPF_debugf("TXT: (%lu) \"%.*s\"",
190 (unsigned long)rdlen, (int)rdlen - 1, rdata + 1);
191 break;
192
193 case ns_t_ptr:
194 err = ns_name_uncompress(responsebuf,
195 responsebuf + responselen,
196 rdata,
197 name_buf, sizeof(name_buf));
198 if (err < 0) /* 0 or -1 */
199 SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
200 err, strerror(errno), errno);
201 else
202 SPF_debugf("PTR: %s", name_buf);
203 break;
204
205 default:
206 SPF_debugf("not parsed: type: %d", ns_rr_type(rr));
207 break;
208 }
209
210 }
211
212 /**
213 * Can return NULL on out-of-memory condition.
214 * Should return a HOST_NOT_FOUND or appropriate rr in all other
215 * error cases.
216 */
217 static SPF_dns_rr_t *
SPF_dns_resolv_lookup(SPF_dns_server_t * spf_dns_server,const char * domain,ns_type rr_type,int should_cache)218 SPF_dns_resolv_lookup(SPF_dns_server_t *spf_dns_server,
219 const char *domain, ns_type rr_type, int should_cache)
220 {
221 SPF_dns_rr_t *spfrr;
222
223 int err;
224 int i;
225 int nrec;
226 int cnt;
227
228 u_char *responsebuf;
229 size_t responselen;
230
231 ns_msg ns_handle;
232 ns_rr rr;
233
234 int ns_sect;
235 // int num_ns_sect = sizeof( ns_sects ) / sizeof( *ns_sects );
236
237 char name_buf[ NS_MAXDNAME ];
238
239 size_t rdlen;
240 const u_char *rdata;
241
242 #if HAVE_DECL_RES_NINIT
243 void *res_spec;
244 struct __res_state *res_state;
245 #endif
246
247 SPF_ASSERT_NOTNULL(spf_dns_server);
248
249 #if HAVE_DECL_RES_NINIT
250 /** Get the thread-local resolver state. */
251 res_spec = pthread_getspecific(res_state_key);
252 if (res_spec == NULL) {
253 res_state = (struct __res_state *)
254 malloc(sizeof(struct __res_state));
255 /* XXX The interface doesn't allow to communicate back failure
256 * to allocate memory, but SPF_errorf aborts anyway. */
257 if (! res_state)
258 SPF_errorf("Failed to allocate %lu bytes for res_state",
259 (unsigned long)sizeof(struct __res_state));
260 memset(res_state, 0, sizeof(struct __res_state));
261 if (res_ninit(res_state) != 0)
262 SPF_error("Failed to call res_ninit()");
263 pthread_setspecific(res_state_key, (void *)res_state);
264 }
265 else {
266 res_state = (struct __res_state *)res_spec;
267 }
268 #endif
269
270 responselen = 2048;
271 responsebuf = (u_char *)malloc(responselen);
272 if (! responsebuf)
273 return NULL; /* NULL always means OOM from DNS lookup. */
274 memset(responsebuf, 0, responselen);
275
276 /*
277 * Retry the lookup until our response buffer is big enough.
278 *
279 * This loop repeats until either we fail a lookup or we succeed.
280 * The size of the response buffer is monotonic increasing, so eventually we
281 * must either succeed, or we try to malloc more RAM than we can.
282 *
283 * The Linux man pages do not describe res_nquery adequately. Solaris says:
284 *
285 * The res_nquery() and res_query() routines return a length that may be bigger
286 * than anslen. In that case, retry the query with a larger buf. The answer to the
287 * second query may be larger still], so it is recommended that you supply a buf
288 * larger than the answer returned by the previous query. answer must be large
289 * enough to receive a maximum UDP response from the server or parts of the answer
290 * will be silently discarded. The default maximum UDP response size is 512 bytes.
291 */
292 for (;;) {
293 int dns_len;
294
295 #if HAVE_DECL_RES_NINIT
296 /* Resolve the name. */
297 dns_len = res_nquery(res_state, domain, ns_c_in, rr_type,
298 responsebuf, responselen);
299 #else
300 dns_len = res_query(domain, ns_c_in, rr_type,
301 responsebuf, responselen);
302 #endif
303
304 if (dns_len < 0) {
305 /* We failed to perform a lookup. */
306 /* This block returns unconditionally. */
307 free(responsebuf);
308 if (spf_dns_server->debug)
309 SPF_debugf("query failed: err = %d %s (%d): %s",
310 dns_len, hstrerror(SPF_h_errno), SPF_h_errno,
311 domain);
312 if ((SPF_h_errno == HOST_NOT_FOUND) &&
313 (spf_dns_server->layer_below != NULL)) {
314 return SPF_dns_lookup(spf_dns_server->layer_below,
315 domain, rr_type, should_cache);
316 }
317 return SPF_dns_rr_new_init(spf_dns_server,
318 domain, rr_type, 0, SPF_h_errno);
319 }
320 else if (dns_len > responselen) {
321 void *tmp;
322 /* We managed a lookup but our buffer was too small. */
323 responselen = dns_len + (dns_len >> 1);
324 #if 0
325 /* Sanity-trap - we should never hit this. */
326 if (responselen > 1048576) { /* One megabyte. */
327 free(responsebuf);
328 return SPF_dns_rr_new_init(spf_dns_server,
329 domain, rr_type, 0, SPF_h_errno);
330 }
331 #endif
332 tmp = realloc(responsebuf, responselen);
333 if (!tmp) {
334 free(responsebuf);
335 return NULL;
336 }
337 responsebuf = tmp;
338 }
339 else {
340 /* We managed a lookup, and our buffer was large enough. */
341 responselen = dns_len;
342 break;
343 }
344 }
345
346
347
348 /*
349 * initialize stuff
350 */
351 spfrr = SPF_dns_rr_new_init(spf_dns_server,
352 domain, rr_type, 0, NETDB_SUCCESS);
353 if (!spfrr) {
354 free(responsebuf);
355 return NULL;
356 }
357
358 err = ns_initparse(responsebuf, responselen, &ns_handle);
359
360 if (err < 0) { /* 0 or -1 */
361 if (spf_dns_server->debug)
362 SPF_debugf("ns_initparse failed: err = %d %s (%d)",
363 err, strerror(errno), errno);
364 free(responsebuf);
365 /* XXX Do we really want to return success with no data
366 * on parse failure? */
367 spfrr->herrno = NO_RECOVERY;
368 return spfrr;
369 }
370
371
372 if (spf_dns_server->debug > 1) {
373 SPF_debugf("msg id: %d", ns_msg_id(ns_handle));
374 SPF_debugf("ns_f_qr quest/resp: %d", ns_msg_getflag(ns_handle, ns_f_qr));
375 SPF_debugf("ns_f_opcode: %d", ns_msg_getflag(ns_handle, ns_f_opcode));
376 SPF_debugf("ns_f_aa auth ans: %d", ns_msg_getflag(ns_handle, ns_f_aa));
377 SPF_debugf("ns_f_tc truncated: %d", ns_msg_getflag(ns_handle, ns_f_tc));
378 SPF_debugf("ns_f_rd rec desire: %d", ns_msg_getflag(ns_handle, ns_f_rd));
379 SPF_debugf("ns_f_ra rec avail: %d", ns_msg_getflag(ns_handle, ns_f_ra));
380 SPF_debugf("ns_f_rcode: %d", ns_msg_getflag(ns_handle, ns_f_rcode));
381 }
382
383
384 /* FIXME the error handling from here on is suspect at best */
385 for (ns_sect = 0; ns_sect < num_ns_sect; ns_sect++) {
386 /* We pass this point if:
387 * - We are the 'answer' section.
388 * - We are debugging.
389 * Otherwise, we continue to the next section.
390 */
391 if (ns_sects[ns_sect].number != ns_s_an && spf_dns_server->debug <= 1)
392 continue;
393
394 nrec = ns_msg_count(ns_handle, ns_sects[ns_sect].number);
395
396 if (spf_dns_server->debug > 1)
397 SPF_debugf("%s: %d", ns_sects[ns_sect].name, nrec);
398
399 spfrr->num_rr = 0;
400 cnt = 0;
401 for (i = 0; i < nrec; i++) {
402 err = ns_parserr(&ns_handle, ns_sects[ns_sect].number, i, &rr);
403 if (err < 0) { /* 0 or -1 */
404 if (spf_dns_server->debug > 1)
405 SPF_debugf("ns_parserr failed: err = %d %s (%d)",
406 err, strerror(errno), errno);
407 free(responsebuf);
408 /* XXX Do we really want to return partial data
409 * on parse failures? */
410 spfrr->herrno = NO_RECOVERY;
411 return spfrr;
412 }
413
414 rdlen = ns_rr_rdlen(rr);
415 if (spf_dns_server->debug > 1)
416 SPF_debugf("name: %s type: %d class: %d ttl: %d rdlen: %lu",
417 ns_rr_name(rr), ns_rr_type(rr), ns_rr_class(rr),
418 ns_rr_ttl(rr), (unsigned long)rdlen);
419
420 if (rdlen <= 0)
421 continue;
422
423 rdata = ns_rr_rdata(rr);
424
425 if (spf_dns_server->debug > 1)
426 SPF_dns_resolv_debug(spf_dns_server, rr,
427 responsebuf, responselen, rdata, rdlen);
428
429 /* And now, if we aren't the answer section, we skip the section. */
430 if (ns_sects[ns_sect].number != ns_s_an)
431 continue;
432
433 /* Now, we are in the answer section. */
434 if (ns_rr_type(rr) != spfrr->rr_type && ns_rr_type(rr) != ns_t_cname) {
435 SPF_debugf("unexpected rr type: %d expected: %d",
436 ns_rr_type(rr), rr_type);
437 continue;
438 }
439
440 switch (ns_rr_type(rr)) {
441 case ns_t_a:
442 if (rdlen != 4) {
443 /* XXX Error handling. */
444 free(responsebuf);
445 return spfrr;
446 }
447 if (SPF_dns_rr_buf_realloc(spfrr, cnt,
448 sizeof(spfrr->rr[cnt]->a)) != SPF_E_SUCCESS) {
449 free(responsebuf);
450 /* XXX Do we really want to return partial data
451 * on out of memory conditions? */
452 return spfrr;
453 }
454 memcpy(&spfrr->rr[cnt]->a, rdata, sizeof(spfrr->rr[cnt]->a));
455 cnt++;
456 break;
457
458 case ns_t_aaaa:
459 if (rdlen != 16) {
460 /* XXX Error handling. */
461 free(responsebuf);
462 return spfrr;
463 }
464 if (SPF_dns_rr_buf_realloc(spfrr, cnt,
465 sizeof(spfrr->rr[cnt]->aaaa)) != SPF_E_SUCCESS) {
466 free(responsebuf);
467 /* XXX Do we really want to return partial data
468 * on out of memory conditions? */
469 return spfrr;
470 }
471 memcpy(&spfrr->rr[cnt]->aaaa, rdata, sizeof(spfrr->rr[cnt]->aaaa));
472 cnt++;
473 break;
474
475 case ns_t_ns:
476 break;
477
478 case ns_t_cname:
479 /* FIXME: are CNAMEs always sent with the real RR? */
480 break;
481
482 case ns_t_mx:
483 if (rdlen < NS_INT16SZ) {
484 /* XXX Error handling. */
485 free(responsebuf);
486 return spfrr;
487 }
488 err = ns_name_uncompress(responsebuf,
489 responsebuf + responselen,
490 rdata + NS_INT16SZ,
491 name_buf, sizeof(name_buf));
492 if (err < 0) { /* 0 or -1 */
493 if (spf_dns_server->debug > 1)
494 SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
495 err, strerror(errno), errno);
496 free(responsebuf);
497 /* XXX Do we really want to return partial data
498 * on parse error? */
499 return spfrr;
500 }
501
502 if (SPF_dns_rr_buf_realloc(spfrr, cnt,
503 strlen(name_buf) + 1 ) != SPF_E_SUCCESS) {
504 free(responsebuf);
505 /* XXX Do we really want to return partial data
506 * on out of memory conditions? */
507 return spfrr;
508 }
509 strcpy(spfrr->rr[cnt]->mx, name_buf);
510 cnt++;
511 break;
512
513 case ns_t_txt:
514 if (rdlen > 1) {
515 u_char *src, *dst;
516 size_t len;
517
518 /* Just rdlen is enough because there is at least one
519 * length byte, which we do not copy. */
520 if (SPF_dns_rr_buf_realloc(spfrr, cnt, rdlen) != SPF_E_SUCCESS) {
521 free(responsebuf);
522 /* XXX Do we really want to return partial data
523 * on out of memory conditions? */
524 return spfrr;
525 }
526
527 dst = (u_char *)spfrr->rr[cnt]->txt;
528 src = (u_char *)rdata;
529 len = 0;
530 while (rdlen > 0) {
531 /* Consume one byte into a length. */
532 len = *src;
533 src++;
534 rdlen--;
535
536 /* Avoid buffer overrun if len is junk. */
537 /* XXX don't we rather want to flag this as error? */
538 if (len > rdlen)
539 len = rdlen;
540 memcpy(dst, src, len);
541
542 /* Consume the data. */
543 src += len;
544 dst += len;
545 rdlen -= len;
546 }
547 *dst = '\0';
548 }
549 else {
550 if (SPF_dns_rr_buf_realloc(spfrr, cnt, 1) != SPF_E_SUCCESS) {
551 free(responsebuf);
552 /* XXX Do we really want to return partial data
553 * on out of memory conditions? */
554 return spfrr;
555 }
556 spfrr->rr[cnt]->txt[0] = '\0';
557 }
558
559 cnt++;
560 break;
561
562 case ns_t_ptr:
563 err = ns_name_uncompress(responsebuf,
564 responsebuf + responselen,
565 rdata,
566 name_buf, sizeof(name_buf));
567 if (err < 0) { /* 0 or -1 */
568 if (spf_dns_server->debug > 1)
569 SPF_debugf("ns_name_uncompress failed: err = %d %s (%d)",
570 err, strerror(errno), errno);
571 free(responsebuf);
572 /* XXX Do we really want to return partial data
573 * on parse error? */
574 return spfrr;
575 }
576
577 if (SPF_dns_rr_buf_realloc(spfrr, cnt,
578 strlen(name_buf) + 1) != SPF_E_SUCCESS) {
579 free(responsebuf);
580 /* XXX Do we really want to return partial data
581 * on out of memory conditions? */
582 return spfrr;
583 }
584 strcpy(spfrr->rr[cnt]->ptr, name_buf);
585 cnt++;
586 break;
587
588 default:
589 break;
590 }
591 }
592
593 spfrr->num_rr = cnt;
594 }
595
596 if (spfrr->num_rr == 0)
597 spfrr->herrno = NO_DATA;
598
599 free(responsebuf);
600 return spfrr;
601 }
602
603
604 static void
SPF_dns_resolv_free(SPF_dns_server_t * spf_dns_server)605 SPF_dns_resolv_free(SPF_dns_server_t *spf_dns_server)
606 {
607 SPF_ASSERT_NOTNULL(spf_dns_server);
608
609 #if ! HAVE_DECL_RES_NINIT
610 res_close();
611 #endif
612
613 free(spf_dns_server);
614 }
615
616 SPF_dns_server_t *
SPF_dns_resolv_new(SPF_dns_server_t * layer_below,const char * name,int debug)617 SPF_dns_resolv_new(SPF_dns_server_t *layer_below,
618 const char *name, int debug)
619 {
620 SPF_dns_server_t *spf_dns_server;
621
622 #if HAVE_DECL_RES_NINIT
623 pthread_once(&res_state_control, SPF_dns_resolv_init_key);
624 #else
625 if (res_init() != 0) {
626 SPF_warning("Failed to call res_init()");
627 return NULL;
628 }
629 #endif
630
631 spf_dns_server = malloc(sizeof(SPF_dns_server_t));
632 if (spf_dns_server == NULL)
633 return NULL;
634 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
635
636 if (name == NULL)
637 name = "resolv";
638
639 spf_dns_server->destroy = SPF_dns_resolv_free;
640 spf_dns_server->lookup = SPF_dns_resolv_lookup;
641 spf_dns_server->get_spf = NULL;
642 spf_dns_server->get_exp = NULL;
643 spf_dns_server->add_cache = NULL;
644 spf_dns_server->layer_below = layer_below;
645 spf_dns_server->name = name;
646 spf_dns_server->debug = debug;
647
648 return spf_dns_server;
649 }
650
651 #endif /* _WIN32 */
652