xref: /freebsd/contrib/sendmail/src/sm_resolve.c (revision 8f1ef87a6b93af292e68f8e33087e2df6325e9bb)
1 /*
2  * Copyright (c) 2000-2004, 2010, 2015, 2020 Proofpoint, Inc. and its suppliers.
3  *	All rights reserved.
4  *
5  * By using this file, you agree to the terms and conditions set
6  * forth in the LICENSE file which can be found at the top level of
7  * the sendmail distribution.
8  *
9  */
10 
11 /*
12  * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska H�gskolan
13  * (Royal Institute of Technology, Stockholm, Sweden).
14  * All rights reserved.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  *
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  *
23  * 2. Redistributions in binary form must reproduce the above copyright
24  *    notice, this list of conditions and the following disclaimer in the
25  *    documentation and/or other materials provided with the distribution.
26  *
27  * 3. Neither the name of the Institute nor the names of its contributors
28  *    may be used to endorse or promote products derived from this software
29  *    without specific prior written permission.
30  *
31  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
32  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
35  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
40  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41  * SUCH DAMAGE.
42  */
43 
44 #include <sendmail.h>
45 #include <sm/sendmail.h>
46 #if DNSMAP || DANE
47 # if NAMED_BIND
48 #  if NETINET
49 #   include <netinet/in_systm.h>
50 #   include <netinet/ip.h>
51 #  endif
52 #  define _DEFINE_SMR_GLOBALS 1
53 #  include "sm_resolve.h"
54 
55 #include <arpa/inet.h>
56 
57 SM_RCSID("$Id: sm_resolve.c,v 8.40 2013-11-22 20:51:56 ca Exp $")
58 
59 static struct stot
60 {
61 	const char	*st_name;
62 	int		st_type;
63 } stot[] =
64 {
65 #  if NETINET
66 	{	"A",		T_A		},
67 #  endif
68 #  if NETINET6
69 	{	"AAAA",		T_AAAA		},
70 #  endif
71 	{	"NS",		T_NS		},
72 	{	"CNAME",	T_CNAME		},
73 	{	"PTR",		T_PTR		},
74 	{	"MX",		T_MX		},
75 	{	"TXT",		T_TXT		},
76 	{	"AFSDB",	T_AFSDB		},
77 	{	"SRV",		T_SRV		},
78 #  ifdef T_DS
79 	{	"DS",		T_DS		},
80 #  endif
81 	{	"RRSIG",	T_RRSIG		},
82 #  ifdef T_NSEC
83 	{	"NSEC",		T_NSEC		},
84 #  endif
85 #  ifdef T_DNSKEY
86 	{	"DNSKEY",	T_DNSKEY	},
87 #  endif
88 	{	"TLSA",		T_TLSA		},
89 	{	NULL,		0		}
90 };
91 
92 static DNS_REPLY_T *parse_dns_reply __P((unsigned char *, int, unsigned int));
93 #  if DNSSEC_TEST && defined(T_TLSA)
94 static char *hex2bin __P((const char *, int));
95 #  endif
96 
97 /*
98 **  DNS_STRING_TO_TYPE -- convert resource record name into type
99 **
100 **	Parameters:
101 **		name -- name of resource record type
102 **
103 **	Returns:
104 **		type if succeeded.
105 **		-1 otherwise.
106 */
107 
108 int
109 dns_string_to_type(name)
110 	const char *name;
111 {
112 	struct stot *p = stot;
113 
114 	for (p = stot; p->st_name != NULL; p++)
115 		if (SM_STRCASEEQ(name, p->st_name))
116 			return p->st_type;
117 	return -1;
118 }
119 
120 /*
121 **  DNS_TYPE_TO_STRING -- convert resource record type into name
122 **
123 **	Parameters:
124 **		type -- resource record type
125 **
126 **	Returns:
127 **		name if succeeded.
128 **		NULL otherwise.
129 */
130 
131 const char *
132 dns_type_to_string(type)
133 	int type;
134 {
135 	struct stot *p = stot;
136 
137 	for (p = stot; p->st_name != NULL; p++)
138 		if (type == p->st_type)
139 			return p->st_name;
140 	return NULL;
141 }
142 
143 /*
144 **  DNS_FREE_DATA -- free all components of a DNS_REPLY_T
145 **
146 **	Parameters:
147 **		dr -- pointer to DNS_REPLY_T
148 **
149 **	Returns:
150 **		none.
151 */
152 
153 void
154 dns_free_data(dr)
155 	DNS_REPLY_T *dr;
156 {
157 	RESOURCE_RECORD_T *rr;
158 
159 	if (dr == NULL)
160 		return;
161 	if (dr->dns_r_q.dns_q_domain != NULL)
162 		sm_free(dr->dns_r_q.dns_q_domain);
163 	for (rr = dr->dns_r_head; rr != NULL; )
164 	{
165 		RESOURCE_RECORD_T *tmp = rr;
166 
167 		if (rr->rr_domain != NULL)
168 			sm_free(rr->rr_domain);
169 		if (rr->rr_u.rr_data != NULL)
170 			sm_free(rr->rr_u.rr_data);
171 		rr = rr->rr_next;
172 		sm_free(tmp);
173 	}
174 	sm_free(dr);
175 }
176 
177 /*
178 **  BIN2HEX -- convert binary TLSA RR to hex string
179 **
180 **	Parameters:
181 **		tlsa -- pointer to result (allocated here)
182 **		p --  binary data (TLSA RR)
183 **		size -- length of p
184 **		min_size -- minimum expected size
185 **
186 **	Returns:
187 **		>0: length of string (*tlsa)
188 **		-1: error
189 */
190 
191 static int bin2hex __P((char **, unsigned char *, int, int));
192 
193 static int
194 bin2hex(tlsa, p, size, min_size)
195 	char **tlsa;
196 	unsigned char *p;
197 	int size;
198 	int min_size;
199 {
200 	int i, pos, txtlen;
201 
202 	txtlen = size * 3;
203 	if (txtlen <= size || size < min_size)
204 	{
205 		if (LogLevel > 5)
206 			sm_syslog(LOG_WARNING, NOQID,
207 				  "ERROR: bin2hex: size %d wrong", size);
208 		return -1;
209 	}
210 	*tlsa = (char *) sm_malloc(txtlen);
211 	if (*tlsa == NULL)
212 	{
213 		if (tTd(8, 17))
214 			sm_dprintf("len=%d, rr_data=NULL\n", txtlen);
215 		return -1;
216 	}
217 	snprintf(*tlsa, txtlen,
218 		"%02X %02X %02X", p[0], p[1], p[2]);
219 	pos = strlen(*tlsa);
220 
221 	/* why isn't there a print function like strlcat? */
222 	for (i = 3; i < size && pos < txtlen; i++, pos += 3)
223 		snprintf(*tlsa + pos, txtlen - pos, "%c%02X",
224 			(i == 3) ? ' ' : ':', p[i]);
225 
226 	return i;
227 }
228 
229 /*
230 **  PARSE_DNS_REPLY -- parse DNS reply data.
231 **
232 **	Parameters:
233 **		data -- pointer to dns data
234 **		len -- len of data
235 **		flags -- flags (RR_*)
236 **
237 **	Returns:
238 **		pointer to DNS_REPLY_T if succeeded.
239 **		NULL otherwise.
240 **
241 **	Note:
242 **		use dns_free_data() to free() the result when no longer needed.
243 */
244 
245 static DNS_REPLY_T *
246 parse_dns_reply(data, len, flags)
247 	unsigned char *data;
248 	int len;
249 	unsigned int flags;
250 {
251 	unsigned char *p;
252 	unsigned short ans_cnt, ui;
253 	int status;
254 	size_t l;
255 	char host[MAXHOSTNAMELEN];
256 	DNS_REPLY_T *dr;
257 	RESOURCE_RECORD_T **rr;
258 
259 	if (tTd(8, 90))
260 	{
261 		FILE *fp;
262 
263 		fp = fopen("dns.buffer", "w");
264 		if (fp != NULL)
265 		{
266 			fwrite(data, 1, len, fp);
267 			fclose(fp);
268 			fp = NULL;
269 		}
270 		else
271 			sm_dprintf("parse_dns_reply: fp=%p, e=%d\n",
272 				(void *)fp, errno);
273 	}
274 
275 	dr = (DNS_REPLY_T *) sm_malloc(sizeof(*dr));
276 	if (dr == NULL)
277 		return NULL;
278 	memset(dr, 0, sizeof(*dr));
279 
280 	p = data;
281 
282 	/* doesn't work on Crays? */
283 	memcpy(&dr->dns_r_h, p, sizeof(dr->dns_r_h));
284 	p += sizeof(dr->dns_r_h);
285 	status = dn_expand(data, data + len, p, host, sizeof(host));
286 	if (status < 0)
287 		goto error;
288 	dr->dns_r_q.dns_q_domain = sm_strdup(host);
289 	if (dr->dns_r_q.dns_q_domain == NULL)
290 		goto error;
291 
292 	ans_cnt = ntohs((unsigned short) dr->dns_r_h.ancount);
293 	if (tTd(8, 17))
294 		sm_dprintf("parse_dns_reply: ac=%d, ad=%d\n", ans_cnt,
295 			dr->dns_r_h.ad);
296 
297 	p += status;
298 	GETSHORT(dr->dns_r_q.dns_q_type, p);
299 	GETSHORT(dr->dns_r_q.dns_q_class, p);
300 	rr = &dr->dns_r_head;
301 	ui = 0;
302 	while (p < data + len && ui < ans_cnt)
303 	{
304 		int type, class, ttl, size, txtlen;
305 
306 		status = dn_expand(data, data + len, p, host, sizeof(host));
307 		if (status < 0)
308 			goto error;
309 		++ui;
310 		p += status;
311 		GETSHORT(type, p);
312 		GETSHORT(class, p);
313 		GETLONG(ttl, p);
314 		GETSHORT(size, p);
315 		if (p + size > data + len)
316 		{
317 			/*
318 			**  announced size of data exceeds length of
319 			**  data paket: someone is cheating.
320 			*/
321 
322 			if (LogLevel > 5)
323 				sm_syslog(LOG_WARNING, NOQID,
324 					  "ERROR: DNS RDLENGTH=%d > data len=%d",
325 					  size, len - (int)(p - data));
326 			goto error;
327 		}
328 		*rr = (RESOURCE_RECORD_T *) sm_malloc(sizeof(**rr));
329 		if (*rr == NULL)
330 			goto error;
331 		memset(*rr, 0, sizeof(**rr));
332 		(*rr)->rr_domain = sm_strdup(host);
333 		if ((*rr)->rr_domain == NULL)
334 			goto error;
335 		(*rr)->rr_type = type;
336 		(*rr)->rr_class = class;
337 		(*rr)->rr_ttl = ttl;
338 		(*rr)->rr_size = size;
339 		switch (type)
340 		{
341 		  case T_NS:
342 		  case T_CNAME:
343 		  case T_PTR:
344 			status = dn_expand(data, data + len, p, host,
345 					   sizeof(host));
346 			if (status < 0)
347 				goto error;
348 			if (tTd(8, 50))
349 				sm_dprintf("parse_dns_reply: type=%s, host=%s\n",
350 					dns_type_to_string(type), host);
351 			(*rr)->rr_u.rr_txt = sm_strdup(host);
352 			if ((*rr)->rr_u.rr_txt == NULL)
353 				goto error;
354 			break;
355 
356 		  case T_MX:
357 		  case T_AFSDB:
358 			status = dn_expand(data, data + len, p + 2, host,
359 					   sizeof(host));
360 			if (status < 0)
361 				goto error;
362 			l = strlen(host) + 1;
363 			(*rr)->rr_u.rr_mx = (MX_RECORD_T *)
364 				sm_malloc(sizeof(*((*rr)->rr_u.rr_mx)) + l);
365 			if ((*rr)->rr_u.rr_mx == NULL)
366 				goto error;
367 			(*rr)->rr_u.rr_mx->mx_r_preference = (p[0] << 8) | p[1];
368 			(void) sm_strlcpy((*rr)->rr_u.rr_mx->mx_r_domain,
369 					  host, l);
370 			if (tTd(8, 50))
371 				sm_dprintf("mx=%s, pref=%d\n", host,
372 					(*rr)->rr_u.rr_mx->mx_r_preference);
373 			break;
374 
375 		  case T_SRV:
376 			status = dn_expand(data, data + len, p + 6, host,
377 					   sizeof(host));
378 			if (status < 0)
379 				goto error;
380 			l = strlen(host) + 1;
381 			(*rr)->rr_u.rr_srv = (SRV_RECORDT_T*)
382 				sm_malloc(sizeof(*((*rr)->rr_u.rr_srv)) + l);
383 			if ((*rr)->rr_u.rr_srv == NULL)
384 				goto error;
385 			(*rr)->rr_u.rr_srv->srv_r_priority = (p[0] << 8) | p[1];
386 			(*rr)->rr_u.rr_srv->srv_r_weight = (p[2] << 8) | p[3];
387 			(*rr)->rr_u.rr_srv->srv_r_port = (p[4] << 8) | p[5];
388 			(void) sm_strlcpy((*rr)->rr_u.rr_srv->srv_r_target,
389 					  host, l);
390 			break;
391 
392 		  case T_TXT:
393 
394 			/*
395 			**  The TXT record contains the length as
396 			**  leading byte, hence the value is restricted
397 			**  to 255, which is less than the maximum value
398 			**  of RDLENGTH (size). Nevertheless, txtlen
399 			**  must be less than size because the latter
400 			**  specifies the length of the entire TXT
401 			**  record.
402 			*/
403 
404 			txtlen = *p;
405 			if (txtlen >= size)
406 			{
407 				if (LogLevel > 5)
408 					sm_syslog(LOG_WARNING, NOQID,
409 						  "ERROR: DNS TXT record size=%d <= text len=%d",
410 						  size, txtlen);
411 				goto error;
412 			}
413 			(*rr)->rr_u.rr_txt = (char *) sm_malloc(txtlen + 1);
414 			if ((*rr)->rr_u.rr_txt == NULL)
415 				goto error;
416 			(void) sm_strlcpy((*rr)->rr_u.rr_txt, (char*) p + 1,
417 					  txtlen + 1);
418 			break;
419 
420 #  ifdef T_TLSA
421 		  case T_TLSA:
422 			if (tTd(8, 61))
423 				sm_dprintf("parse_dns_reply: TLSA, size=%d, flags=%X\n",
424 					size, flags);
425 			if ((flags & RR_AS_TEXT) != 0)
426 			{
427 				txtlen = bin2hex((char **)&((*rr)->rr_u.rr_data),
428 						p, size, 4);
429 				if (txtlen <= 0)
430 					goto error;
431 				break;
432 			}
433 			/* FALLTHROUGH */
434 			/* return "raw" data for caller to use as it pleases */
435 #  endif /* T_TLSA */
436 
437 		  default:
438 			(*rr)->rr_u.rr_data = (unsigned char*) sm_malloc(size);
439 			if ((*rr)->rr_u.rr_data == NULL)
440 				goto error;
441 			(void) memcpy((*rr)->rr_u.rr_data, p, size);
442 			if (tTd(8, 61) && type == T_A)
443 			{
444 				SOCKADDR addr;
445 
446 				(void) memcpy((void *)&addr.sin.sin_addr.s_addr, p, size);
447 				sm_dprintf("parse_dns_reply: IPv4=%s\n",
448 					inet_ntoa(addr.sin.sin_addr));
449 			}
450 			break;
451 		}
452 		p += size;
453 		rr = &(*rr)->rr_next;
454 	}
455 	*rr = NULL;
456 	return dr;
457 
458   error:
459 	dns_free_data(dr);
460 	return NULL;
461 }
462 
463 #  if DNSSEC_TEST
464 
465 #   include <arpa/nameser.h>
466 #   if _FFR_8BITENVADDR
467 #    include <sm/sendmail.h>
468 #   endif
469 
470 static int gen_dns_reply __P((unsigned char *, int, unsigned char *,
471 		const char *, int, const char *, int, int, int, int,
472 		const char *, int, int, int));
473 static int dnscrtrr __P((const char *, const char *, int, char *, int,
474 	unsigned int, int *, int *, unsigned char *, int, unsigned char *));
475 
476 /*
477 **  HERRNO2TXT -- return error text for h_errno
478 **
479 **	Parameters:
480 **		e -- h_errno
481 **
482 **	Returns:
483 **		DNS error text if available
484 */
485 
486 const char *
487 herrno2txt(e)
488 	int e;
489 {
490 	switch (e)
491 	{
492 	  case NETDB_INTERNAL:
493 		return "see errno";
494 	  case NETDB_SUCCESS:
495 		return "OK";
496 	  case HOST_NOT_FOUND:
497 		return "HOST_NOT_FOUND";
498 	  case TRY_AGAIN:
499 		return "TRY_AGAIN";
500 	  case NO_RECOVERY:
501 		return "NO_RECOVERY";
502 	  case NO_DATA:
503 		return "NO_DATA";
504 	}
505 	return "bogus h_errno";
506 }
507 
508 /*
509 **  GEN_DNS_REPLY -- generate DNS reply data.
510 **
511 **	Parameters:
512 **		buf -- buffer to which DNS data is written
513 **		buflen -- length of buffer
514 **		bufpos -- position in buffer where DNS RRs are appended
515 **		query -- name of query
516 **		qtype -- resource record type of query
517 **		domain -- name of domain which has been "found"
518 **		class -- resource record class
519 **		type -- resource record type
520 **		ttl -- TTL
521 **		size -- size of data
522 **		data -- data
523 **		txtlen -- length of text
524 **		pref -- MX preference
525 **		ad -- ad flag
526 **
527 **	Returns:
528 **		>0 length of buffer that has been used.
529 **		<0 error
530 */
531 
532 static int
533 gen_dns_reply(buf, buflen, bufpos, query, qtype, domain, class, type, ttl, size, data, txtlen, pref, ad)
534 	unsigned char *buf;
535 	int buflen;
536 	unsigned char *bufpos;
537 	const char *query;
538 	int qtype;
539 	const char *domain;
540 	int class;
541 	int type;
542 	int ttl;
543 	int size;
544 	const char *data;
545 	int txtlen;
546 	int pref;
547 	int ad;
548 {
549 	unsigned short ans_cnt;
550 	HEADER *hp;
551 	unsigned char *cp, *ep;
552 	int n;
553 	static unsigned char *dnptrs[20], **dpp, **lastdnptr;
554 
555 #define DN_COMP_CHK	do	\
556 	{	\
557 		if (n < 0)	\
558 		{	\
559 			if (tTd(8, 91))	\
560 				sm_dprintf("gen_dns_reply: dn_comp=%d\n", n); \
561 			return n;	\
562 		}	\
563 	} while (0)
564 
565 	SM_REQUIRE(NULL != buf);
566 	SM_REQUIRE(buflen >= HFIXEDSZ);
567 	SM_REQUIRE(query != NULL);
568 	hp = (HEADER *) buf;
569 	ep = buf + buflen;
570 	cp = buf + HFIXEDSZ;
571 
572 	if (bufpos != NULL)
573 		cp = bufpos;
574 	else
575 	{
576 		sm_dprintf("gen_dns_reply: query=%s, domain=%s, type=%s, size=%d, ad=%d\n",
577 			query, domain, dns_type_to_string(type), size, ad);
578 		dpp = dnptrs;
579 		*dpp++ = buf;
580 		*dpp++ = NULL;
581 		lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
582 
583 		memset(buf, 0, HFIXEDSZ);
584 		hp->id = 0xdead;	/* HACK */
585 		hp->qr = 1;
586 		hp->opcode = QUERY;
587 		hp->rd = 0;	/* recursion desired? */
588 		hp->rcode = 0; /* !!! */
589 		/* hp->aa = ?;	* !!! */
590 		/* hp->tc = ?;	* !!! */
591 		/* hp->ra = ?;	* !!! */
592 		hp->qdcount = htons(1);
593 		hp->ancount = 0;
594 
595 		n = dn_comp(query, cp, ep - cp - QFIXEDSZ, dnptrs, lastdnptr);
596 		DN_COMP_CHK;
597 		cp += n;
598 		PUTSHORT(qtype, cp);
599 		PUTSHORT(class, cp);
600 	}
601 	hp->ad = ad;
602 
603 	if (ep - cp < QFIXEDSZ)
604 	{
605 		if (tTd(8, 91))
606 			sm_dprintf("gen_dns_reply: ep-cp=%ld\n",
607 				(long) (ep - cp));
608 		return (-1);
609 	}
610 	n = dn_comp(domain, cp, ep - cp - QFIXEDSZ, dnptrs, lastdnptr);
611 	DN_COMP_CHK;
612 	cp += n;
613 	PUTSHORT(type, cp);
614 	PUTSHORT(class, cp);
615 	PUTLONG(ttl, cp);
616 
617 	ans_cnt = ntohs((unsigned short) hp->ancount);
618 	++ans_cnt;
619 	hp->ancount = htons((unsigned short) ans_cnt);
620 
621 	switch (type)
622 	{
623 	  case T_MX:
624 		n = dn_comp(data, cp + 4, ep - cp - QFIXEDSZ, dnptrs, lastdnptr);
625 		DN_COMP_CHK;
626 		PUTSHORT(n + 2, cp);
627 		PUTSHORT(pref, cp);
628 		cp += n;
629 		break;
630 
631 	  case T_TXT:
632 		if (txtlen >= size)
633 			return -1;
634 		PUTSHORT(txtlen, cp);
635 		(void) sm_strlcpy((char *)cp, data, txtlen + 1);
636 		cp += txtlen;
637 		break;
638 
639 	  case T_CNAME:
640 		n = dn_comp(data, cp + 2, ep - cp - QFIXEDSZ, dnptrs, lastdnptr);
641 		DN_COMP_CHK;
642 		PUTSHORT(n, cp);
643 		cp += n;
644 		break;
645 
646 #   if defined(T_TLSA)
647 	  case T_TLSA:
648 		{
649 		char *tlsa;
650 
651 		tlsa = hex2bin(data, size);
652 		if (tlsa == NULL)
653 			return (-1);
654 		n = size / 2;
655 		PUTSHORT(n, cp);
656 		(void) memcpy(cp, tlsa, n);
657 		cp += n;
658 		}
659 		break;
660 #   endif /* T_TLSA */
661 
662 	  default:
663 		PUTSHORT(size, cp);
664 		(void) memcpy(cp, data, size);
665 		cp += size;
666 		break;
667 	}
668 
669 	return (cp - buf);
670 }
671 
672 /*
673 **  SETHERRNOFROMSTRING -- set h_errno based on text
674 **
675 **	Parameters:
676 **		str -- string which might contain h_errno text
677 **		prc -- pointer to rcode (EX_*)
678 **
679 **	Returns:
680 **		h_errno if found
681 **		0 otherwise
682 */
683 
684 int
685 setherrnofromstring(str, prc)
686 	const char *str;
687 	int *prc;
688 {
689 	SM_SET_H_ERRNO(0);
690 	if (SM_IS_EMPTY(str))
691 		return 0;
692 	if (strstr(str, "herrno:") == NULL)
693 		return 0;
694 	if (prc != NULL)
695 		*prc = EX_NOHOST;
696 	if (strstr(str, "host_not_found"))
697 		SM_SET_H_ERRNO(HOST_NOT_FOUND);
698 	else if (strstr(str, "try_again"))
699 	{
700 		SM_SET_H_ERRNO(TRY_AGAIN);
701 		if (prc != NULL)
702 			*prc = EX_TEMPFAIL;
703 	}
704 	else if (strstr(str, "no_recovery"))
705 		SM_SET_H_ERRNO(NO_RECOVERY);
706 	else if (strstr(str, "no_data"))
707 		SM_SET_H_ERRNO(NO_DATA);
708 	else
709 		SM_SET_H_ERRNO(NETDB_INTERNAL);
710 	return h_errno;
711 }
712 
713 /*
714 **  GETTTLFROMSTRING -- extract ttl from a string
715 **
716 **	Parameters:
717 **		str -- string which might contain ttl
718 **
719 **	Returns:
720 **		ttl if found
721 **		0 otherwise
722 */
723 
724 int
725 getttlfromstring(str)
726 	const char *str;
727 {
728 	if (SM_IS_EMPTY(str))
729 		return 0;
730 #define TTL_PRE "ttl="
731 	if (strstr(str, TTL_PRE) == NULL)
732 		return 0;
733 	return strtoul(str + strlen(TTL_PRE), NULL, 10);
734 }
735 
736 /*
737 **  DNS_SETNS -- set one NS in resolver context
738 **
739 **	Parameters:
740 **		ns -- (IPv4 address of) nameserver
741 **		port -- nameserver port
742 **
743 **	Returns:
744 **		None.
745 */
746 
747 static void dns_setns __P((struct in_addr *, unsigned int));
748 
749 static void
750 dns_setns(ns, port)
751 	struct in_addr *ns;
752 	unsigned int port;
753 {
754 	_res.nsaddr_list[0].sin_family = AF_INET;
755 	_res.nsaddr_list[0].sin_addr = *ns;
756 	if (port != 0)
757 		_res.nsaddr_list[0].sin_port = htons(port);
758 	_res.nscount = 1;
759 	if (tTd(8, 61))
760 		sm_dprintf("dns_setns(%s,%u)\n", inet_ntoa(*ns), port);
761 }
762 
763 /*
764 **  NSPORTIP -- parse port@IPv4 and set NS accordingly
765 **
766 **	Parameters:
767 **		p -- port@Ipv4
768 **
769 **	Returns:
770 **		<0: error
771 **		>0: ok
772 **
773 **	Side Effects:
774 **		sets NS for DNS lookups
775 */
776 
777 /*
778 **  There should be a generic function for this...
779 **  milter_open(), socket_map_open(), others?
780 */
781 
782 int
783 nsportip(p)
784 	char *p;
785 {
786 	char *h;
787 	int r;
788 	unsigned short port;
789 	struct in_addr nsip;
790 
791 	if (SM_IS_EMPTY(p))
792 		return -1;
793 
794 	port = 0;
795 	while (SM_ISSPACE(*p))
796 		p++;
797 	if (*p == '\0')
798 		return -1;
799 	h = strchr(p, '@');
800 	if (h != NULL)
801 	{
802 		*h = '\0';
803 		if (isascii(*p) && isdigit(*p))
804 			port = atoi(p);
805 		*h = '@';
806 		p = h + 1;
807 	}
808 	h = strchr(p, ' ');
809 	if (h != NULL)
810 		*h = '\0';
811 	r = inet_pton(AF_INET, p, &nsip);
812 	if (r > 0)
813 	{
814 		if ((_res.options & RES_INIT) == 0)
815 			(void) res_init();
816 		dns_setns(&nsip, port);
817 	}
818 	if (h != NULL)
819 		*h = ' ';
820 	return r > 0 ? 0 : -1;
821 }
822 
823 #   if defined(T_TLSA)
824 /*
825 **  HEX2BIN -- convert hex string to binary TLSA RR
826 **
827 **	Parameters:
828 **		p --  hex representation of TLSA RR
829 **		size -- length of p
830 **
831 **	Returns:
832 **		pointer to binary TLSA RR
833 **		NULL: error
834 */
835 
836 static char *
837 hex2bin(p, size)
838 	const char *p;
839 	int size;
840 {
841 	int i, pos, txtlen;
842 	char *tlsa;
843 
844 	txtlen = size / 2;
845 	if (txtlen * 2 == size)
846 	{
847 		if (LogLevel > 5)
848 			sm_syslog(LOG_WARNING, NOQID,
849 				  "ERROR: hex2bin: size %d wrong", size);
850 		return NULL;
851 	}
852 	tlsa = sm_malloc(txtlen + 1);
853 	if (tlsa == NULL)
854 	{
855 		if (tTd(8, 17))
856 			sm_dprintf("len=%d, tlsa=NULL\n", txtlen);
857 		return NULL;
858 	}
859 
860 #define CHAR2INT(c)	(((c) <= '9') ? ((c) - '0') : (toupper(c) - 'A' + 10))
861 	for (i = 0, pos = 0; i + 1 < size && pos < txtlen; i += 2, pos++)
862 		tlsa[pos] = CHAR2INT(p[i]) * 16 + CHAR2INT(p[i+1]);
863 
864 	return tlsa;
865 }
866 #   endif /* T_TLSA */
867 
868 const char *
869 rr_type2tag(rr_type)
870 	int rr_type;
871 {
872 	switch (rr_type)
873 	{
874 	  case T_A:
875 		return "ipv4";
876 #   if NETINET6
877 	  case T_AAAA:
878 		return "ipv6";
879 #   endif
880 	  case T_CNAME:
881 		return "cname";
882 	  case T_MX:
883 		return "mx";
884 #   ifdef T_TLSA
885 	  case T_TLSA:
886 		return "tlsa";
887 #   endif
888 	}
889 	return NULL;
890 }
891 
892 /*
893 **  DNSCRTRR -- create DNS RR
894 **
895 **	Parameters:
896 **		domain -- original query domain
897 **		query -- name of query
898 **		qtype -- resource record type of query
899 **		value -- (list of) data to set
900 **		rr_type -- resource record type
901 **		flags -- flags how to handle various lookups
902 **		herr -- (pointer to) h_errno (output if non-NULL)
903 **		adp -- (pointer to) ad flag
904 **		answer -- buffer for RRs
905 **		anslen -- size of answer
906 **		anspos -- current position in answer
907 **
908 **	Returns:
909 **		>0: length of data in answer
910 **		<0: error, check *herr
911 */
912 
913 static int
914 dnscrtrr(domain, query, qtype, value, rr_type, flags, herr, adp, answer, anslen, anspos)
915 	const char *domain;
916 	const char *query;
917 	int qtype;
918 	char *value;
919 	int rr_type;
920 	unsigned int flags;
921 	int *herr;
922 	int *adp;
923 	unsigned char *answer;
924 	int anslen;
925 	unsigned char *anspos;
926 {
927 	SOCKADDR addr;
928 	int ttl, ad, rlen;
929 	char *p, *token;
930 	char data[IN6ADDRSZ];
931 	char rhs[MAXLINE];
932 
933 	rlen = -1;
934 	if (SM_IS_EMPTY(value))
935 		return rlen;
936 	SM_REQUIRE(adp != NULL);
937 	(void) sm_strlcpy(rhs, value, sizeof(rhs));
938 	p = rhs;
939 	if (setherrnofromstring(p, NULL) != 0)
940 	{
941 		if (herr != NULL)
942 			*herr = h_errno;
943 		if (tTd(8, 16))
944 			sm_dprintf("dnscrtrr rhs=%s h_errno=%d (%s)\n",
945 				p, h_errno, herrno2txt(h_errno));
946 		return rlen;
947 	}
948 
949 	ttl = 0;
950 	ad = 0;
951 	for (token = p; token != NULL && *token != '\0'; token = p)
952 	{
953 		rlen = 0;
954 		while (p != NULL && *p != '\0' && !SM_ISSPACE(*p))
955 			++p;
956 		if (SM_ISSPACE(*p))
957 			*p++ = '\0';
958 		sm_dprintf("dnscrtrr: token=%s\n", token);
959 		if (strcmp(token, "ad") == 0)
960 		{
961 			bool adflag;
962 
963 			adflag = (_res.options & RES_USE_DNSSEC) != 0;
964 
965 			/* maybe print this only for the final RR? */
966 			if (tTd(8, 61))
967 				sm_dprintf("dnscrtrr: ad=1, adp=%d, adflag=%d\n",
968 					*adp, adflag);
969 			if (*adp != 0 && adflag)
970 			{
971 				*adp = 1;
972 				ad = 1;
973 			}
974 			continue;
975 		}
976 		if (ttl == 0 && (ttl = getttlfromstring(token)) > 0)
977 		{
978 			if (tTd(8, 61))
979 				sm_dprintf("dnscrtrr: ttl=%d\n", ttl);
980 			continue;
981 		}
982 
983 		if (rr_type == T_A)
984 		{
985 			addr.sin.sin_addr.s_addr = inet_addr(token);
986 			(void) memmove(data, (void *)&addr.sin.sin_addr.s_addr,
987 				INADDRSZ);
988 			rlen = gen_dns_reply(answer, anslen, anspos,
989 				query, qtype, domain, C_IN, rr_type, ttl,
990 				INADDRSZ, data, 0, 0, ad);
991 		}
992 
993 #   if NETINET6
994 		if (rr_type == T_AAAA)
995 		{
996 			anynet_pton(AF_INET6, token, &addr.sin6.sin6_addr);
997 			memmove(data, (void *)&addr.sin6.sin6_addr, IN6ADDRSZ);
998 			rlen = gen_dns_reply(answer, anslen, anspos,
999 				query, qtype, domain, C_IN, rr_type, ttl,
1000 				IN6ADDRSZ, data, 0, 0, ad);
1001 		}
1002 #   endif /* NETINET6 */
1003 
1004 		if (rr_type == T_MX)
1005 		{
1006 			char *endptr;
1007 			int pref;
1008 
1009 			pref = (int) strtoul(token, &endptr, 10);
1010 			if (endptr == NULL || *endptr != ':')
1011 				goto error;
1012 			token = endptr + 1;
1013 			rlen = gen_dns_reply(answer, anslen, anspos,
1014 				query, qtype, domain, C_IN, rr_type, ttl,
1015 				strlen(token) + 1, token, 0, pref, ad);
1016 			if (tTd(8, 50))
1017 				sm_dprintf("dnscrtrr: mx=%s, pref=%d, rlen=%d\n",
1018 					token, pref, rlen);
1019 		}
1020 
1021 #   ifdef T_TLSA
1022 		if (rr_type == T_TLSA)
1023 			rlen = gen_dns_reply(answer, anslen, anspos,
1024 				query, qtype, domain, C_IN, rr_type, ttl,
1025 				strlen(token) + 1, token, 0, 0, ad);
1026 #   endif
1027 
1028 		if (rr_type == T_CNAME)
1029 			rlen = gen_dns_reply(answer, anslen, anspos,
1030 				query, qtype, domain, C_IN, rr_type, ttl,
1031 				strlen(token), token, 0, 0, ad);
1032 		if (rlen < 0)
1033 			goto error;
1034 		if (rlen > 0)
1035 			anspos = answer + rlen;
1036 	}
1037 
1038 	if (ad != 1)
1039 		*adp = 0;
1040 
1041 	return rlen;
1042 
1043   error:
1044 	if (herr != NULL && 0 == *herr)
1045 		*herr = NO_RECOVERY;
1046 	return -1;
1047 }
1048 
1049 /*
1050 **  TSTDNS_SEARCH -- replacement for res_search() for testing
1051 **
1052 **	Parameters:
1053 **		domain -- query domain
1054 **		class -- class
1055 **		type -- resource record type
1056 **		answer -- buffer for RRs
1057 **		anslen -- size of answer
1058 **
1059 **	Returns:
1060 **		>0: length of data in answer
1061 **		<0: error, check h_errno
1062 */
1063 
1064 int
1065 tstdns_search(domain, class, type, answer, anslen)
1066 	const char *domain;
1067 	int class;
1068 	int type;
1069 	unsigned char *answer;
1070 	int anslen;
1071 {
1072 	int rlen, ad, maprcode, cnt, flags, herr;
1073 	bool found_cname;
1074 	const char *query;
1075 	char *p;
1076 	const char *tag;
1077 	char *av[2];
1078 	STAB *map;
1079 #   if _FFR_8BITENVADDR
1080 	char qbuf[MAXNAME_I];
1081 	char *qdomain;
1082 #   else
1083 #    define qdomain domain
1084 #   endif
1085 	char key[MAXNAME_I + 16];
1086 	char rhs[MAXLINE];
1087 	unsigned char *anspos;
1088 
1089 	rlen = -1;
1090 	herr = 0;
1091 	if (class != C_IN)
1092 		goto error;
1093 	if (SM_IS_EMPTY(domain))
1094 		goto error;
1095 	tag = rr_type2tag(type);
1096 	if (tag == NULL)
1097 		goto error;
1098 	maprcode = EX_OK;
1099 	ad = -1;
1100 	flags = 0;
1101 #   if _FFR_8BITENVADDR
1102 	if (tTd(8, 62))
1103 		sm_dprintf("domain=%s\n", domain);
1104 	(void) dequote_internal_chars((char *)domain, qbuf, sizeof(qbuf));
1105 	query = qbuf;
1106 	qdomain = qbuf;
1107 	if (tTd(8, 63))
1108 		sm_dprintf("qdomain=%s\n", qdomain);
1109 #   else
1110 	query = domain;
1111 #   endif /* _FFR_8BITENVADDR */
1112 	anspos = NULL;
1113 
1114 	map = stab("access", ST_MAP, ST_FIND);
1115 	if (NULL == map)
1116 	{
1117 		sm_dprintf("access map not found\n");
1118 		goto error;
1119 	}
1120 	if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
1121 	    !openmap(&(map->s_map)))
1122 	{
1123 		sm_dprintf("access map open failed\n");
1124 		goto error;
1125 	}
1126 
1127 /*
1128 **  Look up tag:domain, if not found and domain does not end with a dot
1129 **  (and the proper debug level is selected), also try with trailing dot.
1130 */
1131 
1132 #define SM_LOOKUP2(tag)	\
1133 	do {	\
1134 		int len;	\
1135 				\
1136 		len = strlen(qdomain);	\
1137 		av[0] = key;	\
1138 		av[1] = NULL;	\
1139 		snprintf(key, sizeof(key), "%s:%s", tag, qdomain); \
1140 		p = (*map->s_map.map_class->map_lookup)(&map->s_map, key, av, \
1141 			&maprcode);	\
1142 		if (p != NULL)	\
1143 			break;	\
1144 		if (!tTd(8, 112) || (len > 0 && '.' == qdomain[len - 1])) \
1145 			break;	\
1146 		snprintf(key, sizeof(key), "%s:%s.", tag, qdomain); \
1147 		p = (*map->s_map.map_class->map_lookup)(&map->s_map, key, av, \
1148 			&maprcode);	\
1149 	} while (0)
1150 
1151 	cnt = 0;
1152 	found_cname = false;
1153 	while (cnt < 6)
1154 	{
1155 		char *last;
1156 
1157 		/* Should this try with/without trailing dot? */
1158 		SM_LOOKUP2(tag);
1159 		if (p != NULL)
1160 		{
1161 			sm_dprintf("access map lookup key=%s, value=%s\n", key,
1162 				p);
1163 			break;
1164 		}
1165 		if (NULL == p && (flags & RR_NO_CNAME) == 0)
1166 		{
1167 			sm_dprintf("access map lookup failed key=%s, try cname\n",
1168 				key);
1169 			SM_LOOKUP2("cname");
1170 			if (p != NULL)
1171 			{
1172 				sm_dprintf("cname lookup key=%s, value=%s, ad=%d\n",
1173 					key, p, ad);
1174 				rlen = dnscrtrr(qdomain, query, type, p, T_CNAME,
1175 						flags, &herr, &ad, answer,
1176 						anslen, anspos);
1177 				if (rlen < 0)
1178 					goto error;
1179 				if (rlen > 0)
1180 					anspos = answer + rlen;
1181 				found_cname = true;
1182 			}
1183 		}
1184 		if (NULL == p)
1185 			break;
1186 
1187 		(void) sm_strlcpy(rhs, p, sizeof(rhs));
1188 		p = rhs;
1189 
1190 		/* skip (leading) ad/ttl: look for last ' ' */
1191 		if ((last = strrchr(p, ' ')) != NULL && last[1] != '\0')
1192 			qdomain = last + 1;
1193 		else
1194 			qdomain = p;
1195 		++cnt;
1196 	}
1197 	if (NULL == p)
1198 	{
1199 		int t;
1200 		char *tags[] = { "ipv4", "mx", "tlsa",
1201 #   if NETINET6
1202 			"ipv6",
1203 #   endif
1204 			NULL
1205 		};
1206 
1207 		for (t = 0; tags[t] != NULL; t++)
1208 		{
1209 			if (strcmp(tag, tags[t]) == 0)
1210 				continue;
1211 			SM_LOOKUP2(tags[t]);
1212 			if (p != NULL)
1213 			{
1214 				sm_dprintf("access map lookup failed key=%s:%s, but found key=%s\n",
1215 					tag, qdomain, key);
1216 				herr = NO_DATA;
1217 				goto error;
1218 			}
1219 		}
1220 		sm_dprintf("access map lookup failed key=%s\n", key);
1221 		herr = HOST_NOT_FOUND;
1222 		goto error;
1223 	}
1224 	if (found_cname && (flags & RR_ONLY_CNAME) != 0)
1225 		return rlen;
1226 	rlen = dnscrtrr(qdomain, query, type, p, type, flags, &herr, &ad,
1227 			answer, anslen, anspos);
1228 	if (rlen < 0)
1229 		goto error;
1230 	return rlen;
1231 
1232   error:
1233 	if (0 == herr)
1234 		herr = NO_RECOVERY;
1235 	SM_SET_H_ERRNO(herr);
1236 	sm_dprintf("rlen=%d, herr=%d\n", rlen, herr);
1237 	return -1;
1238 }
1239 
1240 /*
1241 **  TSTDNS_QUERYDOMAIN -- replacement for res_querydomain() for testing
1242 **
1243 **	Parameters:
1244 **		name -- query name
1245 **		domain -- query domain
1246 **		class -- class
1247 **		type -- resource record type
1248 **		answer -- buffer for RRs
1249 **		anslen -- size of answer
1250 **
1251 **	Returns:
1252 **		>0: length of data in answer
1253 **		<0: error, check h_errno
1254 */
1255 
1256 int
1257 tstdns_querydomain(name, domain, class, type, answer, anslen)
1258 	const char *name;
1259 	const char *domain;
1260 	int class;
1261 	int type;
1262 	unsigned char *answer;
1263 	int anslen;
1264 {
1265 	char query[MAXNAME_I];
1266 	int len;
1267 
1268 	if (NULL == name)
1269 		goto error;
1270 	if (SM_IS_EMPTY(domain))
1271 		return tstdns_search(name, class, type, answer, anslen);
1272 
1273 	len = snprintf(query, sizeof(query), "%s.%s", name, domain);
1274 	if (len >= (int)sizeof(query))
1275 		goto error;
1276 	return tstdns_search(query, class, type, answer, anslen);
1277 
1278   error:
1279 	SM_SET_H_ERRNO(NO_RECOVERY);
1280 	return -1;
1281 }
1282 
1283 #  endif /* DNSSEC_TEST */
1284 
1285 /*
1286 **  DNS_LOOKUP_INT -- perform DNS lookup
1287 **
1288 **	Parameters:
1289 **		domain -- name to lookup
1290 **		rr_class -- resource record class
1291 **		rr_type -- resource record type
1292 **		retrans -- retransmission timeout
1293 **		retry -- number of retries
1294 **		options -- DNS resolver options
1295 **		flags -- currently only passed to parse_dns_reply()
1296 **		err -- (pointer to) errno (output if non-NULL)
1297 **		herr -- (pointer to) h_errno (output if non-NULL)
1298 **
1299 **	Returns:
1300 **		result of lookup if succeeded.
1301 **		NULL otherwise.
1302 */
1303 
1304 DNS_REPLY_T *
1305 dns_lookup_int(domain, rr_class, rr_type, retrans, retry, options, flags, err, herr)
1306 	const char *domain;
1307 	int rr_class;
1308 	int rr_type;
1309 	time_t retrans;
1310 	int retry;
1311 	unsigned int options;
1312 	unsigned int flags;
1313 	int *err;
1314 	int *herr;
1315 {
1316 	int len;
1317 	unsigned long old_options = 0;
1318 	time_t save_retrans = 0;
1319 	int save_retry = 0;
1320 	DNS_REPLY_T *dr = NULL;
1321 	querybuf reply_buf;
1322 	unsigned char *reply;
1323 	int (*resfunc) __P((const char *, int, int, u_char *, int));
1324 
1325 #  define SMRBSIZE ((int) sizeof(reply_buf))
1326 #  ifndef IP_MAXPACKET
1327 #   define IP_MAXPACKET	65535
1328 #  endif
1329 
1330 	resfunc = res_search;
1331 #  if DNSSEC_TEST
1332 	if (tTd(8, 110))
1333 		resfunc = tstdns_search;
1334 #  endif
1335 
1336 	old_options = _res.options;
1337 	_res.options |= options;
1338 	if (err != NULL)
1339 		*err = 0;
1340 	if (herr != NULL)
1341 		*herr = 0;
1342 	if (tTd(8, 16))
1343 	{
1344 		_res.options |= RES_DEBUG;
1345 		sm_dprintf("dns_lookup_int(%s, %d, %s, %x)\n", domain,
1346 			   rr_class, dns_type_to_string(rr_type), options);
1347 	}
1348 #  if DNSSEC_TEST
1349 	if (tTd(8, 15))
1350 		sm_dprintf("NS=%s, port=%d\n",
1351 			inet_ntoa(_res.nsaddr_list[0].sin_addr),
1352 			ntohs(_res.nsaddr_list[0].sin_port));
1353 #  endif
1354 	if (retrans > 0)
1355 	{
1356 		save_retrans = _res.retrans;
1357 		_res.retrans = retrans;
1358 	}
1359 	if (retry > 0)
1360 	{
1361 		save_retry = _res.retry;
1362 		_res.retry = retry;
1363 	}
1364 	errno = 0;
1365 	SM_SET_H_ERRNO(0);
1366 	reply = (unsigned char *)&reply_buf;
1367 	len = (*resfunc)(domain, rr_class, rr_type, reply, SMRBSIZE);
1368 	if (len >= SMRBSIZE)
1369 	{
1370 		if (len >= IP_MAXPACKET)
1371 		{
1372 			if (tTd(8, 4))
1373 				sm_dprintf("dns_lookup: domain=%s, length=%d, default_size=%d, max=%d, status=response too long\n",
1374 					   domain, len, SMRBSIZE, IP_MAXPACKET);
1375 		}
1376 		else
1377 		{
1378 			if (tTd(8, 6))
1379 				sm_dprintf("dns_lookup: domain=%s, length=%d, default_size=%d, max=%d, status=response longer than default size, resizing\n",
1380 					   domain, len, SMRBSIZE, IP_MAXPACKET);
1381 			reply = (unsigned char *)sm_malloc(IP_MAXPACKET);
1382 			if (reply == NULL)
1383 				SM_SET_H_ERRNO(TRY_AGAIN);
1384 			else
1385 			{
1386 				SM_SET_H_ERRNO(0);
1387 				len = (*resfunc)(domain, rr_class, rr_type,
1388 						 reply, IP_MAXPACKET);
1389 			}
1390 		}
1391 	}
1392 	_res.options = old_options;
1393 	if (len < 0)
1394 	{
1395 		if (err != NULL)
1396 			*err = errno;
1397 		if (herr != NULL)
1398 			*herr = h_errno;
1399 		if (tTd(8, 16))
1400 		{
1401 			sm_dprintf("dns_lookup_int(%s, %d, %s, %x)=%d, errno=%d, h_errno=%d"
1402 #  if DNSSEC_TEST
1403 				" (%s)"
1404 #  endif
1405 				"\n",
1406 				domain, rr_class, dns_type_to_string(rr_type),
1407 				options, len, errno, h_errno
1408 #  if DNSSEC_TEST
1409 				, herrno2txt(h_errno)
1410 #  endif
1411 				);
1412 		}
1413 	}
1414 	else if (tTd(8, 16))
1415 	{
1416 		sm_dprintf("dns_lookup_int(%s, %d, %s, %x)=%d\n",
1417 			domain, rr_class, dns_type_to_string(rr_type),
1418 			options, len);
1419 	}
1420 	if (len >= 0 && len < IP_MAXPACKET && reply != NULL)
1421 		dr = parse_dns_reply(reply, len, flags);
1422 	if (reply != (unsigned char *)&reply_buf && reply != NULL)
1423 	{
1424 		sm_free(reply);
1425 		reply = NULL;
1426 	}
1427 	if (retrans > 0)
1428 		_res.retrans = save_retrans;
1429 	if (retry > 0)
1430 		_res.retry = save_retry;
1431 	return dr;
1432 }
1433 
1434 /*
1435 **  DNS_LOOKUP_MAP -- perform DNS map lookup
1436 **
1437 **	Parameters:
1438 **		domain -- name to lookup
1439 **		rr_class -- resource record class
1440 **		rr_type -- resource record type
1441 **		retrans -- retransmission timeout
1442 **		retry -- number of retries
1443 **		options -- DNS resolver options
1444 **
1445 **	Returns:
1446 **		result of lookup if succeeded.
1447 **		NULL otherwise.
1448 */
1449 
1450 DNS_REPLY_T *
1451 dns_lookup_map(domain, rr_class, rr_type, retrans, retry, options)
1452 	const char *domain;
1453 	int rr_class;
1454 	int rr_type;
1455 	time_t retrans;
1456 	int retry;
1457 	unsigned int options;
1458 {
1459 	return dns_lookup_int(domain, rr_class, rr_type, retrans, retry,
1460 			options, RR_AS_TEXT, NULL, NULL);
1461 }
1462 
1463 #  if DANE
1464 /*
1465 **  DNS2HE -- convert DNS_REPLY_T list to hostent struct
1466 **
1467 **	Parameters:
1468 **		dr -- DNS lookup result
1469 **		family -- address family
1470 **
1471 **	Returns:
1472 **		hostent struct if succeeded.
1473 **		NULL otherwise.
1474 **
1475 **	Note:
1476 **		this returns a pointer to a static struct!
1477 */
1478 
1479 struct hostent *
1480 dns2he(dr, family)
1481 	DNS_REPLY_T *dr;
1482 	int family;
1483 {
1484 #   define SM_MAX_ADDRS	256
1485 	static struct hostent he;
1486 	static char *he_aliases[1];
1487 	static char *he_addr_list[SM_MAX_ADDRS];
1488 #   ifdef IN6ADDRSZ
1489 #    define IN_ADDRSZ IN6ADDRSZ
1490 #   else
1491 #    define IN_ADDRSZ INADDRSZ
1492 #   endif
1493 	static char he_addrs[SM_MAX_ADDRS * IN_ADDRSZ];
1494 	static char he_name[MAXNAME_I];
1495 	static bool he_init = false;
1496 	struct hostent *h;
1497 	int i;
1498 	size_t sz;
1499 #   if NETINET6 && DNSSEC_TEST
1500 	struct in6_addr ia6;
1501 	char buf6[INET6_ADDRSTRLEN];
1502 #   endif
1503 	RESOURCE_RECORD_T *rr;
1504 
1505 	if (dr == NULL)
1506 		return NULL;
1507 
1508 	h = &he;
1509 	if (!he_init)
1510 	{
1511 		he_aliases[0] = NULL;
1512 		he.h_aliases = he_aliases;
1513 		he.h_addr_list = he_addr_list;
1514 		he.h_name = he_name;
1515 		he_init = true;
1516 	}
1517 	h->h_addrtype = family;
1518 
1519 	if (tTd(8, 17))
1520 		sm_dprintf("dns2he: ad=%d\n", dr->dns_r_h.ad);
1521 
1522 	/* do we want/need to copy the name? */
1523 	rr = dr->dns_r_head;
1524 	if (rr != NULL && rr->rr_domain != NULL)
1525 		sm_strlcpy(h->h_name, rr->rr_domain, sizeof(he_name));
1526 	else
1527 		h->h_name[0] = '\0';
1528 
1529 	sz = 0;
1530 #   if NETINET
1531 	if (family == AF_INET)
1532 		sz = INADDRSZ;
1533 #   endif
1534 #   if NETINET6
1535 	if (family == AF_INET6)
1536 		sz = IN6ADDRSZ;
1537 #   endif
1538 	if (sz == 0)
1539 		return NULL;
1540 	h->h_length = sz;
1541 
1542 	for (rr = dr->dns_r_head, i = 0; rr != NULL && i < SM_MAX_ADDRS - 1;
1543 	     rr = rr->rr_next)
1544 	{
1545 		h->h_addr_list[i] = he_addrs + i * h->h_length;
1546 		switch (rr->rr_type)
1547 		{
1548 #   if NETINET
1549 		  case T_A:
1550 			if (family != AF_INET)
1551 				continue;
1552 			memmove(h->h_addr_list[i], rr->rr_u.rr_a, INADDRSZ);
1553 			++i;
1554 			break;
1555 #   endif /* NETINET */
1556 #   if NETINET6
1557 		  case T_AAAA:
1558 			if (family != AF_INET6)
1559 				continue;
1560 			memmove(h->h_addr_list[i], rr->rr_u.rr_aaaa, IN6ADDRSZ);
1561 			++i;
1562 			break;
1563 #   endif /* NETINET6 */
1564 		  case T_CNAME:
1565 #   if DNSSEC_TEST
1566 			if (tTd(8, 16))
1567 				sm_dprintf("dns2he: cname: %s ttl=%d\n",
1568 					rr->rr_u.rr_txt, rr->rr_ttl);
1569 #   endif
1570 			break;
1571 		  case T_MX:
1572 #   if DNSSEC_TEST
1573 			if (tTd(8, 16))
1574 				sm_dprintf("dns2he: mx: %d %s ttl=%d\n",
1575 					rr->rr_u.rr_mx->mx_r_preference,
1576 					rr->rr_u.rr_mx->mx_r_domain,
1577 					rr->rr_ttl);
1578 #   endif
1579 			break;
1580 
1581 #   if defined(T_TLSA)
1582 		  case T_TLSA:
1583 #    if DNSSEC_TEST
1584 			if (tTd(8, 16))
1585 			{
1586 				char *tlsa;
1587 				int len;
1588 
1589 				len = bin2hex(&tlsa, rr->rr_u.rr_data,
1590 						rr->rr_size, 4);
1591 				if (len > 0)
1592 					sm_dprintf("dns2he: tlsa: %s ttl=%d\n",
1593 						tlsa, rr->rr_ttl);
1594 			}
1595 #    endif
1596 			break;
1597 #   endif /* T_TLSA */
1598 		}
1599 	}
1600 
1601 	/* complain if list is too long! */
1602 	SM_ASSERT(i < SM_MAX_ADDRS);
1603 	h->h_addr_list[i] = NULL;
1604 
1605 #   if DNSSEC_TEST
1606 	if (tTd(8, 16))
1607 	{
1608 		struct in_addr ia;
1609 
1610 		for (i = 0; h->h_addr_list[i] != NULL && i < SM_MAX_ADDRS; i++)
1611 		{
1612 			char *addr;
1613 
1614 			addr = NULL;
1615 #    if NETINET6
1616 			if (h->h_addrtype == AF_INET6)
1617 			{
1618 				memmove(&ia6, h->h_addr_list[i], IN6ADDRSZ);
1619 				addr = anynet_ntop(&ia6, buf6, sizeof(buf6));
1620 			}
1621 			else
1622 #    endif /* NETINET6 */
1623 			/* "else" in #if code above */
1624 			{
1625 				memmove(&ia, h->h_addr_list[i], INADDRSZ);
1626 				addr = (char *) inet_ntoa(ia);
1627 			}
1628 			if (addr != NULL)
1629 				sm_dprintf("dns2he: addr[%d]: %s\n", i, addr);
1630 		}
1631 	}
1632 #   endif /* DNSSEC_TEST */
1633 	return h;
1634 }
1635 #  endif /* DANE */
1636 # endif /* NAMED_BIND */
1637 #endif /* DNSMAP || DANE */
1638