138032Speter/*
2223067Sgshapiro * Copyright (c) 1998-2004, 2006, 2010 Sendmail, 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"
1638032Speter
1790792Sgshapiro#if NAMED_BIND
18223067SgshapiroSM_RCSID("@(#)$Id: domain.c,v 8.204 2010/06/29 15:35:33 ca Exp $ (with name server)")
1990792Sgshapiro#else /* NAMED_BIND */
20223067SgshapiroSM_RCSID("@(#)$Id: domain.c,v 8.204 2010/06/29 15:35:33 ca Exp $ (without name server)")
2190792Sgshapiro#endif /* NAMED_BIND */
2238032Speter
2338032Speter#if NAMED_BIND
2438032Speter
2564562Sgshapiro# include <arpa/inet.h>
2638032Speter
2790792Sgshapiro
2864562Sgshapiro# ifndef MXHOSTBUFSIZE
2964562Sgshapiro#  define MXHOSTBUFSIZE	(128 * MAXMXHOSTS)
3064562Sgshapiro# endif /* ! MXHOSTBUFSIZE */
3138032Speter
3238032Speterstatic char	MXHostBuf[MXHOSTBUFSIZE];
3390792Sgshapiro#if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2)
3490792Sgshapiro	ERROR: _MXHOSTBUFSIZE is out of range
3590792Sgshapiro#endif /* (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) */
3638032Speter
3764562Sgshapiro# ifndef MAXDNSRCH
3864562Sgshapiro#  define MAXDNSRCH	6	/* number of possible domains to search */
3964562Sgshapiro# endif /* ! MAXDNSRCH */
4038032Speter
4164562Sgshapiro# ifndef RES_DNSRCH_VARIABLE
4264562Sgshapiro#  define RES_DNSRCH_VARIABLE	_res.dnsrch
4364562Sgshapiro# endif /* ! RES_DNSRCH_VARIABLE */
4438032Speter
4564562Sgshapiro# ifndef NO_DATA
4664562Sgshapiro#  define NO_DATA	NO_ADDRESS
4764562Sgshapiro# endif /* ! NO_DATA */
4838032Speter
4964562Sgshapiro# ifndef HFIXEDSZ
5064562Sgshapiro#  define HFIXEDSZ	12	/* sizeof(HEADER) */
5164562Sgshapiro# endif /* ! HFIXEDSZ */
5238032Speter
5364562Sgshapiro# define MAXCNAMEDEPTH	10	/* maximum depth of CNAME recursion */
5464562Sgshapiro
5564562Sgshapiro# if defined(__RES) && (__RES >= 19940415)
5664562Sgshapiro#  define RES_UNC_T	char *
5764562Sgshapiro# else /* defined(__RES) && (__RES >= 19940415) */
5890792Sgshapiro#  define RES_UNC_T	unsigned char *
5964562Sgshapiro# endif /* defined(__RES) && (__RES >= 19940415) */
6064562Sgshapiro
6164562Sgshapirostatic int	mxrand __P((char *));
6290792Sgshapirostatic int	fallbackmxrr __P((int, unsigned short *, char **));
6364562Sgshapiro
6490792Sgshapiro/*
6590792Sgshapiro**  GETFALLBACKMXRR -- get MX resource records for fallback MX host.
6690792Sgshapiro**
6790792Sgshapiro**	We have to initialize this once before doing anything else.
6890792Sgshapiro**	Moreover, we have to repeat this from time to time to avoid
6990792Sgshapiro**	stale data, e.g., in persistent queue runners.
7090792Sgshapiro**	This should be done in a parent process so the child
7190792Sgshapiro**	processes have the right data.
7290792Sgshapiro**
7390792Sgshapiro**	Parameters:
7490792Sgshapiro**		host -- the name of the fallback MX host.
7590792Sgshapiro**
7690792Sgshapiro**	Returns:
7790792Sgshapiro**		number of MX records.
7890792Sgshapiro**
7990792Sgshapiro**	Side Effects:
80132943Sgshapiro**		Populates NumFallbackMXHosts and fbhosts.
8190792Sgshapiro**		Sets renewal time (based on TTL).
8290792Sgshapiro*/
8390792Sgshapiro
84132943Sgshapiroint NumFallbackMXHosts = 0;	/* Number of fallback MX hosts (after MX expansion) */
8590792Sgshapirostatic char *fbhosts[MAXMXHOSTS + 1];
8690792Sgshapiro
8790792Sgshapiroint
8890792Sgshapirogetfallbackmxrr(host)
8990792Sgshapiro	char *host;
9090792Sgshapiro{
9190792Sgshapiro	int i, rcode;
9290792Sgshapiro	int ttl;
9390792Sgshapiro	static time_t renew = 0;
9490792Sgshapiro
9590792Sgshapiro#if 0
9690792Sgshapiro	/* This is currently done before this function is called. */
9790792Sgshapiro	if (host == NULL || *host == '\0')
9890792Sgshapiro		return 0;
9990792Sgshapiro#endif /* 0 */
100132943Sgshapiro	if (NumFallbackMXHosts > 0 && renew > curtime())
101132943Sgshapiro		return NumFallbackMXHosts;
10290792Sgshapiro	if (host[0] == '[')
10390792Sgshapiro	{
10490792Sgshapiro		fbhosts[0] = host;
105132943Sgshapiro		NumFallbackMXHosts = 1;
10690792Sgshapiro	}
10790792Sgshapiro	else
10890792Sgshapiro	{
10990792Sgshapiro		/* free old data */
110132943Sgshapiro		for (i = 0; i < NumFallbackMXHosts; i++)
11190792Sgshapiro			sm_free(fbhosts[i]);
11290792Sgshapiro
11390792Sgshapiro		/* get new data */
114132943Sgshapiro		NumFallbackMXHosts = getmxrr(host, fbhosts, NULL, false,
11590792Sgshapiro					     &rcode, false, &ttl);
11690792Sgshapiro		renew = curtime() + ttl;
117132943Sgshapiro		for (i = 0; i < NumFallbackMXHosts; i++)
11890792Sgshapiro			fbhosts[i] = newstr(fbhosts[i]);
11990792Sgshapiro	}
120132943Sgshapiro	return NumFallbackMXHosts;
12190792Sgshapiro}
12290792Sgshapiro
12390792Sgshapiro/*
12490792Sgshapiro**  FALLBACKMXRR -- add MX resource records for fallback MX host to list.
12590792Sgshapiro**
12690792Sgshapiro**	Parameters:
12790792Sgshapiro**		nmx -- current number of MX records.
12890792Sgshapiro**		prefs -- array of preferences.
12990792Sgshapiro**		mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS)
13090792Sgshapiro**
13190792Sgshapiro**	Returns:
13290792Sgshapiro**		new number of MX records.
13390792Sgshapiro**
13490792Sgshapiro**	Side Effects:
135132943Sgshapiro**		If FallbackMX was set, it appends the MX records for
13690792Sgshapiro**		that host to mxhosts (and modifies prefs accordingly).
13790792Sgshapiro*/
13890792Sgshapiro
13990792Sgshapirostatic int
14090792Sgshapirofallbackmxrr(nmx, prefs, mxhosts)
14190792Sgshapiro	int nmx;
14290792Sgshapiro	unsigned short *prefs;
14390792Sgshapiro	char **mxhosts;
14490792Sgshapiro{
14590792Sgshapiro	int i;
14690792Sgshapiro
147132943Sgshapiro	for (i = 0; i < NumFallbackMXHosts && nmx < MAXMXHOSTS; i++)
14890792Sgshapiro	{
14990792Sgshapiro		if (nmx > 0)
15090792Sgshapiro			prefs[nmx] = prefs[nmx - 1] + 1;
15190792Sgshapiro		else
15290792Sgshapiro			prefs[nmx] = 0;
15390792Sgshapiro		mxhosts[nmx++] = fbhosts[i];
15490792Sgshapiro	}
15590792Sgshapiro	return nmx;
15690792Sgshapiro}
15790792Sgshapiro
15890792Sgshapiro/*
15938032Speter**  GETMXRR -- get MX resource records for a domain
16038032Speter**
16138032Speter**	Parameters:
16238032Speter**		host -- the name of the host to MX.
16338032Speter**		mxhosts -- a pointer to a return buffer of MX records.
16464562Sgshapiro**		mxprefs -- a pointer to a return buffer of MX preferences.
16564562Sgshapiro**			If NULL, don't try to populate.
16690792Sgshapiro**		droplocalhost -- If true, all MX records less preferred
16738032Speter**			than the local host (as determined by $=w) will
16838032Speter**			be discarded.
16938032Speter**		rcode -- a pointer to an EX_ status code.
17090792Sgshapiro**		tryfallback -- add also fallback MX host?
17190792Sgshapiro**		pttl -- pointer to return TTL (can be NULL).
17238032Speter**
17338032Speter**	Returns:
17438032Speter**		The number of MX records found.
17538032Speter**		-1 if there is an internal failure.
17638032Speter**		If no MX records are found, mxhosts[0] is set to host
17738032Speter**			and 1 is returned.
17890792Sgshapiro**
17990792Sgshapiro**	Side Effects:
18090792Sgshapiro**		The entries made for mxhosts point to a static array
18190792Sgshapiro**		MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied,
18290792Sgshapiro**		if it must be preserved across calls to this function.
18338032Speter*/
18438032Speter
18538032Speterint
18690792Sgshapirogetmxrr(host, mxhosts, mxprefs, droplocalhost, rcode, tryfallback, pttl)
18738032Speter	char *host;
18838032Speter	char **mxhosts;
18990792Sgshapiro	unsigned short *mxprefs;
19038032Speter	bool droplocalhost;
19138032Speter	int *rcode;
19290792Sgshapiro	bool tryfallback;
19390792Sgshapiro	int *pttl;
19438032Speter{
19590792Sgshapiro	register unsigned char *eom, *cp;
19638032Speter	register int i, j, n;
19738032Speter	int nmx = 0;
19838032Speter	register char *bp;
19938032Speter	HEADER *hp;
20038032Speter	querybuf answer;
20138032Speter	int ancount, qdcount, buflen;
20290792Sgshapiro	bool seenlocal = false;
20390792Sgshapiro	unsigned short pref, type;
20490792Sgshapiro	unsigned short localpref = 256;
205132943Sgshapiro	char *fallbackMX = FallbackMX;
20690792Sgshapiro	bool trycanon = false;
20790792Sgshapiro	unsigned short *prefs;
208141858Sgshapiro	int (*resfunc) __P((const char *, int, int, u_char *, int));
20990792Sgshapiro	unsigned short prefer[MAXMXHOSTS];
21038032Speter	int weight[MAXMXHOSTS];
21190792Sgshapiro	int ttl = 0;
21264562Sgshapiro	extern int res_query(), res_search();
21338032Speter
21438032Speter	if (tTd(8, 2))
21590792Sgshapiro		sm_dprintf("getmxrr(%s, droplocalhost=%d)\n",
21690792Sgshapiro			   host, droplocalhost);
217147078Sgshapiro	*rcode = EX_OK;
218147078Sgshapiro	if (pttl != NULL)
219147078Sgshapiro		*pttl = SM_DEFAULT_TTL;
220120256Sgshapiro	if (*host == '\0')
221120256Sgshapiro		return 0;
22238032Speter
22390792Sgshapiro	if ((fallbackMX != NULL && droplocalhost &&
22490792Sgshapiro	     wordinclass(fallbackMX, 'w')) || !tryfallback)
22538032Speter	{
22638032Speter		/* don't use fallback for this pass */
22738032Speter		fallbackMX = NULL;
22838032Speter	}
22938032Speter
23064562Sgshapiro	if (mxprefs != NULL)
23164562Sgshapiro		prefs = mxprefs;
23264562Sgshapiro	else
23364562Sgshapiro		prefs = prefer;
23464562Sgshapiro
23538032Speter	/* efficiency hack -- numeric or non-MX lookups */
23638032Speter	if (host[0] == '[')
23738032Speter		goto punt;
23838032Speter
23938032Speter	/*
24038032Speter	**  If we don't have MX records in our host switch, don't
24138032Speter	**  try for MX records.  Note that this really isn't "right",
24238032Speter	**  since we might be set up to try NIS first and then DNS;
24338032Speter	**  if the host is found in NIS we really shouldn't be doing
24438032Speter	**  MX lookups.  However, that should be a degenerate case.
24538032Speter	*/
24638032Speter
24738032Speter	if (!UseNameServer)
24838032Speter		goto punt;
24938032Speter	if (HasWildcardMX && ConfigLevel >= 6)
25038032Speter		resfunc = res_query;
25138032Speter	else
25238032Speter		resfunc = res_search;
25338032Speter
25438032Speter	errno = 0;
25590792Sgshapiro	n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer,
25690792Sgshapiro		       sizeof(answer));
25738032Speter	if (n < 0)
25838032Speter	{
25938032Speter		if (tTd(8, 1))
26090792Sgshapiro			sm_dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
261168515Sgshapiro				host, errno, h_errno);
26238032Speter		switch (h_errno)
26338032Speter		{
26438032Speter		  case NO_DATA:
26590792Sgshapiro			trycanon = true;
26664562Sgshapiro			/* FALLTHROUGH */
26738032Speter
26838032Speter		  case NO_RECOVERY:
26938032Speter			/* no MX data on this host */
27038032Speter			goto punt;
27138032Speter
27238032Speter		  case HOST_NOT_FOUND:
27364562Sgshapiro# if BROKEN_RES_SEARCH
27438032Speter		  case 0:	/* Ultrix resolver retns failure w/ h_errno=0 */
27564562Sgshapiro# endif /* BROKEN_RES_SEARCH */
27638032Speter			/* host doesn't exist in DNS; might be in /etc/hosts */
27790792Sgshapiro			trycanon = true;
27838032Speter			*rcode = EX_NOHOST;
27938032Speter			goto punt;
28038032Speter
28138032Speter		  case TRY_AGAIN:
28238032Speter		  case -1:
28338032Speter			/* couldn't connect to the name server */
28438032Speter			if (fallbackMX != NULL)
28538032Speter			{
28638032Speter				/* name server is hosed -- push to fallback */
28790792Sgshapiro				return fallbackmxrr(nmx, prefs, mxhosts);
28838032Speter			}
28938032Speter			/* it might come up later; better queue it up */
29038032Speter			*rcode = EX_TEMPFAIL;
29138032Speter			break;
29238032Speter
29338032Speter		  default:
29490792Sgshapiro			syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)",
29538032Speter				host, h_errno);
29638032Speter			*rcode = EX_OSERR;
29738032Speter			break;
29838032Speter		}
29938032Speter
30038032Speter		/* irreconcilable differences */
30164562Sgshapiro		return -1;
30238032Speter	}
30338032Speter
30438032Speter	/* avoid problems after truncation in tcp packets */
30538032Speter	if (n > sizeof(answer))
30638032Speter		n = sizeof(answer);
30738032Speter
30838032Speter	/* find first satisfactory answer */
30938032Speter	hp = (HEADER *)&answer;
31090792Sgshapiro	cp = (unsigned char *)&answer + HFIXEDSZ;
31190792Sgshapiro	eom = (unsigned char *)&answer + n;
31290792Sgshapiro	for (qdcount = ntohs((unsigned short) hp->qdcount);
31364562Sgshapiro	     qdcount--;
31464562Sgshapiro	     cp += n + QFIXEDSZ)
31564562Sgshapiro	{
31638032Speter		if ((n = dn_skipname(cp, eom)) < 0)
31738032Speter			goto punt;
31864562Sgshapiro	}
31990792Sgshapiro
32090792Sgshapiro	/* NOTE: see definition of MXHostBuf! */
32138032Speter	buflen = sizeof(MXHostBuf) - 1;
32290792Sgshapiro	SM_ASSERT(buflen > 0);
32338032Speter	bp = MXHostBuf;
32490792Sgshapiro	ancount = ntohs((unsigned short) hp->ancount);
32590792Sgshapiro
32690792Sgshapiro	/* See RFC 1035 for layout of RRs. */
327132943Sgshapiro	/* XXX leave room for FallbackMX ? */
32838032Speter	while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
32938032Speter	{
33090792Sgshapiro		if ((n = dn_expand((unsigned char *)&answer, eom, cp,
33190792Sgshapiro				   (RES_UNC_T) bp, buflen)) < 0)
33238032Speter			break;
33338032Speter		cp += n;
33438032Speter		GETSHORT(type, cp);
33590792Sgshapiro		cp += INT16SZ;		/* skip over class */
33690792Sgshapiro		GETLONG(ttl, cp);
33790792Sgshapiro		GETSHORT(n, cp);	/* rdlength */
33838032Speter		if (type != T_MX)
33938032Speter		{
34038032Speter			if (tTd(8, 8) || _res.options & RES_DEBUG)
34190792Sgshapiro				sm_dprintf("unexpected answer type %d, size %d\n",
34264562Sgshapiro					type, n);
34338032Speter			cp += n;
34438032Speter			continue;
34538032Speter		}
34638032Speter		GETSHORT(pref, cp);
34790792Sgshapiro		if ((n = dn_expand((unsigned char *)&answer, eom, cp,
34838032Speter				   (RES_UNC_T) bp, buflen)) < 0)
34938032Speter			break;
35038032Speter		cp += n;
35190792Sgshapiro		n = strlen(bp);
35290792Sgshapiro# if 0
35390792Sgshapiro		/* Can this happen? */
35490792Sgshapiro		if (n == 0)
35590792Sgshapiro		{
35690792Sgshapiro			if (LogLevel > 4)
35790792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
35890792Sgshapiro					  "MX records for %s contain empty string",
35990792Sgshapiro					  host);
36090792Sgshapiro			continue;
36190792Sgshapiro		}
36290792Sgshapiro# endif /* 0 */
36338032Speter		if (wordinclass(bp, 'w'))
36438032Speter		{
36538032Speter			if (tTd(8, 3))
36690792Sgshapiro				sm_dprintf("found localhost (%s) in MX list, pref=%d\n",
36738032Speter					bp, pref);
36838032Speter			if (droplocalhost)
36938032Speter			{
37038032Speter				if (!seenlocal || pref < localpref)
37138032Speter					localpref = pref;
37290792Sgshapiro				seenlocal = true;
37338032Speter				continue;
37438032Speter			}
37538032Speter			weight[nmx] = 0;
37638032Speter		}
37738032Speter		else
37838032Speter			weight[nmx] = mxrand(bp);
37964562Sgshapiro		prefs[nmx] = pref;
38038032Speter		mxhosts[nmx++] = bp;
38138032Speter		bp += n;
38238032Speter		if (bp[-1] != '.')
38338032Speter		{
38438032Speter			*bp++ = '.';
38538032Speter			n++;
38638032Speter		}
38738032Speter		*bp++ = '\0';
38890792Sgshapiro		if (buflen < n + 1)
38990792Sgshapiro		{
39090792Sgshapiro			/* don't want to wrap buflen */
39190792Sgshapiro			break;
39290792Sgshapiro		}
39338032Speter		buflen -= n + 1;
39438032Speter	}
39538032Speter
39690792Sgshapiro	/* return only one TTL entry, that should be sufficient */
39790792Sgshapiro	if (ttl > 0 && pttl != NULL)
39890792Sgshapiro		*pttl = ttl;
39990792Sgshapiro
40038032Speter	/* sort the records */
40138032Speter	for (i = 0; i < nmx; i++)
40238032Speter	{
40338032Speter		for (j = i + 1; j < nmx; j++)
40438032Speter		{
40564562Sgshapiro			if (prefs[i] > prefs[j] ||
40664562Sgshapiro			    (prefs[i] == prefs[j] && weight[i] > weight[j]))
40738032Speter			{
40838032Speter				register int temp;
40938032Speter				register char *temp1;
41038032Speter
41164562Sgshapiro				temp = prefs[i];
41264562Sgshapiro				prefs[i] = prefs[j];
41364562Sgshapiro				prefs[j] = temp;
41438032Speter				temp1 = mxhosts[i];
41538032Speter				mxhosts[i] = mxhosts[j];
41638032Speter				mxhosts[j] = temp1;
41738032Speter				temp = weight[i];
41838032Speter				weight[i] = weight[j];
41938032Speter				weight[j] = temp;
42038032Speter			}
42138032Speter		}
42264562Sgshapiro		if (seenlocal && prefs[i] >= localpref)
42338032Speter		{
42438032Speter			/* truncate higher preference part of list */
42538032Speter			nmx = i;
42638032Speter		}
42738032Speter	}
42838032Speter
42938032Speter	/* delete duplicates from list (yes, some bozos have duplicates) */
43038032Speter	for (i = 0; i < nmx - 1; )
43138032Speter	{
43290792Sgshapiro		if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0)
43338032Speter			i++;
43438032Speter		else
43538032Speter		{
43638032Speter			/* compress out duplicate */
43738032Speter			for (j = i + 1; j < nmx; j++)
43864562Sgshapiro			{
43938032Speter				mxhosts[j] = mxhosts[j + 1];
44064562Sgshapiro				prefs[j] = prefs[j + 1];
44164562Sgshapiro			}
44238032Speter			nmx--;
44338032Speter		}
44438032Speter	}
44538032Speter
44638032Speter	if (nmx == 0)
44738032Speter	{
44838032Speterpunt:
44964562Sgshapiro		if (seenlocal)
45038032Speter		{
45164562Sgshapiro			struct hostent *h = NULL;
45264562Sgshapiro
45338032Speter			/*
45438032Speter			**  If we have deleted all MX entries, this is
45538032Speter			**  an error -- we should NEVER send to a host that
45638032Speter			**  has an MX, and this should have been caught
45738032Speter			**  earlier in the config file.
45838032Speter			**
45938032Speter			**  Some sites prefer to go ahead and try the
46038032Speter			**  A record anyway; that case is handled by
46138032Speter			**  setting TryNullMXList.  I believe this is a
46238032Speter			**  bad idea, but it's up to you....
46338032Speter			*/
46438032Speter
46564562Sgshapiro			if (TryNullMXList)
46664562Sgshapiro			{
46773188Sgshapiro				SM_SET_H_ERRNO(0);
46864562Sgshapiro				errno = 0;
46964562Sgshapiro				h = sm_gethostbyname(host, AF_INET);
47064562Sgshapiro				if (h == NULL)
47164562Sgshapiro				{
47264562Sgshapiro					if (errno == ETIMEDOUT ||
47364562Sgshapiro					    h_errno == TRY_AGAIN ||
47464562Sgshapiro					    (errno == ECONNREFUSED &&
47564562Sgshapiro					     UseNameServer))
47664562Sgshapiro					{
47764562Sgshapiro						*rcode = EX_TEMPFAIL;
47864562Sgshapiro						return -1;
47964562Sgshapiro					}
48064562Sgshapiro# if NETINET6
48173188Sgshapiro					SM_SET_H_ERRNO(0);
48264562Sgshapiro					errno = 0;
48364562Sgshapiro					h = sm_gethostbyname(host, AF_INET6);
48464562Sgshapiro					if (h == NULL &&
48564562Sgshapiro					    (errno == ETIMEDOUT ||
48664562Sgshapiro					     h_errno == TRY_AGAIN ||
48764562Sgshapiro					     (errno == ECONNREFUSED &&
48864562Sgshapiro					      UseNameServer)))
48964562Sgshapiro					{
49064562Sgshapiro						*rcode = EX_TEMPFAIL;
49164562Sgshapiro						return -1;
49264562Sgshapiro					}
49364562Sgshapiro# endif /* NETINET6 */
49464562Sgshapiro				}
49564562Sgshapiro			}
49664562Sgshapiro
49764562Sgshapiro			if (h == NULL)
49864562Sgshapiro			{
49964562Sgshapiro				*rcode = EX_CONFIG;
50064562Sgshapiro				syserr("MX list for %s points back to %s",
50164562Sgshapiro				       host, MyHostName);
50264562Sgshapiro				return -1;
50364562Sgshapiro			}
50490792Sgshapiro# if NETINET6
50571345Sgshapiro			freehostent(h);
506159609Sgshapiro			h = NULL;
50790792Sgshapiro# endif /* NETINET6 */
50838032Speter		}
509168515Sgshapiro		if (strlen(host) >= sizeof(MXHostBuf))
51038032Speter		{
51138032Speter			*rcode = EX_CONFIG;
51238032Speter			syserr("Host name %s too long",
51338032Speter			       shortenstring(host, MAXSHORTSTR));
51438032Speter			return -1;
51538032Speter		}
516168515Sgshapiro		(void) sm_strlcpy(MXHostBuf, host, sizeof(MXHostBuf));
51738032Speter		mxhosts[0] = MXHostBuf;
51864562Sgshapiro		prefs[0] = 0;
51938032Speter		if (host[0] == '[')
52038032Speter		{
52138032Speter			register char *p;
52264562Sgshapiro# if NETINET6
52364562Sgshapiro			struct sockaddr_in6 tmp6;
52464562Sgshapiro# endif /* NETINET6 */
52538032Speter
52638032Speter			/* this may be an MX suppression-style address */
52738032Speter			p = strchr(MXHostBuf, ']');
52838032Speter			if (p != NULL)
52938032Speter			{
53038032Speter				*p = '\0';
53164562Sgshapiro
53238032Speter				if (inet_addr(&MXHostBuf[1]) != INADDR_NONE)
53338032Speter				{
53438032Speter					nmx++;
53538032Speter					*p = ']';
53638032Speter				}
53764562Sgshapiro# if NETINET6
53890792Sgshapiro				else if (anynet_pton(AF_INET6, &MXHostBuf[1],
53990792Sgshapiro						     &tmp6.sin6_addr) == 1)
54064562Sgshapiro				{
54164562Sgshapiro					nmx++;
54264562Sgshapiro					*p = ']';
54364562Sgshapiro				}
54464562Sgshapiro# endif /* NETINET6 */
54538032Speter				else
54638032Speter				{
54790792Sgshapiro					trycanon = true;
54838032Speter					mxhosts[0]++;
54938032Speter				}
55038032Speter			}
55138032Speter		}
55238032Speter		if (trycanon &&
553168515Sgshapiro		    getcanonname(mxhosts[0], sizeof(MXHostBuf) - 2, false, pttl))
55438032Speter		{
55590792Sgshapiro			/* XXX MXHostBuf == "" ?  is that possible? */
55638032Speter			bp = &MXHostBuf[strlen(MXHostBuf)];
55738032Speter			if (bp[-1] != '.')
55838032Speter			{
55938032Speter				*bp++ = '.';
56038032Speter				*bp = '\0';
56138032Speter			}
56238032Speter			nmx = 1;
56338032Speter		}
56438032Speter	}
56538032Speter
56638032Speter	/* if we have a default lowest preference, include that */
56738032Speter	if (fallbackMX != NULL && !seenlocal)
56864562Sgshapiro	{
56990792Sgshapiro		nmx = fallbackmxrr(nmx, prefs, mxhosts);
57064562Sgshapiro	}
57164562Sgshapiro	return nmx;
57238032Speter}
57390792Sgshapiro/*
57438032Speter**  MXRAND -- create a randomizer for equal MX preferences
57538032Speter**
57638032Speter**	If two MX hosts have equal preferences we want to randomize
57738032Speter**	the selection.  But in order for signatures to be the same,
57838032Speter**	we need to randomize the same way each time.  This function
57938032Speter**	computes a pseudo-random hash function from the host name.
58038032Speter**
58138032Speter**	Parameters:
58238032Speter**		host -- the name of the host.
58338032Speter**
58438032Speter**	Returns:
58538032Speter**		A random but repeatable value based on the host name.
58638032Speter*/
58738032Speter
58864562Sgshapirostatic int
58938032Spetermxrand(host)
59038032Speter	register char *host;
59138032Speter{
59238032Speter	int hfunc;
59338032Speter	static unsigned int seed;
59438032Speter
59538032Speter	if (seed == 0)
59638032Speter	{
59738032Speter		seed = (int) curtime() & 0xffff;
59838032Speter		if (seed == 0)
59938032Speter			seed++;
60038032Speter	}
60138032Speter
60238032Speter	if (tTd(17, 9))
60390792Sgshapiro		sm_dprintf("mxrand(%s)", host);
60438032Speter
60538032Speter	hfunc = seed;
60638032Speter	while (*host != '\0')
60738032Speter	{
60838032Speter		int c = *host++;
60938032Speter
61038032Speter		if (isascii(c) && isupper(c))
61138032Speter			c = tolower(c);
61238032Speter		hfunc = ((hfunc << 1) ^ c) % 2003;
61338032Speter	}
61438032Speter
61538032Speter	hfunc &= 0xff;
61638032Speter	hfunc++;
61738032Speter
61838032Speter	if (tTd(17, 9))
61990792Sgshapiro		sm_dprintf(" = %d\n", hfunc);
62038032Speter	return hfunc;
62138032Speter}
62290792Sgshapiro/*
62338032Speter**  BESTMX -- find the best MX for a name
62438032Speter**
62538032Speter**	This is really a hack, but I don't see any obvious way
62638032Speter**	to generalize it at the moment.
62738032Speter*/
62838032Speter
62938032Speter/* ARGSUSED3 */
63038032Speterchar *
63138032Speterbestmx_map_lookup(map, name, av, statp)
63238032Speter	MAP *map;
63338032Speter	char *name;
63438032Speter	char **av;
63538032Speter	int *statp;
63638032Speter{
63738032Speter	int nmx;
63838032Speter	int saveopts = _res.options;
63990792Sgshapiro	int i;
64090792Sgshapiro	ssize_t len = 0;
64190792Sgshapiro	char *result;
64290792Sgshapiro	char *mxhosts[MAXMXHOSTS + 1];
64390792Sgshapiro#if _FFR_BESTMX_BETTER_TRUNCATION
64490792Sgshapiro	char *buf;
64590792Sgshapiro#else /* _FFR_BESTMX_BETTER_TRUNCATION */
64638032Speter	char *p;
64742575Speter	char buf[PSBUFSIZE / 2];
64890792Sgshapiro#endif /* _FFR_BESTMX_BETTER_TRUNCATION */
64938032Speter
65038032Speter	_res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
651102528Sgshapiro	nmx = getmxrr(name, mxhosts, NULL, false, statp, false, NULL);
65238032Speter	_res.options = saveopts;
65338032Speter	if (nmx <= 0)
65438032Speter		return NULL;
65538032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
65638032Speter		return map_rewrite(map, name, strlen(name), NULL);
65738032Speter	if ((map->map_coldelim == '\0') || (nmx == 1))
65838032Speter		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
65938032Speter
66038032Speter	/*
66142575Speter	**  We were given a -z flag (return all MXs) and there are multiple
66242575Speter	**  ones.  We need to build them all into a list.
66338032Speter	*/
66490792Sgshapiro
66590792Sgshapiro#if _FFR_BESTMX_BETTER_TRUNCATION
66690792Sgshapiro	for (i = 0; i < nmx; i++)
66790792Sgshapiro	{
66890792Sgshapiro		if (strchr(mxhosts[i], map->map_coldelim) != NULL)
66990792Sgshapiro		{
67090792Sgshapiro			syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
67190792Sgshapiro			       mxhosts[i], map->map_coldelim);
67290792Sgshapiro			return NULL;
67390792Sgshapiro		}
67490792Sgshapiro		len += strlen(mxhosts[i]) + 1;
67590792Sgshapiro		if (len < 0)
67690792Sgshapiro		{
67790792Sgshapiro			len -= strlen(mxhosts[i]) + 1;
67890792Sgshapiro			break;
67990792Sgshapiro		}
68090792Sgshapiro	}
68190792Sgshapiro	buf = (char *) sm_malloc(len);
68290792Sgshapiro	if (buf == NULL)
68390792Sgshapiro	{
68490792Sgshapiro		*statp = EX_UNAVAILABLE;
68590792Sgshapiro		return NULL;
68690792Sgshapiro	}
68790792Sgshapiro	*buf = '\0';
68890792Sgshapiro	for (i = 0; i < nmx; i++)
68990792Sgshapiro	{
69090792Sgshapiro		int end;
69190792Sgshapiro
69290792Sgshapiro		end = sm_strlcat(buf, mxhosts[i], len);
69390792Sgshapiro		if (i != nmx && end + 1 < len)
69490792Sgshapiro		{
69590792Sgshapiro			buf[end] = map->map_coldelim;
69690792Sgshapiro			buf[end + 1] = '\0';
69790792Sgshapiro		}
69890792Sgshapiro	}
69990792Sgshapiro
70090792Sgshapiro	/* Cleanly truncate for rulesets */
70190792Sgshapiro	truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim);
70290792Sgshapiro#else /* _FFR_BESTMX_BETTER_TRUNCATION */
70338032Speter	p = buf;
70438032Speter	for (i = 0; i < nmx; i++)
70538032Speter	{
70690792Sgshapiro		size_t slen;
70764562Sgshapiro
70838032Speter		if (strchr(mxhosts[i], map->map_coldelim) != NULL)
70938032Speter		{
71038032Speter			syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
71138032Speter			       mxhosts[i], map->map_coldelim);
71238032Speter			return NULL;
71338032Speter		}
71438032Speter		slen = strlen(mxhosts[i]);
715168515Sgshapiro		if (len + slen + 2 > sizeof(buf))
71638032Speter			break;
71738032Speter		if (i > 0)
71838032Speter		{
71938032Speter			*p++ = map->map_coldelim;
72038032Speter			len++;
72138032Speter		}
722168515Sgshapiro		(void) sm_strlcpy(p, mxhosts[i], sizeof(buf) - len);
72338032Speter		p += slen;
72438032Speter		len += slen;
72538032Speter	}
72690792Sgshapiro#endif /* _FFR_BESTMX_BETTER_TRUNCATION */
72790792Sgshapiro
72890792Sgshapiro	result = map_rewrite(map, buf, len, av);
72990792Sgshapiro#if _FFR_BESTMX_BETTER_TRUNCATION
73090792Sgshapiro	sm_free(buf);
73190792Sgshapiro#endif /* _FFR_BESTMX_BETTER_TRUNCATION */
73290792Sgshapiro	return result;
73338032Speter}
73490792Sgshapiro/*
73538032Speter**  DNS_GETCANONNAME -- get the canonical name for named host using DNS
73638032Speter**
73738032Speter**	This algorithm tries to be smart about wildcard MX records.
73838032Speter**	This is hard to do because DNS doesn't tell is if we matched
73938032Speter**	against a wildcard or a specific MX.
74064562Sgshapiro**
74138032Speter**	We always prefer A & CNAME records, since these are presumed
74238032Speter**	to be specific.
74338032Speter**
74438032Speter**	If we match an MX in one pass and lose it in the next, we use
74538032Speter**	the old one.  For example, consider an MX matching *.FOO.BAR.COM.
74638032Speter**	A hostname bletch.foo.bar.com will match against this MX, but
74738032Speter**	will stop matching when we try bletch.bar.com -- so we know
74838032Speter**	that bletch.foo.bar.com must have been right.  This fails if
74938032Speter**	there was also an MX record matching *.BAR.COM, but there are
75038032Speter**	some things that just can't be fixed.
75138032Speter**
75238032Speter**	Parameters:
75338032Speter**		host -- a buffer containing the name of the host.
75438032Speter**			This is a value-result parameter.
75538032Speter**		hbsize -- the size of the host buffer.
75638032Speter**		trymx -- if set, try MX records as well as A and CNAME.
75738032Speter**		statp -- pointer to place to store status.
75890792Sgshapiro**		pttl -- pointer to return TTL (can be NULL).
75938032Speter**
76038032Speter**	Returns:
76190792Sgshapiro**		true -- if the host matched.
76290792Sgshapiro**		false -- otherwise.
76338032Speter*/
76438032Speter
76538032Speterbool
76690792Sgshapirodns_getcanonname(host, hbsize, trymx, statp, pttl)
76738032Speter	char *host;
76838032Speter	int hbsize;
76938032Speter	bool trymx;
77038032Speter	int *statp;
77190792Sgshapiro	int *pttl;
77238032Speter{
77390792Sgshapiro	register unsigned char *eom, *ap;
77438032Speter	register char *cp;
77564562Sgshapiro	register int n;
77638032Speter	HEADER *hp;
77738032Speter	querybuf answer;
77838032Speter	int ancount, qdcount;
77938032Speter	int ret;
78038032Speter	char **domain;
78138032Speter	int type;
78290792Sgshapiro	int ttl = 0;
78338032Speter	char **dp;
78438032Speter	char *mxmatch;
78538032Speter	bool amatch;
78690792Sgshapiro	bool gotmx = false;
78738032Speter	int qtype;
788120256Sgshapiro	int initial;
78938032Speter	int loopcnt;
79090792Sgshapiro	char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)];
79198121Sgshapiro	char *searchlist[MAXDNSRCH + 2];
79238032Speter
79338032Speter	if (tTd(8, 2))
79490792Sgshapiro		sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
79538032Speter
79638032Speter	if ((_res.options & RES_INIT) == 0 && res_init() == -1)
79738032Speter	{
79838032Speter		*statp = EX_UNAVAILABLE;
79990792Sgshapiro		return false;
80038032Speter	}
80138032Speter
80271345Sgshapiro	*statp = EX_OK;
80371345Sgshapiro
80438032Speter	/*
80538032Speter	**  Initialize domain search list.  If there is at least one
80638032Speter	**  dot in the name, search the unmodified name first so we
80738032Speter	**  find "vse.CS" in Czechoslovakia instead of in the local
80864562Sgshapiro	**  domain (e.g., vse.CS.Berkeley.EDU).  Note that there is no
80964562Sgshapiro	**  longer a country named Czechoslovakia but this type of problem
81064562Sgshapiro	**  is still present.
81138032Speter	**
81238032Speter	**  Older versions of the resolver could create this
81338032Speter	**  list by tearing apart the host name.
81438032Speter	*/
81538032Speter
81638032Speter	loopcnt = 0;
81738032Spetercnameloop:
81838032Speter	/* Check for dots in the name */
81938032Speter	for (cp = host, n = 0; *cp != '\0'; cp++)
82038032Speter		if (*cp == '.')
82138032Speter			n++;
82238032Speter
82338032Speter	/*
82464562Sgshapiro	**  Build the search list.
82538032Speter	**	If there is at least one dot in name, start with a null
82638032Speter	**	domain to search the unmodified name first.
82738032Speter	**	If name does not end with a dot and search up local domain
82838032Speter	**	tree desired, append each local domain component to the
82938032Speter	**	search list; if name contains no dots and default domain
83038032Speter	**	name is desired, append default domain name to search list;
83138032Speter	**	else if name ends in a dot, remove that dot.
83238032Speter	*/
83338032Speter
83438032Speter	dp = searchlist;
83538032Speter	if (n > 0)
83638032Speter		*dp++ = "";
83738032Speter	if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options))
83838032Speter	{
83964562Sgshapiro		/* make sure there are less than MAXDNSRCH domains */
84064562Sgshapiro		for (domain = RES_DNSRCH_VARIABLE, ret = 0;
84164562Sgshapiro		     *domain != NULL && ret < MAXDNSRCH;
84264562Sgshapiro		     ret++)
84338032Speter			*dp++ = *domain++;
84438032Speter	}
84538032Speter	else if (n == 0 && bitset(RES_DEFNAMES, _res.options))
84638032Speter	{
84738032Speter		*dp++ = _res.defdname;
84838032Speter	}
84938032Speter	else if (*cp == '.')
85038032Speter	{
85138032Speter		*cp = '\0';
85238032Speter	}
85338032Speter	*dp = NULL;
85438032Speter
85538032Speter	/*
85638032Speter	**  Now loop through the search list, appending each domain in turn
85738032Speter	**  name and searching for a match.
85838032Speter	*/
85938032Speter
86038032Speter	mxmatch = NULL;
861120256Sgshapiro	initial = T_A;
862120256Sgshapiro# if NETINET6
863120256Sgshapiro	if (InetMode == AF_INET6)
864120256Sgshapiro		initial = T_AAAA;
865120256Sgshapiro# endif /* NETINET6 */
866120256Sgshapiro	qtype = initial;
86738032Speter
86838032Speter	for (dp = searchlist; *dp != NULL; )
86938032Speter	{
870120256Sgshapiro		if (qtype == initial)
87190792Sgshapiro			gotmx = false;
87238032Speter		if (tTd(8, 5))
87390792Sgshapiro			sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n",
87438032Speter				host, *dp,
87564562Sgshapiro# if NETINET6
87664562Sgshapiro				qtype == T_AAAA ? "AAAA" :
87764562Sgshapiro# endif /* NETINET6 */
87864562Sgshapiro				qtype == T_A ? "A" :
87964562Sgshapiro				qtype == T_MX ? "MX" :
88064562Sgshapiro				"???");
88171345Sgshapiro		errno = 0;
88238032Speter		ret = res_querydomain(host, *dp, C_IN, qtype,
88338032Speter				      answer.qb2, sizeof(answer.qb2));
88438032Speter		if (ret <= 0)
88538032Speter		{
88690792Sgshapiro			int save_errno = errno;
88790792Sgshapiro
88838032Speter			if (tTd(8, 7))
88990792Sgshapiro				sm_dprintf("\tNO: errno=%d, h_errno=%d\n",
89090792Sgshapiro					   save_errno, h_errno);
89138032Speter
89290792Sgshapiro			if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN)
89338032Speter			{
89471345Sgshapiro				/*
89590792Sgshapiro				**  the name server seems to be down or broken.
89671345Sgshapiro				*/
89771345Sgshapiro
89873188Sgshapiro				SM_SET_H_ERRNO(TRY_AGAIN);
89994334Sgshapiro				if (**dp == '\0')
90094334Sgshapiro				{
90194334Sgshapiro					if (*statp == EX_OK)
90294334Sgshapiro						*statp = EX_TEMPFAIL;
90394334Sgshapiro					goto nexttype;
90494334Sgshapiro				}
90538032Speter				*statp = EX_TEMPFAIL;
90664562Sgshapiro
90773188Sgshapiro				if (WorkAroundBrokenAAAA)
90873188Sgshapiro				{
90973188Sgshapiro					/*
91073188Sgshapiro					**  Only return if not TRY_AGAIN as an
91173188Sgshapiro					**  attempt with a different qtype may
91273188Sgshapiro					**  succeed (res_querydomain() calls
91373188Sgshapiro					**  res_query() calls res_send() which
91473188Sgshapiro					**  sets errno to ETIMEDOUT if the
91573188Sgshapiro					**  nameservers could be contacted but
91673188Sgshapiro					**  didn't give an answer).
91773188Sgshapiro					*/
91871345Sgshapiro
91990792Sgshapiro					if (save_errno != ETIMEDOUT)
92090792Sgshapiro						return false;
92173188Sgshapiro				}
92290792Sgshapiro				else
92390792Sgshapiro					return false;
92438032Speter			}
92538032Speter
92694334Sgshapironexttype:
92738032Speter			if (h_errno != HOST_NOT_FOUND)
92838032Speter			{
92938032Speter				/* might have another type of interest */
93064562Sgshapiro# if NETINET6
93190792Sgshapiro				if (qtype == T_AAAA)
93238032Speter				{
93364562Sgshapiro					qtype = T_A;
93464562Sgshapiro					continue;
93564562Sgshapiro				}
93690792Sgshapiro				else
93764562Sgshapiro# endif /* NETINET6 */
93890792Sgshapiro				if (qtype == T_A && !gotmx &&
93990792Sgshapiro				    (trymx || **dp == '\0'))
94064562Sgshapiro				{
94138032Speter					qtype = T_MX;
94238032Speter					continue;
94338032Speter				}
94438032Speter			}
94538032Speter
94638032Speter			/* definite no -- try the next domain */
94738032Speter			dp++;
948120256Sgshapiro			qtype = initial;
94938032Speter			continue;
95038032Speter		}
95138032Speter		else if (tTd(8, 7))
95290792Sgshapiro			sm_dprintf("\tYES\n");
95338032Speter
95438032Speter		/* avoid problems after truncation in tcp packets */
95538032Speter		if (ret > sizeof(answer))
95638032Speter			ret = sizeof(answer);
957159609Sgshapiro		SM_ASSERT(ret >= 0);
95838032Speter
95938032Speter		/*
96038032Speter		**  Appear to have a match.  Confirm it by searching for A or
96138032Speter		**  CNAME records.  If we don't have a local domain
96238032Speter		**  wild card MX record, we will accept MX as well.
96338032Speter		*/
96438032Speter
96538032Speter		hp = (HEADER *) &answer;
96690792Sgshapiro		ap = (unsigned char *) &answer + HFIXEDSZ;
96790792Sgshapiro		eom = (unsigned char *) &answer + ret;
96838032Speter
96938032Speter		/* skip question part of response -- we know what we asked */
97090792Sgshapiro		for (qdcount = ntohs((unsigned short) hp->qdcount);
97164562Sgshapiro		     qdcount--;
97264562Sgshapiro		     ap += ret + QFIXEDSZ)
97338032Speter		{
97438032Speter			if ((ret = dn_skipname(ap, eom)) < 0)
97538032Speter			{
97638032Speter				if (tTd(8, 20))
97790792Sgshapiro					sm_dprintf("qdcount failure (%d)\n",
97890792Sgshapiro						ntohs((unsigned short) hp->qdcount));
97938032Speter				*statp = EX_SOFTWARE;
98090792Sgshapiro				return false;		/* ???XXX??? */
98138032Speter			}
98238032Speter		}
98338032Speter
98490792Sgshapiro		amatch = false;
98590792Sgshapiro		for (ancount = ntohs((unsigned short) hp->ancount);
98664562Sgshapiro		     --ancount >= 0 && ap < eom;
98764562Sgshapiro		     ap += n)
98838032Speter		{
98990792Sgshapiro			n = dn_expand((unsigned char *) &answer, eom, ap,
990168515Sgshapiro				      (RES_UNC_T) nbuf, sizeof(nbuf));
99138032Speter			if (n < 0)
99238032Speter				break;
99338032Speter			ap += n;
99438032Speter			GETSHORT(type, ap);
99590792Sgshapiro			ap += INT16SZ;		/* skip over class */
99690792Sgshapiro			GETLONG(ttl, ap);
99790792Sgshapiro			GETSHORT(n, ap);	/* rdlength */
99838032Speter			switch (type)
99938032Speter			{
100038032Speter			  case T_MX:
100190792Sgshapiro				gotmx = true;
100238032Speter				if (**dp != '\0' && HasWildcardMX)
100338032Speter				{
100438032Speter					/*
100538032Speter					**  If we are using MX matches and have
100638032Speter					**  not yet gotten one, save this one
100764562Sgshapiro					**  but keep searching for an A or
100838032Speter					**  CNAME match.
100938032Speter					*/
101038032Speter
101138032Speter					if (trymx && mxmatch == NULL)
101238032Speter						mxmatch = *dp;
101338032Speter					continue;
101438032Speter				}
101538032Speter
101638032Speter				/*
101738032Speter				**  If we did not append a domain name, this
101838032Speter				**  must have been a canonical name to start
101938032Speter				**  with.  Even if we did append a domain name,
102038032Speter				**  in the absence of a wildcard MX this must
102138032Speter				**  still be a real MX match.
102238032Speter				**  Such MX matches are as good as an A match,
102338032Speter				**  fall through.
102438032Speter				*/
102564562Sgshapiro				/* FALLTHROUGH */
102638032Speter
102764562Sgshapiro# if NETINET6
102864562Sgshapiro			  case T_AAAA:
102964562Sgshapiro# endif /* NETINET6 */
103038032Speter			  case T_A:
103138032Speter				/* Flag that a good match was found */
103290792Sgshapiro				amatch = true;
103338032Speter
103438032Speter				/* continue in case a CNAME also exists */
103538032Speter				continue;
103638032Speter
103738032Speter			  case T_CNAME:
103838032Speter				if (DontExpandCnames)
103938032Speter				{
104038032Speter					/* got CNAME -- guaranteed canonical */
104190792Sgshapiro					amatch = true;
104238032Speter					break;
104338032Speter				}
104438032Speter
104538032Speter				if (loopcnt++ > MAXCNAMEDEPTH)
104638032Speter				{
104738032Speter					/*XXX should notify postmaster XXX*/
104838032Speter					message("DNS failure: CNAME loop for %s",
104938032Speter						host);
105038032Speter					if (CurEnv->e_message == NULL)
105138032Speter					{
105238032Speter						char ebuf[MAXLINE];
105338032Speter
105490792Sgshapiro						(void) sm_snprintf(ebuf,
1055168515Sgshapiro							sizeof(ebuf),
105638032Speter							"Deferred: DNS failure: CNAME loop for %.100s",
105738032Speter							host);
105890792Sgshapiro						CurEnv->e_message =
105990792Sgshapiro						    sm_rpool_strdup_x(
106090792Sgshapiro							CurEnv->e_rpool, ebuf);
106138032Speter					}
106273188Sgshapiro					SM_SET_H_ERRNO(NO_RECOVERY);
106338032Speter					*statp = EX_CONFIG;
106490792Sgshapiro					return false;
106538032Speter				}
106638032Speter
106738032Speter				/* value points at name */
106890792Sgshapiro				if ((ret = dn_expand((unsigned char *)&answer,
106990792Sgshapiro						     eom, ap, (RES_UNC_T) nbuf,
107090792Sgshapiro						     sizeof(nbuf))) < 0)
107138032Speter					break;
107290792Sgshapiro				(void) sm_strlcpy(host, nbuf, hbsize);
107338032Speter
107438032Speter				/*
107538032Speter				**  RFC 1034 section 3.6 specifies that CNAME
107638032Speter				**  should point at the canonical name -- but
107738032Speter				**  urges software to try again anyway.
107838032Speter				*/
107938032Speter
108038032Speter				goto cnameloop;
108138032Speter
108238032Speter			  default:
108338032Speter				/* not a record of interest */
108438032Speter				continue;
108538032Speter			}
108638032Speter		}
108738032Speter
108838032Speter		if (amatch)
108938032Speter		{
109064562Sgshapiro			/*
109138032Speter			**  Got a good match -- either an A, CNAME, or an
109238032Speter			**  exact MX record.  Save it and get out of here.
109338032Speter			*/
109438032Speter
109538032Speter			mxmatch = *dp;
109638032Speter			break;
109738032Speter		}
109838032Speter
109938032Speter		/*
110038032Speter		**  Nothing definitive yet.
110138032Speter		**	If this was a T_A query and we haven't yet found a MX
110238032Speter		**		match, try T_MX if allowed to do so.
110338032Speter		**	Otherwise, try the next domain.
110438032Speter		*/
110538032Speter
110664562Sgshapiro# if NETINET6
110790792Sgshapiro		if (qtype == T_AAAA)
110838032Speter			qtype = T_A;
110990792Sgshapiro		else
111064562Sgshapiro# endif /* NETINET6 */
111190792Sgshapiro		if (qtype == T_A && !gotmx && (trymx || **dp == '\0'))
111238032Speter			qtype = T_MX;
111338032Speter		else
111438032Speter		{
1115120256Sgshapiro			qtype = initial;
111638032Speter			dp++;
111738032Speter		}
111838032Speter	}
111938032Speter
112038032Speter	/* if nothing was found, we are done */
112138032Speter	if (mxmatch == NULL)
112238032Speter	{
112371345Sgshapiro		if (*statp == EX_OK)
112471345Sgshapiro			*statp = EX_NOHOST;
112590792Sgshapiro		return false;
112638032Speter	}
112738032Speter
112838032Speter	/*
112938032Speter	**  Create canonical name and return.
113038032Speter	**  If saved domain name is null, name was already canonical.
113138032Speter	**  Otherwise append the saved domain name.
113238032Speter	*/
113338032Speter
1134168515Sgshapiro	(void) sm_snprintf(nbuf, sizeof(nbuf), "%.*s%s%.*s", MAXDNAME, host,
113590792Sgshapiro			   *mxmatch == '\0' ? "" : ".",
113690792Sgshapiro			   MAXDNAME, mxmatch);
113790792Sgshapiro	(void) sm_strlcpy(host, nbuf, hbsize);
113838032Speter	if (tTd(8, 5))
113990792Sgshapiro		sm_dprintf("dns_getcanonname: %s\n", host);
114038032Speter	*statp = EX_OK;
114190792Sgshapiro
114290792Sgshapiro	/* return only one TTL entry, that should be sufficient */
114390792Sgshapiro	if (ttl > 0 && pttl != NULL)
114490792Sgshapiro		*pttl = ttl;
114590792Sgshapiro	return true;
114638032Speter}
114738032Speter#endif /* NAMED_BIND */
1148