xref: /freebsd/contrib/sendmail/src/domain.c (revision d39bd2c1388b520fcba9abed1932acacead60fba)
1c2aa98e2SPeter Wemm /*
2*d39bd2c1SGregory Neil Shapiro  * Copyright (c) 1998-2004, 2006, 2010, 2020-2023 Proofpoint, Inc. and its suppliers.
306f25ae9SGregory Neil Shapiro  *	All rights reserved.
4c2aa98e2SPeter Wemm  * Copyright (c) 1986, 1995-1997 Eric P. Allman.  All rights reserved.
5c2aa98e2SPeter Wemm  * Copyright (c) 1988, 1993
6c2aa98e2SPeter Wemm  *	The Regents of the University of California.  All rights reserved.
7c2aa98e2SPeter Wemm  *
8c2aa98e2SPeter Wemm  * By using this file, you agree to the terms and conditions set
9c2aa98e2SPeter Wemm  * forth in the LICENSE file which can be found at the top level of
10c2aa98e2SPeter Wemm  * the sendmail distribution.
11c2aa98e2SPeter Wemm  *
12c2aa98e2SPeter Wemm  */
13c2aa98e2SPeter Wemm 
1406f25ae9SGregory Neil Shapiro #include <sendmail.h>
15d0cef73dSGregory Neil Shapiro #include "map.h"
16c2aa98e2SPeter Wemm 
17c2aa98e2SPeter Wemm #if NAMED_BIND
184313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: domain.c,v 8.205 2013-11-22 20:51:55 ca Exp $ (with name server)")
195b0945b5SGregory Neil Shapiro #else
204313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: domain.c,v 8.205 2013-11-22 20:51:55 ca Exp $ (without name server)")
215b0945b5SGregory Neil Shapiro #endif
22c2aa98e2SPeter Wemm 
232fb4f839SGregory Neil Shapiro #include <sm/sendmail.h>
24c2aa98e2SPeter Wemm 
252fb4f839SGregory Neil Shapiro #if NAMED_BIND
26c2aa98e2SPeter Wemm # include <arpa/inet.h>
27*d39bd2c1SGregory Neil Shapiro # include "sm_resolve.h"
285b0945b5SGregory Neil Shapiro # if DANE
295b0945b5SGregory Neil Shapiro #  include <tls.h>
305b0945b5SGregory Neil Shapiro #  ifndef SM_NEG_TTL
315b0945b5SGregory Neil Shapiro #   define SM_NEG_TTL 60 /* "negative" TTL */
325b0945b5SGregory Neil Shapiro #  endif
335b0945b5SGregory Neil Shapiro # endif
34c2aa98e2SPeter Wemm 
35*d39bd2c1SGregory Neil Shapiro #if USE_EAI
36*d39bd2c1SGregory Neil Shapiro #include <unicode/uidna.h>
37*d39bd2c1SGregory Neil Shapiro #endif
38*d39bd2c1SGregory Neil Shapiro 
3940266059SGregory Neil Shapiro 
40c2aa98e2SPeter Wemm # ifndef MXHOSTBUFSIZE
41c2aa98e2SPeter Wemm #  define MXHOSTBUFSIZE	(128 * MAXMXHOSTS)
425b0945b5SGregory Neil Shapiro # endif
43c2aa98e2SPeter Wemm 
44c2aa98e2SPeter Wemm static char	MXHostBuf[MXHOSTBUFSIZE];
4540266059SGregory Neil Shapiro # if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2)
4640266059SGregory Neil Shapiro 	ERROR: _MXHOSTBUFSIZE is out of range
475b0945b5SGregory Neil Shapiro # endif
48c2aa98e2SPeter Wemm 
49c2aa98e2SPeter Wemm # ifndef MAXDNSRCH
50c2aa98e2SPeter Wemm #  define MAXDNSRCH	6	/* number of possible domains to search */
515b0945b5SGregory Neil Shapiro # endif
5206f25ae9SGregory Neil Shapiro 
5306f25ae9SGregory Neil Shapiro # ifndef RES_DNSRCH_VARIABLE
5406f25ae9SGregory Neil Shapiro #  define RES_DNSRCH_VARIABLE	_res.dnsrch
555b0945b5SGregory Neil Shapiro # endif
56c2aa98e2SPeter Wemm 
57c2aa98e2SPeter Wemm # ifndef HFIXEDSZ
58c2aa98e2SPeter Wemm #  define HFIXEDSZ	12	/* sizeof(HEADER) */
595b0945b5SGregory Neil Shapiro # endif
60c2aa98e2SPeter Wemm 
61c2aa98e2SPeter Wemm # define MAXCNAMEDEPTH	10	/* maximum depth of CNAME recursion */
62c2aa98e2SPeter Wemm 
63c2aa98e2SPeter Wemm # if defined(__RES) && (__RES >= 19940415)
64c2aa98e2SPeter Wemm #  define RES_UNC_T	char *
655b0945b5SGregory Neil Shapiro # else
6640266059SGregory Neil Shapiro #  define RES_UNC_T	unsigned char *
675b0945b5SGregory Neil Shapiro # endif
6806f25ae9SGregory Neil Shapiro 
6906f25ae9SGregory Neil Shapiro static int	mxrand __P((char *));
7040266059SGregory Neil Shapiro static int	fallbackmxrr __P((int, unsigned short *, char **));
7106f25ae9SGregory Neil Shapiro 
725b0945b5SGregory Neil Shapiro # if DANE
735b0945b5SGregory Neil Shapiro 
74*d39bd2c1SGregory Neil Shapiro static void tlsa_rr_print __P((const unsigned char *, unsigned int));
75*d39bd2c1SGregory Neil Shapiro 
76*d39bd2c1SGregory Neil Shapiro static void
tlsa_rr_print(rr,len)77*d39bd2c1SGregory Neil Shapiro tlsa_rr_print(rr, len)
78*d39bd2c1SGregory Neil Shapiro 	const unsigned char *rr;
79*d39bd2c1SGregory Neil Shapiro 	unsigned int len;
80*d39bd2c1SGregory Neil Shapiro {
81*d39bd2c1SGregory Neil Shapiro 	unsigned int i, l;
82*d39bd2c1SGregory Neil Shapiro 
83*d39bd2c1SGregory Neil Shapiro 	if (!tTd(8, 2))
84*d39bd2c1SGregory Neil Shapiro 		return;
85*d39bd2c1SGregory Neil Shapiro 
86*d39bd2c1SGregory Neil Shapiro 	sm_dprintf("len=%u, %02x-%02x-%02x",
87*d39bd2c1SGregory Neil Shapiro 		len, (int)rr[0], (int)rr[1], (int)rr[2]);
88*d39bd2c1SGregory Neil Shapiro 	l = tTd(8, 8) ? len : 4;
89*d39bd2c1SGregory Neil Shapiro 	for (i = 3; i < l; i++)
90*d39bd2c1SGregory Neil Shapiro 		sm_dprintf(":%02X", (int)rr[i]);
91*d39bd2c1SGregory Neil Shapiro 	sm_dprintf("\n");
92*d39bd2c1SGregory Neil Shapiro }
93*d39bd2c1SGregory Neil Shapiro 
94*d39bd2c1SGregory Neil Shapiro /*
95*d39bd2c1SGregory Neil Shapiro **  TLSA_RR_CMP -- Compare two TLSA RRs
96*d39bd2c1SGregory Neil Shapiro **
97*d39bd2c1SGregory Neil Shapiro **	Parameters:
98*d39bd2c1SGregory Neil Shapiro **		rr1 -- TLSA RR (entry to be added)
99*d39bd2c1SGregory Neil Shapiro **		l1 -- length of rr1
100*d39bd2c1SGregory Neil Shapiro **		rr2 -- TLSA RR
101*d39bd2c1SGregory Neil Shapiro **		l2 -- length of rr2
102*d39bd2c1SGregory Neil Shapiro **
103*d39bd2c1SGregory Neil Shapiro **	Returns:
104*d39bd2c1SGregory Neil Shapiro **		 0: rr1 == rr2
105*d39bd2c1SGregory Neil Shapiro **		 1: rr1 is unsupported
106*d39bd2c1SGregory Neil Shapiro */
107*d39bd2c1SGregory Neil Shapiro 
108*d39bd2c1SGregory Neil Shapiro static int tlsa_rr_cmp __P((unsigned char *, int, unsigned char *, int));
109*d39bd2c1SGregory Neil Shapiro 
110*d39bd2c1SGregory Neil Shapiro static int
tlsa_rr_cmp(rr1,l1,rr2,l2)111*d39bd2c1SGregory Neil Shapiro tlsa_rr_cmp(rr1, l1, rr2, l2)
112*d39bd2c1SGregory Neil Shapiro 	unsigned char *rr1;
113*d39bd2c1SGregory Neil Shapiro 	int l1;
114*d39bd2c1SGregory Neil Shapiro 	unsigned char *rr2;
115*d39bd2c1SGregory Neil Shapiro 	int l2;
116*d39bd2c1SGregory Neil Shapiro {
117*d39bd2c1SGregory Neil Shapiro /* temporary #if while investigating the implications of the alternative */
118*d39bd2c1SGregory Neil Shapiro #if FULL_COMPARE
119*d39bd2c1SGregory Neil Shapiro 	unsigned char r1, r2;
120*d39bd2c1SGregory Neil Shapiro 	int cmp;
121*d39bd2c1SGregory Neil Shapiro #endif /* FULL_COMPARE */
122*d39bd2c1SGregory Neil Shapiro 
123*d39bd2c1SGregory Neil Shapiro 	SM_REQUIRE(NULL != rr1);
124*d39bd2c1SGregory Neil Shapiro 	SM_REQUIRE(NULL != rr2);
125*d39bd2c1SGregory Neil Shapiro 	SM_REQUIRE(l1 > 3);
126*d39bd2c1SGregory Neil Shapiro 	SM_REQUIRE(l2 > 3);
127*d39bd2c1SGregory Neil Shapiro 
128*d39bd2c1SGregory Neil Shapiro #if FULL_COMPARE
129*d39bd2c1SGregory Neil Shapiro 	/*
130*d39bd2c1SGregory Neil Shapiro 	**  certificate usage
131*d39bd2c1SGregory Neil Shapiro 	**  3: cert/fp must match
132*d39bd2c1SGregory Neil Shapiro 	**  2: cert/fp must be trust anchor
133*d39bd2c1SGregory Neil Shapiro 	*/
134*d39bd2c1SGregory Neil Shapiro 
135*d39bd2c1SGregory Neil Shapiro 	/* preference[]: lower value: higher preference */
136*d39bd2c1SGregory Neil Shapiro 	r1 = rr1[0];
137*d39bd2c1SGregory Neil Shapiro 	r2 = rr2[0];
138*d39bd2c1SGregory Neil Shapiro 	if (r1 != r2)
139*d39bd2c1SGregory Neil Shapiro 	{
140*d39bd2c1SGregory Neil Shapiro 		int preference[] = { 3, 2, 1, 0 };
141*d39bd2c1SGregory Neil Shapiro 
142*d39bd2c1SGregory Neil Shapiro 		SM_ASSERT(r1 <= SM_ARRAY_SIZE(preference));
143*d39bd2c1SGregory Neil Shapiro 		SM_ASSERT(r2 <= SM_ARRAY_SIZE(preference));
144*d39bd2c1SGregory Neil Shapiro 		return preference[r1] - preference[r2];
145*d39bd2c1SGregory Neil Shapiro 	}
146*d39bd2c1SGregory Neil Shapiro 
147*d39bd2c1SGregory Neil Shapiro 	/*
148*d39bd2c1SGregory Neil Shapiro 	**  selector:
149*d39bd2c1SGregory Neil Shapiro 	**  0: full cert
150*d39bd2c1SGregory Neil Shapiro 	**  1: fp
151*d39bd2c1SGregory Neil Shapiro 	*/
152*d39bd2c1SGregory Neil Shapiro 
153*d39bd2c1SGregory Neil Shapiro 	r1 = rr1[1];
154*d39bd2c1SGregory Neil Shapiro 	r2 = rr2[1];
155*d39bd2c1SGregory Neil Shapiro 	if (r1 != r2)
156*d39bd2c1SGregory Neil Shapiro 	{
157*d39bd2c1SGregory Neil Shapiro 		int preference[] = { 1, 0 };
158*d39bd2c1SGregory Neil Shapiro 
159*d39bd2c1SGregory Neil Shapiro 		SM_ASSERT(r1 <= SM_ARRAY_SIZE(preference));
160*d39bd2c1SGregory Neil Shapiro 		SM_ASSERT(r2 <= SM_ARRAY_SIZE(preference));
161*d39bd2c1SGregory Neil Shapiro 		return preference[r1] - preference[r2];
162*d39bd2c1SGregory Neil Shapiro 	}
163*d39bd2c1SGregory Neil Shapiro 
164*d39bd2c1SGregory Neil Shapiro 	/*
165*d39bd2c1SGregory Neil Shapiro 	**  matching type:
166*d39bd2c1SGregory Neil Shapiro 	**  0 -- Exact match
167*d39bd2c1SGregory Neil Shapiro 	**  1 -- SHA-256 hash
168*d39bd2c1SGregory Neil Shapiro 	**  2 -- SHA-512 hash
169*d39bd2c1SGregory Neil Shapiro 	*/
170*d39bd2c1SGregory Neil Shapiro 
171*d39bd2c1SGregory Neil Shapiro 	r1 = rr1[2];
172*d39bd2c1SGregory Neil Shapiro 	r2 = rr2[2];
173*d39bd2c1SGregory Neil Shapiro 	if (r1 != r2)
174*d39bd2c1SGregory Neil Shapiro 	{
175*d39bd2c1SGregory Neil Shapiro 		int preference[] = { 2, 0, 1 };
176*d39bd2c1SGregory Neil Shapiro 
177*d39bd2c1SGregory Neil Shapiro 		SM_ASSERT(r1 <= SM_ARRAY_SIZE(preference));
178*d39bd2c1SGregory Neil Shapiro 		SM_ASSERT(r2 <= SM_ARRAY_SIZE(preference));
179*d39bd2c1SGregory Neil Shapiro 		return preference[r1] - preference[r2];
180*d39bd2c1SGregory Neil Shapiro 	}
181*d39bd2c1SGregory Neil Shapiro 
182*d39bd2c1SGregory Neil Shapiro 	/* not the same length despite the same type? */
183*d39bd2c1SGregory Neil Shapiro 	if (l1 != l2)
184*d39bd2c1SGregory Neil Shapiro 		return 1;
185*d39bd2c1SGregory Neil Shapiro 	cmp = memcmp(rr1, rr2, l1);
186*d39bd2c1SGregory Neil Shapiro 	if (0 == cmp)
187*d39bd2c1SGregory Neil Shapiro 		return 0;
188*d39bd2c1SGregory Neil Shapiro 	return 1;
189*d39bd2c1SGregory Neil Shapiro #else /* FULL_COMPARE */
190*d39bd2c1SGregory Neil Shapiro 	/* identical? */
191*d39bd2c1SGregory Neil Shapiro 	if (l1 == l2 && 0 == memcmp(rr1, rr2, l1))
192*d39bd2c1SGregory Neil Shapiro 		return 0;
193*d39bd2c1SGregory Neil Shapiro 
194*d39bd2c1SGregory Neil Shapiro 	/* new entry is unsupported? -> append */
195*d39bd2c1SGregory Neil Shapiro 	if (TLSA_UNSUPP == dane_tlsa_chk(rr1, l1, "", false))
196*d39bd2c1SGregory Neil Shapiro 		return 1;
197*d39bd2c1SGregory Neil Shapiro 	/* current entry is unsupported? -> insert new one */
198*d39bd2c1SGregory Neil Shapiro 	if (TLSA_UNSUPP == dane_tlsa_chk(rr2, l2, "", false))
199*d39bd2c1SGregory Neil Shapiro 		return -1;
200*d39bd2c1SGregory Neil Shapiro 
201*d39bd2c1SGregory Neil Shapiro 	/* default: preserve order */
202*d39bd2c1SGregory Neil Shapiro 	return 1;
203*d39bd2c1SGregory Neil Shapiro #endif /* FULL_COMPARE */
204*d39bd2c1SGregory Neil Shapiro }
205*d39bd2c1SGregory Neil Shapiro 
206*d39bd2c1SGregory Neil Shapiro /*
207*d39bd2c1SGregory Neil Shapiro **  TLSAINSERT -- Insert a TLSA RR
208*d39bd2c1SGregory Neil Shapiro **
209*d39bd2c1SGregory Neil Shapiro **	Parameters:
210*d39bd2c1SGregory Neil Shapiro **		dane_tlsa -- dane_tlsa entry
211*d39bd2c1SGregory Neil Shapiro **		rr -- TLSA RR
212*d39bd2c1SGregory Neil Shapiro **		pn -- (point to) number of entries
213*d39bd2c1SGregory Neil Shapiro **
214*d39bd2c1SGregory Neil Shapiro **	Returns:
215*d39bd2c1SGregory Neil Shapiro **		SM_SUCCESS: rr inserted
216*d39bd2c1SGregory Neil Shapiro **		SM_NOTDONE: rr not inserted: exists
217*d39bd2c1SGregory Neil Shapiro **		SM_FULL: rr not inserted: no space left
218*d39bd2c1SGregory Neil Shapiro */
219*d39bd2c1SGregory Neil Shapiro 
220*d39bd2c1SGregory Neil Shapiro static int tlsainsert __P((dane_tlsa_P, RESOURCE_RECORD_T *, int *));
221*d39bd2c1SGregory Neil Shapiro 
222*d39bd2c1SGregory Neil Shapiro static int
tlsainsert(dane_tlsa,rr,pn)223*d39bd2c1SGregory Neil Shapiro tlsainsert(dane_tlsa, rr, pn)
224*d39bd2c1SGregory Neil Shapiro 	dane_tlsa_P dane_tlsa;
225*d39bd2c1SGregory Neil Shapiro 	RESOURCE_RECORD_T *rr;
226*d39bd2c1SGregory Neil Shapiro 	int *pn;
227*d39bd2c1SGregory Neil Shapiro {
228*d39bd2c1SGregory Neil Shapiro 	int i, l1, ret;
229*d39bd2c1SGregory Neil Shapiro 	unsigned char *r1;
230*d39bd2c1SGregory Neil Shapiro 
231*d39bd2c1SGregory Neil Shapiro 	SM_ASSERT(pn != NULL);
232*d39bd2c1SGregory Neil Shapiro 	SM_ASSERT(*pn <= MAX_TLSA_RR);
233*d39bd2c1SGregory Neil Shapiro 	r1 = rr->rr_u.rr_data;
234*d39bd2c1SGregory Neil Shapiro 	l1 = rr->rr_size;
235*d39bd2c1SGregory Neil Shapiro 
236*d39bd2c1SGregory Neil Shapiro 	ret = SM_SUCCESS;
237*d39bd2c1SGregory Neil Shapiro 	for (i = 0; i < *pn; i++)
238*d39bd2c1SGregory Neil Shapiro 	{
239*d39bd2c1SGregory Neil Shapiro 		int r, j;
240*d39bd2c1SGregory Neil Shapiro 
241*d39bd2c1SGregory Neil Shapiro 		r = tlsa_rr_cmp(r1, l1, dane_tlsa->dane_tlsa_rr[i],
242*d39bd2c1SGregory Neil Shapiro 			dane_tlsa->dane_tlsa_len[i]);
243*d39bd2c1SGregory Neil Shapiro 
244*d39bd2c1SGregory Neil Shapiro 		if (0 == r)
245*d39bd2c1SGregory Neil Shapiro 		{
246*d39bd2c1SGregory Neil Shapiro 			if (tTd(8, 80))
247*d39bd2c1SGregory Neil Shapiro 				sm_dprintf("func=tlsainsert, i=%d, n=%d, status=exists\n", i, *pn);
248*d39bd2c1SGregory Neil Shapiro 			ret = SM_NOTDONE;
249*d39bd2c1SGregory Neil Shapiro 			goto done;
250*d39bd2c1SGregory Neil Shapiro 		}
251*d39bd2c1SGregory Neil Shapiro 		if (r > 0)
252*d39bd2c1SGregory Neil Shapiro 			continue;
253*d39bd2c1SGregory Neil Shapiro 
254*d39bd2c1SGregory Neil Shapiro 		if (*pn + 1 >= MAX_TLSA_RR)
255*d39bd2c1SGregory Neil Shapiro 		{
256*d39bd2c1SGregory Neil Shapiro 			j = MAX_TLSA_RR - 1;
257*d39bd2c1SGregory Neil Shapiro 			SM_FREE(dane_tlsa->dane_tlsa_rr[j]);
258*d39bd2c1SGregory Neil Shapiro 			dane_tlsa->dane_tlsa_len[j] = 0;
259*d39bd2c1SGregory Neil Shapiro 		}
260*d39bd2c1SGregory Neil Shapiro 		else
261*d39bd2c1SGregory Neil Shapiro 			(*pn)++;
262*d39bd2c1SGregory Neil Shapiro 
263*d39bd2c1SGregory Neil Shapiro 		for (j = MAX_TLSA_RR - 2; j >= i; j--)
264*d39bd2c1SGregory Neil Shapiro 		{
265*d39bd2c1SGregory Neil Shapiro 			dane_tlsa->dane_tlsa_rr[j + 1] = dane_tlsa->dane_tlsa_rr[j];
266*d39bd2c1SGregory Neil Shapiro 			dane_tlsa->dane_tlsa_len[j + 1] = dane_tlsa->dane_tlsa_len[j];
267*d39bd2c1SGregory Neil Shapiro 		}
268*d39bd2c1SGregory Neil Shapiro 		SM_ASSERT(i < MAX_TLSA_RR);
269*d39bd2c1SGregory Neil Shapiro 		dane_tlsa->dane_tlsa_rr[i] = r1;
270*d39bd2c1SGregory Neil Shapiro 		dane_tlsa->dane_tlsa_len[i] = l1;
271*d39bd2c1SGregory Neil Shapiro 		if (tTd(8, 80))
272*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("func=tlsainsert, i=%d, n=%d, status=inserted\n", i, *pn);
273*d39bd2c1SGregory Neil Shapiro 		goto added;
274*d39bd2c1SGregory Neil Shapiro 	}
275*d39bd2c1SGregory Neil Shapiro 
276*d39bd2c1SGregory Neil Shapiro 	if (*pn + 1 <= MAX_TLSA_RR)
277*d39bd2c1SGregory Neil Shapiro 	{
278*d39bd2c1SGregory Neil Shapiro 		dane_tlsa->dane_tlsa_rr[*pn] = r1;
279*d39bd2c1SGregory Neil Shapiro 		dane_tlsa->dane_tlsa_len[*pn] = l1;
280*d39bd2c1SGregory Neil Shapiro 		(*pn)++;
281*d39bd2c1SGregory Neil Shapiro 		if (tTd(8, 80))
282*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("func=tlsainsert, n=%d, status=appended\n", *pn);
283*d39bd2c1SGregory Neil Shapiro 	}
284*d39bd2c1SGregory Neil Shapiro 	else
285*d39bd2c1SGregory Neil Shapiro 	{
286*d39bd2c1SGregory Neil Shapiro 		if (tTd(8, 80))
287*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("func=tlsainsert, n=%d, status=full\n", *pn);
288*d39bd2c1SGregory Neil Shapiro 		return SM_FULL;
289*d39bd2c1SGregory Neil Shapiro 	}
290*d39bd2c1SGregory Neil Shapiro 
291*d39bd2c1SGregory Neil Shapiro   added:
292*d39bd2c1SGregory Neil Shapiro 	/* hack: instead of copying the data, just "take it over" */
293*d39bd2c1SGregory Neil Shapiro 	rr->rr_u.rr_data = NULL;
294*d39bd2c1SGregory Neil Shapiro   done:
295*d39bd2c1SGregory Neil Shapiro 	return ret;
296*d39bd2c1SGregory Neil Shapiro }
297*d39bd2c1SGregory Neil Shapiro 
2985b0945b5SGregory Neil Shapiro /*
2995b0945b5SGregory Neil Shapiro **  TLSAADD -- add TLSA records to dane_tlsa entry
3005b0945b5SGregory Neil Shapiro **
3015b0945b5SGregory Neil Shapiro **	Parameters:
3025b0945b5SGregory Neil Shapiro **		name -- key for stab entry (for debugging output)
3035b0945b5SGregory Neil Shapiro **		dr -- DNS reply
3045b0945b5SGregory Neil Shapiro **		dane_tlsa -- dane_tlsa entry
3055b0945b5SGregory Neil Shapiro **		dnsrc -- DNS lookup return code (h_errno)
306*d39bd2c1SGregory Neil Shapiro **		nr -- current number of TLSA records in dane_tlsa entry
3075b0945b5SGregory Neil Shapiro **		pttl -- (pointer to) TTL (in/out)
3085b0945b5SGregory Neil Shapiro **		level -- recursion level (CNAMEs)
3095b0945b5SGregory Neil Shapiro **
3105b0945b5SGregory Neil Shapiro **	Returns:
3115b0945b5SGregory Neil Shapiro **		new number of TLSA records
312*d39bd2c1SGregory Neil Shapiro **
313*d39bd2c1SGregory Neil Shapiro **	NOTE: the array for TLSA RRs could be "full" which is not
314*d39bd2c1SGregory Neil Shapiro **		handled well (yet).
3155b0945b5SGregory Neil Shapiro */
3165b0945b5SGregory Neil Shapiro 
3175b0945b5SGregory Neil Shapiro static int tlsaadd __P((const char *, DNS_REPLY_T *, dane_tlsa_P, int, int,
3185b0945b5SGregory Neil Shapiro 			unsigned int *, int));
3195b0945b5SGregory Neil Shapiro 
3205b0945b5SGregory Neil Shapiro static int
tlsaadd(name,dr,dane_tlsa,dnsrc,nr,pttl,level)321*d39bd2c1SGregory Neil Shapiro tlsaadd(name, dr, dane_tlsa, dnsrc, nr, pttl, level)
3225b0945b5SGregory Neil Shapiro 	const char *name;
3235b0945b5SGregory Neil Shapiro 	DNS_REPLY_T *dr;
3245b0945b5SGregory Neil Shapiro 	dane_tlsa_P dane_tlsa;
3255b0945b5SGregory Neil Shapiro 	int dnsrc;
326*d39bd2c1SGregory Neil Shapiro 	int nr;
3275b0945b5SGregory Neil Shapiro 	unsigned int *pttl;
3285b0945b5SGregory Neil Shapiro 	int level;
3295b0945b5SGregory Neil Shapiro {
3305b0945b5SGregory Neil Shapiro 	RESOURCE_RECORD_T *rr;
3315b0945b5SGregory Neil Shapiro 	unsigned int ttl;
3325b0945b5SGregory Neil Shapiro 	int nprev;
3335b0945b5SGregory Neil Shapiro 
3345b0945b5SGregory Neil Shapiro 	if (dnsrc != 0)
3355b0945b5SGregory Neil Shapiro 	{
3365b0945b5SGregory Neil Shapiro 		if (tTd(8, 2))
337*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("tlsaadd, name=%s, prev=%d, dnsrc=%d\n",
3385b0945b5SGregory Neil Shapiro 				name, dane_tlsa->dane_tlsa_dnsrc, dnsrc);
3395b0945b5SGregory Neil Shapiro 
3405b0945b5SGregory Neil Shapiro 		/* check previous error and keep the "most important" one? */
3415b0945b5SGregory Neil Shapiro 		dane_tlsa->dane_tlsa_dnsrc = dnsrc;
3425b0945b5SGregory Neil Shapiro #  if DNSSEC_TEST
3435b0945b5SGregory Neil Shapiro 		if (tTd(8, 110))
3445b0945b5SGregory Neil Shapiro 			*pttl = tTdlevel(8)-110; /* how to make this an option? */
3455b0945b5SGregory Neil Shapiro 		else
3465b0945b5SGregory Neil Shapiro #  endif
3472fb4f839SGregory Neil Shapiro 		/* "else" in #if code above */
3482fb4f839SGregory Neil Shapiro 			*pttl = SM_NEG_TTL;
349*d39bd2c1SGregory Neil Shapiro 		return nr;
3505b0945b5SGregory Neil Shapiro 	}
3515b0945b5SGregory Neil Shapiro 	if (dr == NULL)
352*d39bd2c1SGregory Neil Shapiro 		return nr;
3535b0945b5SGregory Neil Shapiro 	if (dr->dns_r_h.ad != 1 && Dane == DANE_SECURE)	/* not secure? */
354*d39bd2c1SGregory Neil Shapiro 		return nr;
3555b0945b5SGregory Neil Shapiro 	ttl = *pttl;
3565b0945b5SGregory Neil Shapiro 
3575b0945b5SGregory Neil Shapiro 	/* first: try to find TLSA records */
358*d39bd2c1SGregory Neil Shapiro 	nprev = nr;
359*d39bd2c1SGregory Neil Shapiro 	for (rr = dr->dns_r_head; rr != NULL; rr = rr->rr_next)
3605b0945b5SGregory Neil Shapiro 	{
361*d39bd2c1SGregory Neil Shapiro 		int tlsa_chk, r;
3625b0945b5SGregory Neil Shapiro 
3635b0945b5SGregory Neil Shapiro 		if (rr->rr_type != T_TLSA)
3645b0945b5SGregory Neil Shapiro 		{
3655b0945b5SGregory Neil Shapiro 			if (rr->rr_type != T_CNAME && tTd(8, 8))
366*d39bd2c1SGregory Neil Shapiro 				sm_dprintf("tlsaadd: name=%s, type=%s\n", name,
3675b0945b5SGregory Neil Shapiro 					dns_type_to_string(rr->rr_type));
3685b0945b5SGregory Neil Shapiro 			continue;
3695b0945b5SGregory Neil Shapiro 		}
3705b0945b5SGregory Neil Shapiro 		tlsa_chk = dane_tlsa_chk(rr->rr_u.rr_data, rr->rr_size, name,
3715b0945b5SGregory Neil Shapiro 					true);
372*d39bd2c1SGregory Neil Shapiro 		if (TLSA_UNSUPP == tlsa_chk)
373*d39bd2c1SGregory Neil Shapiro 			TLSA_SET_FL(dane_tlsa, TLSAFLUNS);
3745b0945b5SGregory Neil Shapiro 		if (!TLSA_IS_VALID(tlsa_chk))
3755b0945b5SGregory Neil Shapiro 			continue;
376*d39bd2c1SGregory Neil Shapiro 		if (TLSA_IS_SUPPORTED(tlsa_chk))
377*d39bd2c1SGregory Neil Shapiro 			TLSA_SET_FL(dane_tlsa, TLSAFLSUP);
3785b0945b5SGregory Neil Shapiro 
3795b0945b5SGregory Neil Shapiro 		/*
380*d39bd2c1SGregory Neil Shapiro 		**  Note: rr_u.rr_data might be NULL after tlsainsert()
381*d39bd2c1SGregory Neil Shapiro 		**  for nice debug output: print the data into a string
382*d39bd2c1SGregory Neil Shapiro 		**  and then use it after tlsainsert().
3835b0945b5SGregory Neil Shapiro 		*/
3845b0945b5SGregory Neil Shapiro 
3855b0945b5SGregory Neil Shapiro 		if (tTd(8, 2))
3865b0945b5SGregory Neil Shapiro 		{
387*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("tlsaadd: name=%s, nr=%d, ", name, nr);
388*d39bd2c1SGregory Neil Shapiro 			tlsa_rr_print(rr->rr_u.rr_data, rr->rr_size);
3895b0945b5SGregory Neil Shapiro 		}
390*d39bd2c1SGregory Neil Shapiro 		r = tlsainsert(dane_tlsa, rr, &nr);
391*d39bd2c1SGregory Neil Shapiro 		if (SM_FULL == r)
392*d39bd2c1SGregory Neil Shapiro 			TLSA_SET_FL(dane_tlsa, TLSAFL2MANY);
393*d39bd2c1SGregory Neil Shapiro 		if (tTd(8, 2))
394*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("tlsainsert=%d, nr=%d\n", r, nr);
3955b0945b5SGregory Neil Shapiro 
3965b0945b5SGregory Neil Shapiro 		/* require some minimum TTL? */
3975b0945b5SGregory Neil Shapiro 		if (ttl > rr->rr_ttl && rr->rr_ttl > 0)
3985b0945b5SGregory Neil Shapiro 			ttl = rr->rr_ttl;
399*d39bd2c1SGregory Neil Shapiro 	}
4005b0945b5SGregory Neil Shapiro 
401*d39bd2c1SGregory Neil Shapiro 	if (tTd(8, 2))
402*d39bd2c1SGregory Neil Shapiro 	{
403*d39bd2c1SGregory Neil Shapiro 		unsigned int ui;
404*d39bd2c1SGregory Neil Shapiro 
405*d39bd2c1SGregory Neil Shapiro 		SM_ASSERT(nr <= MAX_TLSA_RR);
406*d39bd2c1SGregory Neil Shapiro 		for (ui = 0; ui < (unsigned int)nr; ui++)
407*d39bd2c1SGregory Neil Shapiro 		{
408*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("tlsaadd: name=%s, ui=%u, ", name, ui);
409*d39bd2c1SGregory Neil Shapiro 			tlsa_rr_print(dane_tlsa->dane_tlsa_rr[ui],
410*d39bd2c1SGregory Neil Shapiro 				dane_tlsa->dane_tlsa_len[ui]);
411*d39bd2c1SGregory Neil Shapiro 		}
412*d39bd2c1SGregory Neil Shapiro 	}
413*d39bd2c1SGregory Neil Shapiro 
414*d39bd2c1SGregory Neil Shapiro 	if (TLSA_IS_FL(dane_tlsa, TLSAFL2MANY))
415*d39bd2c1SGregory Neil Shapiro 	{
416*d39bd2c1SGregory Neil Shapiro 		if (tTd(8, 20))
417*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("tlsaadd: name=%s, rr=%p, nr=%d, toomany=%d\n", name, rr, nr, TLSA_IS_FL(dane_tlsa, TLSAFL2MANY));
4185b0945b5SGregory Neil Shapiro 	}
4195b0945b5SGregory Neil Shapiro 
4205b0945b5SGregory Neil Shapiro 	/* second: check for CNAME records, but only if no TLSA RR was added */
421*d39bd2c1SGregory Neil Shapiro 	for (rr = dr->dns_r_head; rr != NULL && nprev == nr; rr = rr->rr_next)
4225b0945b5SGregory Neil Shapiro 	{
4235b0945b5SGregory Neil Shapiro 		DNS_REPLY_T *drc;
4245b0945b5SGregory Neil Shapiro 		int err, herr;
4255b0945b5SGregory Neil Shapiro 
4265b0945b5SGregory Neil Shapiro 		if (rr->rr_type != T_CNAME)
4275b0945b5SGregory Neil Shapiro 			continue;
4285b0945b5SGregory Neil Shapiro 		if (level > 1)
4295b0945b5SGregory Neil Shapiro 		{
4305b0945b5SGregory Neil Shapiro 			if (tTd(8, 2))
431*d39bd2c1SGregory Neil Shapiro 				sm_dprintf("tlsaadd: name=%s, CNAME=%s, level=%d\n",
4325b0945b5SGregory Neil Shapiro 					name, rr->rr_u.rr_txt, level);
4335b0945b5SGregory Neil Shapiro 			continue;
4345b0945b5SGregory Neil Shapiro 		}
4355b0945b5SGregory Neil Shapiro 
4365b0945b5SGregory Neil Shapiro 		drc = dns_lookup_int(rr->rr_u.rr_txt, C_IN, T_TLSA, 0, 0,
437*d39bd2c1SGregory Neil Shapiro 			Dane == DANE_SECURE ? SM_RES_DNSSEC : 0,
4385b0945b5SGregory Neil Shapiro 			RR_RAW, &err, &herr);
4395b0945b5SGregory Neil Shapiro 
4405b0945b5SGregory Neil Shapiro 		if (tTd(8, 2))
441*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("tlsaadd: name=%s, CNAME=%s, level=%d, dr=%p, ad=%d, err=%d, herr=%d\n",
4425b0945b5SGregory Neil Shapiro 				name, rr->rr_u.rr_txt, level,
4435b0945b5SGregory Neil Shapiro 				(void *)drc, drc != NULL ? drc->dns_r_h.ad : -1,
4445b0945b5SGregory Neil Shapiro 				err, herr);
445*d39bd2c1SGregory Neil Shapiro 		nprev = nr = tlsaadd(name, drc, dane_tlsa, herr, nr, pttl,
4465b0945b5SGregory Neil Shapiro 				level + 1);
4475b0945b5SGregory Neil Shapiro 		dns_free_data(drc);
4485b0945b5SGregory Neil Shapiro 		drc = NULL;
4495b0945b5SGregory Neil Shapiro 	}
4505b0945b5SGregory Neil Shapiro 
451*d39bd2c1SGregory Neil Shapiro 	if (TLSA_IS_FL(dane_tlsa, TLSAFLUNS) &&
452*d39bd2c1SGregory Neil Shapiro 	    !TLSA_IS_FL(dane_tlsa, TLSAFLSUP) && LogLevel > 9)
453*d39bd2c1SGregory Neil Shapiro 	{
454*d39bd2c1SGregory Neil Shapiro 		sm_syslog(LOG_NOTICE, NOQID,
455*d39bd2c1SGregory Neil Shapiro 			  "TLSA=%s, records=%d%s",
456*d39bd2c1SGregory Neil Shapiro 			  name, nr, ONLYUNSUPTLSARR);
457*d39bd2c1SGregory Neil Shapiro 	}
4585b0945b5SGregory Neil Shapiro 	*pttl = ttl;
459*d39bd2c1SGregory Neil Shapiro 	return nr;
4605b0945b5SGregory Neil Shapiro }
4615b0945b5SGregory Neil Shapiro 
4625b0945b5SGregory Neil Shapiro /*
4635b0945b5SGregory Neil Shapiro **  GETTLSA -- get TLSA records for named host using DNS
4645b0945b5SGregory Neil Shapiro **
4655b0945b5SGregory Neil Shapiro **	Parameters:
4665b0945b5SGregory Neil Shapiro **		host -- host
4675b0945b5SGregory Neil Shapiro **		name -- name for stab entry key (if NULL: host)
4685b0945b5SGregory Neil Shapiro **		pste -- (pointer to) stab entry (output)
4695b0945b5SGregory Neil Shapiro **		flags -- TLSAFL*
4705b0945b5SGregory Neil Shapiro **		mxttl -- TTL of MX (or host)
471*d39bd2c1SGregory Neil Shapiro **		port -- port number used in TLSA queries (_PORT._tcp.)
4725b0945b5SGregory Neil Shapiro **
4735b0945b5SGregory Neil Shapiro **	Returns:
4745b0945b5SGregory Neil Shapiro **		The number of TLSA records found.
4755b0945b5SGregory Neil Shapiro **		<0 if there is an internal failure.
4765b0945b5SGregory Neil Shapiro **
4775b0945b5SGregory Neil Shapiro **	Side effects:
4785b0945b5SGregory Neil Shapiro **		Enters TLSA RRs into stab().
4795b0945b5SGregory Neil Shapiro **		If the DNS lookup fails temporarily, an "empty" entry is
4805b0945b5SGregory Neil Shapiro **		created with that DNS error code.
4815b0945b5SGregory Neil Shapiro */
4825b0945b5SGregory Neil Shapiro 
4835b0945b5SGregory Neil Shapiro int
gettlsa(host,name,pste,flags,mxttl,port)4845b0945b5SGregory Neil Shapiro gettlsa(host, name, pste, flags, mxttl, port)
4855b0945b5SGregory Neil Shapiro 	char *host;
4865b0945b5SGregory Neil Shapiro 	char *name;
4875b0945b5SGregory Neil Shapiro 	STAB **pste;
4885b0945b5SGregory Neil Shapiro 	unsigned long flags;
4895b0945b5SGregory Neil Shapiro 	unsigned int mxttl;
4905b0945b5SGregory Neil Shapiro 	unsigned int port;
4915b0945b5SGregory Neil Shapiro {
4925b0945b5SGregory Neil Shapiro 	DNS_REPLY_T *dr;
4935b0945b5SGregory Neil Shapiro 	dane_tlsa_P dane_tlsa;
4945b0945b5SGregory Neil Shapiro 	STAB *ste;
4955b0945b5SGregory Neil Shapiro 	time_t now;
4965b0945b5SGregory Neil Shapiro 	unsigned int ttl;
4975b0945b5SGregory Neil Shapiro 	int n_rrs, len, err, herr;
498*d39bd2c1SGregory Neil Shapiro 	bool isrname, expired;
4995b0945b5SGregory Neil Shapiro 	char nbuf[MAXDNAME];
5005b0945b5SGregory Neil Shapiro 	char key[MAXDNAME];
5015b0945b5SGregory Neil Shapiro 
5025b0945b5SGregory Neil Shapiro 	SM_REQUIRE(host != NULL);
5035b0945b5SGregory Neil Shapiro 	if (pste != NULL)
5045b0945b5SGregory Neil Shapiro 		*pste = NULL;
5055b0945b5SGregory Neil Shapiro 	if ('\0' == *host)
5065b0945b5SGregory Neil Shapiro 		return 0;
5075b0945b5SGregory Neil Shapiro 
508*d39bd2c1SGregory Neil Shapiro 	expired = false;
5095b0945b5SGregory Neil Shapiro 	isrname = NULL == name;
5105b0945b5SGregory Neil Shapiro 	if (isrname)
5115b0945b5SGregory Neil Shapiro 		name = host;
512*d39bd2c1SGregory Neil Shapiro 
513*d39bd2c1SGregory Neil Shapiro 	/*
514*d39bd2c1SGregory Neil Shapiro 	**  If host->MX lookup was not secure then do not look up TLSA RRs.
515*d39bd2c1SGregory Neil Shapiro 	**  Note: this is currently a hack: TLSAFLADMX is used as input flag,
516*d39bd2c1SGregory Neil Shapiro 	**  it is (SHOULD!) NOT stored in dane_tlsa->dane_tlsa_flags
517*d39bd2c1SGregory Neil Shapiro 	*/
518*d39bd2c1SGregory Neil Shapiro 
519*d39bd2c1SGregory Neil Shapiro 	if (DANE_SECURE == Dane && 0 == (TLSAFLADMX & flags) &&
520*d39bd2c1SGregory Neil Shapiro 	    0 != (TLSAFLNEW & flags))
521*d39bd2c1SGregory Neil Shapiro 	{
522*d39bd2c1SGregory Neil Shapiro 		if (tTd(8, 2))
523*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("gettlsa: host=%s, flags=%#lx, no ad but Dane=Secure\n",
524*d39bd2c1SGregory Neil Shapiro 				host, flags);
525*d39bd2c1SGregory Neil Shapiro 		return 0;
526*d39bd2c1SGregory Neil Shapiro 	}
527*d39bd2c1SGregory Neil Shapiro 
5285b0945b5SGregory Neil Shapiro 	now = 0;
5295b0945b5SGregory Neil Shapiro 	n_rrs = 0;
5305b0945b5SGregory Neil Shapiro 	dr = NULL;
5315b0945b5SGregory Neil Shapiro 	dane_tlsa = NULL;
5325b0945b5SGregory Neil Shapiro 	len = strlen(name);
5335b0945b5SGregory Neil Shapiro 	if (len > 1 && name[len - 1] == '.')
5345b0945b5SGregory Neil Shapiro 	{
5355b0945b5SGregory Neil Shapiro 		len--;
5365b0945b5SGregory Neil Shapiro 		name[len] = '\0';
5375b0945b5SGregory Neil Shapiro 	}
5385b0945b5SGregory Neil Shapiro 	else
5395b0945b5SGregory Neil Shapiro 		len = -1;
540*d39bd2c1SGregory Neil Shapiro 	if (0 == port || tTd(66, 101))
5415b0945b5SGregory Neil Shapiro 		port = 25;
542*d39bd2c1SGregory Neil Shapiro 	(void) sm_snprintf(key, sizeof(key), "_%u.%s", port, name);
5435b0945b5SGregory Neil Shapiro 	ste = stab(key, ST_TLSA_RR, ST_FIND);
5445b0945b5SGregory Neil Shapiro 	if (tTd(8, 2))
545*d39bd2c1SGregory Neil Shapiro 		sm_dprintf("gettlsa: host=%s, %s, ste=%p, pste=%p, flags=%#lx, port=%d\n",
5465b0945b5SGregory Neil Shapiro 			host, isrname ? "" : name, (void *)ste, (void *)pste,
5475b0945b5SGregory Neil Shapiro 			flags, port);
5485b0945b5SGregory Neil Shapiro 
5495b0945b5SGregory Neil Shapiro 	if (ste != NULL)
5505b0945b5SGregory Neil Shapiro 		dane_tlsa = ste->s_tlsa;
5515b0945b5SGregory Neil Shapiro 
552*d39bd2c1SGregory Neil Shapiro #if 0
553*d39bd2c1SGregory Neil Shapiro //	/* Do not reload TLSA RRs if the MX RRs were not securely retrieved. */
554*d39bd2c1SGregory Neil Shapiro //	if (pste != NULL
555*d39bd2c1SGregory Neil Shapiro //	    && dane_tlsa != NULL && TLSA_IS_FL(dane_tlsa, TLSAFLNOADMX)
556*d39bd2c1SGregory Neil Shapiro //	    && DANE_SECURE == Dane)
557*d39bd2c1SGregory Neil Shapiro //		goto end;
558*d39bd2c1SGregory Neil Shapiro #endif
5595b0945b5SGregory Neil Shapiro 	if (ste != NULL)
5605b0945b5SGregory Neil Shapiro 	{
5615b0945b5SGregory Neil Shapiro 		SM_ASSERT(dane_tlsa != NULL);
5625b0945b5SGregory Neil Shapiro 		now = curtime();
563*d39bd2c1SGregory Neil Shapiro 		if (tTd(8, 20))
564*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("gettlsa: host=%s, found-ste=%p, ste_flags=%#lx, expired=%d\n", host, ste, ste->s_tlsa->dane_tlsa_flags, dane_tlsa->dane_tlsa_exp <= now);
5655b0945b5SGregory Neil Shapiro 		if (dane_tlsa->dane_tlsa_exp <= now
5665b0945b5SGregory Neil Shapiro 		    && 0 == (TLSAFLNOEXP & flags))
567*d39bd2c1SGregory Neil Shapiro 		{
5685b0945b5SGregory Neil Shapiro 			dane_tlsa_clr(dane_tlsa);
569*d39bd2c1SGregory Neil Shapiro 			expired = true;
570*d39bd2c1SGregory Neil Shapiro 		}
5715b0945b5SGregory Neil Shapiro 		else
5725b0945b5SGregory Neil Shapiro 		{
5735b0945b5SGregory Neil Shapiro 			n_rrs = dane_tlsa->dane_tlsa_n;
5745b0945b5SGregory Neil Shapiro 			goto end;
5755b0945b5SGregory Neil Shapiro 		}
5765b0945b5SGregory Neil Shapiro 	}
5775b0945b5SGregory Neil Shapiro 
578*d39bd2c1SGregory Neil Shapiro 	/* get entries if none exist yet? */
579*d39bd2c1SGregory Neil Shapiro 	if ((0 == (TLSAFLNEW & flags)) && !expired)
580*d39bd2c1SGregory Neil Shapiro 		goto end;
581*d39bd2c1SGregory Neil Shapiro 
5825b0945b5SGregory Neil Shapiro 	if (dane_tlsa == NULL)
5835b0945b5SGregory Neil Shapiro 	{
5845b0945b5SGregory Neil Shapiro 		dane_tlsa = (dane_tlsa_P) sm_malloc(sizeof(*dane_tlsa));
5855b0945b5SGregory Neil Shapiro 		if (dane_tlsa == NULL)
5865b0945b5SGregory Neil Shapiro 		{
5875b0945b5SGregory Neil Shapiro 			n_rrs = -ENOMEM;
5885b0945b5SGregory Neil Shapiro 			goto end;
5895b0945b5SGregory Neil Shapiro 		}
5905b0945b5SGregory Neil Shapiro 		memset(dane_tlsa, '\0', sizeof(*dane_tlsa));
5915b0945b5SGregory Neil Shapiro 	}
5925b0945b5SGregory Neil Shapiro 
5935b0945b5SGregory Neil Shapiro 	/* There are flags to store -- just set those, do nothing else. */
5945b0945b5SGregory Neil Shapiro 	if (TLSA_STORE_FL(flags))
5955b0945b5SGregory Neil Shapiro 	{
5965b0945b5SGregory Neil Shapiro 		dane_tlsa->dane_tlsa_flags = flags;
5975b0945b5SGregory Neil Shapiro 		ttl = mxttl > 0 ? mxttl: SM_DEFAULT_TTL;
5985b0945b5SGregory Neil Shapiro 		goto done;
5995b0945b5SGregory Neil Shapiro 	}
6005b0945b5SGregory Neil Shapiro 
6015b0945b5SGregory Neil Shapiro 	(void) sm_snprintf(nbuf, sizeof(nbuf), "_%u._tcp.%s", port, host);
6025b0945b5SGregory Neil Shapiro 	dr = dns_lookup_int(nbuf, C_IN, T_TLSA, 0, 0,
603*d39bd2c1SGregory Neil Shapiro 		(TLSAFLADMX & flags) ? SM_RES_DNSSEC : 0,
6045b0945b5SGregory Neil Shapiro 		RR_RAW, &err, &herr);
6055b0945b5SGregory Neil Shapiro 	if (tTd(8, 2))
606*d39bd2c1SGregory Neil Shapiro 	{
607*d39bd2c1SGregory Neil Shapiro #if 0
608*d39bd2c1SGregory Neil Shapiro /* disabled -- what to do with these two counters? log them "somewhere"? */
609*d39bd2c1SGregory Neil Shapiro //		if (NULL != dr && tTd(8, 12))
610*d39bd2c1SGregory Neil Shapiro //		{
611*d39bd2c1SGregory Neil Shapiro //			RESOURCE_RECORD_T *rr;
612*d39bd2c1SGregory Neil Shapiro //			unsigned int ntlsarrs, usable;
613*d39bd2c1SGregory Neil Shapiro //
614*d39bd2c1SGregory Neil Shapiro //			ntlsarrs = usable = 0;
615*d39bd2c1SGregory Neil Shapiro //			for (rr = dr->dns_r_head; rr != NULL; rr = rr->rr_next)
616*d39bd2c1SGregory Neil Shapiro //			{
617*d39bd2c1SGregory Neil Shapiro //				int tlsa_chk;
618*d39bd2c1SGregory Neil Shapiro //
619*d39bd2c1SGregory Neil Shapiro //				if (rr->rr_type != T_TLSA)
620*d39bd2c1SGregory Neil Shapiro //					continue;
621*d39bd2c1SGregory Neil Shapiro //				++ntlsarrs;
622*d39bd2c1SGregory Neil Shapiro //				tlsa_chk = dane_tlsa_chk(rr->rr_u.rr_data,
623*d39bd2c1SGregory Neil Shapiro //						rr->rr_size, name, false);
624*d39bd2c1SGregory Neil Shapiro //				if (TLSA_IS_SUPPORTED(tlsa_chk))
625*d39bd2c1SGregory Neil Shapiro //					++usable;
626*d39bd2c1SGregory Neil Shapiro //
627*d39bd2c1SGregory Neil Shapiro //			}
628*d39bd2c1SGregory Neil Shapiro //			sm_dprintf("gettlsa: host=%s, ntlsarrs=%u, usable\%u\n", host, ntlsarrs, usable);
629*d39bd2c1SGregory Neil Shapiro //		}
630*d39bd2c1SGregory Neil Shapiro #endif /* 0 */
631*d39bd2c1SGregory Neil Shapiro 		sm_dprintf("gettlsa: host=%s, dr=%p, ad=%d, err=%d, herr=%d\n",
632*d39bd2c1SGregory Neil Shapiro 			host, (void *)dr,
633*d39bd2c1SGregory Neil Shapiro 			dr != NULL ? dr->dns_r_h.ad : -1, err, herr);
634*d39bd2c1SGregory Neil Shapiro 	}
6355b0945b5SGregory Neil Shapiro 	ttl = UINT_MAX;
6365b0945b5SGregory Neil Shapiro 	n_rrs = tlsaadd(key, dr, dane_tlsa, herr, n_rrs, &ttl, 0);
6375b0945b5SGregory Neil Shapiro 
6385b0945b5SGregory Neil Shapiro 	/* no valid entries found? */
6395b0945b5SGregory Neil Shapiro 	if (n_rrs == 0 && !TLSA_RR_TEMPFAIL(dane_tlsa))
6405b0945b5SGregory Neil Shapiro 	{
6415b0945b5SGregory Neil Shapiro 		if (tTd(8, 2))
642*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("gettlsa: host=%s, n_rrs=%d, herr=%d, status=NOT_ADDED\n",
6435b0945b5SGregory Neil Shapiro 				host, n_rrs, dane_tlsa->dane_tlsa_dnsrc);
6445b0945b5SGregory Neil Shapiro 		goto cleanup;
6455b0945b5SGregory Neil Shapiro 	}
6465b0945b5SGregory Neil Shapiro 
6475b0945b5SGregory Neil Shapiro   done:
6485b0945b5SGregory Neil Shapiro 	dane_tlsa->dane_tlsa_n = n_rrs;
6495b0945b5SGregory Neil Shapiro 	if (!isrname)
6505b0945b5SGregory Neil Shapiro 	{
6515b0945b5SGregory Neil Shapiro 		SM_FREE(dane_tlsa->dane_tlsa_sni);
6525b0945b5SGregory Neil Shapiro 		dane_tlsa->dane_tlsa_sni = sm_strdup(host);
6535b0945b5SGregory Neil Shapiro 	}
6545b0945b5SGregory Neil Shapiro 	if (NULL == ste)
6555b0945b5SGregory Neil Shapiro 	{
6565b0945b5SGregory Neil Shapiro 		ste = stab(key, ST_TLSA_RR, ST_ENTER);
6575b0945b5SGregory Neil Shapiro 		if (NULL == ste)
6585b0945b5SGregory Neil Shapiro 			goto error;
6595b0945b5SGregory Neil Shapiro 	}
6605b0945b5SGregory Neil Shapiro 	ste->s_tlsa = dane_tlsa;
6615b0945b5SGregory Neil Shapiro 	if (now == 0)
6625b0945b5SGregory Neil Shapiro 		now = curtime();
6635b0945b5SGregory Neil Shapiro 	dane_tlsa->dane_tlsa_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
6645b0945b5SGregory Neil Shapiro 	dns_free_data(dr);
6655b0945b5SGregory Neil Shapiro 	dr = NULL;
6665b0945b5SGregory Neil Shapiro 	goto end;
6675b0945b5SGregory Neil Shapiro 
6685b0945b5SGregory Neil Shapiro   error:
6695b0945b5SGregory Neil Shapiro 	if (tTd(8, 2))
670*d39bd2c1SGregory Neil Shapiro 		sm_dprintf("gettlsa: host=%s, key=%s, status=error\n", host, key);
6715b0945b5SGregory Neil Shapiro 	n_rrs = -1;
6725b0945b5SGregory Neil Shapiro   cleanup:
6735b0945b5SGregory Neil Shapiro 	if (NULL == ste)
6745b0945b5SGregory Neil Shapiro 		dane_tlsa_free(dane_tlsa);
6755b0945b5SGregory Neil Shapiro 	dns_free_data(dr);
6765b0945b5SGregory Neil Shapiro 	dr = NULL;
6775b0945b5SGregory Neil Shapiro 
6785b0945b5SGregory Neil Shapiro   end:
6795b0945b5SGregory Neil Shapiro 	if (pste != NULL && ste != NULL)
6805b0945b5SGregory Neil Shapiro 		*pste = ste;
6815b0945b5SGregory Neil Shapiro 	if (len > 0)
6825b0945b5SGregory Neil Shapiro 		host[len] = '.';
6835b0945b5SGregory Neil Shapiro 	return n_rrs;
6845b0945b5SGregory Neil Shapiro }
6855b0945b5SGregory Neil Shapiro # endif /* DANE */
6865b0945b5SGregory Neil Shapiro 
68740266059SGregory Neil Shapiro /*
68840266059SGregory Neil Shapiro **  GETFALLBACKMXRR -- get MX resource records for fallback MX host.
68940266059SGregory Neil Shapiro **
69040266059SGregory Neil Shapiro **	We have to initialize this once before doing anything else.
69140266059SGregory Neil Shapiro **	Moreover, we have to repeat this from time to time to avoid
69240266059SGregory Neil Shapiro **	stale data, e.g., in persistent queue runners.
69340266059SGregory Neil Shapiro **	This should be done in a parent process so the child
69440266059SGregory Neil Shapiro **	processes have the right data.
69540266059SGregory Neil Shapiro **
69640266059SGregory Neil Shapiro **	Parameters:
69740266059SGregory Neil Shapiro **		host -- the name of the fallback MX host.
69840266059SGregory Neil Shapiro **
69940266059SGregory Neil Shapiro **	Returns:
70040266059SGregory Neil Shapiro **		number of MX records.
70140266059SGregory Neil Shapiro **
70240266059SGregory Neil Shapiro **	Side Effects:
703e92d3f3fSGregory Neil Shapiro **		Populates NumFallbackMXHosts and fbhosts.
70440266059SGregory Neil Shapiro **		Sets renewal time (based on TTL).
70540266059SGregory Neil Shapiro */
70640266059SGregory Neil Shapiro 
707e92d3f3fSGregory Neil Shapiro int NumFallbackMXHosts = 0;	/* Number of fallback MX hosts (after MX expansion) */
70840266059SGregory Neil Shapiro static char *fbhosts[MAXMXHOSTS + 1];
70940266059SGregory Neil Shapiro 
71040266059SGregory Neil Shapiro int
getfallbackmxrr(host)71140266059SGregory Neil Shapiro getfallbackmxrr(host)
71240266059SGregory Neil Shapiro 	char *host;
71340266059SGregory Neil Shapiro {
71440266059SGregory Neil Shapiro 	int i, rcode;
71540266059SGregory Neil Shapiro 	int ttl;
71640266059SGregory Neil Shapiro 	static time_t renew = 0;
71740266059SGregory Neil Shapiro 
71840266059SGregory Neil Shapiro # if 0
71940266059SGregory Neil Shapiro 	/* This is currently done before this function is called. */
7202fb4f839SGregory Neil Shapiro 	if (SM_IS_EMPTY(host))
72140266059SGregory Neil Shapiro 		return 0;
72240266059SGregory Neil Shapiro # endif /* 0 */
723e92d3f3fSGregory Neil Shapiro 	if (NumFallbackMXHosts > 0 && renew > curtime())
724e92d3f3fSGregory Neil Shapiro 		return NumFallbackMXHosts;
7255b0945b5SGregory Neil Shapiro 
726*d39bd2c1SGregory Neil Shapiro 	/*
727*d39bd2c1SGregory Neil Shapiro 	**  For DANE we need to invoke getmxrr() to get the TLSA RRs.
728*d39bd2c1SGregory Neil Shapiro 	**  Hack: don't do that if its not a FQHN (e.g., [localhost])
729*d39bd2c1SGregory Neil Shapiro 	**  This also triggers for IPv4 addresses, but not IPv6!
730*d39bd2c1SGregory Neil Shapiro 	*/
731*d39bd2c1SGregory Neil Shapiro 
732*d39bd2c1SGregory Neil Shapiro 	if (host[0] == '[' && (!Dane || strchr(host, '.') == NULL))
73340266059SGregory Neil Shapiro 	{
73440266059SGregory Neil Shapiro 		fbhosts[0] = host;
735e92d3f3fSGregory Neil Shapiro 		NumFallbackMXHosts = 1;
73640266059SGregory Neil Shapiro 	}
73740266059SGregory Neil Shapiro 	else
73840266059SGregory Neil Shapiro 	{
73940266059SGregory Neil Shapiro 		/* free old data */
740e92d3f3fSGregory Neil Shapiro 		for (i = 0; i < NumFallbackMXHosts; i++)
74140266059SGregory Neil Shapiro 			sm_free(fbhosts[i]);
74240266059SGregory Neil Shapiro 
7435b0945b5SGregory Neil Shapiro 		/*
7445b0945b5SGregory Neil Shapiro 		**  Get new data.
7455b0945b5SGregory Neil Shapiro 		**  Note: passing 0 as port is not correct but we cannot
7465b0945b5SGregory Neil Shapiro 		**  determine the port number as there is no mailer.
7475b0945b5SGregory Neil Shapiro 		*/
7485b0945b5SGregory Neil Shapiro 
7495b0945b5SGregory Neil Shapiro 		NumFallbackMXHosts = getmxrr(host, fbhosts, NULL,
7505b0945b5SGregory Neil Shapiro # if DANE
7515b0945b5SGregory Neil Shapiro 					(DANE_SECURE == Dane) ? ISAD :
7525b0945b5SGregory Neil Shapiro # endif
753*d39bd2c1SGregory Neil Shapiro 					0, &rcode, &ttl, 0, NULL);
75440266059SGregory Neil Shapiro 		renew = curtime() + ttl;
755e92d3f3fSGregory Neil Shapiro 		for (i = 0; i < NumFallbackMXHosts; i++)
75640266059SGregory Neil Shapiro 			fbhosts[i] = newstr(fbhosts[i]);
75740266059SGregory Neil Shapiro 	}
7585b0945b5SGregory Neil Shapiro 	if (NumFallbackMXHosts == NULLMX)
7595b0945b5SGregory Neil Shapiro 		NumFallbackMXHosts = 0;
760e92d3f3fSGregory Neil Shapiro 	return NumFallbackMXHosts;
76140266059SGregory Neil Shapiro }
76240266059SGregory Neil Shapiro 
76340266059SGregory Neil Shapiro /*
76440266059SGregory Neil Shapiro **  FALLBACKMXRR -- add MX resource records for fallback MX host to list.
76540266059SGregory Neil Shapiro **
76640266059SGregory Neil Shapiro **	Parameters:
76740266059SGregory Neil Shapiro **		nmx -- current number of MX records.
76840266059SGregory Neil Shapiro **		prefs -- array of preferences.
76940266059SGregory Neil Shapiro **		mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS)
77040266059SGregory Neil Shapiro **
77140266059SGregory Neil Shapiro **	Returns:
77240266059SGregory Neil Shapiro **		new number of MX records.
77340266059SGregory Neil Shapiro **
77440266059SGregory Neil Shapiro **	Side Effects:
775e92d3f3fSGregory Neil Shapiro **		If FallbackMX was set, it appends the MX records for
77640266059SGregory Neil Shapiro **		that host to mxhosts (and modifies prefs accordingly).
77740266059SGregory Neil Shapiro */
77840266059SGregory Neil Shapiro 
77940266059SGregory Neil Shapiro static int
fallbackmxrr(nmx,prefs,mxhosts)78040266059SGregory Neil Shapiro fallbackmxrr(nmx, prefs, mxhosts)
78140266059SGregory Neil Shapiro 	int nmx;
78240266059SGregory Neil Shapiro 	unsigned short *prefs;
78340266059SGregory Neil Shapiro 	char **mxhosts;
78440266059SGregory Neil Shapiro {
78540266059SGregory Neil Shapiro 	int i;
78640266059SGregory Neil Shapiro 
787e92d3f3fSGregory Neil Shapiro 	for (i = 0; i < NumFallbackMXHosts && nmx < MAXMXHOSTS; i++)
78840266059SGregory Neil Shapiro 	{
78940266059SGregory Neil Shapiro 		if (nmx > 0)
79040266059SGregory Neil Shapiro 			prefs[nmx] = prefs[nmx - 1] + 1;
79140266059SGregory Neil Shapiro 		else
79240266059SGregory Neil Shapiro 			prefs[nmx] = 0;
79340266059SGregory Neil Shapiro 		mxhosts[nmx++] = fbhosts[i];
79440266059SGregory Neil Shapiro 	}
79540266059SGregory Neil Shapiro 	return nmx;
79640266059SGregory Neil Shapiro }
79740266059SGregory Neil Shapiro 
7982fb4f839SGregory Neil Shapiro # if USE_EAI
7992fb4f839SGregory Neil Shapiro 
8002fb4f839SGregory Neil Shapiro /*
8012fb4f839SGregory Neil Shapiro **  HN2ALABEL -- convert hostname in U-label format to A-label format
8022fb4f839SGregory Neil Shapiro **
8032fb4f839SGregory Neil Shapiro **	Parameters:
8042fb4f839SGregory Neil Shapiro **		hostname -- hostname in U-label format
8052fb4f839SGregory Neil Shapiro **
8062fb4f839SGregory Neil Shapiro **	Returns:
8072fb4f839SGregory Neil Shapiro **		hostname in A-label format in a local static buffer.
8082fb4f839SGregory Neil Shapiro **		It must be copied before the function is called again.
8092fb4f839SGregory Neil Shapiro */
8102fb4f839SGregory Neil Shapiro 
8112fb4f839SGregory Neil Shapiro const char *
hn2alabel(hostname)8122fb4f839SGregory Neil Shapiro hn2alabel(hostname)
8132fb4f839SGregory Neil Shapiro 	const char *hostname;
8142fb4f839SGregory Neil Shapiro {
8152fb4f839SGregory Neil Shapiro 	UErrorCode error = U_ZERO_ERROR;
8162fb4f839SGregory Neil Shapiro 	UIDNAInfo info = UIDNA_INFO_INITIALIZER;
8172fb4f839SGregory Neil Shapiro 	UIDNA *idna;
8182fb4f839SGregory Neil Shapiro 	static char buf[MAXNAME_I];	/* XXX ??? */
8192fb4f839SGregory Neil Shapiro 
820*d39bd2c1SGregory Neil Shapiro 	if (str_is_print(hostname))
8212fb4f839SGregory Neil Shapiro 		return hostname;
8222fb4f839SGregory Neil Shapiro 	idna = uidna_openUTS46(UIDNA_NONTRANSITIONAL_TO_ASCII, &error);
8232fb4f839SGregory Neil Shapiro 	(void) uidna_nameToASCII_UTF8(idna, hostname, strlen(hostname),
8242fb4f839SGregory Neil Shapiro 				     buf, sizeof(buf) - 1,
8252fb4f839SGregory Neil Shapiro 				     &info, &error);
8262fb4f839SGregory Neil Shapiro 	uidna_close(idna);
8272fb4f839SGregory Neil Shapiro 	return buf;
8282fb4f839SGregory Neil Shapiro }
8292fb4f839SGregory Neil Shapiro # endif /* USE_EAI */
8302fb4f839SGregory Neil Shapiro 
83140266059SGregory Neil Shapiro /*
832c2aa98e2SPeter Wemm **  GETMXRR -- get MX resource records for a domain
833c2aa98e2SPeter Wemm **
834c2aa98e2SPeter Wemm **	Parameters:
8352fb4f839SGregory Neil Shapiro **		host -- the name of the host to MX [must be x]
836c2aa98e2SPeter Wemm **		mxhosts -- a pointer to a return buffer of MX records.
83706f25ae9SGregory Neil Shapiro **		mxprefs -- a pointer to a return buffer of MX preferences.
83806f25ae9SGregory Neil Shapiro **			If NULL, don't try to populate.
8395b0945b5SGregory Neil Shapiro **		flags -- flags:
840*d39bd2c1SGregory Neil Shapiro **			DROPLOCALHOST -- If true, all MX records less preferred
841c2aa98e2SPeter Wemm **			than the local host (as determined by $=w) will
842c2aa98e2SPeter Wemm **			be discarded.
8435b0945b5SGregory Neil Shapiro **			TRYFALLBACK -- add also fallback MX host?
8445b0945b5SGregory Neil Shapiro **			ISAD -- host lookup was secure?
845c2aa98e2SPeter Wemm **		rcode -- a pointer to an EX_ status code.
84640266059SGregory Neil Shapiro **		pttl -- pointer to return TTL (can be NULL).
847*d39bd2c1SGregory Neil Shapiro **		port -- port number used in TLSA queries (_PORT._tcp.)
848*d39bd2c1SGregory Neil Shapiro **		pad -- (output parameter, pointer to) AD flag (can be NULL)
849c2aa98e2SPeter Wemm **
850c2aa98e2SPeter Wemm **	Returns:
851c2aa98e2SPeter Wemm **		The number of MX records found.
852c2aa98e2SPeter Wemm **		-1 if there is an internal failure.
853c2aa98e2SPeter Wemm **		If no MX records are found, mxhosts[0] is set to host
854c2aa98e2SPeter Wemm **			and 1 is returned.
85540266059SGregory Neil Shapiro **
85640266059SGregory Neil Shapiro **	Side Effects:
85740266059SGregory Neil Shapiro **		The entries made for mxhosts point to a static array
85840266059SGregory Neil Shapiro **		MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied,
85940266059SGregory Neil Shapiro **		if it must be preserved across calls to this function.
860c2aa98e2SPeter Wemm */
861c2aa98e2SPeter Wemm 
862c2aa98e2SPeter Wemm int
getmxrr(host,mxhosts,mxprefs,flags,rcode,pttl,port,pad)863*d39bd2c1SGregory Neil Shapiro getmxrr(host, mxhosts, mxprefs, flags, rcode, pttl, port, pad)
864c2aa98e2SPeter Wemm 	char *host;
865c2aa98e2SPeter Wemm 	char **mxhosts;
86640266059SGregory Neil Shapiro 	unsigned short *mxprefs;
8675b0945b5SGregory Neil Shapiro 	unsigned int flags;
868c2aa98e2SPeter Wemm 	int *rcode;
86940266059SGregory Neil Shapiro 	int *pttl;
8705b0945b5SGregory Neil Shapiro 	int port;
871*d39bd2c1SGregory Neil Shapiro 	int *pad;
872c2aa98e2SPeter Wemm {
87340266059SGregory Neil Shapiro 	register unsigned char *eom, *cp;
874c2aa98e2SPeter Wemm 	register int i, j, n;
875c2aa98e2SPeter Wemm 	int nmx = 0;
876c2aa98e2SPeter Wemm 	register char *bp;
877c2aa98e2SPeter Wemm 	HEADER *hp;
878c2aa98e2SPeter Wemm 	querybuf answer;
879c2aa98e2SPeter Wemm 	int ancount, qdcount, buflen;
88040266059SGregory Neil Shapiro 	bool seenlocal = false;
88140266059SGregory Neil Shapiro 	unsigned short pref, type;
88240266059SGregory Neil Shapiro 	unsigned short localpref = 256;
883e92d3f3fSGregory Neil Shapiro 	char *fallbackMX = FallbackMX;
88440266059SGregory Neil Shapiro 	bool trycanon = false;
88540266059SGregory Neil Shapiro 	unsigned short *prefs;
886b6bacd31SGregory Neil Shapiro 	int (*resfunc) __P((const char *, int, int, u_char *, int));
88740266059SGregory Neil Shapiro 	unsigned short prefer[MAXMXHOSTS];
888c2aa98e2SPeter Wemm 	int weight[MAXMXHOSTS];
88940266059SGregory Neil Shapiro 	int ttl = 0;
8905b0945b5SGregory Neil Shapiro 	bool ad;
8915b0945b5SGregory Neil Shapiro 	bool seennullmx = false;
8922fb4f839SGregory Neil Shapiro 	extern int res_query __P((const char *, int, int, u_char *, int));
8932fb4f839SGregory Neil Shapiro 	extern int res_search __P((const char *, int, int , u_char *, int));
8945b0945b5SGregory Neil Shapiro # if DANE
8955b0945b5SGregory Neil Shapiro 	bool cname2mx;
8962fb4f839SGregory Neil Shapiro 	char qname[MAXNAME];	/* EAI: copy of host: ok? */
8975b0945b5SGregory Neil Shapiro 	unsigned long old_options = 0;
8985b0945b5SGregory Neil Shapiro # endif
899c2aa98e2SPeter Wemm 
900c2aa98e2SPeter Wemm 	if (tTd(8, 2))
9015b0945b5SGregory Neil Shapiro 		sm_dprintf("getmxrr(%s, droplocalhost=%d, flags=%X, port=%d)\n",
9025b0945b5SGregory Neil Shapiro 			   host, (flags & DROPLOCALHOST) != 0, flags, port);
9035b0945b5SGregory Neil Shapiro 	ad = (flags & ISAD) != 0;
90413d88268SGregory Neil Shapiro 	*rcode = EX_OK;
90513d88268SGregory Neil Shapiro 	if (pttl != NULL)
90613d88268SGregory Neil Shapiro 		*pttl = SM_DEFAULT_TTL;
907a7ec597cSGregory Neil Shapiro 	if (*host == '\0')
908a7ec597cSGregory Neil Shapiro 		return 0;
9095b0945b5SGregory Neil Shapiro # if DANE
9105b0945b5SGregory Neil Shapiro 	cname2mx = false;
9115b0945b5SGregory Neil Shapiro 	qname[0] = '\0';
9125b0945b5SGregory Neil Shapiro 	old_options = _res.options;
9135b0945b5SGregory Neil Shapiro 	if (ad)
9145b0945b5SGregory Neil Shapiro 		_res.options |= SM_RES_DNSSEC;
9155b0945b5SGregory Neil Shapiro # endif
916c2aa98e2SPeter Wemm 
9175b0945b5SGregory Neil Shapiro 	if ((fallbackMX != NULL && (flags & DROPLOCALHOST) != 0 &&
9185b0945b5SGregory Neil Shapiro 	     wordinclass(fallbackMX, 'w')) || (flags & TRYFALLBACK) == 0)
919c2aa98e2SPeter Wemm 	{
920c2aa98e2SPeter Wemm 		/* don't use fallback for this pass */
921c2aa98e2SPeter Wemm 		fallbackMX = NULL;
922c2aa98e2SPeter Wemm 	}
923c2aa98e2SPeter Wemm 
92406f25ae9SGregory Neil Shapiro 	if (mxprefs != NULL)
92506f25ae9SGregory Neil Shapiro 		prefs = mxprefs;
92606f25ae9SGregory Neil Shapiro 	else
92706f25ae9SGregory Neil Shapiro 		prefs = prefer;
92806f25ae9SGregory Neil Shapiro 
929c2aa98e2SPeter Wemm 	/* efficiency hack -- numeric or non-MX lookups */
930c2aa98e2SPeter Wemm 	if (host[0] == '[')
931c2aa98e2SPeter Wemm 		goto punt;
932c2aa98e2SPeter Wemm 
9335b0945b5SGregory Neil Shapiro # if DANE
9345b0945b5SGregory Neil Shapiro 	/*
9355b0945b5SGregory Neil Shapiro 	**  NOTE: This only works if nocanonify is used,
9365b0945b5SGregory Neil Shapiro 	**  otherwise the name is already rewritten.
9375b0945b5SGregory Neil Shapiro 	*/
9385b0945b5SGregory Neil Shapiro 
9395b0945b5SGregory Neil Shapiro 	/* always or only when "needed"? */
9405b0945b5SGregory Neil Shapiro 	if (DANE_ALWAYS == Dane || (ad && DANE_SECURE == Dane))
9415b0945b5SGregory Neil Shapiro 		(void) sm_strlcpy(qname, host, sizeof(qname));
9425b0945b5SGregory Neil Shapiro # endif /* DANE */
9435b0945b5SGregory Neil Shapiro 
9442fb4f839SGregory Neil Shapiro # if USE_EAI
945*d39bd2c1SGregory Neil Shapiro 	if (!str_is_print(host))
9465b0945b5SGregory Neil Shapiro 	{
9472fb4f839SGregory Neil Shapiro 		/* XXX memory leak? */
9482fb4f839SGregory Neil Shapiro 		host = sm_rpool_strdup_x(CurEnv->e_rpool, hn2alabel(host));
9495b0945b5SGregory Neil Shapiro 	}
9502fb4f839SGregory Neil Shapiro # endif /* USE_EAI */
9515b0945b5SGregory Neil Shapiro 
952c2aa98e2SPeter Wemm 	/*
953c2aa98e2SPeter Wemm 	**  If we don't have MX records in our host switch, don't
954c2aa98e2SPeter Wemm 	**  try for MX records.  Note that this really isn't "right",
955c2aa98e2SPeter Wemm 	**  since we might be set up to try NIS first and then DNS;
956c2aa98e2SPeter Wemm 	**  if the host is found in NIS we really shouldn't be doing
957c2aa98e2SPeter Wemm 	**  MX lookups.  However, that should be a degenerate case.
958c2aa98e2SPeter Wemm 	*/
959c2aa98e2SPeter Wemm 
960c2aa98e2SPeter Wemm 	if (!UseNameServer)
961c2aa98e2SPeter Wemm 		goto punt;
962c2aa98e2SPeter Wemm 	if (HasWildcardMX && ConfigLevel >= 6)
963c2aa98e2SPeter Wemm 		resfunc = res_query;
964c2aa98e2SPeter Wemm 	else
965c2aa98e2SPeter Wemm 		resfunc = res_search;
9665b0945b5SGregory Neil Shapiro # if DNSSEC_TEST
9675b0945b5SGregory Neil Shapiro 	if (tTd(8, 110))
9685b0945b5SGregory Neil Shapiro 		resfunc = tstdns_search;
9695b0945b5SGregory Neil Shapiro # endif
970c2aa98e2SPeter Wemm 
971c2aa98e2SPeter Wemm 	errno = 0;
9725b0945b5SGregory Neil Shapiro 	hp = (HEADER *)&answer;
97340266059SGregory Neil Shapiro 	n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer,
97440266059SGregory Neil Shapiro 		       sizeof(answer));
975c2aa98e2SPeter Wemm 	if (n < 0)
976c2aa98e2SPeter Wemm 	{
977c2aa98e2SPeter Wemm 		if (tTd(8, 1))
9785b0945b5SGregory Neil Shapiro # if DNSSEC_TEST
9795b0945b5SGregory Neil Shapiro 			sm_dprintf("getmxrr: res_search(%s) failed (errno=%d (%s), h_errno=%d (%s))\n",
9805b0945b5SGregory Neil Shapiro 				host, errno, strerror(errno),
9815b0945b5SGregory Neil Shapiro 				h_errno, herrno2txt(h_errno));
9825b0945b5SGregory Neil Shapiro # else
9835b0945b5SGregory Neil Shapiro 			sm_dprintf("getmxrr: res_search(%s) failed, h_errno=%d\n",
9845b0945b5SGregory Neil Shapiro 				host, h_errno);
9855b0945b5SGregory Neil Shapiro # endif
986c2aa98e2SPeter Wemm 		switch (h_errno)
987c2aa98e2SPeter Wemm 		{
988c2aa98e2SPeter Wemm 		  case NO_DATA:
98940266059SGregory Neil Shapiro 			trycanon = true;
99006f25ae9SGregory Neil Shapiro 			/* FALLTHROUGH */
991c2aa98e2SPeter Wemm 
992c2aa98e2SPeter Wemm 		  case NO_RECOVERY:
993c2aa98e2SPeter Wemm 			/* no MX data on this host */
994c2aa98e2SPeter Wemm 			goto punt;
995c2aa98e2SPeter Wemm 
996c2aa98e2SPeter Wemm 		  case HOST_NOT_FOUND:
997c2aa98e2SPeter Wemm # if BROKEN_RES_SEARCH
9985b0945b5SGregory Neil Shapiro 		  case 0: /* Ultrix resolver returns failure w/ h_errno=0 */
9995b0945b5SGregory Neil Shapiro # endif
1000c2aa98e2SPeter Wemm 			/* host doesn't exist in DNS; might be in /etc/hosts */
100140266059SGregory Neil Shapiro 			trycanon = true;
1002c2aa98e2SPeter Wemm 			*rcode = EX_NOHOST;
1003c2aa98e2SPeter Wemm 			goto punt;
1004c2aa98e2SPeter Wemm 
1005c2aa98e2SPeter Wemm 		  case TRY_AGAIN:
1006c2aa98e2SPeter Wemm 		  case -1:
1007c2aa98e2SPeter Wemm 			/* couldn't connect to the name server */
1008c2aa98e2SPeter Wemm 			if (fallbackMX != NULL)
1009c2aa98e2SPeter Wemm 			{
1010c2aa98e2SPeter Wemm 				/* name server is hosed -- push to fallback */
10115b0945b5SGregory Neil Shapiro 				nmx = fallbackmxrr(nmx, prefs, mxhosts);
10125b0945b5SGregory Neil Shapiro 				goto done;
1013c2aa98e2SPeter Wemm 			}
1014c2aa98e2SPeter Wemm 			/* it might come up later; better queue it up */
1015c2aa98e2SPeter Wemm 			*rcode = EX_TEMPFAIL;
1016c2aa98e2SPeter Wemm 			break;
1017c2aa98e2SPeter Wemm 
1018c2aa98e2SPeter Wemm 		  default:
101940266059SGregory Neil Shapiro 			syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)",
1020c2aa98e2SPeter Wemm 				host, h_errno);
1021c2aa98e2SPeter Wemm 			*rcode = EX_OSERR;
1022c2aa98e2SPeter Wemm 			break;
1023c2aa98e2SPeter Wemm 		}
1024c2aa98e2SPeter Wemm 
1025c2aa98e2SPeter Wemm 		/* irreconcilable differences */
10265b0945b5SGregory Neil Shapiro 		goto error;
1027c2aa98e2SPeter Wemm 	}
1028c2aa98e2SPeter Wemm 
10295b0945b5SGregory Neil Shapiro 	ad = ad && hp->ad;
10305b0945b5SGregory Neil Shapiro 	if (tTd(8, 2))
10315b0945b5SGregory Neil Shapiro 		sm_dprintf("getmxrr(%s), hp=%p, ad=%d\n", host, (void*)hp, ad);
1032*d39bd2c1SGregory Neil Shapiro 	if (pad != NULL)
1033*d39bd2c1SGregory Neil Shapiro 		*pad = ad;
10345b0945b5SGregory Neil Shapiro 
1035c2aa98e2SPeter Wemm 	/* avoid problems after truncation in tcp packets */
1036c2aa98e2SPeter Wemm 	if (n > sizeof(answer))
1037c2aa98e2SPeter Wemm 		n = sizeof(answer);
1038c2aa98e2SPeter Wemm 
1039c2aa98e2SPeter Wemm 	/* find first satisfactory answer */
104040266059SGregory Neil Shapiro 	cp = (unsigned char *)&answer + HFIXEDSZ;
104140266059SGregory Neil Shapiro 	eom = (unsigned char *)&answer + n;
10425b0945b5SGregory Neil Shapiro 
104340266059SGregory Neil Shapiro 	for (qdcount = ntohs((unsigned short) hp->qdcount);
104406f25ae9SGregory Neil Shapiro 	     qdcount--;
104506f25ae9SGregory Neil Shapiro 	     cp += n + QFIXEDSZ)
104606f25ae9SGregory Neil Shapiro 	{
1047c2aa98e2SPeter Wemm 		if ((n = dn_skipname(cp, eom)) < 0)
1048c2aa98e2SPeter Wemm 			goto punt;
104906f25ae9SGregory Neil Shapiro 	}
105040266059SGregory Neil Shapiro 
105140266059SGregory Neil Shapiro 	/* NOTE: see definition of MXHostBuf! */
1052c2aa98e2SPeter Wemm 	buflen = sizeof(MXHostBuf) - 1;
105340266059SGregory Neil Shapiro 	SM_ASSERT(buflen > 0);
1054c2aa98e2SPeter Wemm 	bp = MXHostBuf;
105540266059SGregory Neil Shapiro 	ancount = ntohs((unsigned short) hp->ancount);
105640266059SGregory Neil Shapiro 
105740266059SGregory Neil Shapiro 	/* See RFC 1035 for layout of RRs. */
1058e92d3f3fSGregory Neil Shapiro 	/* XXX leave room for FallbackMX ? */
1059c2aa98e2SPeter Wemm 	while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
1060c2aa98e2SPeter Wemm 	{
106140266059SGregory Neil Shapiro 		if ((n = dn_expand((unsigned char *)&answer, eom, cp,
106240266059SGregory Neil Shapiro 				   (RES_UNC_T) bp, buflen)) < 0)
1063c2aa98e2SPeter Wemm 			break;
1064c2aa98e2SPeter Wemm 		cp += n;
1065c2aa98e2SPeter Wemm 		GETSHORT(type, cp);
106640266059SGregory Neil Shapiro 		cp += INT16SZ;		/* skip over class */
106740266059SGregory Neil Shapiro 		GETLONG(ttl, cp);
106840266059SGregory Neil Shapiro 		GETSHORT(n, cp);	/* rdlength */
10695b0945b5SGregory Neil Shapiro # if DANE
10705b0945b5SGregory Neil Shapiro 		if (type == T_CNAME)
10715b0945b5SGregory Neil Shapiro 			cname2mx = true;
10725b0945b5SGregory Neil Shapiro # endif
1073c2aa98e2SPeter Wemm 		if (type != T_MX)
1074c2aa98e2SPeter Wemm 		{
10755b0945b5SGregory Neil Shapiro 			if ((tTd(8, 8) || _res.options & RES_DEBUG)
10765b0945b5SGregory Neil Shapiro # if DANE
10775b0945b5SGregory Neil Shapiro 			    && type != T_RRSIG
10785b0945b5SGregory Neil Shapiro # endif
10795b0945b5SGregory Neil Shapiro 			    )
10805b0945b5SGregory Neil Shapiro 				sm_dprintf("unexpected answer type %s, size %d\n",
10815b0945b5SGregory Neil Shapiro 					dns_type_to_string(type), n);
1082c2aa98e2SPeter Wemm 			cp += n;
1083c2aa98e2SPeter Wemm 			continue;
1084c2aa98e2SPeter Wemm 		}
1085c2aa98e2SPeter Wemm 		GETSHORT(pref, cp);
108640266059SGregory Neil Shapiro 		if ((n = dn_expand((unsigned char *)&answer, eom, cp,
1087c2aa98e2SPeter Wemm 				   (RES_UNC_T) bp, buflen)) < 0)
1088c2aa98e2SPeter Wemm 			break;
1089c2aa98e2SPeter Wemm 		cp += n;
109040266059SGregory Neil Shapiro 		n = strlen(bp);
10915b0945b5SGregory Neil Shapiro 
10925b0945b5SGregory Neil Shapiro 		/* Support for RFC7505 "MX 0 ." */
10935b0945b5SGregory Neil Shapiro 		if (pref == 0 && *bp == '\0')
10945b0945b5SGregory Neil Shapiro 			seennullmx = true;
10955b0945b5SGregory Neil Shapiro 
1096c2aa98e2SPeter Wemm 		if (wordinclass(bp, 'w'))
1097c2aa98e2SPeter Wemm 		{
1098c2aa98e2SPeter Wemm 			if (tTd(8, 3))
109940266059SGregory Neil Shapiro 				sm_dprintf("found localhost (%s) in MX list, pref=%d\n",
1100c2aa98e2SPeter Wemm 					bp, pref);
11015b0945b5SGregory Neil Shapiro 			if ((flags & DROPLOCALHOST) != 0)
1102c2aa98e2SPeter Wemm 			{
1103c2aa98e2SPeter Wemm 				if (!seenlocal || pref < localpref)
1104c2aa98e2SPeter Wemm 					localpref = pref;
110540266059SGregory Neil Shapiro 				seenlocal = true;
1106c2aa98e2SPeter Wemm 				continue;
1107c2aa98e2SPeter Wemm 			}
1108c2aa98e2SPeter Wemm 			weight[nmx] = 0;
1109c2aa98e2SPeter Wemm 		}
1110c2aa98e2SPeter Wemm 		else
1111c2aa98e2SPeter Wemm 			weight[nmx] = mxrand(bp);
111206f25ae9SGregory Neil Shapiro 		prefs[nmx] = pref;
1113c2aa98e2SPeter Wemm 		mxhosts[nmx++] = bp;
11145b0945b5SGregory Neil Shapiro # if DANE
11155b0945b5SGregory Neil Shapiro 		if (CHK_DANE(Dane) && port >= 0)
11165b0945b5SGregory Neil Shapiro 		{
11175b0945b5SGregory Neil Shapiro 			int nrr;
11185b0945b5SGregory Neil Shapiro 			unsigned long flags;
11195b0945b5SGregory Neil Shapiro 
1120*d39bd2c1SGregory Neil Shapiro 			flags = TLSAFLNEW;
1121*d39bd2c1SGregory Neil Shapiro 			if (pad != NULL && *pad)
1122*d39bd2c1SGregory Neil Shapiro 				flags |= TLSAFLADMX;
1123*d39bd2c1SGregory Neil Shapiro 			if (tTd(8, 20))
1124*d39bd2c1SGregory Neil Shapiro 				sm_dprintf("getmxrr: 1: host=%s, mx=%s, flags=%#lx\n", host, bp, flags);
11255b0945b5SGregory Neil Shapiro 			nrr = gettlsa(bp, NULL, NULL, flags, ttl, port);
11265b0945b5SGregory Neil Shapiro 
11275b0945b5SGregory Neil Shapiro 			/* Only check qname if no TLSA RRs were found */
11285b0945b5SGregory Neil Shapiro 			if (0 == nrr && cname2mx && '\0' != qname[0] &&
11295b0945b5SGregory Neil Shapiro 			    strcmp(qname, bp))
1130*d39bd2c1SGregory Neil Shapiro 			{
1131*d39bd2c1SGregory Neil Shapiro 				if (tTd(8, 20))
1132*d39bd2c1SGregory Neil Shapiro 					sm_dprintf("getmxrr: 2: host=%s, qname=%s, flags=%#lx\n", host, qname, flags);
11335b0945b5SGregory Neil Shapiro 				gettlsa(qname, bp, NULL, flags, ttl, port);
11345b0945b5SGregory Neil Shapiro 			/* XXX is this the right ad flag? */
11355b0945b5SGregory Neil Shapiro 			}
1136*d39bd2c1SGregory Neil Shapiro 		}
11375b0945b5SGregory Neil Shapiro # endif
11385b0945b5SGregory Neil Shapiro 
11395b0945b5SGregory Neil Shapiro 		/*
11405b0945b5SGregory Neil Shapiro 		**  Note: n can be 0 for something like:
11415b0945b5SGregory Neil Shapiro 		**  host MX 0 .
11425b0945b5SGregory Neil Shapiro 		**  See RFC 7505
11435b0945b5SGregory Neil Shapiro 		*/
11445b0945b5SGregory Neil Shapiro 
1145c2aa98e2SPeter Wemm 		bp += n;
11465b0945b5SGregory Neil Shapiro 		if (0 == n || bp[-1] != '.')
1147c2aa98e2SPeter Wemm 		{
1148c2aa98e2SPeter Wemm 			*bp++ = '.';
1149c2aa98e2SPeter Wemm 			n++;
1150c2aa98e2SPeter Wemm 		}
1151c2aa98e2SPeter Wemm 		*bp++ = '\0';
115240266059SGregory Neil Shapiro 		if (buflen < n + 1)
115340266059SGregory Neil Shapiro 		{
115440266059SGregory Neil Shapiro 			/* don't want to wrap buflen */
115540266059SGregory Neil Shapiro 			break;
115640266059SGregory Neil Shapiro 		}
1157c2aa98e2SPeter Wemm 		buflen -= n + 1;
1158c2aa98e2SPeter Wemm 	}
1159c2aa98e2SPeter Wemm 
11605b0945b5SGregory Neil Shapiro 	/* Support for RFC7505 "MX 0 ." */
11615b0945b5SGregory Neil Shapiro 	if (seennullmx && nmx == 1)
11625b0945b5SGregory Neil Shapiro 	{
11635b0945b5SGregory Neil Shapiro 		if (tTd(8, 4))
11645b0945b5SGregory Neil Shapiro 			sm_dprintf("getmxrr: Null MX record found, domain doesn't accept mail (RFC7505)\n");
11655b0945b5SGregory Neil Shapiro 		*rcode = EX_UNAVAILABLE;
11665b0945b5SGregory Neil Shapiro 		return NULLMX;
11675b0945b5SGregory Neil Shapiro 	}
11685b0945b5SGregory Neil Shapiro 
116940266059SGregory Neil Shapiro 	/* return only one TTL entry, that should be sufficient */
117040266059SGregory Neil Shapiro 	if (ttl > 0 && pttl != NULL)
117140266059SGregory Neil Shapiro 		*pttl = ttl;
117240266059SGregory Neil Shapiro 
1173c2aa98e2SPeter Wemm 	/* sort the records */
1174c2aa98e2SPeter Wemm 	for (i = 0; i < nmx; i++)
1175c2aa98e2SPeter Wemm 	{
1176c2aa98e2SPeter Wemm 		for (j = i + 1; j < nmx; j++)
1177c2aa98e2SPeter Wemm 		{
117806f25ae9SGregory Neil Shapiro 			if (prefs[i] > prefs[j] ||
117906f25ae9SGregory Neil Shapiro 			    (prefs[i] == prefs[j] && weight[i] > weight[j]))
1180c2aa98e2SPeter Wemm 			{
1181c2aa98e2SPeter Wemm 				register int temp;
1182c2aa98e2SPeter Wemm 				register char *temp1;
1183c2aa98e2SPeter Wemm 
118406f25ae9SGregory Neil Shapiro 				temp = prefs[i];
118506f25ae9SGregory Neil Shapiro 				prefs[i] = prefs[j];
118606f25ae9SGregory Neil Shapiro 				prefs[j] = temp;
1187c2aa98e2SPeter Wemm 				temp1 = mxhosts[i];
1188c2aa98e2SPeter Wemm 				mxhosts[i] = mxhosts[j];
1189c2aa98e2SPeter Wemm 				mxhosts[j] = temp1;
1190c2aa98e2SPeter Wemm 				temp = weight[i];
1191c2aa98e2SPeter Wemm 				weight[i] = weight[j];
1192c2aa98e2SPeter Wemm 				weight[j] = temp;
1193c2aa98e2SPeter Wemm 			}
1194c2aa98e2SPeter Wemm 		}
119506f25ae9SGregory Neil Shapiro 		if (seenlocal && prefs[i] >= localpref)
1196c2aa98e2SPeter Wemm 		{
1197c2aa98e2SPeter Wemm 			/* truncate higher preference part of list */
1198c2aa98e2SPeter Wemm 			nmx = i;
1199c2aa98e2SPeter Wemm 		}
1200c2aa98e2SPeter Wemm 	}
1201c2aa98e2SPeter Wemm 
1202c2aa98e2SPeter Wemm 	/* delete duplicates from list (yes, some bozos have duplicates) */
1203c2aa98e2SPeter Wemm 	for (i = 0; i < nmx - 1; )
1204c2aa98e2SPeter Wemm 	{
12052fb4f839SGregory Neil Shapiro 		if (!SM_STRCASEEQ(mxhosts[i], mxhosts[i + 1]))
1206c2aa98e2SPeter Wemm 			i++;
1207c2aa98e2SPeter Wemm 		else
1208c2aa98e2SPeter Wemm 		{
1209c2aa98e2SPeter Wemm 			/* compress out duplicate */
1210c2aa98e2SPeter Wemm 			for (j = i + 1; j < nmx; j++)
121106f25ae9SGregory Neil Shapiro 			{
1212c2aa98e2SPeter Wemm 				mxhosts[j] = mxhosts[j + 1];
121306f25ae9SGregory Neil Shapiro 				prefs[j] = prefs[j + 1];
121406f25ae9SGregory Neil Shapiro 			}
1215c2aa98e2SPeter Wemm 			nmx--;
1216c2aa98e2SPeter Wemm 		}
1217c2aa98e2SPeter Wemm 	}
1218c2aa98e2SPeter Wemm 
1219c2aa98e2SPeter Wemm 	if (nmx == 0)
1220c2aa98e2SPeter Wemm 	{
1221c2aa98e2SPeter Wemm punt:
122206f25ae9SGregory Neil Shapiro 		if (seenlocal)
1223c2aa98e2SPeter Wemm 		{
122406f25ae9SGregory Neil Shapiro 			struct hostent *h = NULL;
122506f25ae9SGregory Neil Shapiro 
1226c2aa98e2SPeter Wemm 			/*
1227c2aa98e2SPeter Wemm 			**  If we have deleted all MX entries, this is
1228c2aa98e2SPeter Wemm 			**  an error -- we should NEVER send to a host that
1229c2aa98e2SPeter Wemm 			**  has an MX, and this should have been caught
1230c2aa98e2SPeter Wemm 			**  earlier in the config file.
1231c2aa98e2SPeter Wemm 			**
1232c2aa98e2SPeter Wemm 			**  Some sites prefer to go ahead and try the
1233c2aa98e2SPeter Wemm 			**  A record anyway; that case is handled by
1234c2aa98e2SPeter Wemm 			**  setting TryNullMXList.  I believe this is a
1235c2aa98e2SPeter Wemm 			**  bad idea, but it's up to you....
1236c2aa98e2SPeter Wemm 			*/
1237c2aa98e2SPeter Wemm 
123806f25ae9SGregory Neil Shapiro 			if (TryNullMXList)
123906f25ae9SGregory Neil Shapiro 			{
1240602a2b1bSGregory Neil Shapiro 				SM_SET_H_ERRNO(0);
124106f25ae9SGregory Neil Shapiro 				errno = 0;
124206f25ae9SGregory Neil Shapiro 				h = sm_gethostbyname(host, AF_INET);
124306f25ae9SGregory Neil Shapiro 				if (h == NULL)
124406f25ae9SGregory Neil Shapiro 				{
124506f25ae9SGregory Neil Shapiro 					if (errno == ETIMEDOUT ||
124606f25ae9SGregory Neil Shapiro 					    h_errno == TRY_AGAIN ||
124706f25ae9SGregory Neil Shapiro 					    (errno == ECONNREFUSED &&
124806f25ae9SGregory Neil Shapiro 					     UseNameServer))
124906f25ae9SGregory Neil Shapiro 					{
125006f25ae9SGregory Neil Shapiro 						*rcode = EX_TEMPFAIL;
12515b0945b5SGregory Neil Shapiro 						goto error;
125206f25ae9SGregory Neil Shapiro 					}
125306f25ae9SGregory Neil Shapiro # if NETINET6
1254602a2b1bSGregory Neil Shapiro 					SM_SET_H_ERRNO(0);
125506f25ae9SGregory Neil Shapiro 					errno = 0;
125606f25ae9SGregory Neil Shapiro 					h = sm_gethostbyname(host, AF_INET6);
125706f25ae9SGregory Neil Shapiro 					if (h == NULL &&
125806f25ae9SGregory Neil Shapiro 					    (errno == ETIMEDOUT ||
125906f25ae9SGregory Neil Shapiro 					     h_errno == TRY_AGAIN ||
126006f25ae9SGregory Neil Shapiro 					     (errno == ECONNREFUSED &&
126106f25ae9SGregory Neil Shapiro 					      UseNameServer)))
126206f25ae9SGregory Neil Shapiro 					{
126306f25ae9SGregory Neil Shapiro 						*rcode = EX_TEMPFAIL;
12645b0945b5SGregory Neil Shapiro 						goto error;
126506f25ae9SGregory Neil Shapiro 					}
126606f25ae9SGregory Neil Shapiro # endif /* NETINET6 */
126706f25ae9SGregory Neil Shapiro 				}
126806f25ae9SGregory Neil Shapiro 			}
126906f25ae9SGregory Neil Shapiro 
127006f25ae9SGregory Neil Shapiro 			if (h == NULL)
127106f25ae9SGregory Neil Shapiro 			{
1272c2aa98e2SPeter Wemm 				*rcode = EX_CONFIG;
1273c2aa98e2SPeter Wemm 				syserr("MX list for %s points back to %s",
1274c2aa98e2SPeter Wemm 				       host, MyHostName);
12755b0945b5SGregory Neil Shapiro 				goto error;
1276c2aa98e2SPeter Wemm 			}
127740266059SGregory Neil Shapiro # if NETINET6
1278193538b7SGregory Neil Shapiro 			freehostent(h);
1279af9557fdSGregory Neil Shapiro 			h = NULL;
12805b0945b5SGregory Neil Shapiro # endif
128106f25ae9SGregory Neil Shapiro 		}
1282d0cef73dSGregory Neil Shapiro 		if (strlen(host) >= sizeof(MXHostBuf))
1283c2aa98e2SPeter Wemm 		{
1284c2aa98e2SPeter Wemm 			*rcode = EX_CONFIG;
1285c2aa98e2SPeter Wemm 			syserr("Host name %s too long",
1286c2aa98e2SPeter Wemm 			       shortenstring(host, MAXSHORTSTR));
12875b0945b5SGregory Neil Shapiro 			goto error;
1288c2aa98e2SPeter Wemm 		}
1289d0cef73dSGregory Neil Shapiro 		(void) sm_strlcpy(MXHostBuf, host, sizeof(MXHostBuf));
1290c2aa98e2SPeter Wemm 		mxhosts[0] = MXHostBuf;
129106f25ae9SGregory Neil Shapiro 		prefs[0] = 0;
1292c2aa98e2SPeter Wemm 		if (host[0] == '[')
1293c2aa98e2SPeter Wemm 		{
1294c2aa98e2SPeter Wemm 			register char *p;
129506f25ae9SGregory Neil Shapiro # if NETINET6
129606f25ae9SGregory Neil Shapiro 			struct sockaddr_in6 tmp6;
12975b0945b5SGregory Neil Shapiro # endif
1298c2aa98e2SPeter Wemm 
1299c2aa98e2SPeter Wemm 			/* this may be an MX suppression-style address */
1300c2aa98e2SPeter Wemm 			p = strchr(MXHostBuf, ']');
1301c2aa98e2SPeter Wemm 			if (p != NULL)
1302c2aa98e2SPeter Wemm 			{
1303c2aa98e2SPeter Wemm 				*p = '\0';
130406f25ae9SGregory Neil Shapiro 
1305c2aa98e2SPeter Wemm 				if (inet_addr(&MXHostBuf[1]) != INADDR_NONE)
1306c2aa98e2SPeter Wemm 				{
1307c2aa98e2SPeter Wemm 					nmx++;
1308c2aa98e2SPeter Wemm 					*p = ']';
1309c2aa98e2SPeter Wemm 				}
131006f25ae9SGregory Neil Shapiro # if NETINET6
131140266059SGregory Neil Shapiro 				else if (anynet_pton(AF_INET6, &MXHostBuf[1],
131206f25ae9SGregory Neil Shapiro 						     &tmp6.sin6_addr) == 1)
131306f25ae9SGregory Neil Shapiro 				{
131406f25ae9SGregory Neil Shapiro 					nmx++;
131506f25ae9SGregory Neil Shapiro 					*p = ']';
131606f25ae9SGregory Neil Shapiro 				}
131706f25ae9SGregory Neil Shapiro # endif /* NETINET6 */
1318c2aa98e2SPeter Wemm 				else
1319c2aa98e2SPeter Wemm 				{
13202fb4f839SGregory Neil Shapiro # if USE_EAI
13212fb4f839SGregory Neil Shapiro 					char *hn;
13222fb4f839SGregory Neil Shapiro 
13232fb4f839SGregory Neil Shapiro 					hn = MXHostBuf + 1;
1324*d39bd2c1SGregory Neil Shapiro 					if (!str_is_print(hn))
13252fb4f839SGregory Neil Shapiro 					{
13262fb4f839SGregory Neil Shapiro 						const char *ahn;
13272fb4f839SGregory Neil Shapiro 
13282fb4f839SGregory Neil Shapiro 						ahn = hn2alabel(hn);
13292fb4f839SGregory Neil Shapiro 						if (strlen(ahn) >= sizeof(MXHostBuf) - 1)
13302fb4f839SGregory Neil Shapiro 						{
13312fb4f839SGregory Neil Shapiro 							*rcode = EX_CONFIG;
13322fb4f839SGregory Neil Shapiro 							syserr("Encoded host name %s too long",
13332fb4f839SGregory Neil Shapiro 							       shortenstring(ahn, MAXSHORTSTR));
13342fb4f839SGregory Neil Shapiro 							goto error;
13352fb4f839SGregory Neil Shapiro 						}
13362fb4f839SGregory Neil Shapiro 						(void) sm_strlcpy(hn, ahn, sizeof(MXHostBuf) - 1);
13372fb4f839SGregory Neil Shapiro 					}
13382fb4f839SGregory Neil Shapiro # endif /* USE_EAI */
133940266059SGregory Neil Shapiro 					trycanon = true;
1340c2aa98e2SPeter Wemm 					mxhosts[0]++;
1341c2aa98e2SPeter Wemm 				}
1342c2aa98e2SPeter Wemm 			}
1343c2aa98e2SPeter Wemm 		}
1344c2aa98e2SPeter Wemm 		if (trycanon &&
13455b0945b5SGregory Neil Shapiro 		    (n = getcanonname(mxhosts[0], sizeof(MXHostBuf) - 2, false,
13465b0945b5SGregory Neil Shapiro 				pttl)) != HOST_NOTFOUND)
1347c2aa98e2SPeter Wemm 		{
134840266059SGregory Neil Shapiro 			/* XXX MXHostBuf == "" ?  is that possible? */
1349c2aa98e2SPeter Wemm 			bp = &MXHostBuf[strlen(MXHostBuf)];
1350c2aa98e2SPeter Wemm 			if (bp[-1] != '.')
1351c2aa98e2SPeter Wemm 			{
1352c2aa98e2SPeter Wemm 				*bp++ = '.';
1353c2aa98e2SPeter Wemm 				*bp = '\0';
1354c2aa98e2SPeter Wemm 			}
1355c2aa98e2SPeter Wemm 			nmx = 1;
13565b0945b5SGregory Neil Shapiro # if DANE
13575b0945b5SGregory Neil Shapiro 			if (tTd(8, 3))
13585b0945b5SGregory Neil Shapiro 				sm_dprintf("getmxrr=%s, getcanonname=%d\n",
13595b0945b5SGregory Neil Shapiro 					mxhosts[0], n);
13605b0945b5SGregory Neil Shapiro 			if (CHK_DANE(Dane) && port >= 0)
13615b0945b5SGregory Neil Shapiro 			{
13625b0945b5SGregory Neil Shapiro 				int nrr;
13635b0945b5SGregory Neil Shapiro 				unsigned long flags;
13645b0945b5SGregory Neil Shapiro 				unsigned int cttl;
13655b0945b5SGregory Neil Shapiro 
13665b0945b5SGregory Neil Shapiro 				if (pttl != NULL)
13675b0945b5SGregory Neil Shapiro 					cttl = *pttl;
13685b0945b5SGregory Neil Shapiro 				else if (ttl > 0)
13695b0945b5SGregory Neil Shapiro 					cttl = ttl;
13705b0945b5SGregory Neil Shapiro 				else
13715b0945b5SGregory Neil Shapiro 					cttl = SM_DEFAULT_TTL;
13725b0945b5SGregory Neil Shapiro 
1373*d39bd2c1SGregory Neil Shapiro 				flags = TLSAFLNEW;
1374*d39bd2c1SGregory Neil Shapiro 				if (ad && HOST_SECURE == n)
1375*d39bd2c1SGregory Neil Shapiro 				{
1376*d39bd2c1SGregory Neil Shapiro 					flags |= TLSAFLADMX;
1377*d39bd2c1SGregory Neil Shapiro 					if (pad != NULL)
1378*d39bd2c1SGregory Neil Shapiro 						*pad = ad;
1379*d39bd2c1SGregory Neil Shapiro 				}
1380*d39bd2c1SGregory Neil Shapiro 				if (TTD(8, 20))
1381*d39bd2c1SGregory Neil Shapiro 					sm_dprintf("getmxrr: 3: host=%s, mx=%s, flags=%#lx, ad=%d\n",
1382*d39bd2c1SGregory Neil Shapiro 						host, mxhosts[0], flags, ad);
13835b0945b5SGregory Neil Shapiro 				nrr = gettlsa(mxhosts[0], NULL, NULL, flags,
13845b0945b5SGregory Neil Shapiro 						cttl, port);
13855b0945b5SGregory Neil Shapiro 
13865b0945b5SGregory Neil Shapiro 				/*
13875b0945b5SGregory Neil Shapiro 				**  Only check qname if no TLSA RRs were found
13885b0945b5SGregory Neil Shapiro 				**  XXX: what about (temp) DNS errors?
13895b0945b5SGregory Neil Shapiro 				*/
13905b0945b5SGregory Neil Shapiro 
13915b0945b5SGregory Neil Shapiro 				if (0 == nrr && '\0' != qname[0] &&
13925b0945b5SGregory Neil Shapiro 				    strcmp(qname, mxhosts[0]))
1393*d39bd2c1SGregory Neil Shapiro 				{
13945b0945b5SGregory Neil Shapiro 					gettlsa(qname, mxhosts[0], NULL, flags,
13955b0945b5SGregory Neil Shapiro 						cttl, port);
1396*d39bd2c1SGregory Neil Shapiro 					if (tTd(8, 20))
1397*d39bd2c1SGregory Neil Shapiro 						sm_dprintf("getmxrr: 4: host=%s, qname=%s, flags=%#lx\n", host, qname, flags);
13985b0945b5SGregory Neil Shapiro 				/* XXX is this the right ad flag? */
13995b0945b5SGregory Neil Shapiro 				}
1400*d39bd2c1SGregory Neil Shapiro 			}
14015b0945b5SGregory Neil Shapiro # endif
1402c2aa98e2SPeter Wemm 		}
1403c2aa98e2SPeter Wemm 	}
1404c2aa98e2SPeter Wemm 
1405c2aa98e2SPeter Wemm 	/* if we have a default lowest preference, include that */
1406c2aa98e2SPeter Wemm 	if (fallbackMX != NULL && !seenlocal)
140706f25ae9SGregory Neil Shapiro 	{
1408*d39bd2c1SGregory Neil Shapiro 		/* TODO: DNSSEC status of fallbacks */
140940266059SGregory Neil Shapiro 		nmx = fallbackmxrr(nmx, prefs, mxhosts);
141006f25ae9SGregory Neil Shapiro 	}
14115b0945b5SGregory Neil Shapiro     done:
14125b0945b5SGregory Neil Shapiro # if DANE
14135b0945b5SGregory Neil Shapiro 	_res.options = old_options;
14145b0945b5SGregory Neil Shapiro # endif
141506f25ae9SGregory Neil Shapiro 	return nmx;
14165b0945b5SGregory Neil Shapiro 
14175b0945b5SGregory Neil Shapiro    error:
14185b0945b5SGregory Neil Shapiro # if DANE
14195b0945b5SGregory Neil Shapiro 	_res.options = old_options;
14205b0945b5SGregory Neil Shapiro # endif
14215b0945b5SGregory Neil Shapiro 	return -1;
1422c2aa98e2SPeter Wemm }
14235b0945b5SGregory Neil Shapiro 
142440266059SGregory Neil Shapiro /*
1425c2aa98e2SPeter Wemm **  MXRAND -- create a randomizer for equal MX preferences
1426c2aa98e2SPeter Wemm **
1427c2aa98e2SPeter Wemm **	If two MX hosts have equal preferences we want to randomize
1428c2aa98e2SPeter Wemm **	the selection.  But in order for signatures to be the same,
1429c2aa98e2SPeter Wemm **	we need to randomize the same way each time.  This function
1430c2aa98e2SPeter Wemm **	computes a pseudo-random hash function from the host name.
1431c2aa98e2SPeter Wemm **
1432c2aa98e2SPeter Wemm **	Parameters:
1433c2aa98e2SPeter Wemm **		host -- the name of the host.
1434c2aa98e2SPeter Wemm **
1435c2aa98e2SPeter Wemm **	Returns:
1436c2aa98e2SPeter Wemm **		A random but repeatable value based on the host name.
1437c2aa98e2SPeter Wemm */
1438c2aa98e2SPeter Wemm 
143906f25ae9SGregory Neil Shapiro static int
mxrand(host)1440c2aa98e2SPeter Wemm mxrand(host)
1441c2aa98e2SPeter Wemm 	register char *host;
1442c2aa98e2SPeter Wemm {
1443c2aa98e2SPeter Wemm 	int hfunc;
1444c2aa98e2SPeter Wemm 	static unsigned int seed;
1445c2aa98e2SPeter Wemm 
1446c2aa98e2SPeter Wemm 	if (seed == 0)
1447c2aa98e2SPeter Wemm 	{
1448c2aa98e2SPeter Wemm 		seed = (int) curtime() & 0xffff;
1449c2aa98e2SPeter Wemm 		if (seed == 0)
1450c2aa98e2SPeter Wemm 			seed++;
1451c2aa98e2SPeter Wemm 	}
1452c2aa98e2SPeter Wemm 
1453c2aa98e2SPeter Wemm 	if (tTd(17, 9))
145440266059SGregory Neil Shapiro 		sm_dprintf("mxrand(%s)", host);
1455c2aa98e2SPeter Wemm 
1456c2aa98e2SPeter Wemm 	hfunc = seed;
1457c2aa98e2SPeter Wemm 	while (*host != '\0')
1458c2aa98e2SPeter Wemm 	{
1459c2aa98e2SPeter Wemm 		int c = *host++;
1460c2aa98e2SPeter Wemm 
1461c2aa98e2SPeter Wemm 		if (isascii(c) && isupper(c))
1462c2aa98e2SPeter Wemm 			c = tolower(c);
1463c2aa98e2SPeter Wemm 		hfunc = ((hfunc << 1) ^ c) % 2003;
1464c2aa98e2SPeter Wemm 	}
1465c2aa98e2SPeter Wemm 
1466c2aa98e2SPeter Wemm 	hfunc &= 0xff;
1467c2aa98e2SPeter Wemm 	hfunc++;
1468c2aa98e2SPeter Wemm 
1469c2aa98e2SPeter Wemm 	if (tTd(17, 9))
147040266059SGregory Neil Shapiro 		sm_dprintf(" = %d\n", hfunc);
1471c2aa98e2SPeter Wemm 	return hfunc;
1472c2aa98e2SPeter Wemm }
147340266059SGregory Neil Shapiro /*
1474c2aa98e2SPeter Wemm **  BESTMX -- find the best MX for a name
1475c2aa98e2SPeter Wemm **
1476c2aa98e2SPeter Wemm **	This is really a hack, but I don't see any obvious way
1477c2aa98e2SPeter Wemm **	to generalize it at the moment.
1478c2aa98e2SPeter Wemm */
1479c2aa98e2SPeter Wemm 
1480c2aa98e2SPeter Wemm /* ARGSUSED3 */
1481c2aa98e2SPeter Wemm char *
bestmx_map_lookup(map,name,av,statp)1482c2aa98e2SPeter Wemm bestmx_map_lookup(map, name, av, statp)
1483c2aa98e2SPeter Wemm 	MAP *map;
1484c2aa98e2SPeter Wemm 	char *name;
1485c2aa98e2SPeter Wemm 	char **av;
1486c2aa98e2SPeter Wemm 	int *statp;
1487c2aa98e2SPeter Wemm {
1488c2aa98e2SPeter Wemm 	int nmx;
1489c2aa98e2SPeter Wemm 	int saveopts = _res.options;
149040266059SGregory Neil Shapiro 	int i;
149140266059SGregory Neil Shapiro 	ssize_t len = 0;
149240266059SGregory Neil Shapiro 	char *result;
1493c2aa98e2SPeter Wemm 	char *mxhosts[MAXMXHOSTS + 1];
149440266059SGregory Neil Shapiro # if _FFR_BESTMX_BETTER_TRUNCATION
149540266059SGregory Neil Shapiro 	char *buf;
14965b0945b5SGregory Neil Shapiro # else
149740266059SGregory Neil Shapiro 	char *p;
1498065a643dSPeter Wemm 	char buf[PSBUFSIZE / 2];
14995b0945b5SGregory Neil Shapiro # endif
1500c2aa98e2SPeter Wemm 
1501c2aa98e2SPeter Wemm 	_res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
1502*d39bd2c1SGregory Neil Shapiro 	nmx = getmxrr(name, mxhosts, NULL, 0, statp, NULL, -1, NULL);
1503c2aa98e2SPeter Wemm 	_res.options = saveopts;
1504c2aa98e2SPeter Wemm 	if (nmx <= 0)
1505c2aa98e2SPeter Wemm 		return NULL;
1506c2aa98e2SPeter Wemm 	if (bitset(MF_MATCHONLY, map->map_mflags))
1507c2aa98e2SPeter Wemm 		return map_rewrite(map, name, strlen(name), NULL);
1508c2aa98e2SPeter Wemm 	if ((map->map_coldelim == '\0') || (nmx == 1))
1509c2aa98e2SPeter Wemm 		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
1510c2aa98e2SPeter Wemm 
1511c2aa98e2SPeter Wemm 	/*
1512c2aa98e2SPeter Wemm 	**  We were given a -z flag (return all MXs) and there are multiple
1513c2aa98e2SPeter Wemm 	**  ones.  We need to build them all into a list.
1514c2aa98e2SPeter Wemm 	*/
151540266059SGregory Neil Shapiro 
151640266059SGregory Neil Shapiro # if _FFR_BESTMX_BETTER_TRUNCATION
151740266059SGregory Neil Shapiro 	for (i = 0; i < nmx; i++)
151840266059SGregory Neil Shapiro 	{
151940266059SGregory Neil Shapiro 		if (strchr(mxhosts[i], map->map_coldelim) != NULL)
152040266059SGregory Neil Shapiro 		{
152140266059SGregory Neil Shapiro 			syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
152240266059SGregory Neil Shapiro 			       mxhosts[i], map->map_coldelim);
152340266059SGregory Neil Shapiro 			return NULL;
152440266059SGregory Neil Shapiro 		}
152540266059SGregory Neil Shapiro 		len += strlen(mxhosts[i]) + 1;
152640266059SGregory Neil Shapiro 		if (len < 0)
152740266059SGregory Neil Shapiro 		{
152840266059SGregory Neil Shapiro 			len -= strlen(mxhosts[i]) + 1;
152940266059SGregory Neil Shapiro 			break;
153040266059SGregory Neil Shapiro 		}
153140266059SGregory Neil Shapiro 	}
153240266059SGregory Neil Shapiro 	buf = (char *) sm_malloc(len);
153340266059SGregory Neil Shapiro 	if (buf == NULL)
153440266059SGregory Neil Shapiro 	{
153540266059SGregory Neil Shapiro 		*statp = EX_UNAVAILABLE;
153640266059SGregory Neil Shapiro 		return NULL;
153740266059SGregory Neil Shapiro 	}
153840266059SGregory Neil Shapiro 	*buf = '\0';
153940266059SGregory Neil Shapiro 	for (i = 0; i < nmx; i++)
154040266059SGregory Neil Shapiro 	{
154140266059SGregory Neil Shapiro 		int end;
154240266059SGregory Neil Shapiro 
154340266059SGregory Neil Shapiro 		end = sm_strlcat(buf, mxhosts[i], len);
154440266059SGregory Neil Shapiro 		if (i != nmx && end + 1 < len)
154540266059SGregory Neil Shapiro 		{
154640266059SGregory Neil Shapiro 			buf[end] = map->map_coldelim;
154740266059SGregory Neil Shapiro 			buf[end + 1] = '\0';
154840266059SGregory Neil Shapiro 		}
154940266059SGregory Neil Shapiro 	}
155040266059SGregory Neil Shapiro 
155140266059SGregory Neil Shapiro 	/* Cleanly truncate for rulesets */
155240266059SGregory Neil Shapiro 	truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim);
155340266059SGregory Neil Shapiro # else /* _FFR_BESTMX_BETTER_TRUNCATION */
1554c2aa98e2SPeter Wemm 	p = buf;
1555c2aa98e2SPeter Wemm 	for (i = 0; i < nmx; i++)
1556c2aa98e2SPeter Wemm 	{
155740266059SGregory Neil Shapiro 		size_t slen;
1558c2aa98e2SPeter Wemm 
1559c2aa98e2SPeter Wemm 		if (strchr(mxhosts[i], map->map_coldelim) != NULL)
1560c2aa98e2SPeter Wemm 		{
1561c2aa98e2SPeter Wemm 			syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
1562c2aa98e2SPeter Wemm 			       mxhosts[i], map->map_coldelim);
1563c2aa98e2SPeter Wemm 			return NULL;
1564c2aa98e2SPeter Wemm 		}
1565c2aa98e2SPeter Wemm 		slen = strlen(mxhosts[i]);
1566d0cef73dSGregory Neil Shapiro 		if (len + slen + 2 > sizeof(buf))
1567c2aa98e2SPeter Wemm 			break;
1568c2aa98e2SPeter Wemm 		if (i > 0)
1569c2aa98e2SPeter Wemm 		{
1570c2aa98e2SPeter Wemm 			*p++ = map->map_coldelim;
1571c2aa98e2SPeter Wemm 			len++;
1572c2aa98e2SPeter Wemm 		}
1573d0cef73dSGregory Neil Shapiro 		(void) sm_strlcpy(p, mxhosts[i], sizeof(buf) - len);
1574c2aa98e2SPeter Wemm 		p += slen;
1575c2aa98e2SPeter Wemm 		len += slen;
1576c2aa98e2SPeter Wemm 	}
157740266059SGregory Neil Shapiro # endif /* _FFR_BESTMX_BETTER_TRUNCATION */
157840266059SGregory Neil Shapiro 
157940266059SGregory Neil Shapiro 	result = map_rewrite(map, buf, len, av);
158040266059SGregory Neil Shapiro # if _FFR_BESTMX_BETTER_TRUNCATION
158140266059SGregory Neil Shapiro 	sm_free(buf);
15825b0945b5SGregory Neil Shapiro # endif
158340266059SGregory Neil Shapiro 	return result;
1584c2aa98e2SPeter Wemm }
158540266059SGregory Neil Shapiro /*
1586c2aa98e2SPeter Wemm **  DNS_GETCANONNAME -- get the canonical name for named host using DNS
1587c2aa98e2SPeter Wemm **
1588c2aa98e2SPeter Wemm **	This algorithm tries to be smart about wildcard MX records.
1589c2aa98e2SPeter Wemm **	This is hard to do because DNS doesn't tell is if we matched
1590c2aa98e2SPeter Wemm **	against a wildcard or a specific MX.
1591c2aa98e2SPeter Wemm **
1592c2aa98e2SPeter Wemm **	We always prefer A & CNAME records, since these are presumed
1593c2aa98e2SPeter Wemm **	to be specific.
1594c2aa98e2SPeter Wemm **
1595c2aa98e2SPeter Wemm **	If we match an MX in one pass and lose it in the next, we use
1596c2aa98e2SPeter Wemm **	the old one.  For example, consider an MX matching *.FOO.BAR.COM.
1597c2aa98e2SPeter Wemm **	A hostname bletch.foo.bar.com will match against this MX, but
1598c2aa98e2SPeter Wemm **	will stop matching when we try bletch.bar.com -- so we know
1599c2aa98e2SPeter Wemm **	that bletch.foo.bar.com must have been right.  This fails if
1600c2aa98e2SPeter Wemm **	there was also an MX record matching *.BAR.COM, but there are
1601c2aa98e2SPeter Wemm **	some things that just can't be fixed.
1602c2aa98e2SPeter Wemm **
1603c2aa98e2SPeter Wemm **	Parameters:
1604c2aa98e2SPeter Wemm **		host -- a buffer containing the name of the host.
1605c2aa98e2SPeter Wemm **			This is a value-result parameter.
1606c2aa98e2SPeter Wemm **		hbsize -- the size of the host buffer.
1607c2aa98e2SPeter Wemm **		trymx -- if set, try MX records as well as A and CNAME.
1608c2aa98e2SPeter Wemm **		statp -- pointer to place to store status.
160940266059SGregory Neil Shapiro **		pttl -- pointer to return TTL (can be NULL).
1610c2aa98e2SPeter Wemm **
1611c2aa98e2SPeter Wemm **	Returns:
16125b0945b5SGregory Neil Shapiro **		>0 -- if the host was found.
16135b0945b5SGregory Neil Shapiro **		0 -- otherwise.
1614c2aa98e2SPeter Wemm */
1615c2aa98e2SPeter Wemm 
16165b0945b5SGregory Neil Shapiro int
dns_getcanonname(host,hbsize,trymx,statp,pttl)161740266059SGregory Neil Shapiro dns_getcanonname(host, hbsize, trymx, statp, pttl)
1618c2aa98e2SPeter Wemm 	char *host;
1619c2aa98e2SPeter Wemm 	int hbsize;
1620c2aa98e2SPeter Wemm 	bool trymx;
1621c2aa98e2SPeter Wemm 	int *statp;
162240266059SGregory Neil Shapiro 	int *pttl;
1623c2aa98e2SPeter Wemm {
162440266059SGregory Neil Shapiro 	register unsigned char *eom, *ap;
1625c2aa98e2SPeter Wemm 	register char *cp;
1626c2aa98e2SPeter Wemm 	register int n;
1627c2aa98e2SPeter Wemm 	HEADER *hp;
1628c2aa98e2SPeter Wemm 	querybuf answer;
16295b0945b5SGregory Neil Shapiro 	int ancount, qdcount, ret, type, qtype, initial, loopcnt, ttl, sli;
1630c2aa98e2SPeter Wemm 	char **domain;
16315b0945b5SGregory Neil Shapiro 	char *dp;
1632c2aa98e2SPeter Wemm 	char *mxmatch;
16335b0945b5SGregory Neil Shapiro 	bool amatch, gotmx, ad;
163440266059SGregory Neil Shapiro 	char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)];
16355b0945b5SGregory Neil Shapiro # if DNSSEC_TEST
16365b0945b5SGregory Neil Shapiro #  define ADDSL	1 /* NameSearchList may add another entry to searchlist! */
16375b0945b5SGregory Neil Shapiro # else
16385b0945b5SGregory Neil Shapiro #  define ADDSL	0
16395b0945b5SGregory Neil Shapiro # endif
16405b0945b5SGregory Neil Shapiro 	char *searchlist[MAXDNSRCH + 2 + ADDSL];
16415b0945b5SGregory Neil Shapiro # define SLSIZE SM_ARRAY_SIZE(searchlist)
16425b0945b5SGregory Neil Shapiro 	int (*resqdomain) __P((const char *, const char *, int, int, unsigned char *, int));
16435b0945b5SGregory Neil Shapiro # if DANE
16445b0945b5SGregory Neil Shapiro 	unsigned long old_options = 0;
16455b0945b5SGregory Neil Shapiro # endif
1646c2aa98e2SPeter Wemm 
16475b0945b5SGregory Neil Shapiro 	ttl = 0;
16485b0945b5SGregory Neil Shapiro 	gotmx = false;
16495b0945b5SGregory Neil Shapiro 	ad = true;
1650c2aa98e2SPeter Wemm 	if (tTd(8, 2))
165140266059SGregory Neil Shapiro 		sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
1652c2aa98e2SPeter Wemm 
1653c2aa98e2SPeter Wemm 	if ((_res.options & RES_INIT) == 0 && res_init() == -1)
1654c2aa98e2SPeter Wemm 	{
1655c2aa98e2SPeter Wemm 		*statp = EX_UNAVAILABLE;
16565b0945b5SGregory Neil Shapiro 		return HOST_NOTFOUND;
1657c2aa98e2SPeter Wemm 	}
1658c2aa98e2SPeter Wemm 
16595b0945b5SGregory Neil Shapiro # if DANE
16605b0945b5SGregory Neil Shapiro 	old_options = _res.options;
16615b0945b5SGregory Neil Shapiro 	if (DANE_SECURE == Dane)
16625b0945b5SGregory Neil Shapiro 		_res.options |= SM_RES_DNSSEC;
16635b0945b5SGregory Neil Shapiro # endif
16645b0945b5SGregory Neil Shapiro 
1665193538b7SGregory Neil Shapiro 	*statp = EX_OK;
16665b0945b5SGregory Neil Shapiro 	resqdomain = res_querydomain;
16675b0945b5SGregory Neil Shapiro # if DNSSEC_TEST
16685b0945b5SGregory Neil Shapiro 	if (tTd(8, 110))
16695b0945b5SGregory Neil Shapiro 		resqdomain = tstdns_querydomain;
16705b0945b5SGregory Neil Shapiro # endif
1671193538b7SGregory Neil Shapiro 
1672c2aa98e2SPeter Wemm 	/*
1673c2aa98e2SPeter Wemm 	**  Initialize domain search list.  If there is at least one
1674c2aa98e2SPeter Wemm 	**  dot in the name, search the unmodified name first so we
1675c2aa98e2SPeter Wemm 	**  find "vse.CS" in Czechoslovakia instead of in the local
167606f25ae9SGregory Neil Shapiro 	**  domain (e.g., vse.CS.Berkeley.EDU).  Note that there is no
167706f25ae9SGregory Neil Shapiro 	**  longer a country named Czechoslovakia but this type of problem
167806f25ae9SGregory Neil Shapiro 	**  is still present.
1679c2aa98e2SPeter Wemm 	**
1680c2aa98e2SPeter Wemm 	**  Older versions of the resolver could create this
1681c2aa98e2SPeter Wemm 	**  list by tearing apart the host name.
1682c2aa98e2SPeter Wemm 	*/
1683c2aa98e2SPeter Wemm 
1684c2aa98e2SPeter Wemm 	loopcnt = 0;
1685c2aa98e2SPeter Wemm cnameloop:
1686c2aa98e2SPeter Wemm 	/* Check for dots in the name */
1687c2aa98e2SPeter Wemm 	for (cp = host, n = 0; *cp != '\0'; cp++)
1688c2aa98e2SPeter Wemm 		if (*cp == '.')
1689c2aa98e2SPeter Wemm 			n++;
1690c2aa98e2SPeter Wemm 
1691c2aa98e2SPeter Wemm 	/*
1692c2aa98e2SPeter Wemm 	**  Build the search list.
1693c2aa98e2SPeter Wemm 	**	If there is at least one dot in name, start with a null
1694c2aa98e2SPeter Wemm 	**	domain to search the unmodified name first.
1695c2aa98e2SPeter Wemm 	**	If name does not end with a dot and search up local domain
1696c2aa98e2SPeter Wemm 	**	tree desired, append each local domain component to the
1697c2aa98e2SPeter Wemm 	**	search list; if name contains no dots and default domain
1698c2aa98e2SPeter Wemm 	**	name is desired, append default domain name to search list;
1699c2aa98e2SPeter Wemm 	**	else if name ends in a dot, remove that dot.
1700c2aa98e2SPeter Wemm 	*/
1701c2aa98e2SPeter Wemm 
17025b0945b5SGregory Neil Shapiro 	sli = 0;
1703c2aa98e2SPeter Wemm 	if (n > 0)
17045b0945b5SGregory Neil Shapiro 		searchlist[sli++] = "";
17055b0945b5SGregory Neil Shapiro # if DNSSEC_TEST
17065b0945b5SGregory Neil Shapiro 	if (NameSearchList != NULL)
17075b0945b5SGregory Neil Shapiro 	{
17085b0945b5SGregory Neil Shapiro 		SM_ASSERT(sli < SLSIZE);
17095b0945b5SGregory Neil Shapiro 		searchlist[sli++] = NameSearchList;
17105b0945b5SGregory Neil Shapiro 	}
17115b0945b5SGregory Neil Shapiro # endif
1712c2aa98e2SPeter Wemm 	if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options))
1713c2aa98e2SPeter Wemm 	{
171406f25ae9SGregory Neil Shapiro 		/* make sure there are less than MAXDNSRCH domains */
171506f25ae9SGregory Neil Shapiro 		for (domain = RES_DNSRCH_VARIABLE, ret = 0;
17165b0945b5SGregory Neil Shapiro 		     *domain != NULL && ret < MAXDNSRCH && sli < SLSIZE;
171706f25ae9SGregory Neil Shapiro 		     ret++)
17185b0945b5SGregory Neil Shapiro 			searchlist[sli++] = *domain++;
1719c2aa98e2SPeter Wemm 	}
1720c2aa98e2SPeter Wemm 	else if (n == 0 && bitset(RES_DEFNAMES, _res.options))
1721c2aa98e2SPeter Wemm 	{
17225b0945b5SGregory Neil Shapiro 		SM_ASSERT(sli < SLSIZE);
17235b0945b5SGregory Neil Shapiro 		searchlist[sli++] = _res.defdname;
1724c2aa98e2SPeter Wemm 	}
1725c2aa98e2SPeter Wemm 	else if (*cp == '.')
1726c2aa98e2SPeter Wemm 	{
1727c2aa98e2SPeter Wemm 		*cp = '\0';
1728c2aa98e2SPeter Wemm 	}
17295b0945b5SGregory Neil Shapiro 	SM_ASSERT(sli < SLSIZE);
17305b0945b5SGregory Neil Shapiro 	searchlist[sli] = NULL;
1731c2aa98e2SPeter Wemm 
1732c2aa98e2SPeter Wemm 	/*
1733c2aa98e2SPeter Wemm 	**  Now loop through the search list, appending each domain in turn
1734c2aa98e2SPeter Wemm 	**  name and searching for a match.
1735c2aa98e2SPeter Wemm 	*/
1736c2aa98e2SPeter Wemm 
1737c2aa98e2SPeter Wemm 	mxmatch = NULL;
1738a7ec597cSGregory Neil Shapiro 	initial = T_A;
1739a7ec597cSGregory Neil Shapiro # if NETINET6
1740a7ec597cSGregory Neil Shapiro 	if (InetMode == AF_INET6)
1741a7ec597cSGregory Neil Shapiro 		initial = T_AAAA;
17425b0945b5SGregory Neil Shapiro # endif
1743a7ec597cSGregory Neil Shapiro 	qtype = initial;
1744c2aa98e2SPeter Wemm 
17455b0945b5SGregory Neil Shapiro 	for (sli = 0; sli < SLSIZE; )
1746c2aa98e2SPeter Wemm 	{
17475b0945b5SGregory Neil Shapiro 		dp = searchlist[sli];
17485b0945b5SGregory Neil Shapiro 		if (NULL == dp)
17495b0945b5SGregory Neil Shapiro 			break;
1750a7ec597cSGregory Neil Shapiro 		if (qtype == initial)
175140266059SGregory Neil Shapiro 			gotmx = false;
1752c2aa98e2SPeter Wemm 		if (tTd(8, 5))
175340266059SGregory Neil Shapiro 			sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n",
17545b0945b5SGregory Neil Shapiro 				host, dp,
175506f25ae9SGregory Neil Shapiro # if NETINET6
175606f25ae9SGregory Neil Shapiro 				qtype == T_AAAA ? "AAAA" :
17575b0945b5SGregory Neil Shapiro # endif
175806f25ae9SGregory Neil Shapiro 				qtype == T_A ? "A" :
175906f25ae9SGregory Neil Shapiro 				qtype == T_MX ? "MX" :
176006f25ae9SGregory Neil Shapiro 				"???");
1761193538b7SGregory Neil Shapiro 		errno = 0;
17625b0945b5SGregory Neil Shapiro 		hp = (HEADER *) &answer;
17635b0945b5SGregory Neil Shapiro 		ret = (*resqdomain)(host, dp, C_IN, qtype,
1764c2aa98e2SPeter Wemm 				      answer.qb2, sizeof(answer.qb2));
1765c2aa98e2SPeter Wemm 		if (ret <= 0)
1766c2aa98e2SPeter Wemm 		{
176740266059SGregory Neil Shapiro 			int save_errno = errno;
1768c2aa98e2SPeter Wemm 
176940266059SGregory Neil Shapiro 			if (tTd(8, 7))
177040266059SGregory Neil Shapiro 				sm_dprintf("\tNO: errno=%d, h_errno=%d\n",
177140266059SGregory Neil Shapiro 					   save_errno, h_errno);
177240266059SGregory Neil Shapiro 
177340266059SGregory Neil Shapiro 			if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN)
1774c2aa98e2SPeter Wemm 			{
1775193538b7SGregory Neil Shapiro 				/*
177640266059SGregory Neil Shapiro 				**  the name server seems to be down or broken.
1777193538b7SGregory Neil Shapiro 				*/
1778193538b7SGregory Neil Shapiro 
1779602a2b1bSGregory Neil Shapiro 				SM_SET_H_ERRNO(TRY_AGAIN);
17805b0945b5SGregory Neil Shapiro 				if (*dp == '\0')
1781605302a5SGregory Neil Shapiro 				{
1782605302a5SGregory Neil Shapiro 					if (*statp == EX_OK)
1783605302a5SGregory Neil Shapiro 						*statp = EX_TEMPFAIL;
1784605302a5SGregory Neil Shapiro 					goto nexttype;
1785605302a5SGregory Neil Shapiro 				}
1786c2aa98e2SPeter Wemm 				*statp = EX_TEMPFAIL;
178706f25ae9SGregory Neil Shapiro 
1788602a2b1bSGregory Neil Shapiro 				if (WorkAroundBrokenAAAA)
1789602a2b1bSGregory Neil Shapiro 				{
1790193538b7SGregory Neil Shapiro 					/*
1791193538b7SGregory Neil Shapiro 					**  Only return if not TRY_AGAIN as an
1792193538b7SGregory Neil Shapiro 					**  attempt with a different qtype may
1793193538b7SGregory Neil Shapiro 					**  succeed (res_querydomain() calls
1794193538b7SGregory Neil Shapiro 					**  res_query() calls res_send() which
1795193538b7SGregory Neil Shapiro 					**  sets errno to ETIMEDOUT if the
1796193538b7SGregory Neil Shapiro 					**  nameservers could be contacted but
1797193538b7SGregory Neil Shapiro 					**  didn't give an answer).
1798193538b7SGregory Neil Shapiro 					*/
1799193538b7SGregory Neil Shapiro 
180040266059SGregory Neil Shapiro 					if (save_errno != ETIMEDOUT)
18015b0945b5SGregory Neil Shapiro 						goto error;
1802602a2b1bSGregory Neil Shapiro 				}
180340266059SGregory Neil Shapiro 				else
18045b0945b5SGregory Neil Shapiro 					goto error;
1805c2aa98e2SPeter Wemm 			}
1806c2aa98e2SPeter Wemm 
1807605302a5SGregory Neil Shapiro nexttype:
1808c2aa98e2SPeter Wemm 			if (h_errno != HOST_NOT_FOUND)
1809c2aa98e2SPeter Wemm 			{
1810c2aa98e2SPeter Wemm 				/* might have another type of interest */
181106f25ae9SGregory Neil Shapiro # if NETINET6
181240266059SGregory Neil Shapiro 				if (qtype == T_AAAA)
181306f25ae9SGregory Neil Shapiro 				{
1814c2aa98e2SPeter Wemm 					qtype = T_A;
1815c2aa98e2SPeter Wemm 					continue;
1816c2aa98e2SPeter Wemm 				}
181740266059SGregory Neil Shapiro 				else
181806f25ae9SGregory Neil Shapiro # endif /* NETINET6 */
181940266059SGregory Neil Shapiro 				if (qtype == T_A && !gotmx &&
18205b0945b5SGregory Neil Shapiro 				    (trymx || *dp == '\0'))
1821c2aa98e2SPeter Wemm 				{
1822c2aa98e2SPeter Wemm 					qtype = T_MX;
1823c2aa98e2SPeter Wemm 					continue;
1824c2aa98e2SPeter Wemm 				}
1825c2aa98e2SPeter Wemm 			}
1826c2aa98e2SPeter Wemm 
1827c2aa98e2SPeter Wemm 			/* definite no -- try the next domain */
18285b0945b5SGregory Neil Shapiro 			sli++;
1829a7ec597cSGregory Neil Shapiro 			qtype = initial;
1830c2aa98e2SPeter Wemm 			continue;
1831c2aa98e2SPeter Wemm 		}
1832c2aa98e2SPeter Wemm 		else if (tTd(8, 7))
183340266059SGregory Neil Shapiro 			sm_dprintf("\tYES\n");
1834c2aa98e2SPeter Wemm 
1835c2aa98e2SPeter Wemm 		/* avoid problems after truncation in tcp packets */
1836c2aa98e2SPeter Wemm 		if (ret > sizeof(answer))
1837c2aa98e2SPeter Wemm 			ret = sizeof(answer);
1838af9557fdSGregory Neil Shapiro 		SM_ASSERT(ret >= 0);
1839c2aa98e2SPeter Wemm 
1840c2aa98e2SPeter Wemm 		/*
1841c2aa98e2SPeter Wemm 		**  Appear to have a match.  Confirm it by searching for A or
1842c2aa98e2SPeter Wemm 		**  CNAME records.  If we don't have a local domain
1843c2aa98e2SPeter Wemm 		**  wild card MX record, we will accept MX as well.
1844c2aa98e2SPeter Wemm 		*/
1845c2aa98e2SPeter Wemm 
184640266059SGregory Neil Shapiro 		ap = (unsigned char *) &answer + HFIXEDSZ;
184740266059SGregory Neil Shapiro 		eom = (unsigned char *) &answer + ret;
1848c2aa98e2SPeter Wemm 
18495b0945b5SGregory Neil Shapiro 		if (0 == hp->ad)
18505b0945b5SGregory Neil Shapiro 			ad = false;
18515b0945b5SGregory Neil Shapiro 
1852c2aa98e2SPeter Wemm 		/* skip question part of response -- we know what we asked */
185340266059SGregory Neil Shapiro 		for (qdcount = ntohs((unsigned short) hp->qdcount);
185406f25ae9SGregory Neil Shapiro 		     qdcount--;
185506f25ae9SGregory Neil Shapiro 		     ap += ret + QFIXEDSZ)
1856c2aa98e2SPeter Wemm 		{
1857c2aa98e2SPeter Wemm 			if ((ret = dn_skipname(ap, eom)) < 0)
1858c2aa98e2SPeter Wemm 			{
1859c2aa98e2SPeter Wemm 				if (tTd(8, 20))
186040266059SGregory Neil Shapiro 					sm_dprintf("qdcount failure (%d)\n",
186140266059SGregory Neil Shapiro 						ntohs((unsigned short) hp->qdcount));
1862c2aa98e2SPeter Wemm 				*statp = EX_SOFTWARE;
18635b0945b5SGregory Neil Shapiro 				goto error;
1864c2aa98e2SPeter Wemm 			}
1865c2aa98e2SPeter Wemm 		}
1866c2aa98e2SPeter Wemm 
186740266059SGregory Neil Shapiro 		amatch = false;
186840266059SGregory Neil Shapiro 		for (ancount = ntohs((unsigned short) hp->ancount);
186906f25ae9SGregory Neil Shapiro 		     --ancount >= 0 && ap < eom;
1870c2aa98e2SPeter Wemm 		     ap += n)
1871c2aa98e2SPeter Wemm 		{
187240266059SGregory Neil Shapiro 			n = dn_expand((unsigned char *) &answer, eom, ap,
1873d0cef73dSGregory Neil Shapiro 				      (RES_UNC_T) nbuf, sizeof(nbuf));
1874c2aa98e2SPeter Wemm 			if (n < 0)
1875c2aa98e2SPeter Wemm 				break;
1876c2aa98e2SPeter Wemm 			ap += n;
1877c2aa98e2SPeter Wemm 			GETSHORT(type, ap);
187840266059SGregory Neil Shapiro 			ap += INT16SZ;		/* skip over class */
187940266059SGregory Neil Shapiro 			GETLONG(ttl, ap);
188040266059SGregory Neil Shapiro 			GETSHORT(n, ap);	/* rdlength */
1881c2aa98e2SPeter Wemm 			switch (type)
1882c2aa98e2SPeter Wemm 			{
1883c2aa98e2SPeter Wemm 			  case T_MX:
188440266059SGregory Neil Shapiro 				gotmx = true;
18855b0945b5SGregory Neil Shapiro 				if (*dp != '\0' && HasWildcardMX)
1886c2aa98e2SPeter Wemm 				{
1887c2aa98e2SPeter Wemm 					/*
1888c2aa98e2SPeter Wemm 					**  If we are using MX matches and have
1889c2aa98e2SPeter Wemm 					**  not yet gotten one, save this one
1890c2aa98e2SPeter Wemm 					**  but keep searching for an A or
1891c2aa98e2SPeter Wemm 					**  CNAME match.
1892c2aa98e2SPeter Wemm 					*/
1893c2aa98e2SPeter Wemm 
1894c2aa98e2SPeter Wemm 					if (trymx && mxmatch == NULL)
18955b0945b5SGregory Neil Shapiro 						mxmatch = dp;
1896c2aa98e2SPeter Wemm 					continue;
1897c2aa98e2SPeter Wemm 				}
1898c2aa98e2SPeter Wemm 
1899c2aa98e2SPeter Wemm 				/*
1900c2aa98e2SPeter Wemm 				**  If we did not append a domain name, this
1901c2aa98e2SPeter Wemm 				**  must have been a canonical name to start
1902c2aa98e2SPeter Wemm 				**  with.  Even if we did append a domain name,
1903c2aa98e2SPeter Wemm 				**  in the absence of a wildcard MX this must
1904c2aa98e2SPeter Wemm 				**  still be a real MX match.
1905c2aa98e2SPeter Wemm 				**  Such MX matches are as good as an A match,
1906c2aa98e2SPeter Wemm 				**  fall through.
1907c2aa98e2SPeter Wemm 				*/
190806f25ae9SGregory Neil Shapiro 				/* FALLTHROUGH */
190906f25ae9SGregory Neil Shapiro 
191006f25ae9SGregory Neil Shapiro # if NETINET6
191106f25ae9SGregory Neil Shapiro 			  case T_AAAA:
19125b0945b5SGregory Neil Shapiro # endif
1913c2aa98e2SPeter Wemm 			  case T_A:
1914c2aa98e2SPeter Wemm 				/* Flag that a good match was found */
191540266059SGregory Neil Shapiro 				amatch = true;
1916c2aa98e2SPeter Wemm 
1917c2aa98e2SPeter Wemm 				/* continue in case a CNAME also exists */
1918c2aa98e2SPeter Wemm 				continue;
1919c2aa98e2SPeter Wemm 
1920c2aa98e2SPeter Wemm 			  case T_CNAME:
1921c2aa98e2SPeter Wemm 				if (DontExpandCnames)
1922c2aa98e2SPeter Wemm 				{
1923c2aa98e2SPeter Wemm 					/* got CNAME -- guaranteed canonical */
192440266059SGregory Neil Shapiro 					amatch = true;
1925c2aa98e2SPeter Wemm 					break;
1926c2aa98e2SPeter Wemm 				}
1927c2aa98e2SPeter Wemm 
1928c2aa98e2SPeter Wemm 				if (loopcnt++ > MAXCNAMEDEPTH)
1929c2aa98e2SPeter Wemm 				{
1930c2aa98e2SPeter Wemm 					/*XXX should notify postmaster XXX*/
1931c2aa98e2SPeter Wemm 					message("DNS failure: CNAME loop for %s",
1932c2aa98e2SPeter Wemm 						host);
1933c2aa98e2SPeter Wemm 					if (CurEnv->e_message == NULL)
1934c2aa98e2SPeter Wemm 					{
1935c2aa98e2SPeter Wemm 						char ebuf[MAXLINE];
1936c2aa98e2SPeter Wemm 
193740266059SGregory Neil Shapiro 						(void) sm_snprintf(ebuf,
1938d0cef73dSGregory Neil Shapiro 							sizeof(ebuf),
1939c2aa98e2SPeter Wemm 							"Deferred: DNS failure: CNAME loop for %.100s",
1940c2aa98e2SPeter Wemm 							host);
194140266059SGregory Neil Shapiro 						CurEnv->e_message =
194240266059SGregory Neil Shapiro 						    sm_rpool_strdup_x(
194340266059SGregory Neil Shapiro 							CurEnv->e_rpool, ebuf);
1944c2aa98e2SPeter Wemm 					}
1945602a2b1bSGregory Neil Shapiro 					SM_SET_H_ERRNO(NO_RECOVERY);
1946c2aa98e2SPeter Wemm 					*statp = EX_CONFIG;
19475b0945b5SGregory Neil Shapiro 					goto error;
1948c2aa98e2SPeter Wemm 				}
1949c2aa98e2SPeter Wemm 
1950c2aa98e2SPeter Wemm 				/* value points at name */
195140266059SGregory Neil Shapiro 				if ((ret = dn_expand((unsigned char *)&answer,
195240266059SGregory Neil Shapiro 						     eom, ap, (RES_UNC_T) nbuf,
195340266059SGregory Neil Shapiro 						     sizeof(nbuf))) < 0)
1954c2aa98e2SPeter Wemm 					break;
195540266059SGregory Neil Shapiro 				(void) sm_strlcpy(host, nbuf, hbsize);
1956c2aa98e2SPeter Wemm 
1957c2aa98e2SPeter Wemm 				/*
1958c2aa98e2SPeter Wemm 				**  RFC 1034 section 3.6 specifies that CNAME
1959c2aa98e2SPeter Wemm 				**  should point at the canonical name -- but
1960c2aa98e2SPeter Wemm 				**  urges software to try again anyway.
1961c2aa98e2SPeter Wemm 				*/
1962c2aa98e2SPeter Wemm 
1963c2aa98e2SPeter Wemm 				goto cnameloop;
1964c2aa98e2SPeter Wemm 
1965c2aa98e2SPeter Wemm 			  default:
1966c2aa98e2SPeter Wemm 				/* not a record of interest */
1967c2aa98e2SPeter Wemm 				continue;
1968c2aa98e2SPeter Wemm 			}
1969c2aa98e2SPeter Wemm 		}
1970c2aa98e2SPeter Wemm 
1971c2aa98e2SPeter Wemm 		if (amatch)
1972c2aa98e2SPeter Wemm 		{
1973c2aa98e2SPeter Wemm 			/*
1974c2aa98e2SPeter Wemm 			**  Got a good match -- either an A, CNAME, or an
1975c2aa98e2SPeter Wemm 			**  exact MX record.  Save it and get out of here.
1976c2aa98e2SPeter Wemm 			*/
1977c2aa98e2SPeter Wemm 
19785b0945b5SGregory Neil Shapiro 			mxmatch = dp;
1979c2aa98e2SPeter Wemm 			break;
1980c2aa98e2SPeter Wemm 		}
1981c2aa98e2SPeter Wemm 
1982c2aa98e2SPeter Wemm 		/*
1983c2aa98e2SPeter Wemm 		**  Nothing definitive yet.
1984c2aa98e2SPeter Wemm 		**	If this was a T_A query and we haven't yet found a MX
1985c2aa98e2SPeter Wemm 		**		match, try T_MX if allowed to do so.
1986c2aa98e2SPeter Wemm 		**	Otherwise, try the next domain.
1987c2aa98e2SPeter Wemm 		*/
1988c2aa98e2SPeter Wemm 
198906f25ae9SGregory Neil Shapiro # if NETINET6
199040266059SGregory Neil Shapiro 		if (qtype == T_AAAA)
1991c2aa98e2SPeter Wemm 			qtype = T_A;
199240266059SGregory Neil Shapiro 		else
19935b0945b5SGregory Neil Shapiro # endif
19945b0945b5SGregory Neil Shapiro 		if (qtype == T_A && !gotmx && (trymx || *dp == '\0'))
1995c2aa98e2SPeter Wemm 			qtype = T_MX;
1996c2aa98e2SPeter Wemm 		else
1997c2aa98e2SPeter Wemm 		{
1998a7ec597cSGregory Neil Shapiro 			qtype = initial;
19995b0945b5SGregory Neil Shapiro 			sli++;
2000c2aa98e2SPeter Wemm 		}
2001c2aa98e2SPeter Wemm 	}
2002c2aa98e2SPeter Wemm 
2003c2aa98e2SPeter Wemm 	/* if nothing was found, we are done */
2004c2aa98e2SPeter Wemm 	if (mxmatch == NULL)
2005c2aa98e2SPeter Wemm 	{
2006193538b7SGregory Neil Shapiro 		if (*statp == EX_OK)
2007c2aa98e2SPeter Wemm 			*statp = EX_NOHOST;
20085b0945b5SGregory Neil Shapiro 		goto error;
2009c2aa98e2SPeter Wemm 	}
2010c2aa98e2SPeter Wemm 
2011c2aa98e2SPeter Wemm 	/*
2012c2aa98e2SPeter Wemm 	**  Create canonical name and return.
2013c2aa98e2SPeter Wemm 	**  If saved domain name is null, name was already canonical.
2014c2aa98e2SPeter Wemm 	**  Otherwise append the saved domain name.
2015c2aa98e2SPeter Wemm 	*/
2016c2aa98e2SPeter Wemm 
2017d0cef73dSGregory Neil Shapiro 	(void) sm_snprintf(nbuf, sizeof(nbuf), "%.*s%s%.*s", MAXDNAME, host,
2018c2aa98e2SPeter Wemm 			   *mxmatch == '\0' ? "" : ".",
2019c2aa98e2SPeter Wemm 			   MAXDNAME, mxmatch);
202040266059SGregory Neil Shapiro 	(void) sm_strlcpy(host, nbuf, hbsize);
2021c2aa98e2SPeter Wemm 	if (tTd(8, 5))
202240266059SGregory Neil Shapiro 		sm_dprintf("dns_getcanonname: %s\n", host);
2023c2aa98e2SPeter Wemm 	*statp = EX_OK;
202440266059SGregory Neil Shapiro 
202540266059SGregory Neil Shapiro 	/* return only one TTL entry, that should be sufficient */
202640266059SGregory Neil Shapiro 	if (ttl > 0 && pttl != NULL)
202740266059SGregory Neil Shapiro 		*pttl = ttl;
20285b0945b5SGregory Neil Shapiro # if DANE
20295b0945b5SGregory Neil Shapiro 	_res.options = old_options;
20305b0945b5SGregory Neil Shapiro # endif
20315b0945b5SGregory Neil Shapiro 	return ad ? HOST_SECURE : HOST_OK;
20325b0945b5SGregory Neil Shapiro 
20335b0945b5SGregory Neil Shapiro   error:
20345b0945b5SGregory Neil Shapiro # if DANE
20355b0945b5SGregory Neil Shapiro 	_res.options = old_options;
20365b0945b5SGregory Neil Shapiro # endif
20375b0945b5SGregory Neil Shapiro 	return HOST_NOTFOUND;
2038c2aa98e2SPeter Wemm }
20395b0945b5SGregory Neil Shapiro 
2040c2aa98e2SPeter Wemm #endif /* NAMED_BIND */
2041