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