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