1 #include "opendmarc_internal.h"
2 
3 /* libbsd if found */
4 #ifdef USE_BSD_H
5 # include <bsd/string.h>
6 #endif /* USE_BSD_H */
7 
8 /* libstrl if needed */
9 #ifdef USE_STRL_H
10 # include <strl.h>
11 #endif /* USE_STRL_H */
12 
13 /* opendmarc_strl if needed */
14 #ifdef USE_DMARCSTRL_H
15 # include <opendmarc_strl.h>
16 #endif /* USE_DMARCSTRL_H */
17 
18 #include <netdb.h>
19 
20 #include "dmarc.h"
21 
22 #if WITH_SPF && ! HAVE_SPF2_H
23 /*
24 ** Beware that some Linux versions incorrectly define
25 ** MAXHOSTNAMELEN as 64, but DNS lookups require a length
26 ** of 255. So we don't use MAXHOSTNAMELEN here.
27 */
28 #define MAXDNSHOSTNAME 256
29 #ifndef MAXPACKET
30 # define MAXPACKET        (8192)
31 #endif
32 #ifndef T_SPF
33 # define T_SPF	(99)
34 #endif
35 
36 /***************************************************************************************************
37 ** opendmarc_spf_dns_lookup_a_actual -- Looks type of address that is sought
38 **
39 ** Arguments:
40 **	domain		-- the domain name to look up.
41 **	sought		-- type of lookup A or AAAA
42 **	ary		-- array of strings containing list of IP addresses
43 **	cnt		-- Pointer to count of lines in array
44 ** Returns:
45 **	ary	-- on success
46 **	NULL	-- otherise, and place the h_errno error into reply
47 ** Side Effects:
48 **	Makes a connection to the local name server and blocks
49 **	waiting for a reply.
50 ***************************************************************************************************/
51 
52 char **
opendmarc_spf_dns_lookup_a_actual(char * domain,int sought,char ** ary,int * cnt)53 opendmarc_spf_dns_lookup_a_actual(char *domain, int sought, char **ary, int *cnt)
54 {
55 	char *		bp;
56 	u_char *	cp;
57 	u_char *	eom	= NULL;
58 	char		hbuf[MAXDNSHOSTNAME];
59 	char		namebuf[MAXDNSHOSTNAME + 1];
60 	u_char		a_buf[MAXPACKET];
61 	struct in_addr	in;
62 	uint32_t	a;
63 	HEADER		hdr;
64 	int		k;
65 	short		l	= 0;
66 	int		class	= -1;
67 	int		acnt	= -1;
68 	int		qdcnt	= -1;
69 	u_short		type	= 0;
70 	u_long		ttl	= 0;
71 #if HAVE_RES_NINIT
72 	struct __res_state resp;
73 #endif /* HAVE_RES_NINIT */
74 
75 	/*
76 	 * If a null or empy domain was given to us, just say it
77 	 * was not found.
78 	 */
79 	if (domain == NULL || *domain == '\0')
80 	{
81 		return NULL;
82 	}
83 
84 #ifdef HAVE_RES_NINIT
85         memset(&resp, '\0', sizeof resp);
86 	res_ninit(&resp);
87 #endif /* HAVE_RES_NINIT */
88 	/*
89 	 * Copy the domain so we can scribble on it. The orginal
90 	 * may point to a static string.
91 	 */
92 	(void) memcpy(hbuf, domain, sizeof hbuf);
93 	bp = hbuf;
94 
95 	/*
96 	 * Make sure host ends in a dot to short circuit lookups
97 	 */
98 	bp = hbuf + strlen(hbuf) - 1;
99 	if (*bp != '.')
100 		*++bp = '.';
101 	*++bp = '\0';
102 	/*
103 	 * Make sure host does not begin with a dot.
104 	 */
105 	bp = hbuf;
106 	if (*bp == '.')
107 		++bp;
108 
109 #ifdef HAVE_RES_NINIT
110 	k = res_nquery(&resp, bp, C_IN, sought, a_buf, sizeof a_buf);
111 #ifdef HAVE_RES_NDESTROY
112 	res_ndestroy(&resp);
113 #else
114 	res_nclose(&resp);
115 #endif
116 #else /* HAVE_RES_NINIT */
117 	k = res_query(bp, C_IN, sought, a_buf, sizeof a_buf);
118 #endif /* HAVE_RES_NINIT */
119 	if (k < 0)
120 	{
121 		return NULL;
122 	}
123 	if (k > (int)(sizeof a_buf))
124 	{
125 		k = sizeof a_buf;
126 	}
127 	(void) memcpy(&hdr, a_buf, sizeof hdr);
128 	cp = (u_char *)&a_buf + HFIXEDSZ;
129 	eom = (u_char *)&a_buf + k;
130 
131 	(void) memset(namebuf, '\0', sizeof namebuf);
132 	/* skip question part of response -- we know what we asked */
133 	for (qdcnt = ntohs(hdr.qdcount); qdcnt > 0; qdcnt--)
134 	{
135 		k = dn_expand((unsigned char *) &a_buf, eom, cp, namebuf, sizeof namebuf);
136 		cp += k;
137 		if (cp + INT16SZ + INT16SZ > eom)
138 		{
139 			return NULL;
140 		}
141 		GETSHORT(type, cp);
142 		GETSHORT(class, cp);
143 	}
144 	if (hdr.rcode != NOERROR)
145 	{
146 		return NULL;
147 	}
148 	acnt = ntohs((unsigned short) hdr.ancount);
149 	if (acnt == 0)
150 	{
151 		return NULL;
152 	}
153 	while (--acnt >= 0 && cp < eom)
154 	{
155 		if ((k = dn_expand((unsigned char *) &a_buf, eom, cp,
156 				   namebuf, sizeof namebuf)) < 0)
157 		{
158 			break;
159 		}
160 		cp += k;
161 
162 		GETSHORT(type, cp);
163 		GETSHORT(class, cp);
164 		GETLONG(ttl, cp);
165 		GETSHORT(l, cp);
166 		if (type == T_CNAME)
167 		{
168 			/* CNAME; walk past the expanded name and continue */
169 			char cname[MAXDNSHOSTNAME + 1];
170 
171 			k = dn_expand((u_char *) &a_buf, eom, cp,
172 				cname, MAXDNSHOSTNAME);
173 			cp += k;
174 			continue;
175 		}
176 		else if (type != sought)
177 		{
178 			/* not a type we want; skip the rest and continue */
179 			cp += l;
180 			continue;
181 		}
182 		else if (type == T_A)
183 		{
184 			GETLONG(a, cp);
185 			a = htonl(a);
186 			(void) memcpy(&in.s_addr, &a, sizeof(uint32_t));
187 			(void) memset(hbuf, '\0', sizeof hbuf);
188 			(void) strncpy(hbuf, inet_ntoa(in), sizeof hbuf);
189 			ary = opendmarc_util_pushnargv(hbuf, ary, cnt);
190 		}
191 #ifdef T_AAAA
192 		else if (type == T_AAAA)
193 		{
194 			struct in6_addr s6;
195 
196 			/* just to be sure... */
197 			if (l != sizeof s6.s6_addr)
198 			{
199 				cp += l;
200 				continue;
201 			}
202 
203 			(void) memcpy(&s6.s6_addr, cp, sizeof s6.s6_addr);
204 			(void) memset(hbuf, '\0', sizeof hbuf);
205 			inet_ntop(AF_INET6, &s6.s6_addr, hbuf, sizeof hbuf - 1);
206 			ary = opendmarc_util_pushnargv(hbuf, ary, cnt);
207 		}
208 #endif /* T_AAAA */
209 	}
210 	return ary;
211 }
212 
213 /***************************************************************************************************
214 ** opendmarc_spf_dns_lookup_a -- Looks up the IPv4 and IPv6 addresses of the domain
215 **
216 ** Arguments:
217 **	domain		-- the domain name to look up.
218 **	ary		-- array of strings containing list of IP addresses
219 **	cnt		-- Pointer to count of lines in array
220 ** Returns:
221 **	ary	-- on success
222 **	NULL	-- otherwise, and place the h_errno error into reply
223 ** Side Effects:
224 **	Makes a connection to the local name server and blocks
225 **	waiting for a reply.
226 ***************************************************************************************************/
227 char **
opendmarc_spf_dns_lookup_a(char * domain,char ** ary,int * cnt)228 opendmarc_spf_dns_lookup_a(char *domain, char **ary, int *cnt)
229 {
230 	bool found = FALSE;
231 	char **a_retp;
232 	char **aaaa_retp;
233 
234 	a_retp = opendmarc_spf_dns_lookup_a_actual(domain, T_A, ary, cnt);
235 	if (a_retp != (char **) NULL)
236 	{
237 		ary = a_retp;
238 		found = TRUE;
239 	}
240 
241 #ifdef T_AAAA
242 	aaaa_retp = opendmarc_spf_dns_lookup_a_actual(domain, T_AAAA, ary, cnt);
243 	if (aaaa_retp != (char **) NULL)
244 	{
245 		ary = aaaa_retp;
246 		found = TRUE;
247 	}
248 #endif /* T_AAAA */
249 
250 	return *cnt > 0 ? ary : NULL;
251 }
252 
253 /***************************************************************************************************
254 ** opendmarc_spf_dns_lookup_mx -- Looks up the MX records for a domain
255 **
256 ** Arguments:
257 **	domain		-- The domain name to look up.
258 **	ary		-- Array of strings containing list MX hosts
259 ##			   Note that spf only cares if they exist.
260 **	cnt		-- Pointer to count of lines in array
261 ** Returns:
262 **	ary	-- on success
263 **	NULL	-- otherise, and place the h_errno error into reply
264 ** Side Effects:
265 **	Makes a connection to the local name server and blocks
266 **	waiting for a reply.
267 ***************************************************************************************************/
268 char **
opendmarc_spf_dns_lookup_mx(char * domain,char ** ary,int * cnt)269 opendmarc_spf_dns_lookup_mx(char *domain, char **ary, int *cnt)
270 {
271 	register u_char *eob, *cp;
272 	register int k;
273 	u_char buf[BUFSIZ];
274 	HEADER *hp;
275 	union {
276 		HEADER  h;
277 		u_char  u[PACKETSZ];
278 	} q;
279 	int acnt, qdcnt;
280 	u_short pref;
281 	u_short type;
282 	u_long ttl;
283 #if HAVE_RES_NINIT
284 	struct __res_state resp;
285 #endif /* HAVE_RES_NINIT */
286 
287 	if (domain == NULL)
288 	{
289 		return NULL;
290 	}
291 
292 #ifdef HAVE_RES_NINIT
293         memset(&resp, '\0', sizeof resp);
294 	res_ninit(&resp);
295 	k = res_nquery(&resp, domain, C_IN, T_MX, (u_char *) &q, sizeof(q));
296 #ifdef HAVE_RES_NDESTROY
297 	res_ndestroy(&resp);
298 #else
299 	res_nclose(&resp);
300 #endif
301 #else /* HAVE_RES_NINIT */
302 	k = res_query(domain, C_IN, T_MX, (u_char *) &q, sizeof(q));
303 #endif /* HAVE_RES_NINIT */
304 
305 	if (k < 0)
306 	{
307 		return NULL;
308 	}
309 	hp  = &(q.h);
310 	cp  = q.u + HFIXEDSZ;
311 	eob = q.u + k;
312 
313 	for (qdcnt = ntohs(hp->qdcount); qdcnt--; cp += k + QFIXEDSZ)
314 		if ((k = dn_skipname(cp, eob)) < 0)
315 		{
316 			return NULL;
317 		}
318 
319 	acnt = ntohs(hp->ancount);
320 	while (--acnt >= 0 && cp < eob)
321 	{
322 		if ((k = dn_expand(q.u, eob, cp, (char *)buf, BUFSIZ-1)) < 0)
323 			break;
324 		cp += k;
325 		if (cp > eob)
326 			break;
327 		GETSHORT(type, cp);
328 		cp += INT16SZ;
329 		GETLONG(ttl, cp);
330 		GETSHORT(k, cp);
331 		if (type != T_MX)
332 		{
333 			cp += k;
334 			continue;
335 		}
336 		GETSHORT(pref, cp);
337 		if ((k = dn_expand(q.u, eob, cp, (char *)buf, BUFSIZ-1)) < 0)
338 			break;
339 		cp += k;
340 		ary = opendmarc_spf_dns_lookup_a((char *)buf, ary, cnt);
341 	}
342 	return ary;
343 }
344 
345 /***************************************************************************************************
346 ** opendmarc_spf_dns_lookup_ptr -- Looks up IP address to get domain
347 **
348 ** Arguments:
349 **	domain		-- The domain name to look up.
350 **	ary		-- Array of strings containing list MX hosts
351 ##			   Note that spf only cares if they exist.
352 **	cnt		-- Pointer to count of lines in array
353 ** Returns:
354 **	ary	-- on success
355 **	NULL	-- otherise, and place the h_errno error into reply
356 ** Side Effects:
357 **	Makes a connection to the local name server and blocks
358 **	waiting for a reply.
359 ***************************************************************************************************/
360 char **
opendmarc_spf_dns_lookup_ptr(char * ip,char ** ary,int * cnt)361 opendmarc_spf_dns_lookup_ptr(char *ip, char **ary, int *cnt)
362 {
363 	register u_char *eob, *cp;
364 	register int k;
365 	u_char buf[BUFSIZ];
366 	char	ip_buf[512];
367 	HEADER *hp;
368 	union {
369 		HEADER  h;
370 		u_char  u[PACKETSZ];
371 	} q;
372 	int acnt, qdcnt;
373 	u_short type;
374 	u_long ttl;
375 	char *icp;
376 #if HAVE_RES_NINIT
377 	struct __res_state resp;
378 #endif /* HAVE_RES_NINIT */
379 
380 	if (ip == NULL)
381 	{
382 		return NULL;
383 	}
384 	(void) memset(buf, '\0', sizeof buf);
385 	(void) memset(ip_buf, '\0', sizeof ip_buf);
386 	(void) strlcpy(ip_buf, ip, sizeof ip_buf);
387 	icp = strrchr(ip_buf, '.');
388 	if (icp == NULL)
389 		return NULL;
390 	strlcpy((char *)buf, icp+1, sizeof buf);
391 	*icp = '\0';
392 	icp = strrchr(ip_buf, '.');
393 	if (icp == NULL)
394 		return NULL;
395 	strlcat((char *)buf, ".", sizeof buf);
396 	strlcat((char *)buf, icp+1, sizeof buf);
397 	*icp = '\0';
398 	icp = strrchr(ip_buf, '.');
399 	if (icp == NULL)
400 		return NULL;
401 	strlcat((char *)buf, ".", sizeof buf);
402 	strlcat((char *)buf, icp+1, sizeof buf);
403 	*icp = '\0';
404 	icp = ip_buf;
405 	strlcat((char *)buf, ".", sizeof buf);
406 	strlcat((char *)buf, icp, sizeof buf);
407 	strlcat((char *)buf, ".in-addr.arpa.", sizeof buf);
408 
409 #ifdef HAVE_RES_NINIT
410         memset(&resp, '\0', sizeof resp);
411 	res_ninit(&resp);
412 	k = res_nquery(&resp, (char *)buf, C_IN, T_PTR, (u_char *) &q, sizeof(q));
413 #ifdef HAVE_RES_NDESTROY
414 	res_ndestroy(&resp);
415 #else
416 	res_nclose(&resp);
417 #endif
418 #else /* HAVE_RES_NINIT */
419 	k = res_query((char *)buf, C_IN, T_PTR, (u_char *) &q, sizeof(q));
420 #endif /* HAVE_RES_NINIT */
421 
422 	if (k < 0)
423 	{
424 		return NULL;
425 	}
426 	hp  = &(q.h);
427 	cp  = q.u + HFIXEDSZ;
428 	eob = q.u + k;
429 
430 	for (qdcnt = ntohs(hp->qdcount); qdcnt--; cp += k + QFIXEDSZ)
431 	{
432 		if ((k = dn_skipname(cp, eob)) < 0)
433 		{
434 			return NULL;
435 		}
436 	}
437 
438 	acnt = ntohs(hp->ancount);
439 	while (--acnt >= 0 && cp < eob)
440 	{
441 		char ptr[MAXDNSHOSTNAME + 1];
442 
443 		if ((k = dn_expand(q.u, eob, cp, (char *)buf, BUFSIZ-1)) < 0)
444 			break;
445 		cp += k;
446 		if (cp > eob)
447 			break;
448 		GETSHORT(type, cp);
449 		cp += INT16SZ;
450 		GETLONG(ttl, cp);
451 		GETSHORT(k, cp);
452 
453 		k = dn_expand(q.u, eob, cp, ptr, MAXDNSHOSTNAME);
454 		ary = opendmarc_util_pushnargv(ptr, ary, cnt);
455 		cp += k;
456 		continue;
457 	}
458 	return ary;
459 }
460 
461 /***************************************************************
462 ** opendmarc_spf_dns_does_domain_exist -- does an a, aaaa, or mx record exist?
463 **
464 ** Arguments:
465 **	domain	-- the domain name to look up.
466 **	reply	-- pointer to an integer
467 **
468 ** Returns:
469 **	TRUE	-- if any of those records existed.
470 **	FALSE	-- otherise, and place the h_errno error
471 **		   into reply
472 **
473 ** Side Effects:
474 **	Makes a connection to the local name server and bloks
475 **	waiting for a reply.
476 ***************************************************************/
477 int
opendmarc_spf_dns_does_domain_exist(char * domain,int * reply)478 opendmarc_spf_dns_does_domain_exist(char *domain, int *reply)
479 {
480 	HEADER  hdr;
481 	u_char	a_q[MAXPACKET];
482 	u_char	aaaa_q[MAXPACKET];
483 	u_char	mx_q[MAXPACKET];
484 	int	r;
485 	int *	rp;
486 #if HAVE_RES_NINIT
487 	struct __res_state resp;
488 #endif /* HAVE_RES_NINIT */
489 
490 	if (reply == NULL)
491 		rp = &r;
492 	else
493 		rp = reply;
494 
495 	if (domain == NULL || *domain == '\0')
496 	{
497 		*rp = HOST_NOT_FOUND;
498 		return FALSE;
499 	}
500 
501         /*
502          * Make sure the domain exists.
503          */
504 #ifdef HAVE_RES_NINIT
505         memset(&resp, '\0', sizeof resp);
506 	res_ninit(&resp);
507         (void) res_nquery(&resp, domain, C_IN, T_A, a_q, sizeof a_q);
508 #ifdef T_AAAA
509         (void) res_nquery(&resp, domain, C_IN, T_AAAA, aaaa_q, sizeof aaaa_q);
510 #endif /* T_AAAA */
511         (void) res_nquery(&resp, domain, C_IN, T_MX, mx_q, sizeof mx_q);
512 #ifdef HAVE_RES_NDESTROY
513 	res_ndestroy(&resp);
514 #else
515 	res_nclose(&resp);
516 #endif
517 #else /* HAVE_RES_NINIT */
518         (void) res_query(domain, C_IN, T_A, a_q, sizeof a_q);
519 #ifdef T_AAAA
520         (void) res_query(domain, C_IN, T_AAAA, aaaa_q, sizeof aaaa_q);
521 #endif /* T_AAAA */
522         (void) res_query(domain, C_IN, T_MX, mx_q, sizeof mx_q);
523 #endif /* HAVE_RES_NINIT */
524 
525         memcpy(&hdr, a_q, sizeof hdr);
526 	*rp = hdr.rcode;
527         if (hdr.rcode == NOERROR)
528 		return TRUE;
529 
530         memcpy(&hdr, aaaa_q, sizeof hdr);
531 	*rp = hdr.rcode;
532         if (hdr.rcode == NOERROR)
533 		return TRUE;
534 
535         memcpy(&hdr, aaaa_q, sizeof hdr);
536 	*rp = hdr.rcode;
537         if (hdr.rcode == NOERROR)
538 		return TRUE;
539 
540 	return FALSE;
541 }
542 
543 /***************************************************************************************************
544 ** opendmarc_dns_get_record -- looks up and returns the txt record
545 **
546 ** Arguments:
547 **	domain		-- the domain name to look up.
548 **	reply		-- pointer to an integer
549 **	txt		-- where to scribble the found txt record
550 **	txtlen		-- size of txt record buffer
551 **	cname		-- buffer to hold CNAME if one found
552 **	cnamelen	-- size of cname buffer
553 **	spfcheck	-- restrict text records returned to just those beginning with v= or spf2.0
554 **
555 ** Returns:
556 **	txt	-- on success
557 **	NULL	-- otherise, and place the h_errno error
558 **		   into reply
559 **	NULL	-- if no data, but cname may still contain a hostname
560 **
561 ** Side Effects:
562 **	Makes a connection to the local name server and blocks
563 **	waiting for a reply.
564 ***************************************************************************************************/
565 char *
opendmarc_spf_dns_get_record(char * domain,int * reply,char * txt,size_t txtlen,char * cname,size_t cnamelen,int spfcheck)566 opendmarc_spf_dns_get_record(char *domain, int *reply, char *txt, size_t txtlen, char *cname, size_t cnamelen, int spfcheck)
567 {
568 	u_char *	eom	= NULL;
569 	u_char *	eop	= NULL;
570 	u_char *	cp	= NULL;
571 	int 		k;
572 	u_char *	p	= NULL;
573 	int		ch	= 0;
574 	short		l	= 0;
575 	HEADER		hdr;
576 	int		class	= -1;
577 	int		acnt	= -1;
578 	int		qdcnt	= -1;
579 	u_short		type	= 0;
580 	u_long		ttl	= 0;
581 	char *		bp	= NULL;
582 	int		r	= 0;
583 	int *		rp	= NULL;
584 	u_char		txt_buf[MAXPACKET];
585 	char		hbuf[MAXDNSHOSTNAME];
586 	char		namebuf[MAXDNSHOSTNAME + 1];
587 #if HAVE_RES_NINIT
588 	struct __res_state resp;
589 #endif /* HAVE_RES_NINIT */
590 
591 	if (reply == NULL)
592 		rp = &r;
593 	else
594 		rp = reply;
595 
596 	/*
597 	 * If a null or empy domain was given to us, just say it
598 	 * was not found.
599 	 */
600 	*rp = 0;
601 	if (domain == NULL || *domain == '\0')
602 	{
603 		*rp = HOST_NOT_FOUND;
604 		return NULL;
605 	}
606 
607 	if (cname != NULL && cnamelen > 0)
608 		(void) memset(cname, '\0', cnamelen);
609 
610 	/*
611 	 * Copy the domain so we can scribble on it. The orginal
612 	 * may point to a static string.
613 	 */
614 	(void) memcpy(hbuf, domain, sizeof hbuf);
615 	bp = hbuf;
616 	if (txt != NULL)
617 		(void) memset(txt, '\0', txtlen);
618 
619 	/*
620 	 * Make sure host ends in a dot to short circuit lookups
621 	 */
622 	bp = hbuf + strlen(hbuf) - 1;
623 	if (*bp != '.')
624 		*++bp = '.';
625 	*++bp = '\0';
626 	/*
627 	 * Make user host does not begin with a dot.
628 	 */
629 	bp = hbuf;
630 	if (*bp == '.')
631 		++bp;
632 
633 #ifdef HAVE_RES_NINIT
634         memset(&resp, '\0', sizeof resp);
635 	res_ninit(&resp);
636 	k = res_nquery(&resp, bp, C_IN, T_TXT, txt_buf, sizeof txt_buf);
637 #else /* HAVE_RES_NINIT */
638 	k = res_query(bp, C_IN, T_TXT, txt_buf, sizeof txt_buf);
639 #endif /* HAVE_RES_NINIT */
640 	if (k < 0)
641 	{
642 		/*
643 		 * TXT records apppear more common than SPF records, so
644 		 * we fall back to SPF instead of looking up SPF first.
645 		 */
646 		if (h_errno == NO_DATA || h_errno == NXDOMAIN)
647 		{
648 #ifdef HAVE_RES_NINIT
649 			k = res_nquery(&resp, bp, C_IN, T_SPF, txt_buf, sizeof txt_buf);
650 #else /* HAVE_RES_NINIT */
651 			k = res_query(bp, C_IN, T_SPF, txt_buf, sizeof txt_buf);
652 #endif /* HAVE_RES_NINIT */
653 			if (k >= 0)
654 				goto got_spf_record;
655 		}
656 		*rp = h_errno;
657 #ifdef HAVE_RES_NINIT
658 #ifdef HAVE_RES_NDESTROY
659 		res_ndestroy(&resp);
660 #else
661 		res_nclose(&resp);
662 #endif
663 #endif /* HAVE_RES_NINIT */
664 		return NULL;
665 	}
666 got_spf_record:
667 #ifdef HAVE_RES_NINIT
668 #ifdef HAVE_RES_NDESTROY
669 	res_ndestroy(&resp);
670 #else
671 	res_nclose(&resp);
672 #endif
673 #endif /* HAVE_RES_NINIT */
674 
675 	if (k > (int)(sizeof txt_buf))
676 		k = sizeof txt_buf;
677 	(void) memcpy(&hdr, txt_buf, sizeof hdr);
678 	cp = (u_char *)&txt_buf + HFIXEDSZ;
679 	eom = (u_char *)&txt_buf + k;
680 
681 	(void) memset(namebuf, '\0', sizeof namebuf);
682 	/* skip question part of response -- we know what we asked */
683 	for (qdcnt = ntohs(hdr.qdcount); qdcnt > 0; qdcnt--)
684 	{
685 		(void) dn_expand((unsigned char *) &txt_buf, eom, cp, namebuf, sizeof namebuf);
686 		if ((k = dn_skipname(cp, eom)) < 0)
687 		{
688 			*rp = NO_DATA;
689 			return NULL;
690 		}
691 		cp += k;
692 		if (cp + INT16SZ + INT16SZ > eom)
693 		{
694 			*rp = NO_DATA;
695 			return NULL;
696 		}
697 		GETSHORT(type, cp);
698 		GETSHORT(class, cp);
699 	}
700 	if (hdr.rcode != NOERROR)
701 	{
702 		*rp = NO_DATA;
703 		return NULL;
704 	}
705 	acnt = ntohs((unsigned short) hdr.ancount);
706 	if (acnt == 0)
707 	{
708 		*rp = NO_DATA;
709 		return NULL;
710 	}
711 	while (--acnt >= 0 && cp < eom)
712 	{
713 		if ((k = dn_expand((unsigned char *) &txt_buf, eom, cp,
714 				   namebuf, sizeof namebuf)) < 0)
715 		{
716 			*rp = NO_DATA;
717 			return NULL;
718 		}
719 		cp += k;
720 
721 		if (cp + INT16SZ + INT16SZ > eom)
722 		{
723 			/* currupt answer */
724 			*rp = NO_DATA;
725 			return NULL;
726 		}
727 		GETSHORT(type, cp);
728 		GETSHORT(class, cp);
729 		if (type == T_CNAME)
730 		{
731 			/*
732 			 * CNAMEs are supposed to be invisible, but somtimes
733 			 * a CNAME points to a TXT record that times out, so
734 			 * all we get on the initial query is the CNAME.
735 			 */
736 			char	xname[MAXDNSHOSTNAME + 1];
737 			char *	xp;
738 			size_t	xlen;
739 
740 			if (cname == NULL || cnamelen == 0)
741 			{
742 				xp = xname;
743 				xlen = sizeof xname;
744 			}
745 			else
746 			{
747 				xp = cname;
748 				xlen = cnamelen;
749 			}
750 			k = dn_expand((u_char *) &txt_buf, eom, (u_char *)cname, xp, xlen);
751 			cp += k;
752 			continue;
753 		}
754 		else if (type != T_TXT)
755 		{
756 
757 			*rp = NO_DATA;
758 			return NULL;
759 		}
760 		/* we may want to cache the ttl later */
761 		GETLONG(ttl, cp);
762 
763 		if (cp + INT16SZ > eom)
764 		{
765 			/* no payload length */
766 			*rp = NO_DATA;
767 			return NULL;
768 		}
769 		GETSHORT(l, cp);
770 
771 		if (cp + l > eom)
772 		{
773 			/* payload length greater than remaining buffer */
774 			*rp = NO_DATA;
775 			return NULL;
776 		}
777 		if (txt != NULL)
778 		{
779 			(void) memset(txt, '\0', txtlen);
780 			/*
781 			 * copy the returned record into txt
782 			 */
783 			p = (u_char *)txt;
784 			eop = (u_char *)txt + txtlen -1;
785 			while (l > 0 && p < eop)
786 			{
787 				ch = *cp++;
788 				l--;
789 				while (ch > 0 && p < eop)
790 				{
791 					*p++ = *cp++;
792 					ch--;
793 					l--;
794 				}
795 			}
796 		}
797 		if (spfcheck == TRUE)
798 		{
799 			/*
800 			 * Honor both SPF and Sender Identity type records
801 			 * But ignore mfrom/pra, because DMARC uses only SPF records.
802 			 */
803 			if (strstr(txt, "v=spf") != NULL || strncasecmp(txt, "spf2.0", 6) == 0)
804 			{
805 				*rp = 0;
806 				return txt;
807 			}
808 		}
809 		cp += l;
810 		continue;
811 	}
812 	*rp = NO_DATA;
813 	return NULL;
814 }
815 
816 #endif /* WITH_SPF && ! HAVE_SPF2_H */
817