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