138032Speter/*
2261194Sgshapiro * Copyright (c) 1998-2004, 2006, 2010 Proofpoint, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1986, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
15168515Sgshapiro#include "map.h"
16363466Sgshapiro#if _FFR_EAI
17363466Sgshapiro#include <unicode/uidna.h>
18363466Sgshapiro#endif
1938032Speter
2090792Sgshapiro#if NAMED_BIND
21266527SgshapiroSM_RCSID("@(#)$Id: domain.c,v 8.205 2013-11-22 20:51:55 ca Exp $ (with name server)")
22363466Sgshapiro#else
23266527SgshapiroSM_RCSID("@(#)$Id: domain.c,v 8.205 2013-11-22 20:51:55 ca Exp $ (without name server)")
24363466Sgshapiro#endif
2538032Speter
2638032Speter#if NAMED_BIND
2738032Speter
2864562Sgshapiro# include <arpa/inet.h>
29363466Sgshapiro# include <sm_resolve.h>
30363466Sgshapiro# if DANE
31363466Sgshapiro#  include <tls.h>
32363466Sgshapiro#  ifndef SM_NEG_TTL
33363466Sgshapiro#   define SM_NEG_TTL 60 /* "negative" TTL */
34363466Sgshapiro#  endif
35363466Sgshapiro# endif
3638032Speter
3790792Sgshapiro
3864562Sgshapiro# ifndef MXHOSTBUFSIZE
3964562Sgshapiro#  define MXHOSTBUFSIZE	(128 * MAXMXHOSTS)
40363466Sgshapiro# endif
4138032Speter
4238032Speterstatic char	MXHostBuf[MXHOSTBUFSIZE];
43363466Sgshapiro# if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2)
4490792Sgshapiro	ERROR: _MXHOSTBUFSIZE is out of range
45363466Sgshapiro# endif
4638032Speter
4764562Sgshapiro# ifndef MAXDNSRCH
4864562Sgshapiro#  define MAXDNSRCH	6	/* number of possible domains to search */
49363466Sgshapiro# endif
5038032Speter
5164562Sgshapiro# ifndef RES_DNSRCH_VARIABLE
5264562Sgshapiro#  define RES_DNSRCH_VARIABLE	_res.dnsrch
53363466Sgshapiro# endif
5438032Speter
5564562Sgshapiro# ifndef NO_DATA
5664562Sgshapiro#  define NO_DATA	NO_ADDRESS
57363466Sgshapiro# endif
5838032Speter
5964562Sgshapiro# ifndef HFIXEDSZ
6064562Sgshapiro#  define HFIXEDSZ	12	/* sizeof(HEADER) */
61363466Sgshapiro# endif
6238032Speter
6364562Sgshapiro# define MAXCNAMEDEPTH	10	/* maximum depth of CNAME recursion */
6464562Sgshapiro
6564562Sgshapiro# if defined(__RES) && (__RES >= 19940415)
6664562Sgshapiro#  define RES_UNC_T	char *
67363466Sgshapiro# else
6890792Sgshapiro#  define RES_UNC_T	unsigned char *
69363466Sgshapiro# endif
7064562Sgshapiro
7164562Sgshapirostatic int	mxrand __P((char *));
7290792Sgshapirostatic int	fallbackmxrr __P((int, unsigned short *, char **));
7364562Sgshapiro
74363466Sgshapiro# if DANE
75363466Sgshapiro
7690792Sgshapiro/*
77363466Sgshapiro**  TLSAADD -- add TLSA records to dane_tlsa entry
78363466Sgshapiro**
79363466Sgshapiro**	Parameters:
80363466Sgshapiro**		name -- key for stab entry (for debugging output)
81363466Sgshapiro**		dr -- DNS reply
82363466Sgshapiro**		dane_tlsa -- dane_tlsa entry
83363466Sgshapiro**		dnsrc -- DNS lookup return code (h_errno)
84363466Sgshapiro**		n -- current number of TLSA records in dane_tlsa entry
85363466Sgshapiro**		pttl -- (pointer to) TTL (in/out)
86363466Sgshapiro**		level -- recursion level (CNAMEs)
87363466Sgshapiro**
88363466Sgshapiro**	Returns:
89363466Sgshapiro**		new number of TLSA records
90363466Sgshapiro*/
91363466Sgshapiro
92363466Sgshapirostatic int tlsaadd __P((const char *, DNS_REPLY_T *, dane_tlsa_P, int, int,
93363466Sgshapiro			unsigned int *, int));
94363466Sgshapiro
95363466Sgshapirostatic int
96363466Sgshapirotlsaadd(name, dr, dane_tlsa, dnsrc, n, pttl, level)
97363466Sgshapiro	const char *name;
98363466Sgshapiro	DNS_REPLY_T *dr;
99363466Sgshapiro	dane_tlsa_P dane_tlsa;
100363466Sgshapiro	int dnsrc;
101363466Sgshapiro	int n;
102363466Sgshapiro	unsigned int *pttl;
103363466Sgshapiro	int level;
104363466Sgshapiro{
105363466Sgshapiro	RESOURCE_RECORD_T *rr;
106363466Sgshapiro	unsigned int ttl;
107363466Sgshapiro	int nprev;
108363466Sgshapiro
109363466Sgshapiro	if (dnsrc != 0)
110363466Sgshapiro	{
111363466Sgshapiro		if (tTd(8, 2))
112363466Sgshapiro			sm_dprintf("tlsaadd(%s), prev=%d, dnsrc=%d\n",
113363466Sgshapiro				name, dane_tlsa->dane_tlsa_dnsrc, dnsrc);
114363466Sgshapiro
115363466Sgshapiro		/* check previous error and keep the "most important" one? */
116363466Sgshapiro		dane_tlsa->dane_tlsa_dnsrc = dnsrc;
117363466Sgshapiro# if DNSSEC_TEST
118363466Sgshapiro		if (tTd(8, 110))
119363466Sgshapiro			*pttl = tTdlevel(8)-110;	/* how to make this an option? */
120363466Sgshapiro		else
121363466Sgshapiro# else
122363466Sgshapiro			*pttl = SM_NEG_TTL;
123363466Sgshapiro# endif
124363466Sgshapiro
125363466Sgshapiro		return n;
126363466Sgshapiro	}
127363466Sgshapiro	if (dr == NULL)
128363466Sgshapiro		return n;
129363466Sgshapiro	if (dr->dns_r_h.ad != 1 && Dane == DANE_SECURE)	/* not secure? */
130363466Sgshapiro		return n;
131363466Sgshapiro	ttl = *pttl;
132363466Sgshapiro
133363466Sgshapiro	/* first: try to find TLSA records */
134363466Sgshapiro	nprev = n;
135363466Sgshapiro	for (rr = dr->dns_r_head; rr != NULL && n < MAX_TLSA_RR;
136363466Sgshapiro	     rr = rr->rr_next)
137363466Sgshapiro	{
138363466Sgshapiro		int tlsa_chk;
139363466Sgshapiro
140363466Sgshapiro		if (rr->rr_type != T_TLSA)
141363466Sgshapiro		{
142363466Sgshapiro			if (rr->rr_type != T_CNAME && tTd(8, 8))
143363466Sgshapiro				sm_dprintf("tlsaadd(%s), type=%s\n", name,
144363466Sgshapiro					dns_type_to_string(rr->rr_type));
145363466Sgshapiro			continue;
146363466Sgshapiro		}
147363466Sgshapiro		tlsa_chk = dane_tlsa_chk(rr->rr_u.rr_data, rr->rr_size, name,
148363466Sgshapiro					true);
149363466Sgshapiro		if (!TLSA_IS_VALID(tlsa_chk))
150363466Sgshapiro			continue;
151363466Sgshapiro
152363466Sgshapiro		/*
153363466Sgshapiro		**  To do: the RRs should be sorted (by "complexity") --
154363466Sgshapiro		**  when more than one type is supported.
155363466Sgshapiro		*/
156363466Sgshapiro
157363466Sgshapiro		dane_tlsa->dane_tlsa_rr[n] = rr->rr_u.rr_data;
158363466Sgshapiro		dane_tlsa->dane_tlsa_len[n] = rr->rr_size;
159363466Sgshapiro		if (tTd(8, 2))
160363466Sgshapiro		{
161363466Sgshapiro			unsigned char *p;
162363466Sgshapiro
163363466Sgshapiro			p = rr->rr_u.rr_data;
164363466Sgshapiro			sm_dprintf("tlsaadd(%s), n=%d, %d-%d-%d:%02x\n", name,
165363466Sgshapiro				n, (int)p[0], (int)p[1], (int)p[2], (int)p[3]);
166363466Sgshapiro		}
167363466Sgshapiro
168363466Sgshapiro		/* require some minimum TTL? */
169363466Sgshapiro		if (ttl > rr->rr_ttl && rr->rr_ttl > 0)
170363466Sgshapiro			ttl = rr->rr_ttl;
171363466Sgshapiro
172363466Sgshapiro		/* hack: instead of copying the data, just "take it over" */
173363466Sgshapiro		rr->rr_u.rr_data = NULL;
174363466Sgshapiro		++n;
175363466Sgshapiro	}
176363466Sgshapiro
177363466Sgshapiro	/* second: check for CNAME records, but only if no TLSA RR was added */
178363466Sgshapiro	for (rr = dr->dns_r_head; rr != NULL && n < MAX_TLSA_RR && nprev == n;
179363466Sgshapiro	     rr = rr->rr_next)
180363466Sgshapiro	{
181363466Sgshapiro		DNS_REPLY_T *drc;
182363466Sgshapiro		int err, herr;
183363466Sgshapiro
184363466Sgshapiro		if (rr->rr_type != T_CNAME)
185363466Sgshapiro			continue;
186363466Sgshapiro		if (level > 1)
187363466Sgshapiro		{
188363466Sgshapiro			if (tTd(8, 2))
189363466Sgshapiro				sm_dprintf("tlsaadd(%s), CNAME=%s, level=%d\n",
190363466Sgshapiro					name, rr->rr_u.rr_txt, level);
191363466Sgshapiro			continue;
192363466Sgshapiro		}
193363466Sgshapiro
194363466Sgshapiro		drc = dns_lookup_int(rr->rr_u.rr_txt, C_IN, T_TLSA, 0, 0,
195363466Sgshapiro			(Dane == DANE_SECURE &&
196363466Sgshapiro			 !TLSA_IS_FL(dane_tlsa, TLSAFLNOADMX))
197363466Sgshapiro			? SM_RES_DNSSEC : 0,
198363466Sgshapiro			RR_RAW, &err, &herr);
199363466Sgshapiro
200363466Sgshapiro		if (tTd(8, 2))
201363466Sgshapiro			sm_dprintf("tlsaadd(%s), CNAME=%s, level=%d, dr=%p, ad=%d, err=%d, herr=%d\n",
202363466Sgshapiro				name, rr->rr_u.rr_txt, level,
203363466Sgshapiro				(void *)drc, drc != NULL ? drc->dns_r_h.ad : -1,
204363466Sgshapiro				err, herr);
205363466Sgshapiro		nprev = n = tlsaadd(name, drc, dane_tlsa, herr, n, pttl,
206363466Sgshapiro				level + 1);
207363466Sgshapiro		dns_free_data(drc);
208363466Sgshapiro		drc = NULL;
209363466Sgshapiro	}
210363466Sgshapiro
211363466Sgshapiro	*pttl = ttl;
212363466Sgshapiro	return n;
213363466Sgshapiro}
214363466Sgshapiro
215363466Sgshapiro/*
216363466Sgshapiro**  GETTLSA -- get TLSA records for named host using DNS
217363466Sgshapiro**
218363466Sgshapiro**	Parameters:
219363466Sgshapiro**		host -- host
220363466Sgshapiro**		name -- name for stab entry key (if NULL: host)
221363466Sgshapiro**		pste -- (pointer to) stab entry (output)
222363466Sgshapiro**		flags -- TLSAFL*
223363466Sgshapiro**		mxttl -- TTL of MX (or host)
224363466Sgshapiro**		port -- port
225363466Sgshapiro**
226363466Sgshapiro**	Returns:
227363466Sgshapiro**		The number of TLSA records found.
228363466Sgshapiro**		<0 if there is an internal failure.
229363466Sgshapiro**
230363466Sgshapiro**	Side effects:
231363466Sgshapiro**		Enters TLSA RRs into stab().
232363466Sgshapiro**		If the DNS lookup fails temporarily, an "empty" entry is
233363466Sgshapiro**		created with that DNS error code.
234363466Sgshapiro*/
235363466Sgshapiro
236363466Sgshapiroint
237363466Sgshapirogettlsa(host, name, pste, flags, mxttl, port)
238363466Sgshapiro	char *host;
239363466Sgshapiro	char *name;
240363466Sgshapiro	STAB **pste;
241363466Sgshapiro	unsigned long flags;
242363466Sgshapiro	unsigned int mxttl;
243363466Sgshapiro	unsigned int port;
244363466Sgshapiro{
245363466Sgshapiro	DNS_REPLY_T *dr;
246363466Sgshapiro	dane_tlsa_P dane_tlsa;
247363466Sgshapiro	STAB *ste;
248363466Sgshapiro	time_t now;
249363466Sgshapiro	unsigned int ttl;
250363466Sgshapiro	int n_rrs, len, err, herr;
251363466Sgshapiro	bool isrname;
252363466Sgshapiro	char nbuf[MAXDNAME];
253363466Sgshapiro	char key[MAXDNAME];
254363466Sgshapiro
255363466Sgshapiro	SM_REQUIRE(host != NULL);
256363466Sgshapiro	if (pste != NULL)
257363466Sgshapiro		*pste = NULL;
258363466Sgshapiro	if ('\0' == *host)
259363466Sgshapiro		return 0;
260363466Sgshapiro
261363466Sgshapiro	isrname = NULL == name;
262363466Sgshapiro	if (isrname)
263363466Sgshapiro		name = host;
264363466Sgshapiro	now = 0;
265363466Sgshapiro	n_rrs = 0;
266363466Sgshapiro	dr = NULL;
267363466Sgshapiro	dane_tlsa = NULL;
268363466Sgshapiro	len = strlen(name);
269363466Sgshapiro	if (len > 1 && name[len - 1] == '.')
270363466Sgshapiro	{
271363466Sgshapiro		len--;
272363466Sgshapiro		name[len] = '\0';
273363466Sgshapiro	}
274363466Sgshapiro	else
275363466Sgshapiro		len = -1;
276363466Sgshapiro	if (0 == port || tTd(66, 10))
277363466Sgshapiro		port = 25;
278363466Sgshapiro	(void) sm_snprintf(key, sizeof(key), "_%u..%s", port, name);
279363466Sgshapiro	ste = stab(key, ST_TLSA_RR, ST_FIND);
280363466Sgshapiro	if (tTd(8, 2))
281363466Sgshapiro		sm_dprintf("gettlsa(%s, %s, ste=%p, pste=%p, flags=%lX, port=%d)\n",
282363466Sgshapiro			host, isrname ? "" : name, (void *)ste, (void *)pste,
283363466Sgshapiro			flags, port);
284363466Sgshapiro
285363466Sgshapiro	if (ste != NULL)
286363466Sgshapiro	{
287363466Sgshapiro		dane_tlsa = ste->s_tlsa;
288363466Sgshapiro		if ((TLSAFLADMX & flags) != 0)
289363466Sgshapiro			TLSA_CLR_FL(ste->s_tlsa, TLSAFLNOADMX);
290363466Sgshapiro	}
291363466Sgshapiro
292363466Sgshapiro	/* Do not reload TLSA RRs if the MX RRs were not securely retrieved. */
293363466Sgshapiro	if (pste != NULL
294363466Sgshapiro	    && dane_tlsa != NULL && TLSA_IS_FL(dane_tlsa, TLSAFLNOADMX)
295363466Sgshapiro	    && DANE_SECURE == Dane)
296363466Sgshapiro		goto end;
297363466Sgshapiro
298363466Sgshapiro	if (ste != NULL)
299363466Sgshapiro	{
300363466Sgshapiro		SM_ASSERT(dane_tlsa != NULL);
301363466Sgshapiro		now = curtime();
302363466Sgshapiro		if (dane_tlsa->dane_tlsa_exp <= now
303363466Sgshapiro		    && 0 == (TLSAFLNOEXP & flags))
304363466Sgshapiro			dane_tlsa_clr(dane_tlsa);
305363466Sgshapiro		else
306363466Sgshapiro		{
307363466Sgshapiro			n_rrs = dane_tlsa->dane_tlsa_n;
308363466Sgshapiro			goto end;
309363466Sgshapiro		}
310363466Sgshapiro	}
311363466Sgshapiro
312363466Sgshapiro	if (dane_tlsa == NULL)
313363466Sgshapiro	{
314363466Sgshapiro		dane_tlsa = (dane_tlsa_P) sm_malloc(sizeof(*dane_tlsa));
315363466Sgshapiro		if (dane_tlsa == NULL)
316363466Sgshapiro		{
317363466Sgshapiro			n_rrs = -ENOMEM;
318363466Sgshapiro			goto end;
319363466Sgshapiro		}
320363466Sgshapiro		memset(dane_tlsa, '\0', sizeof(*dane_tlsa));
321363466Sgshapiro	}
322363466Sgshapiro
323363466Sgshapiro	/* There are flags to store -- just set those, do nothing else. */
324363466Sgshapiro	if (TLSA_STORE_FL(flags))
325363466Sgshapiro	{
326363466Sgshapiro		dane_tlsa->dane_tlsa_flags = flags;
327363466Sgshapiro		ttl = mxttl > 0 ? mxttl: SM_DEFAULT_TTL;
328363466Sgshapiro		goto done;
329363466Sgshapiro	}
330363466Sgshapiro
331363466Sgshapiro	(void) sm_snprintf(nbuf, sizeof(nbuf), "_%u._tcp.%s", port, host);
332363466Sgshapiro	dr = dns_lookup_int(nbuf, C_IN, T_TLSA, 0, 0,
333363466Sgshapiro		TLSA_IS_FL(dane_tlsa, TLSAFLNOADMX) ? 0 : SM_RES_DNSSEC,
334363466Sgshapiro		RR_RAW, &err, &herr);
335363466Sgshapiro	if (tTd(8, 2))
336363466Sgshapiro		sm_dprintf("gettlsa(%s), dr=%p, ad=%d, err=%d, herr=%d\n", host,
337363466Sgshapiro			(void *)dr, dr != NULL ? dr->dns_r_h.ad : -1, err, herr);
338363466Sgshapiro	ttl = UINT_MAX;
339363466Sgshapiro	n_rrs = tlsaadd(key, dr, dane_tlsa, herr, n_rrs, &ttl, 0);
340363466Sgshapiro
341363466Sgshapiro	/* no valid entries found? */
342363466Sgshapiro	if (n_rrs == 0 && !TLSA_RR_TEMPFAIL(dane_tlsa))
343363466Sgshapiro	{
344363466Sgshapiro		if (tTd(8, 2))
345363466Sgshapiro			sm_dprintf("gettlsa(%s), n_rrs=%d, herr=%d, status=NOT_ADDED\n",
346363466Sgshapiro				host, n_rrs, dane_tlsa->dane_tlsa_dnsrc);
347363466Sgshapiro		goto cleanup;
348363466Sgshapiro	}
349363466Sgshapiro
350363466Sgshapiro  done:
351363466Sgshapiro	dane_tlsa->dane_tlsa_n = n_rrs;
352363466Sgshapiro	if (!isrname)
353363466Sgshapiro	{
354363466Sgshapiro		SM_FREE(dane_tlsa->dane_tlsa_sni);
355363466Sgshapiro		dane_tlsa->dane_tlsa_sni = sm_strdup(host);
356363466Sgshapiro	}
357363466Sgshapiro	if (NULL == ste)
358363466Sgshapiro	{
359363466Sgshapiro		ste = stab(key, ST_TLSA_RR, ST_ENTER);
360363466Sgshapiro		if (NULL == ste)
361363466Sgshapiro			goto error;
362363466Sgshapiro	}
363363466Sgshapiro	ste->s_tlsa = dane_tlsa;
364363466Sgshapiro	if (now == 0)
365363466Sgshapiro		now = curtime();
366363466Sgshapiro	dane_tlsa->dane_tlsa_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL);
367363466Sgshapiro	dns_free_data(dr);
368363466Sgshapiro	dr = NULL;
369363466Sgshapiro	goto end;
370363466Sgshapiro
371363466Sgshapiro  error:
372363466Sgshapiro	if (tTd(8, 2))
373363466Sgshapiro		sm_dprintf("gettlsa(%s, %s), status=error\n", host, key);
374363466Sgshapiro	n_rrs = -1;
375363466Sgshapiro  cleanup:
376363466Sgshapiro	if (NULL == ste)
377363466Sgshapiro		dane_tlsa_free(dane_tlsa);
378363466Sgshapiro	dns_free_data(dr);
379363466Sgshapiro	dr = NULL;
380363466Sgshapiro
381363466Sgshapiro  end:
382363466Sgshapiro	if (pste != NULL && ste != NULL)
383363466Sgshapiro		*pste = ste;
384363466Sgshapiro	if (len > 0)
385363466Sgshapiro		host[len] = '.';
386363466Sgshapiro	return n_rrs;
387363466Sgshapiro}
388363466Sgshapiro# endif /* DANE */
389363466Sgshapiro
390363466Sgshapiro/*
39190792Sgshapiro**  GETFALLBACKMXRR -- get MX resource records for fallback MX host.
39290792Sgshapiro**
39390792Sgshapiro**	We have to initialize this once before doing anything else.
39490792Sgshapiro**	Moreover, we have to repeat this from time to time to avoid
39590792Sgshapiro**	stale data, e.g., in persistent queue runners.
39690792Sgshapiro**	This should be done in a parent process so the child
39790792Sgshapiro**	processes have the right data.
39890792Sgshapiro**
39990792Sgshapiro**	Parameters:
40090792Sgshapiro**		host -- the name of the fallback MX host.
40190792Sgshapiro**
40290792Sgshapiro**	Returns:
40390792Sgshapiro**		number of MX records.
40490792Sgshapiro**
40590792Sgshapiro**	Side Effects:
406132943Sgshapiro**		Populates NumFallbackMXHosts and fbhosts.
40790792Sgshapiro**		Sets renewal time (based on TTL).
40890792Sgshapiro*/
40990792Sgshapiro
410132943Sgshapiroint NumFallbackMXHosts = 0;	/* Number of fallback MX hosts (after MX expansion) */
41190792Sgshapirostatic char *fbhosts[MAXMXHOSTS + 1];
41290792Sgshapiro
41390792Sgshapiroint
41490792Sgshapirogetfallbackmxrr(host)
41590792Sgshapiro	char *host;
41690792Sgshapiro{
41790792Sgshapiro	int i, rcode;
41890792Sgshapiro	int ttl;
41990792Sgshapiro	static time_t renew = 0;
42090792Sgshapiro
42190792Sgshapiro#if 0
42290792Sgshapiro	/* This is currently done before this function is called. */
42390792Sgshapiro	if (host == NULL || *host == '\0')
42490792Sgshapiro		return 0;
42590792Sgshapiro#endif /* 0 */
426132943Sgshapiro	if (NumFallbackMXHosts > 0 && renew > curtime())
427132943Sgshapiro		return NumFallbackMXHosts;
428363466Sgshapiro
429363466Sgshapiro	/* for DANE we need to invoke getmxrr() to get the TLSA RRs. */
430363466Sgshapiro#if !DANE
43190792Sgshapiro	if (host[0] == '[')
43290792Sgshapiro	{
43390792Sgshapiro		fbhosts[0] = host;
434132943Sgshapiro		NumFallbackMXHosts = 1;
43590792Sgshapiro	}
43690792Sgshapiro	else
437363466Sgshapiro#endif
43890792Sgshapiro	{
43990792Sgshapiro		/* free old data */
440132943Sgshapiro		for (i = 0; i < NumFallbackMXHosts; i++)
44190792Sgshapiro			sm_free(fbhosts[i]);
44290792Sgshapiro
443363466Sgshapiro		/*
444363466Sgshapiro		**  Get new data.
445363466Sgshapiro		**  Note: passing 0 as port is not correct but we cannot
446363466Sgshapiro		**  determine the port number as there is no mailer.
447363466Sgshapiro		*/
448363466Sgshapiro
449363466Sgshapiro		NumFallbackMXHosts = getmxrr(host, fbhosts, NULL,
450363466Sgshapiro#if DANE
451363466Sgshapiro					(DANE_SECURE == Dane) ?  ISAD :
452363466Sgshapiro#endif
453363466Sgshapiro					0,
454363466Sgshapiro					&rcode, &ttl, 0);
45590792Sgshapiro		renew = curtime() + ttl;
456132943Sgshapiro		for (i = 0; i < NumFallbackMXHosts; i++)
45790792Sgshapiro			fbhosts[i] = newstr(fbhosts[i]);
45890792Sgshapiro	}
459363466Sgshapiro	if (NumFallbackMXHosts == NULLMX)
460363466Sgshapiro		NumFallbackMXHosts = 0;
461132943Sgshapiro	return NumFallbackMXHosts;
46290792Sgshapiro}
46390792Sgshapiro
46490792Sgshapiro/*
46590792Sgshapiro**  FALLBACKMXRR -- add MX resource records for fallback MX host to list.
46690792Sgshapiro**
46790792Sgshapiro**	Parameters:
46890792Sgshapiro**		nmx -- current number of MX records.
46990792Sgshapiro**		prefs -- array of preferences.
47090792Sgshapiro**		mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS)
47190792Sgshapiro**
47290792Sgshapiro**	Returns:
47390792Sgshapiro**		new number of MX records.
47490792Sgshapiro**
47590792Sgshapiro**	Side Effects:
476132943Sgshapiro**		If FallbackMX was set, it appends the MX records for
47790792Sgshapiro**		that host to mxhosts (and modifies prefs accordingly).
47890792Sgshapiro*/
47990792Sgshapiro
48090792Sgshapirostatic int
48190792Sgshapirofallbackmxrr(nmx, prefs, mxhosts)
48290792Sgshapiro	int nmx;
48390792Sgshapiro	unsigned short *prefs;
48490792Sgshapiro	char **mxhosts;
48590792Sgshapiro{
48690792Sgshapiro	int i;
48790792Sgshapiro
488132943Sgshapiro	for (i = 0; i < NumFallbackMXHosts && nmx < MAXMXHOSTS; i++)
48990792Sgshapiro	{
49090792Sgshapiro		if (nmx > 0)
49190792Sgshapiro			prefs[nmx] = prefs[nmx - 1] + 1;
49290792Sgshapiro		else
49390792Sgshapiro			prefs[nmx] = 0;
49490792Sgshapiro		mxhosts[nmx++] = fbhosts[i];
49590792Sgshapiro	}
49690792Sgshapiro	return nmx;
49790792Sgshapiro}
49890792Sgshapiro
49990792Sgshapiro/*
50038032Speter**  GETMXRR -- get MX resource records for a domain
50138032Speter**
50238032Speter**	Parameters:
50338032Speter**		host -- the name of the host to MX.
50438032Speter**		mxhosts -- a pointer to a return buffer of MX records.
50564562Sgshapiro**		mxprefs -- a pointer to a return buffer of MX preferences.
50664562Sgshapiro**			If NULL, don't try to populate.
507363466Sgshapiro**		flags -- flags:
508363466Sgshapiro**			DROPLOCALHOSt -- If true, all MX records less preferred
50938032Speter**			than the local host (as determined by $=w) will
51038032Speter**			be discarded.
511363466Sgshapiro**			TRYFALLBACK -- add also fallback MX host?
512363466Sgshapiro**			ISAD -- host lookup was secure?
51338032Speter**		rcode -- a pointer to an EX_ status code.
51490792Sgshapiro**		pttl -- pointer to return TTL (can be NULL).
51538032Speter**
51638032Speter**	Returns:
51738032Speter**		The number of MX records found.
51838032Speter**		-1 if there is an internal failure.
51938032Speter**		If no MX records are found, mxhosts[0] is set to host
52038032Speter**			and 1 is returned.
52190792Sgshapiro**
52290792Sgshapiro**	Side Effects:
52390792Sgshapiro**		The entries made for mxhosts point to a static array
52490792Sgshapiro**		MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied,
52590792Sgshapiro**		if it must be preserved across calls to this function.
52638032Speter*/
52738032Speter
52838032Speterint
529363466Sgshapirogetmxrr(host, mxhosts, mxprefs, flags, rcode, pttl, port)
53038032Speter	char *host;
53138032Speter	char **mxhosts;
53290792Sgshapiro	unsigned short *mxprefs;
533363466Sgshapiro	unsigned int flags;
53438032Speter	int *rcode;
53590792Sgshapiro	int *pttl;
536363466Sgshapiro	int port;
53738032Speter{
53890792Sgshapiro	register unsigned char *eom, *cp;
53938032Speter	register int i, j, n;
54038032Speter	int nmx = 0;
54138032Speter	register char *bp;
54238032Speter	HEADER *hp;
54338032Speter	querybuf answer;
54438032Speter	int ancount, qdcount, buflen;
54590792Sgshapiro	bool seenlocal = false;
54690792Sgshapiro	unsigned short pref, type;
54790792Sgshapiro	unsigned short localpref = 256;
548132943Sgshapiro	char *fallbackMX = FallbackMX;
54990792Sgshapiro	bool trycanon = false;
55090792Sgshapiro	unsigned short *prefs;
551141858Sgshapiro	int (*resfunc) __P((const char *, int, int, u_char *, int));
55290792Sgshapiro	unsigned short prefer[MAXMXHOSTS];
55338032Speter	int weight[MAXMXHOSTS];
55490792Sgshapiro	int ttl = 0;
555363466Sgshapiro	bool ad;
556363466Sgshapiro	bool seennullmx = false;
55764562Sgshapiro	extern int res_query(), res_search();
558363466Sgshapiro# if DANE
559363466Sgshapiro	bool cname2mx;
560363466Sgshapiro	char qname[MAXNAME];
561363466Sgshapiro	unsigned long old_options = 0;
562363466Sgshapiro# endif
56338032Speter
56438032Speter	if (tTd(8, 2))
565363466Sgshapiro		sm_dprintf("getmxrr(%s, droplocalhost=%d, flags=%X, port=%d)\n",
566363466Sgshapiro			   host, (flags & DROPLOCALHOST) != 0, flags, port);
567363466Sgshapiro	ad = (flags & ISAD) != 0;
568147078Sgshapiro	*rcode = EX_OK;
569147078Sgshapiro	if (pttl != NULL)
570147078Sgshapiro		*pttl = SM_DEFAULT_TTL;
571120256Sgshapiro	if (*host == '\0')
572120256Sgshapiro		return 0;
573363466Sgshapiro# if DANE
574363466Sgshapiro	cname2mx = false;
575363466Sgshapiro	qname[0] = '\0';
576363466Sgshapiro	old_options = _res.options;
577363466Sgshapiro	if (ad)
578363466Sgshapiro		_res.options |= SM_RES_DNSSEC;
579363466Sgshapiro# endif
58038032Speter
581363466Sgshapiro	if ((fallbackMX != NULL && (flags & DROPLOCALHOST) != 0 &&
582363466Sgshapiro	     wordinclass(fallbackMX, 'w')) || (flags & TRYFALLBACK) == 0)
58338032Speter	{
58438032Speter		/* don't use fallback for this pass */
58538032Speter		fallbackMX = NULL;
58638032Speter	}
58738032Speter
58864562Sgshapiro	if (mxprefs != NULL)
58964562Sgshapiro		prefs = mxprefs;
59064562Sgshapiro	else
59164562Sgshapiro		prefs = prefer;
59264562Sgshapiro
59338032Speter	/* efficiency hack -- numeric or non-MX lookups */
59438032Speter	if (host[0] == '[')
59538032Speter		goto punt;
59638032Speter
597363466Sgshapiro# if DANE
59838032Speter	/*
599363466Sgshapiro	**  NOTE: This only works if nocanonify is used,
600363466Sgshapiro	**  otherwise the name is already rewritten.
601363466Sgshapiro	*/
602363466Sgshapiro
603363466Sgshapiro	/* always or only when "needed"? */
604363466Sgshapiro	if (DANE_ALWAYS == Dane || (ad && DANE_SECURE == Dane))
605363466Sgshapiro		(void) sm_strlcpy(qname, host, sizeof(qname));
606363466Sgshapiro# endif /* DANE */
607363466Sgshapiro
608363466Sgshapiro# if _FFR_EAI
609363466Sgshapiro	if (!addr_is_ascii(host))
610363466Sgshapiro	{
611363466Sgshapiro		char buf[1024];
612363466Sgshapiro		UErrorCode error = U_ZERO_ERROR;
613363466Sgshapiro		UIDNAInfo info = UIDNA_INFO_INITIALIZER;
614363466Sgshapiro		UIDNA *idna;
615363466Sgshapiro
616363466Sgshapiro		idna = uidna_openUTS46(UIDNA_NONTRANSITIONAL_TO_ASCII, &error);
617363466Sgshapiro		(void) uidna_nameToASCII_UTF8(idna, host, strlen(host),
618363466Sgshapiro					     buf, sizeof(buf) - 1,
619363466Sgshapiro					     &info, &error);
620363466Sgshapiro		uidna_close(idna);
621363466Sgshapiro		host = sm_rpool_strdup_x(CurEnv->e_rpool, buf);
622363466Sgshapiro	}
623363466Sgshapiro# endif /* _FFR_EAI */
624363466Sgshapiro
625363466Sgshapiro	/*
62638032Speter	**  If we don't have MX records in our host switch, don't
62738032Speter	**  try for MX records.  Note that this really isn't "right",
62838032Speter	**  since we might be set up to try NIS first and then DNS;
62938032Speter	**  if the host is found in NIS we really shouldn't be doing
63038032Speter	**  MX lookups.  However, that should be a degenerate case.
63138032Speter	*/
63238032Speter
63338032Speter	if (!UseNameServer)
63438032Speter		goto punt;
63538032Speter	if (HasWildcardMX && ConfigLevel >= 6)
63638032Speter		resfunc = res_query;
63738032Speter	else
63838032Speter		resfunc = res_search;
639363466Sgshapiro# if DNSSEC_TEST
640363466Sgshapiro	if (tTd(8, 110))
641363466Sgshapiro		resfunc = tstdns_search;
642363466Sgshapiro# endif
64338032Speter
64438032Speter	errno = 0;
645363466Sgshapiro	hp = (HEADER *)&answer;
64690792Sgshapiro	n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer,
64790792Sgshapiro		       sizeof(answer));
64838032Speter	if (n < 0)
64938032Speter	{
65038032Speter		if (tTd(8, 1))
651363466Sgshapiro# if DNSSEC_TEST
652363466Sgshapiro			sm_dprintf("getmxrr: res_search(%s) failed (errno=%d (%s), h_errno=%d (%s))\n",
653363466Sgshapiro				host, errno, strerror(errno),
654363466Sgshapiro				h_errno, herrno2txt(h_errno));
655363466Sgshapiro# else
656363466Sgshapiro			sm_dprintf("getmxrr: res_search(%s) failed, h_errno=%d\n",
657363466Sgshapiro				host, h_errno);
658363466Sgshapiro# endif
65938032Speter		switch (h_errno)
66038032Speter		{
66138032Speter		  case NO_DATA:
66290792Sgshapiro			trycanon = true;
66364562Sgshapiro			/* FALLTHROUGH */
66438032Speter
66538032Speter		  case NO_RECOVERY:
66638032Speter			/* no MX data on this host */
66738032Speter			goto punt;
66838032Speter
66938032Speter		  case HOST_NOT_FOUND:
67064562Sgshapiro# if BROKEN_RES_SEARCH
671363466Sgshapiro		  case 0: /* Ultrix resolver returns failure w/ h_errno=0 */
672363466Sgshapiro# endif
67338032Speter			/* host doesn't exist in DNS; might be in /etc/hosts */
67490792Sgshapiro			trycanon = true;
67538032Speter			*rcode = EX_NOHOST;
67638032Speter			goto punt;
67738032Speter
67838032Speter		  case TRY_AGAIN:
67938032Speter		  case -1:
68038032Speter			/* couldn't connect to the name server */
68138032Speter			if (fallbackMX != NULL)
68238032Speter			{
68338032Speter				/* name server is hosed -- push to fallback */
684363466Sgshapiro				nmx = fallbackmxrr(nmx, prefs, mxhosts);
685363466Sgshapiro				goto done;
68638032Speter			}
68738032Speter			/* it might come up later; better queue it up */
68838032Speter			*rcode = EX_TEMPFAIL;
68938032Speter			break;
69038032Speter
69138032Speter		  default:
69290792Sgshapiro			syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)",
69338032Speter				host, h_errno);
69438032Speter			*rcode = EX_OSERR;
69538032Speter			break;
69638032Speter		}
69738032Speter
69838032Speter		/* irreconcilable differences */
699363466Sgshapiro		goto error;
70038032Speter	}
70138032Speter
702363466Sgshapiro	ad = ad && hp->ad;
703363466Sgshapiro	if (tTd(8, 2))
704363466Sgshapiro		sm_dprintf("getmxrr(%s), hp=%p, ad=%d\n", host, (void*)hp, ad);
705363466Sgshapiro
70638032Speter	/* avoid problems after truncation in tcp packets */
70738032Speter	if (n > sizeof(answer))
70838032Speter		n = sizeof(answer);
70938032Speter
71038032Speter	/* find first satisfactory answer */
71190792Sgshapiro	cp = (unsigned char *)&answer + HFIXEDSZ;
71290792Sgshapiro	eom = (unsigned char *)&answer + n;
713363466Sgshapiro
71490792Sgshapiro	for (qdcount = ntohs((unsigned short) hp->qdcount);
71564562Sgshapiro	     qdcount--;
71664562Sgshapiro	     cp += n + QFIXEDSZ)
71764562Sgshapiro	{
71838032Speter		if ((n = dn_skipname(cp, eom)) < 0)
71938032Speter			goto punt;
72064562Sgshapiro	}
72190792Sgshapiro
72290792Sgshapiro	/* NOTE: see definition of MXHostBuf! */
72338032Speter	buflen = sizeof(MXHostBuf) - 1;
72490792Sgshapiro	SM_ASSERT(buflen > 0);
72538032Speter	bp = MXHostBuf;
72690792Sgshapiro	ancount = ntohs((unsigned short) hp->ancount);
72790792Sgshapiro
72890792Sgshapiro	/* See RFC 1035 for layout of RRs. */
729132943Sgshapiro	/* XXX leave room for FallbackMX ? */
73038032Speter	while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
73138032Speter	{
73290792Sgshapiro		if ((n = dn_expand((unsigned char *)&answer, eom, cp,
73390792Sgshapiro				   (RES_UNC_T) bp, buflen)) < 0)
73438032Speter			break;
73538032Speter		cp += n;
73638032Speter		GETSHORT(type, cp);
73790792Sgshapiro		cp += INT16SZ;		/* skip over class */
73890792Sgshapiro		GETLONG(ttl, cp);
73990792Sgshapiro		GETSHORT(n, cp);	/* rdlength */
740363466Sgshapiro# if DANE
741363466Sgshapiro		if (type == T_CNAME)
742363466Sgshapiro			cname2mx = true;
743363466Sgshapiro# endif
74438032Speter		if (type != T_MX)
74538032Speter		{
746363466Sgshapiro			if ((tTd(8, 8) || _res.options & RES_DEBUG)
747363466Sgshapiro# if DANE
748363466Sgshapiro			    && type != T_RRSIG
749363466Sgshapiro# endif
750363466Sgshapiro			    )
751363466Sgshapiro				sm_dprintf("unexpected answer type %s, size %d\n",
752363466Sgshapiro					dns_type_to_string(type), n);
75338032Speter			cp += n;
75438032Speter			continue;
75538032Speter		}
75638032Speter		GETSHORT(pref, cp);
75790792Sgshapiro		if ((n = dn_expand((unsigned char *)&answer, eom, cp,
75838032Speter				   (RES_UNC_T) bp, buflen)) < 0)
75938032Speter			break;
76038032Speter		cp += n;
76190792Sgshapiro		n = strlen(bp);
762363466Sgshapiro
763363466Sgshapiro		/* Support for RFC7505 "MX 0 ." */
764363466Sgshapiro		if (pref == 0 && *bp == '\0')
765363466Sgshapiro			seennullmx = true;
766363466Sgshapiro
76738032Speter		if (wordinclass(bp, 'w'))
76838032Speter		{
76938032Speter			if (tTd(8, 3))
77090792Sgshapiro				sm_dprintf("found localhost (%s) in MX list, pref=%d\n",
77138032Speter					bp, pref);
772363466Sgshapiro			if ((flags & DROPLOCALHOST) != 0)
77338032Speter			{
77438032Speter				if (!seenlocal || pref < localpref)
77538032Speter					localpref = pref;
77690792Sgshapiro				seenlocal = true;
77738032Speter				continue;
77838032Speter			}
77938032Speter			weight[nmx] = 0;
78038032Speter		}
78138032Speter		else
78238032Speter			weight[nmx] = mxrand(bp);
78364562Sgshapiro		prefs[nmx] = pref;
78438032Speter		mxhosts[nmx++] = bp;
785363466Sgshapiro# if DANE
786363466Sgshapiro		if (CHK_DANE(Dane) && port >= 0)
787363466Sgshapiro		{
788363466Sgshapiro			int nrr;
789363466Sgshapiro			unsigned long flags;
790363466Sgshapiro
791363466Sgshapiro			flags = ad ? TLSAFLADMX : TLSAFLNOADMX;
792363466Sgshapiro			nrr = gettlsa(bp, NULL, NULL, flags, ttl, port);
793363466Sgshapiro
794363466Sgshapiro			/* Only check qname if no TLSA RRs were found */
795363466Sgshapiro			if (0 == nrr && cname2mx && '\0' != qname[0] &&
796363466Sgshapiro			    strcmp(qname, bp))
797363466Sgshapiro				gettlsa(qname, bp, NULL, flags, ttl, port);
798363466Sgshapiro			/* XXX is this the right ad flag? */
799363466Sgshapiro		}
800363466Sgshapiro# endif
801363466Sgshapiro
802363466Sgshapiro		/*
803363466Sgshapiro		**  Note: n can be 0 for something like:
804363466Sgshapiro		**  host MX 0 .
805363466Sgshapiro		**  See RFC 7505
806363466Sgshapiro		*/
807363466Sgshapiro
80838032Speter		bp += n;
809363466Sgshapiro		if (0 == n || bp[-1] != '.')
81038032Speter		{
81138032Speter			*bp++ = '.';
81238032Speter			n++;
81338032Speter		}
81438032Speter		*bp++ = '\0';
81590792Sgshapiro		if (buflen < n + 1)
81690792Sgshapiro		{
81790792Sgshapiro			/* don't want to wrap buflen */
81890792Sgshapiro			break;
81990792Sgshapiro		}
82038032Speter		buflen -= n + 1;
82138032Speter	}
82238032Speter
823363466Sgshapiro	/* Support for RFC7505 "MX 0 ." */
824363466Sgshapiro	if (seennullmx && nmx == 1)
825363466Sgshapiro	{
826363466Sgshapiro		if (tTd(8, 4))
827363466Sgshapiro			sm_dprintf("getmxrr: Null MX record found, domain doesn't accept mail (RFC7505)\n");
828363466Sgshapiro		*rcode = EX_UNAVAILABLE;
829363466Sgshapiro		return NULLMX;
830363466Sgshapiro	}
831363466Sgshapiro
83290792Sgshapiro	/* return only one TTL entry, that should be sufficient */
83390792Sgshapiro	if (ttl > 0 && pttl != NULL)
83490792Sgshapiro		*pttl = ttl;
83590792Sgshapiro
83638032Speter	/* sort the records */
83738032Speter	for (i = 0; i < nmx; i++)
83838032Speter	{
83938032Speter		for (j = i + 1; j < nmx; j++)
84038032Speter		{
84164562Sgshapiro			if (prefs[i] > prefs[j] ||
84264562Sgshapiro			    (prefs[i] == prefs[j] && weight[i] > weight[j]))
84338032Speter			{
84438032Speter				register int temp;
84538032Speter				register char *temp1;
84638032Speter
84764562Sgshapiro				temp = prefs[i];
84864562Sgshapiro				prefs[i] = prefs[j];
84964562Sgshapiro				prefs[j] = temp;
85038032Speter				temp1 = mxhosts[i];
85138032Speter				mxhosts[i] = mxhosts[j];
85238032Speter				mxhosts[j] = temp1;
85338032Speter				temp = weight[i];
85438032Speter				weight[i] = weight[j];
85538032Speter				weight[j] = temp;
85638032Speter			}
85738032Speter		}
85864562Sgshapiro		if (seenlocal && prefs[i] >= localpref)
85938032Speter		{
86038032Speter			/* truncate higher preference part of list */
86138032Speter			nmx = i;
86238032Speter		}
86338032Speter	}
86438032Speter
86538032Speter	/* delete duplicates from list (yes, some bozos have duplicates) */
86638032Speter	for (i = 0; i < nmx - 1; )
86738032Speter	{
86890792Sgshapiro		if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0)
86938032Speter			i++;
87038032Speter		else
87138032Speter		{
87238032Speter			/* compress out duplicate */
87338032Speter			for (j = i + 1; j < nmx; j++)
87464562Sgshapiro			{
87538032Speter				mxhosts[j] = mxhosts[j + 1];
87664562Sgshapiro				prefs[j] = prefs[j + 1];
87764562Sgshapiro			}
87838032Speter			nmx--;
87938032Speter		}
88038032Speter	}
88138032Speter
88238032Speter	if (nmx == 0)
88338032Speter	{
88438032Speterpunt:
88564562Sgshapiro		if (seenlocal)
88638032Speter		{
88764562Sgshapiro			struct hostent *h = NULL;
88864562Sgshapiro
88938032Speter			/*
89038032Speter			**  If we have deleted all MX entries, this is
89138032Speter			**  an error -- we should NEVER send to a host that
89238032Speter			**  has an MX, and this should have been caught
89338032Speter			**  earlier in the config file.
89438032Speter			**
89538032Speter			**  Some sites prefer to go ahead and try the
89638032Speter			**  A record anyway; that case is handled by
89738032Speter			**  setting TryNullMXList.  I believe this is a
89838032Speter			**  bad idea, but it's up to you....
89938032Speter			*/
90038032Speter
90164562Sgshapiro			if (TryNullMXList)
90264562Sgshapiro			{
90373188Sgshapiro				SM_SET_H_ERRNO(0);
90464562Sgshapiro				errno = 0;
90564562Sgshapiro				h = sm_gethostbyname(host, AF_INET);
90664562Sgshapiro				if (h == NULL)
90764562Sgshapiro				{
90864562Sgshapiro					if (errno == ETIMEDOUT ||
90964562Sgshapiro					    h_errno == TRY_AGAIN ||
91064562Sgshapiro					    (errno == ECONNREFUSED &&
91164562Sgshapiro					     UseNameServer))
91264562Sgshapiro					{
91364562Sgshapiro						*rcode = EX_TEMPFAIL;
914363466Sgshapiro						goto error;
91564562Sgshapiro					}
91664562Sgshapiro# if NETINET6
91773188Sgshapiro					SM_SET_H_ERRNO(0);
91864562Sgshapiro					errno = 0;
91964562Sgshapiro					h = sm_gethostbyname(host, AF_INET6);
92064562Sgshapiro					if (h == NULL &&
92164562Sgshapiro					    (errno == ETIMEDOUT ||
92264562Sgshapiro					     h_errno == TRY_AGAIN ||
92364562Sgshapiro					     (errno == ECONNREFUSED &&
92464562Sgshapiro					      UseNameServer)))
92564562Sgshapiro					{
92664562Sgshapiro						*rcode = EX_TEMPFAIL;
927363466Sgshapiro						goto error;
92864562Sgshapiro					}
92964562Sgshapiro# endif /* NETINET6 */
93064562Sgshapiro				}
93164562Sgshapiro			}
93264562Sgshapiro
93364562Sgshapiro			if (h == NULL)
93464562Sgshapiro			{
93564562Sgshapiro				*rcode = EX_CONFIG;
93664562Sgshapiro				syserr("MX list for %s points back to %s",
93764562Sgshapiro				       host, MyHostName);
938363466Sgshapiro				goto error;
93964562Sgshapiro			}
94090792Sgshapiro# if NETINET6
94171345Sgshapiro			freehostent(h);
942159609Sgshapiro			h = NULL;
943363466Sgshapiro# endif
94438032Speter		}
945168515Sgshapiro		if (strlen(host) >= sizeof(MXHostBuf))
94638032Speter		{
94738032Speter			*rcode = EX_CONFIG;
94838032Speter			syserr("Host name %s too long",
94938032Speter			       shortenstring(host, MAXSHORTSTR));
950363466Sgshapiro			goto error;
95138032Speter		}
952168515Sgshapiro		(void) sm_strlcpy(MXHostBuf, host, sizeof(MXHostBuf));
95338032Speter		mxhosts[0] = MXHostBuf;
95464562Sgshapiro		prefs[0] = 0;
95538032Speter		if (host[0] == '[')
95638032Speter		{
95738032Speter			register char *p;
95864562Sgshapiro# if NETINET6
95964562Sgshapiro			struct sockaddr_in6 tmp6;
960363466Sgshapiro# endif
96138032Speter
96238032Speter			/* this may be an MX suppression-style address */
96338032Speter			p = strchr(MXHostBuf, ']');
96438032Speter			if (p != NULL)
96538032Speter			{
96638032Speter				*p = '\0';
96764562Sgshapiro
96838032Speter				if (inet_addr(&MXHostBuf[1]) != INADDR_NONE)
96938032Speter				{
97038032Speter					nmx++;
97138032Speter					*p = ']';
97238032Speter				}
97364562Sgshapiro# if NETINET6
97490792Sgshapiro				else if (anynet_pton(AF_INET6, &MXHostBuf[1],
97590792Sgshapiro						     &tmp6.sin6_addr) == 1)
97664562Sgshapiro				{
97764562Sgshapiro					nmx++;
97864562Sgshapiro					*p = ']';
97964562Sgshapiro				}
98064562Sgshapiro# endif /* NETINET6 */
98138032Speter				else
98238032Speter				{
98390792Sgshapiro					trycanon = true;
98438032Speter					mxhosts[0]++;
98538032Speter				}
98638032Speter			}
98738032Speter		}
98838032Speter		if (trycanon &&
989363466Sgshapiro		    (n = getcanonname(mxhosts[0], sizeof(MXHostBuf) - 2, false,
990363466Sgshapiro				pttl)) != HOST_NOTFOUND)
99138032Speter		{
99290792Sgshapiro			/* XXX MXHostBuf == "" ?  is that possible? */
99338032Speter			bp = &MXHostBuf[strlen(MXHostBuf)];
99438032Speter			if (bp[-1] != '.')
99538032Speter			{
99638032Speter				*bp++ = '.';
99738032Speter				*bp = '\0';
99838032Speter			}
99938032Speter			nmx = 1;
1000363466Sgshapiro# if DANE
1001363466Sgshapiro			if (tTd(8, 3))
1002363466Sgshapiro				sm_dprintf("getmxrr=%s, getcanonname=%d\n",
1003363466Sgshapiro					mxhosts[0], n);
1004363466Sgshapiro			if (CHK_DANE(Dane) && port >= 0)
1005363466Sgshapiro			{
1006363466Sgshapiro				int nrr;
1007363466Sgshapiro				unsigned long flags;
1008363466Sgshapiro				unsigned int cttl;
1009363466Sgshapiro
1010363466Sgshapiro				if (pttl != NULL)
1011363466Sgshapiro					cttl = *pttl;
1012363466Sgshapiro				else if (ttl > 0)
1013363466Sgshapiro					cttl = ttl;
1014363466Sgshapiro				else
1015363466Sgshapiro					cttl = SM_DEFAULT_TTL;
1016363466Sgshapiro
1017363466Sgshapiro				flags = (ad && n == HOST_SECURE)
1018363466Sgshapiro					? TLSAFLADMX : TLSAFLNOADMX;
1019363466Sgshapiro				nrr = gettlsa(mxhosts[0], NULL, NULL, flags,
1020363466Sgshapiro						cttl, port);
1021363466Sgshapiro
1022363466Sgshapiro				/*
1023363466Sgshapiro				**  Only check qname if no TLSA RRs were found
1024363466Sgshapiro				**  XXX: what about (temp) DNS errors?
1025363466Sgshapiro				*/
1026363466Sgshapiro
1027363466Sgshapiro				if (0 == nrr && '\0' != qname[0] &&
1028363466Sgshapiro				    strcmp(qname, mxhosts[0]))
1029363466Sgshapiro					gettlsa(qname, mxhosts[0], NULL, flags,
1030363466Sgshapiro						cttl, port);
1031363466Sgshapiro				/* XXX is this the right ad flag? */
1032363466Sgshapiro			}
1033363466Sgshapiro# endif
103438032Speter		}
103538032Speter	}
103638032Speter
103738032Speter	/* if we have a default lowest preference, include that */
103838032Speter	if (fallbackMX != NULL && !seenlocal)
103964562Sgshapiro	{
1040363466Sgshapiro		/* TODO: DNSsec status of fallbacks */
104190792Sgshapiro		nmx = fallbackmxrr(nmx, prefs, mxhosts);
104264562Sgshapiro	}
1043363466Sgshapiro    done:
1044363466Sgshapiro# if DANE
1045363466Sgshapiro	_res.options = old_options;
1046363466Sgshapiro# endif
104764562Sgshapiro	return nmx;
1048363466Sgshapiro
1049363466Sgshapiro   error:
1050363466Sgshapiro# if DANE
1051363466Sgshapiro	_res.options = old_options;
1052363466Sgshapiro# endif
1053363466Sgshapiro	return -1;
105438032Speter}
1055363466Sgshapiro
105690792Sgshapiro/*
105738032Speter**  MXRAND -- create a randomizer for equal MX preferences
105838032Speter**
105938032Speter**	If two MX hosts have equal preferences we want to randomize
106038032Speter**	the selection.  But in order for signatures to be the same,
106138032Speter**	we need to randomize the same way each time.  This function
106238032Speter**	computes a pseudo-random hash function from the host name.
106338032Speter**
106438032Speter**	Parameters:
106538032Speter**		host -- the name of the host.
106638032Speter**
106738032Speter**	Returns:
106838032Speter**		A random but repeatable value based on the host name.
106938032Speter*/
107038032Speter
107164562Sgshapirostatic int
107238032Spetermxrand(host)
107338032Speter	register char *host;
107438032Speter{
107538032Speter	int hfunc;
107638032Speter	static unsigned int seed;
107738032Speter
107838032Speter	if (seed == 0)
107938032Speter	{
108038032Speter		seed = (int) curtime() & 0xffff;
108138032Speter		if (seed == 0)
108238032Speter			seed++;
108338032Speter	}
108438032Speter
108538032Speter	if (tTd(17, 9))
108690792Sgshapiro		sm_dprintf("mxrand(%s)", host);
108738032Speter
108838032Speter	hfunc = seed;
108938032Speter	while (*host != '\0')
109038032Speter	{
109138032Speter		int c = *host++;
109238032Speter
109338032Speter		if (isascii(c) && isupper(c))
109438032Speter			c = tolower(c);
109538032Speter		hfunc = ((hfunc << 1) ^ c) % 2003;
109638032Speter	}
109738032Speter
109838032Speter	hfunc &= 0xff;
109938032Speter	hfunc++;
110038032Speter
110138032Speter	if (tTd(17, 9))
110290792Sgshapiro		sm_dprintf(" = %d\n", hfunc);
110338032Speter	return hfunc;
110438032Speter}
110590792Sgshapiro/*
110638032Speter**  BESTMX -- find the best MX for a name
110738032Speter**
110838032Speter**	This is really a hack, but I don't see any obvious way
110938032Speter**	to generalize it at the moment.
111038032Speter*/
111138032Speter
111238032Speter/* ARGSUSED3 */
111338032Speterchar *
111438032Speterbestmx_map_lookup(map, name, av, statp)
111538032Speter	MAP *map;
111638032Speter	char *name;
111738032Speter	char **av;
111838032Speter	int *statp;
111938032Speter{
112038032Speter	int nmx;
112138032Speter	int saveopts = _res.options;
112290792Sgshapiro	int i;
112390792Sgshapiro	ssize_t len = 0;
112490792Sgshapiro	char *result;
112590792Sgshapiro	char *mxhosts[MAXMXHOSTS + 1];
1126363466Sgshapiro# if _FFR_BESTMX_BETTER_TRUNCATION
112790792Sgshapiro	char *buf;
1128363466Sgshapiro# else
112938032Speter	char *p;
113042575Speter	char buf[PSBUFSIZE / 2];
1131363466Sgshapiro# endif
113238032Speter
113338032Speter	_res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
1134363466Sgshapiro	nmx = getmxrr(name, mxhosts, NULL, 0, statp, NULL, -1);
113538032Speter	_res.options = saveopts;
113638032Speter	if (nmx <= 0)
113738032Speter		return NULL;
113838032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
113938032Speter		return map_rewrite(map, name, strlen(name), NULL);
114038032Speter	if ((map->map_coldelim == '\0') || (nmx == 1))
114138032Speter		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
114238032Speter
114338032Speter	/*
114442575Speter	**  We were given a -z flag (return all MXs) and there are multiple
114542575Speter	**  ones.  We need to build them all into a list.
114638032Speter	*/
114790792Sgshapiro
1148363466Sgshapiro# if _FFR_BESTMX_BETTER_TRUNCATION
114990792Sgshapiro	for (i = 0; i < nmx; i++)
115090792Sgshapiro	{
115190792Sgshapiro		if (strchr(mxhosts[i], map->map_coldelim) != NULL)
115290792Sgshapiro		{
115390792Sgshapiro			syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
115490792Sgshapiro			       mxhosts[i], map->map_coldelim);
115590792Sgshapiro			return NULL;
115690792Sgshapiro		}
115790792Sgshapiro		len += strlen(mxhosts[i]) + 1;
115890792Sgshapiro		if (len < 0)
115990792Sgshapiro		{
116090792Sgshapiro			len -= strlen(mxhosts[i]) + 1;
116190792Sgshapiro			break;
116290792Sgshapiro		}
116390792Sgshapiro	}
116490792Sgshapiro	buf = (char *) sm_malloc(len);
116590792Sgshapiro	if (buf == NULL)
116690792Sgshapiro	{
116790792Sgshapiro		*statp = EX_UNAVAILABLE;
116890792Sgshapiro		return NULL;
116990792Sgshapiro	}
117090792Sgshapiro	*buf = '\0';
117190792Sgshapiro	for (i = 0; i < nmx; i++)
117290792Sgshapiro	{
117390792Sgshapiro		int end;
117490792Sgshapiro
117590792Sgshapiro		end = sm_strlcat(buf, mxhosts[i], len);
117690792Sgshapiro		if (i != nmx && end + 1 < len)
117790792Sgshapiro		{
117890792Sgshapiro			buf[end] = map->map_coldelim;
117990792Sgshapiro			buf[end + 1] = '\0';
118090792Sgshapiro		}
118190792Sgshapiro	}
118290792Sgshapiro
118390792Sgshapiro	/* Cleanly truncate for rulesets */
118490792Sgshapiro	truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim);
1185363466Sgshapiro# else /* _FFR_BESTMX_BETTER_TRUNCATION */
118638032Speter	p = buf;
118738032Speter	for (i = 0; i < nmx; i++)
118838032Speter	{
118990792Sgshapiro		size_t slen;
119064562Sgshapiro
119138032Speter		if (strchr(mxhosts[i], map->map_coldelim) != NULL)
119238032Speter		{
119338032Speter			syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
119438032Speter			       mxhosts[i], map->map_coldelim);
119538032Speter			return NULL;
119638032Speter		}
119738032Speter		slen = strlen(mxhosts[i]);
1198168515Sgshapiro		if (len + slen + 2 > sizeof(buf))
119938032Speter			break;
120038032Speter		if (i > 0)
120138032Speter		{
120238032Speter			*p++ = map->map_coldelim;
120338032Speter			len++;
120438032Speter		}
1205168515Sgshapiro		(void) sm_strlcpy(p, mxhosts[i], sizeof(buf) - len);
120638032Speter		p += slen;
120738032Speter		len += slen;
120838032Speter	}
1209363466Sgshapiro# endif /* _FFR_BESTMX_BETTER_TRUNCATION */
121090792Sgshapiro
121190792Sgshapiro	result = map_rewrite(map, buf, len, av);
1212363466Sgshapiro# if _FFR_BESTMX_BETTER_TRUNCATION
121390792Sgshapiro	sm_free(buf);
1214363466Sgshapiro# endif
121590792Sgshapiro	return result;
121638032Speter}
121790792Sgshapiro/*
121838032Speter**  DNS_GETCANONNAME -- get the canonical name for named host using DNS
121938032Speter**
122038032Speter**	This algorithm tries to be smart about wildcard MX records.
122138032Speter**	This is hard to do because DNS doesn't tell is if we matched
122238032Speter**	against a wildcard or a specific MX.
122364562Sgshapiro**
122438032Speter**	We always prefer A & CNAME records, since these are presumed
122538032Speter**	to be specific.
122638032Speter**
122738032Speter**	If we match an MX in one pass and lose it in the next, we use
122838032Speter**	the old one.  For example, consider an MX matching *.FOO.BAR.COM.
122938032Speter**	A hostname bletch.foo.bar.com will match against this MX, but
123038032Speter**	will stop matching when we try bletch.bar.com -- so we know
123138032Speter**	that bletch.foo.bar.com must have been right.  This fails if
123238032Speter**	there was also an MX record matching *.BAR.COM, but there are
123338032Speter**	some things that just can't be fixed.
123438032Speter**
123538032Speter**	Parameters:
123638032Speter**		host -- a buffer containing the name of the host.
123738032Speter**			This is a value-result parameter.
123838032Speter**		hbsize -- the size of the host buffer.
123938032Speter**		trymx -- if set, try MX records as well as A and CNAME.
124038032Speter**		statp -- pointer to place to store status.
124190792Sgshapiro**		pttl -- pointer to return TTL (can be NULL).
124238032Speter**
124338032Speter**	Returns:
1244363466Sgshapiro**		>0 -- if the host was found.
1245363466Sgshapiro**		0 -- otherwise.
124638032Speter*/
124738032Speter
1248363466Sgshapiroint
124990792Sgshapirodns_getcanonname(host, hbsize, trymx, statp, pttl)
125038032Speter	char *host;
125138032Speter	int hbsize;
125238032Speter	bool trymx;
125338032Speter	int *statp;
125490792Sgshapiro	int *pttl;
125538032Speter{
125690792Sgshapiro	register unsigned char *eom, *ap;
125738032Speter	register char *cp;
125864562Sgshapiro	register int n;
125938032Speter	HEADER *hp;
126038032Speter	querybuf answer;
1261363466Sgshapiro	int ancount, qdcount, ret, type, qtype, initial, loopcnt, ttl, sli;
126238032Speter	char **domain;
1263363466Sgshapiro	char *dp;
126438032Speter	char *mxmatch;
1265363466Sgshapiro	bool amatch, gotmx, ad;
126690792Sgshapiro	char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)];
1267363466Sgshapiro# if DNSSEC_TEST
1268363466Sgshapiro#  define ADDSL	1 /* NameSearchList may add another entry to searchlist! */
1269363466Sgshapiro# else
1270363466Sgshapiro#  define ADDSL	0
1271363466Sgshapiro# endif
1272363466Sgshapiro	char *searchlist[MAXDNSRCH + 2 + ADDSL];
1273363466Sgshapiro# define SLSIZE SM_ARRAY_SIZE(searchlist)
1274363466Sgshapiro	int (*resqdomain) __P((const char *, const char *, int, int, unsigned char *, int));
1275363466Sgshapiro# if DANE
1276363466Sgshapiro	unsigned long old_options = 0;
1277363466Sgshapiro# endif
127838032Speter
1279363466Sgshapiro	ttl = 0;
1280363466Sgshapiro	gotmx = false;
1281363466Sgshapiro	ad = true;
128238032Speter	if (tTd(8, 2))
128390792Sgshapiro		sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
128438032Speter
128538032Speter	if ((_res.options & RES_INIT) == 0 && res_init() == -1)
128638032Speter	{
128738032Speter		*statp = EX_UNAVAILABLE;
1288363466Sgshapiro		return HOST_NOTFOUND;
128938032Speter	}
129038032Speter
1291363466Sgshapiro# if DANE
1292363466Sgshapiro	old_options = _res.options;
1293363466Sgshapiro	if (DANE_SECURE == Dane)
1294363466Sgshapiro		_res.options |= SM_RES_DNSSEC;
1295363466Sgshapiro# endif
1296363466Sgshapiro
129771345Sgshapiro	*statp = EX_OK;
1298363466Sgshapiro	resqdomain = res_querydomain;
1299363466Sgshapiro# if DNSSEC_TEST
1300363466Sgshapiro	if (tTd(8, 110))
1301363466Sgshapiro		resqdomain = tstdns_querydomain;
1302363466Sgshapiro# endif
130371345Sgshapiro
130438032Speter	/*
130538032Speter	**  Initialize domain search list.  If there is at least one
130638032Speter	**  dot in the name, search the unmodified name first so we
130738032Speter	**  find "vse.CS" in Czechoslovakia instead of in the local
130864562Sgshapiro	**  domain (e.g., vse.CS.Berkeley.EDU).  Note that there is no
130964562Sgshapiro	**  longer a country named Czechoslovakia but this type of problem
131064562Sgshapiro	**  is still present.
131138032Speter	**
131238032Speter	**  Older versions of the resolver could create this
131338032Speter	**  list by tearing apart the host name.
131438032Speter	*/
131538032Speter
131638032Speter	loopcnt = 0;
131738032Spetercnameloop:
131838032Speter	/* Check for dots in the name */
131938032Speter	for (cp = host, n = 0; *cp != '\0'; cp++)
132038032Speter		if (*cp == '.')
132138032Speter			n++;
132238032Speter
132338032Speter	/*
132464562Sgshapiro	**  Build the search list.
132538032Speter	**	If there is at least one dot in name, start with a null
132638032Speter	**	domain to search the unmodified name first.
132738032Speter	**	If name does not end with a dot and search up local domain
132838032Speter	**	tree desired, append each local domain component to the
132938032Speter	**	search list; if name contains no dots and default domain
133038032Speter	**	name is desired, append default domain name to search list;
133138032Speter	**	else if name ends in a dot, remove that dot.
133238032Speter	*/
133338032Speter
1334363466Sgshapiro	sli = 0;
133538032Speter	if (n > 0)
1336363466Sgshapiro		searchlist[sli++] = "";
1337363466Sgshapiro# if DNSSEC_TEST
1338363466Sgshapiro	if (NameSearchList != NULL)
1339363466Sgshapiro	{
1340363466Sgshapiro		SM_ASSERT(sli < SLSIZE);
1341363466Sgshapiro		searchlist[sli++] = NameSearchList;
1342363466Sgshapiro	}
1343363466Sgshapiro# endif
134438032Speter	if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options))
134538032Speter	{
134664562Sgshapiro		/* make sure there are less than MAXDNSRCH domains */
134764562Sgshapiro		for (domain = RES_DNSRCH_VARIABLE, ret = 0;
1348363466Sgshapiro		     *domain != NULL && ret < MAXDNSRCH && sli < SLSIZE;
134964562Sgshapiro		     ret++)
1350363466Sgshapiro			searchlist[sli++] = *domain++;
135138032Speter	}
135238032Speter	else if (n == 0 && bitset(RES_DEFNAMES, _res.options))
135338032Speter	{
1354363466Sgshapiro		SM_ASSERT(sli < SLSIZE);
1355363466Sgshapiro		searchlist[sli++] = _res.defdname;
135638032Speter	}
135738032Speter	else if (*cp == '.')
135838032Speter	{
135938032Speter		*cp = '\0';
136038032Speter	}
1361363466Sgshapiro	SM_ASSERT(sli < SLSIZE);
1362363466Sgshapiro	searchlist[sli] = NULL;
136338032Speter
136438032Speter	/*
136538032Speter	**  Now loop through the search list, appending each domain in turn
136638032Speter	**  name and searching for a match.
136738032Speter	*/
136838032Speter
136938032Speter	mxmatch = NULL;
1370120256Sgshapiro	initial = T_A;
1371120256Sgshapiro# if NETINET6
1372120256Sgshapiro	if (InetMode == AF_INET6)
1373120256Sgshapiro		initial = T_AAAA;
1374363466Sgshapiro# endif
1375120256Sgshapiro	qtype = initial;
137638032Speter
1377363466Sgshapiro	for (sli = 0; sli < SLSIZE; )
137838032Speter	{
1379363466Sgshapiro		dp = searchlist[sli];
1380363466Sgshapiro		if (NULL == dp)
1381363466Sgshapiro			break;
1382120256Sgshapiro		if (qtype == initial)
138390792Sgshapiro			gotmx = false;
138438032Speter		if (tTd(8, 5))
138590792Sgshapiro			sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n",
1386363466Sgshapiro				host, dp,
138764562Sgshapiro# if NETINET6
138864562Sgshapiro				qtype == T_AAAA ? "AAAA" :
1389363466Sgshapiro# endif
139064562Sgshapiro				qtype == T_A ? "A" :
139164562Sgshapiro				qtype == T_MX ? "MX" :
139264562Sgshapiro				"???");
139371345Sgshapiro		errno = 0;
1394363466Sgshapiro		hp = (HEADER *) &answer;
1395363466Sgshapiro		ret = (*resqdomain)(host, dp, C_IN, qtype,
139638032Speter				      answer.qb2, sizeof(answer.qb2));
139738032Speter		if (ret <= 0)
139838032Speter		{
139990792Sgshapiro			int save_errno = errno;
140090792Sgshapiro
140138032Speter			if (tTd(8, 7))
140290792Sgshapiro				sm_dprintf("\tNO: errno=%d, h_errno=%d\n",
140390792Sgshapiro					   save_errno, h_errno);
140438032Speter
140590792Sgshapiro			if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN)
140638032Speter			{
140771345Sgshapiro				/*
140890792Sgshapiro				**  the name server seems to be down or broken.
140971345Sgshapiro				*/
141071345Sgshapiro
141173188Sgshapiro				SM_SET_H_ERRNO(TRY_AGAIN);
1412363466Sgshapiro				if (*dp == '\0')
141394334Sgshapiro				{
141494334Sgshapiro					if (*statp == EX_OK)
141594334Sgshapiro						*statp = EX_TEMPFAIL;
141694334Sgshapiro					goto nexttype;
141794334Sgshapiro				}
141838032Speter				*statp = EX_TEMPFAIL;
141964562Sgshapiro
142073188Sgshapiro				if (WorkAroundBrokenAAAA)
142173188Sgshapiro				{
142273188Sgshapiro					/*
142373188Sgshapiro					**  Only return if not TRY_AGAIN as an
142473188Sgshapiro					**  attempt with a different qtype may
142573188Sgshapiro					**  succeed (res_querydomain() calls
142673188Sgshapiro					**  res_query() calls res_send() which
142773188Sgshapiro					**  sets errno to ETIMEDOUT if the
142873188Sgshapiro					**  nameservers could be contacted but
142973188Sgshapiro					**  didn't give an answer).
143073188Sgshapiro					*/
143171345Sgshapiro
143290792Sgshapiro					if (save_errno != ETIMEDOUT)
1433363466Sgshapiro						goto error;
143473188Sgshapiro				}
143590792Sgshapiro				else
1436363466Sgshapiro					goto error;
143738032Speter			}
143838032Speter
143994334Sgshapironexttype:
144038032Speter			if (h_errno != HOST_NOT_FOUND)
144138032Speter			{
144238032Speter				/* might have another type of interest */
144364562Sgshapiro# if NETINET6
144490792Sgshapiro				if (qtype == T_AAAA)
144538032Speter				{
144664562Sgshapiro					qtype = T_A;
144764562Sgshapiro					continue;
144864562Sgshapiro				}
144990792Sgshapiro				else
145064562Sgshapiro# endif /* NETINET6 */
145190792Sgshapiro				if (qtype == T_A && !gotmx &&
1452363466Sgshapiro				    (trymx || *dp == '\0'))
145364562Sgshapiro				{
145438032Speter					qtype = T_MX;
145538032Speter					continue;
145638032Speter				}
145738032Speter			}
145838032Speter
145938032Speter			/* definite no -- try the next domain */
1460363466Sgshapiro			sli++;
1461120256Sgshapiro			qtype = initial;
146238032Speter			continue;
146338032Speter		}
146438032Speter		else if (tTd(8, 7))
146590792Sgshapiro			sm_dprintf("\tYES\n");
146638032Speter
146738032Speter		/* avoid problems after truncation in tcp packets */
146838032Speter		if (ret > sizeof(answer))
146938032Speter			ret = sizeof(answer);
1470159609Sgshapiro		SM_ASSERT(ret >= 0);
147138032Speter
147238032Speter		/*
147338032Speter		**  Appear to have a match.  Confirm it by searching for A or
147438032Speter		**  CNAME records.  If we don't have a local domain
147538032Speter		**  wild card MX record, we will accept MX as well.
147638032Speter		*/
147738032Speter
147890792Sgshapiro		ap = (unsigned char *) &answer + HFIXEDSZ;
147990792Sgshapiro		eom = (unsigned char *) &answer + ret;
148038032Speter
1481363466Sgshapiro		if (0 == hp->ad)
1482363466Sgshapiro			ad = false;
1483363466Sgshapiro
148438032Speter		/* skip question part of response -- we know what we asked */
148590792Sgshapiro		for (qdcount = ntohs((unsigned short) hp->qdcount);
148664562Sgshapiro		     qdcount--;
148764562Sgshapiro		     ap += ret + QFIXEDSZ)
148838032Speter		{
148938032Speter			if ((ret = dn_skipname(ap, eom)) < 0)
149038032Speter			{
149138032Speter				if (tTd(8, 20))
149290792Sgshapiro					sm_dprintf("qdcount failure (%d)\n",
149390792Sgshapiro						ntohs((unsigned short) hp->qdcount));
149438032Speter				*statp = EX_SOFTWARE;
1495363466Sgshapiro				goto error;
149638032Speter			}
149738032Speter		}
149838032Speter
149990792Sgshapiro		amatch = false;
150090792Sgshapiro		for (ancount = ntohs((unsigned short) hp->ancount);
150164562Sgshapiro		     --ancount >= 0 && ap < eom;
150264562Sgshapiro		     ap += n)
150338032Speter		{
150490792Sgshapiro			n = dn_expand((unsigned char *) &answer, eom, ap,
1505168515Sgshapiro				      (RES_UNC_T) nbuf, sizeof(nbuf));
150638032Speter			if (n < 0)
150738032Speter				break;
150838032Speter			ap += n;
150938032Speter			GETSHORT(type, ap);
151090792Sgshapiro			ap += INT16SZ;		/* skip over class */
151190792Sgshapiro			GETLONG(ttl, ap);
151290792Sgshapiro			GETSHORT(n, ap);	/* rdlength */
151338032Speter			switch (type)
151438032Speter			{
151538032Speter			  case T_MX:
151690792Sgshapiro				gotmx = true;
1517363466Sgshapiro				if (*dp != '\0' && HasWildcardMX)
151838032Speter				{
151938032Speter					/*
152038032Speter					**  If we are using MX matches and have
152138032Speter					**  not yet gotten one, save this one
152264562Sgshapiro					**  but keep searching for an A or
152338032Speter					**  CNAME match.
152438032Speter					*/
152538032Speter
152638032Speter					if (trymx && mxmatch == NULL)
1527363466Sgshapiro						mxmatch = dp;
152838032Speter					continue;
152938032Speter				}
153038032Speter
153138032Speter				/*
153238032Speter				**  If we did not append a domain name, this
153338032Speter				**  must have been a canonical name to start
153438032Speter				**  with.  Even if we did append a domain name,
153538032Speter				**  in the absence of a wildcard MX this must
153638032Speter				**  still be a real MX match.
153738032Speter				**  Such MX matches are as good as an A match,
153838032Speter				**  fall through.
153938032Speter				*/
154064562Sgshapiro				/* FALLTHROUGH */
154138032Speter
154264562Sgshapiro# if NETINET6
154364562Sgshapiro			  case T_AAAA:
1544363466Sgshapiro# endif
154538032Speter			  case T_A:
154638032Speter				/* Flag that a good match was found */
154790792Sgshapiro				amatch = true;
154838032Speter
154938032Speter				/* continue in case a CNAME also exists */
155038032Speter				continue;
155138032Speter
155238032Speter			  case T_CNAME:
155338032Speter				if (DontExpandCnames)
155438032Speter				{
155538032Speter					/* got CNAME -- guaranteed canonical */
155690792Sgshapiro					amatch = true;
155738032Speter					break;
155838032Speter				}
155938032Speter
156038032Speter				if (loopcnt++ > MAXCNAMEDEPTH)
156138032Speter				{
156238032Speter					/*XXX should notify postmaster XXX*/
156338032Speter					message("DNS failure: CNAME loop for %s",
156438032Speter						host);
156538032Speter					if (CurEnv->e_message == NULL)
156638032Speter					{
156738032Speter						char ebuf[MAXLINE];
156838032Speter
156990792Sgshapiro						(void) sm_snprintf(ebuf,
1570168515Sgshapiro							sizeof(ebuf),
157138032Speter							"Deferred: DNS failure: CNAME loop for %.100s",
157238032Speter							host);
157390792Sgshapiro						CurEnv->e_message =
157490792Sgshapiro						    sm_rpool_strdup_x(
157590792Sgshapiro							CurEnv->e_rpool, ebuf);
157638032Speter					}
157773188Sgshapiro					SM_SET_H_ERRNO(NO_RECOVERY);
157838032Speter					*statp = EX_CONFIG;
1579363466Sgshapiro					goto error;
158038032Speter				}
158138032Speter
158238032Speter				/* value points at name */
158390792Sgshapiro				if ((ret = dn_expand((unsigned char *)&answer,
158490792Sgshapiro						     eom, ap, (RES_UNC_T) nbuf,
158590792Sgshapiro						     sizeof(nbuf))) < 0)
158638032Speter					break;
158790792Sgshapiro				(void) sm_strlcpy(host, nbuf, hbsize);
158838032Speter
158938032Speter				/*
159038032Speter				**  RFC 1034 section 3.6 specifies that CNAME
159138032Speter				**  should point at the canonical name -- but
159238032Speter				**  urges software to try again anyway.
159338032Speter				*/
159438032Speter
159538032Speter				goto cnameloop;
159638032Speter
159738032Speter			  default:
159838032Speter				/* not a record of interest */
159938032Speter				continue;
160038032Speter			}
160138032Speter		}
160238032Speter
160338032Speter		if (amatch)
160438032Speter		{
160564562Sgshapiro			/*
160638032Speter			**  Got a good match -- either an A, CNAME, or an
160738032Speter			**  exact MX record.  Save it and get out of here.
160838032Speter			*/
160938032Speter
1610363466Sgshapiro			mxmatch = dp;
161138032Speter			break;
161238032Speter		}
161338032Speter
161438032Speter		/*
161538032Speter		**  Nothing definitive yet.
161638032Speter		**	If this was a T_A query and we haven't yet found a MX
161738032Speter		**		match, try T_MX if allowed to do so.
161838032Speter		**	Otherwise, try the next domain.
161938032Speter		*/
162038032Speter
162164562Sgshapiro# if NETINET6
162290792Sgshapiro		if (qtype == T_AAAA)
162338032Speter			qtype = T_A;
162490792Sgshapiro		else
1625363466Sgshapiro# endif
1626363466Sgshapiro		if (qtype == T_A && !gotmx && (trymx || *dp == '\0'))
162738032Speter			qtype = T_MX;
162838032Speter		else
162938032Speter		{
1630120256Sgshapiro			qtype = initial;
1631363466Sgshapiro			sli++;
163238032Speter		}
163338032Speter	}
163438032Speter
163538032Speter	/* if nothing was found, we are done */
163638032Speter	if (mxmatch == NULL)
163738032Speter	{
163871345Sgshapiro		if (*statp == EX_OK)
163971345Sgshapiro			*statp = EX_NOHOST;
1640363466Sgshapiro		goto error;
164138032Speter	}
164238032Speter
164338032Speter	/*
164438032Speter	**  Create canonical name and return.
164538032Speter	**  If saved domain name is null, name was already canonical.
164638032Speter	**  Otherwise append the saved domain name.
164738032Speter	*/
164838032Speter
1649168515Sgshapiro	(void) sm_snprintf(nbuf, sizeof(nbuf), "%.*s%s%.*s", MAXDNAME, host,
165090792Sgshapiro			   *mxmatch == '\0' ? "" : ".",
165190792Sgshapiro			   MAXDNAME, mxmatch);
165290792Sgshapiro	(void) sm_strlcpy(host, nbuf, hbsize);
165338032Speter	if (tTd(8, 5))
165490792Sgshapiro		sm_dprintf("dns_getcanonname: %s\n", host);
165538032Speter	*statp = EX_OK;
165690792Sgshapiro
165790792Sgshapiro	/* return only one TTL entry, that should be sufficient */
165890792Sgshapiro	if (ttl > 0 && pttl != NULL)
165990792Sgshapiro		*pttl = ttl;
1660363466Sgshapiro# if DANE
1661363466Sgshapiro	_res.options = old_options;
1662363466Sgshapiro# endif
1663363466Sgshapiro	return ad ? HOST_SECURE : HOST_OK;
1664363466Sgshapiro
1665363466Sgshapiro  error:
1666363466Sgshapiro# if DANE
1667363466Sgshapiro	_res.options = old_options;
1668363466Sgshapiro# endif
1669363466Sgshapiro	return HOST_NOTFOUND;
167038032Speter}
1671363466Sgshapiro
167238032Speter#endif /* NAMED_BIND */
1673