domain.c revision 38032
138032Speter/*
238032Speter * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
338032Speter * Copyright (c) 1986, 1995-1997 Eric P. Allman.  All rights reserved.
438032Speter * Copyright (c) 1988, 1993
538032Speter *	The Regents of the University of California.  All rights reserved.
638032Speter *
738032Speter * By using this file, you agree to the terms and conditions set
838032Speter * forth in the LICENSE file which can be found at the top level of
938032Speter * the sendmail distribution.
1038032Speter *
1138032Speter */
1238032Speter
1338032Speter#include "sendmail.h"
1438032Speter
1538032Speter#ifndef lint
1638032Speter#if NAMED_BIND
1738032Speterstatic char sccsid[] = "@(#)domain.c	8.77 (Berkeley) 6/4/98 (with name server)";
1838032Speter#else
1938032Speterstatic char sccsid[] = "@(#)domain.c	8.77 (Berkeley) 6/4/98 (without name server)";
2038032Speter#endif
2138032Speter#endif /* not lint */
2238032Speter
2338032Speter#if NAMED_BIND
2438032Speter
2538032Speter#include <errno.h>
2638032Speter#include <resolv.h>
2738032Speter#include <arpa/inet.h>
2838032Speter
2938032Speter/*
3038032Speter**  The standard udp packet size PACKETSZ (512) is not sufficient for some
3138032Speter**  nameserver answers containing very many resource records. The resolver
3238032Speter**  may switch to tcp and retry if it detects udp packet overflow.
3338032Speter**  Also note that the resolver routines res_query and res_search return
3438032Speter**  the size of the *un*truncated answer in case the supplied answer buffer
3538032Speter**  it not big enough to accommodate the entire answer.
3638032Speter*/
3738032Speter
3838032Speter#ifndef MAXPACKET
3938032Speter# define MAXPACKET 8192		/* max packet size used internally by BIND */
4038032Speter#endif
4138032Speter
4238032Spetertypedef union
4338032Speter{
4438032Speter	HEADER	qb1;
4538032Speter	u_char	qb2[MAXPACKET];
4638032Speter} querybuf;
4738032Speter
4838032Speter#ifndef MXHOSTBUFSIZE
4938032Speter# define MXHOSTBUFSIZE	(128 * MAXMXHOSTS)
5038032Speter#endif
5138032Speter
5238032Speterstatic char	MXHostBuf[MXHOSTBUFSIZE];
5338032Speter
5438032Speter#ifndef MAXDNSRCH
5538032Speter# define MAXDNSRCH	6	/* number of possible domains to search */
5638032Speter#endif
5738032Speter
5838032Speter#ifndef MAX
5938032Speter# define MAX(a, b)	((a) > (b) ? (a) : (b))
6038032Speter#endif
6138032Speter
6238032Speter#ifndef NO_DATA
6338032Speter# define NO_DATA	NO_ADDRESS
6438032Speter#endif
6538032Speter
6638032Speter#ifndef HFIXEDSZ
6738032Speter# define HFIXEDSZ	12	/* sizeof(HEADER) */
6838032Speter#endif
6938032Speter
7038032Speter#define MAXCNAMEDEPTH	10	/* maximum depth of CNAME recursion */
7138032Speter
7238032Speter#if defined(__RES) && (__RES >= 19940415)
7338032Speter# define RES_UNC_T	char *
7438032Speter#else
7538032Speter# define RES_UNC_T	u_char *
7638032Speter#endif
7738032Speter/*
7838032Speter**  GETMXRR -- get MX resource records for a domain
7938032Speter**
8038032Speter**	Parameters:
8138032Speter**		host -- the name of the host to MX.
8238032Speter**		mxhosts -- a pointer to a return buffer of MX records.
8338032Speter**		droplocalhost -- If TRUE, all MX records less preferred
8438032Speter**			than the local host (as determined by $=w) will
8538032Speter**			be discarded.
8638032Speter**		rcode -- a pointer to an EX_ status code.
8738032Speter**
8838032Speter**	Returns:
8938032Speter**		The number of MX records found.
9038032Speter**		-1 if there is an internal failure.
9138032Speter**		If no MX records are found, mxhosts[0] is set to host
9238032Speter**			and 1 is returned.
9338032Speter*/
9438032Speter
9538032Speterint
9638032Spetergetmxrr(host, mxhosts, droplocalhost, rcode)
9738032Speter	char *host;
9838032Speter	char **mxhosts;
9938032Speter	bool droplocalhost;
10038032Speter	int *rcode;
10138032Speter{
10238032Speter	register u_char *eom, *cp;
10338032Speter	register int i, j, n;
10438032Speter	int nmx = 0;
10538032Speter	register char *bp;
10638032Speter	HEADER *hp;
10738032Speter	querybuf answer;
10838032Speter	int ancount, qdcount, buflen;
10938032Speter	bool seenlocal = FALSE;
11038032Speter	u_short pref, type;
11138032Speter	u_short localpref = 256;
11238032Speter	char *fallbackMX = FallBackMX;
11338032Speter	bool trycanon = FALSE;
11438032Speter	int (*resfunc)();
11538032Speter	extern int res_query(), res_search();
11638032Speter	u_short prefer[MAXMXHOSTS];
11738032Speter	int weight[MAXMXHOSTS];
11838032Speter	extern int mxrand __P((char *));
11938032Speter
12038032Speter	if (tTd(8, 2))
12138032Speter		printf("getmxrr(%s, droplocalhost=%d)\n", host, droplocalhost);
12238032Speter
12338032Speter	if (fallbackMX != NULL && droplocalhost &&
12438032Speter	    wordinclass(fallbackMX, 'w'))
12538032Speter	{
12638032Speter		/* don't use fallback for this pass */
12738032Speter		fallbackMX = NULL;
12838032Speter	}
12938032Speter
13038032Speter	*rcode = EX_OK;
13138032Speter
13238032Speter	/* efficiency hack -- numeric or non-MX lookups */
13338032Speter	if (host[0] == '[')
13438032Speter		goto punt;
13538032Speter
13638032Speter	/*
13738032Speter	**  If we don't have MX records in our host switch, don't
13838032Speter	**  try for MX records.  Note that this really isn't "right",
13938032Speter	**  since we might be set up to try NIS first and then DNS;
14038032Speter	**  if the host is found in NIS we really shouldn't be doing
14138032Speter	**  MX lookups.  However, that should be a degenerate case.
14238032Speter	*/
14338032Speter
14438032Speter	if (!UseNameServer)
14538032Speter		goto punt;
14638032Speter	if (HasWildcardMX && ConfigLevel >= 6)
14738032Speter		resfunc = res_query;
14838032Speter	else
14938032Speter		resfunc = res_search;
15038032Speter
15138032Speter	errno = 0;
15238032Speter	n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer));
15338032Speter	if (n < 0)
15438032Speter	{
15538032Speter		if (tTd(8, 1))
15638032Speter			printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
15738032Speter			    (host == NULL) ? "<NULL>" : host, errno, h_errno);
15838032Speter		switch (h_errno)
15938032Speter		{
16038032Speter		  case NO_DATA:
16138032Speter			trycanon = TRUE;
16238032Speter			/* fall through */
16338032Speter
16438032Speter		  case NO_RECOVERY:
16538032Speter			/* no MX data on this host */
16638032Speter			goto punt;
16738032Speter
16838032Speter		  case HOST_NOT_FOUND:
16938032Speter#if BROKEN_RES_SEARCH
17038032Speter		  case 0:	/* Ultrix resolver retns failure w/ h_errno=0 */
17138032Speter#endif
17238032Speter			/* host doesn't exist in DNS; might be in /etc/hosts */
17338032Speter			trycanon = TRUE;
17438032Speter			*rcode = EX_NOHOST;
17538032Speter			goto punt;
17638032Speter
17738032Speter		  case TRY_AGAIN:
17838032Speter		  case -1:
17938032Speter			/* couldn't connect to the name server */
18038032Speter			if (fallbackMX != NULL)
18138032Speter			{
18238032Speter				/* name server is hosed -- push to fallback */
18338032Speter				mxhosts[nmx++] = fallbackMX;
18438032Speter				return nmx;
18538032Speter			}
18638032Speter			/* it might come up later; better queue it up */
18738032Speter			*rcode = EX_TEMPFAIL;
18838032Speter			break;
18938032Speter
19038032Speter		  default:
19138032Speter			syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n",
19238032Speter				host, h_errno);
19338032Speter			*rcode = EX_OSERR;
19438032Speter			break;
19538032Speter		}
19638032Speter
19738032Speter		/* irreconcilable differences */
19838032Speter		return (-1);
19938032Speter	}
20038032Speter
20138032Speter	/* avoid problems after truncation in tcp packets */
20238032Speter	if (n > sizeof(answer))
20338032Speter		n = sizeof(answer);
20438032Speter
20538032Speter	/* find first satisfactory answer */
20638032Speter	hp = (HEADER *)&answer;
20738032Speter	cp = (u_char *)&answer + HFIXEDSZ;
20838032Speter	eom = (u_char *)&answer + n;
20938032Speter	for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ)
21038032Speter		if ((n = dn_skipname(cp, eom)) < 0)
21138032Speter			goto punt;
21238032Speter	buflen = sizeof(MXHostBuf) - 1;
21338032Speter	bp = MXHostBuf;
21438032Speter	ancount = ntohs(hp->ancount);
21538032Speter	while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
21638032Speter	{
21738032Speter		if ((n = dn_expand((u_char *)&answer,
21838032Speter		    eom, cp, (RES_UNC_T) bp, buflen)) < 0)
21938032Speter			break;
22038032Speter		cp += n;
22138032Speter		GETSHORT(type, cp);
22238032Speter 		cp += INT16SZ + INT32SZ;
22338032Speter		GETSHORT(n, cp);
22438032Speter		if (type != T_MX)
22538032Speter		{
22638032Speter			if (tTd(8, 8) || _res.options & RES_DEBUG)
22738032Speter				printf("unexpected answer type %d, size %d\n",
22838032Speter				    type, n);
22938032Speter			cp += n;
23038032Speter			continue;
23138032Speter		}
23238032Speter		GETSHORT(pref, cp);
23338032Speter		if ((n = dn_expand((u_char *)&answer, eom, cp,
23438032Speter				   (RES_UNC_T) bp, buflen)) < 0)
23538032Speter			break;
23638032Speter		cp += n;
23738032Speter		if (wordinclass(bp, 'w'))
23838032Speter		{
23938032Speter			if (tTd(8, 3))
24038032Speter				printf("found localhost (%s) in MX list, pref=%d\n",
24138032Speter					bp, pref);
24238032Speter			if (droplocalhost)
24338032Speter			{
24438032Speter				if (!seenlocal || pref < localpref)
24538032Speter					localpref = pref;
24638032Speter				seenlocal = TRUE;
24738032Speter				continue;
24838032Speter			}
24938032Speter			weight[nmx] = 0;
25038032Speter		}
25138032Speter		else
25238032Speter			weight[nmx] = mxrand(bp);
25338032Speter		prefer[nmx] = pref;
25438032Speter		mxhosts[nmx++] = bp;
25538032Speter		n = strlen(bp);
25638032Speter		bp += n;
25738032Speter		if (bp[-1] != '.')
25838032Speter		{
25938032Speter			*bp++ = '.';
26038032Speter			n++;
26138032Speter		}
26238032Speter		*bp++ = '\0';
26338032Speter		buflen -= n + 1;
26438032Speter	}
26538032Speter
26638032Speter	/* sort the records */
26738032Speter	for (i = 0; i < nmx; i++)
26838032Speter	{
26938032Speter		for (j = i + 1; j < nmx; j++)
27038032Speter		{
27138032Speter			if (prefer[i] > prefer[j] ||
27238032Speter			    (prefer[i] == prefer[j] && weight[i] > weight[j]))
27338032Speter			{
27438032Speter				register int temp;
27538032Speter				register char *temp1;
27638032Speter
27738032Speter				temp = prefer[i];
27838032Speter				prefer[i] = prefer[j];
27938032Speter				prefer[j] = temp;
28038032Speter				temp1 = mxhosts[i];
28138032Speter				mxhosts[i] = mxhosts[j];
28238032Speter				mxhosts[j] = temp1;
28338032Speter				temp = weight[i];
28438032Speter				weight[i] = weight[j];
28538032Speter				weight[j] = temp;
28638032Speter			}
28738032Speter		}
28838032Speter		if (seenlocal && prefer[i] >= localpref)
28938032Speter		{
29038032Speter			/* truncate higher preference part of list */
29138032Speter			nmx = i;
29238032Speter		}
29338032Speter	}
29438032Speter
29538032Speter	/* delete duplicates from list (yes, some bozos have duplicates) */
29638032Speter	for (i = 0; i < nmx - 1; )
29738032Speter	{
29838032Speter		if (strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0)
29938032Speter			i++;
30038032Speter		else
30138032Speter		{
30238032Speter			/* compress out duplicate */
30338032Speter			for (j = i + 1; j < nmx; j++)
30438032Speter				mxhosts[j] = mxhosts[j + 1];
30538032Speter			nmx--;
30638032Speter		}
30738032Speter	}
30838032Speter
30938032Speter	if (nmx == 0)
31038032Speter	{
31138032Speterpunt:
31238032Speter		if (seenlocal &&
31338032Speter		    (!TryNullMXList || sm_gethostbyname(host) == NULL))
31438032Speter		{
31538032Speter			/*
31638032Speter			**  If we have deleted all MX entries, this is
31738032Speter			**  an error -- we should NEVER send to a host that
31838032Speter			**  has an MX, and this should have been caught
31938032Speter			**  earlier in the config file.
32038032Speter			**
32138032Speter			**  Some sites prefer to go ahead and try the
32238032Speter			**  A record anyway; that case is handled by
32338032Speter			**  setting TryNullMXList.  I believe this is a
32438032Speter			**  bad idea, but it's up to you....
32538032Speter			*/
32638032Speter
32738032Speter			*rcode = EX_CONFIG;
32838032Speter			syserr("MX list for %s points back to %s",
32938032Speter				host, MyHostName);
33038032Speter			return -1;
33138032Speter		}
33238032Speter		if (strlen(host) >= (SIZE_T) sizeof MXHostBuf)
33338032Speter		{
33438032Speter			*rcode = EX_CONFIG;
33538032Speter			syserr("Host name %s too long",
33638032Speter			       shortenstring(host, MAXSHORTSTR));
33738032Speter			return -1;
33838032Speter		}
33938032Speter		snprintf(MXHostBuf, sizeof MXHostBuf, "%s", host);
34038032Speter		mxhosts[0] = MXHostBuf;
34138032Speter		if (host[0] == '[')
34238032Speter		{
34338032Speter			register char *p;
34438032Speter
34538032Speter			/* this may be an MX suppression-style address */
34638032Speter			p = strchr(MXHostBuf, ']');
34738032Speter			if (p != NULL)
34838032Speter			{
34938032Speter				*p = '\0';
35038032Speter				if (inet_addr(&MXHostBuf[1]) != INADDR_NONE)
35138032Speter				{
35238032Speter					nmx++;
35338032Speter					*p = ']';
35438032Speter				}
35538032Speter				else
35638032Speter				{
35738032Speter					trycanon = TRUE;
35838032Speter					mxhosts[0]++;
35938032Speter				}
36038032Speter			}
36138032Speter		}
36238032Speter		if (trycanon &&
36338032Speter		    getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE))
36438032Speter		{
36538032Speter			bp = &MXHostBuf[strlen(MXHostBuf)];
36638032Speter			if (bp[-1] != '.')
36738032Speter			{
36838032Speter				*bp++ = '.';
36938032Speter				*bp = '\0';
37038032Speter			}
37138032Speter			nmx = 1;
37238032Speter		}
37338032Speter	}
37438032Speter
37538032Speter	/* if we have a default lowest preference, include that */
37638032Speter	if (fallbackMX != NULL && !seenlocal)
37738032Speter		mxhosts[nmx++] = fallbackMX;
37838032Speter
37938032Speter	return (nmx);
38038032Speter}
38138032Speter/*
38238032Speter**  MXRAND -- create a randomizer for equal MX preferences
38338032Speter**
38438032Speter**	If two MX hosts have equal preferences we want to randomize
38538032Speter**	the selection.  But in order for signatures to be the same,
38638032Speter**	we need to randomize the same way each time.  This function
38738032Speter**	computes a pseudo-random hash function from the host name.
38838032Speter**
38938032Speter**	Parameters:
39038032Speter**		host -- the name of the host.
39138032Speter**
39238032Speter**	Returns:
39338032Speter**		A random but repeatable value based on the host name.
39438032Speter**
39538032Speter**	Side Effects:
39638032Speter**		none.
39738032Speter*/
39838032Speter
39938032Speterint
40038032Spetermxrand(host)
40138032Speter	register char *host;
40238032Speter{
40338032Speter	int hfunc;
40438032Speter	static unsigned int seed;
40538032Speter
40638032Speter	if (seed == 0)
40738032Speter	{
40838032Speter		seed = (int) curtime() & 0xffff;
40938032Speter		if (seed == 0)
41038032Speter			seed++;
41138032Speter	}
41238032Speter
41338032Speter	if (tTd(17, 9))
41438032Speter		printf("mxrand(%s)", host);
41538032Speter
41638032Speter	hfunc = seed;
41738032Speter	while (*host != '\0')
41838032Speter	{
41938032Speter		int c = *host++;
42038032Speter
42138032Speter		if (isascii(c) && isupper(c))
42238032Speter			c = tolower(c);
42338032Speter		hfunc = ((hfunc << 1) ^ c) % 2003;
42438032Speter	}
42538032Speter
42638032Speter	hfunc &= 0xff;
42738032Speter	hfunc++;
42838032Speter
42938032Speter	if (tTd(17, 9))
43038032Speter		printf(" = %d\n", hfunc);
43138032Speter	return hfunc;
43238032Speter}
43338032Speter/*
43438032Speter**  BESTMX -- find the best MX for a name
43538032Speter**
43638032Speter**	This is really a hack, but I don't see any obvious way
43738032Speter**	to generalize it at the moment.
43838032Speter*/
43938032Speter
44038032Speter/* ARGSUSED3 */
44138032Speterchar *
44238032Speterbestmx_map_lookup(map, name, av, statp)
44338032Speter	MAP *map;
44438032Speter	char *name;
44538032Speter	char **av;
44638032Speter	int *statp;
44738032Speter{
44838032Speter	int nmx;
44938032Speter	auto int rcode;
45038032Speter	int saveopts = _res.options;
45138032Speter	int i, len = 0;
45238032Speter	char *p;
45338032Speter	char *mxhosts[MAXMXHOSTS + 1];
45438032Speter	char buf[MXHOSTBUFSIZE + 1];
45538032Speter
45638032Speter	_res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
45738032Speter	nmx = getmxrr(name, mxhosts, FALSE, &rcode);
45838032Speter	_res.options = saveopts;
45938032Speter	if (nmx <= 0)
46038032Speter		return NULL;
46138032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
46238032Speter		return map_rewrite(map, name, strlen(name), NULL);
46338032Speter	if ((map->map_coldelim == '\0') || (nmx == 1))
46438032Speter		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
46538032Speter
46638032Speter	/*
46738032Speter	** We were given a -z flag (return all MXs) and there are multiple
46838032Speter	** ones.  We need to build them all into a list.
46938032Speter	*/
47038032Speter	p = buf;
47138032Speter	for (i = 0; i < nmx; i++)
47238032Speter	{
47338032Speter		int slen;
47438032Speter
47538032Speter		if (strchr(mxhosts[i], map->map_coldelim) != NULL)
47638032Speter		{
47738032Speter			syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
47838032Speter			       mxhosts[i], map->map_coldelim);
47938032Speter			return NULL;
48038032Speter		}
48138032Speter		slen = strlen(mxhosts[i]);
48238032Speter		if (len + slen + 2 > sizeof buf)
48338032Speter			break;
48438032Speter		if (i > 0)
48538032Speter		{
48638032Speter			*p++ = map->map_coldelim;
48738032Speter			len++;
48838032Speter		}
48938032Speter		strcpy(p, mxhosts[i]);
49038032Speter		p += slen;
49138032Speter		len += slen;
49238032Speter	}
49338032Speter	return map_rewrite(map, buf, len, av);
49438032Speter}
49538032Speter/*
49638032Speter**  DNS_GETCANONNAME -- get the canonical name for named host using DNS
49738032Speter**
49838032Speter**	This algorithm tries to be smart about wildcard MX records.
49938032Speter**	This is hard to do because DNS doesn't tell is if we matched
50038032Speter**	against a wildcard or a specific MX.
50138032Speter**
50238032Speter**	We always prefer A & CNAME records, since these are presumed
50338032Speter**	to be specific.
50438032Speter**
50538032Speter**	If we match an MX in one pass and lose it in the next, we use
50638032Speter**	the old one.  For example, consider an MX matching *.FOO.BAR.COM.
50738032Speter**	A hostname bletch.foo.bar.com will match against this MX, but
50838032Speter**	will stop matching when we try bletch.bar.com -- so we know
50938032Speter**	that bletch.foo.bar.com must have been right.  This fails if
51038032Speter**	there was also an MX record matching *.BAR.COM, but there are
51138032Speter**	some things that just can't be fixed.
51238032Speter**
51338032Speter**	Parameters:
51438032Speter**		host -- a buffer containing the name of the host.
51538032Speter**			This is a value-result parameter.
51638032Speter**		hbsize -- the size of the host buffer.
51738032Speter**		trymx -- if set, try MX records as well as A and CNAME.
51838032Speter**		statp -- pointer to place to store status.
51938032Speter**
52038032Speter**	Returns:
52138032Speter**		TRUE -- if the host matched.
52238032Speter**		FALSE -- otherwise.
52338032Speter*/
52438032Speter
52538032Speterbool
52638032Speterdns_getcanonname(host, hbsize, trymx, statp)
52738032Speter	char *host;
52838032Speter	int hbsize;
52938032Speter	bool trymx;
53038032Speter	int *statp;
53138032Speter{
53238032Speter	register u_char *eom, *ap;
53338032Speter	register char *cp;
53438032Speter	register int n;
53538032Speter	HEADER *hp;
53638032Speter	querybuf answer;
53738032Speter	int ancount, qdcount;
53838032Speter	int ret;
53938032Speter	char **domain;
54038032Speter	int type;
54138032Speter	char **dp;
54238032Speter	char *mxmatch;
54338032Speter	bool amatch;
54438032Speter	bool gotmx = FALSE;
54538032Speter	int qtype;
54638032Speter	int loopcnt;
54738032Speter	char *xp;
54838032Speter	char nbuf[MAX(MAXPACKET, MAXDNAME*2+2)];
54938032Speter	char *searchlist[MAXDNSRCH+2];
55038032Speter	extern char *gethostalias __P((char *));
55138032Speter
55238032Speter	if (tTd(8, 2))
55338032Speter		printf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
55438032Speter
55538032Speter	if ((_res.options & RES_INIT) == 0 && res_init() == -1)
55638032Speter	{
55738032Speter		*statp = EX_UNAVAILABLE;
55838032Speter		return FALSE;
55938032Speter	}
56038032Speter
56138032Speter	/*
56238032Speter	**  Initialize domain search list.  If there is at least one
56338032Speter	**  dot in the name, search the unmodified name first so we
56438032Speter	**  find "vse.CS" in Czechoslovakia instead of in the local
56538032Speter	**  domain (e.g., vse.CS.Berkeley.EDU).
56638032Speter	**
56738032Speter	**  Older versions of the resolver could create this
56838032Speter	**  list by tearing apart the host name.
56938032Speter	*/
57038032Speter
57138032Speter	loopcnt = 0;
57238032Spetercnameloop:
57338032Speter	/* Check for dots in the name */
57438032Speter	for (cp = host, n = 0; *cp != '\0'; cp++)
57538032Speter		if (*cp == '.')
57638032Speter			n++;
57738032Speter
57838032Speter	/*
57938032Speter	**  If this is a simple name, determine whether it matches an
58038032Speter	**  alias in the file defined by the environment variable HOSTALIASES.
58138032Speter	*/
58238032Speter	if (n == 0 && (xp = gethostalias(host)) != NULL)
58338032Speter	{
58438032Speter		if (loopcnt++ > MAXCNAMEDEPTH)
58538032Speter		{
58638032Speter			syserr("loop in ${HOSTALIASES} file");
58738032Speter		}
58838032Speter		else
58938032Speter		{
59038032Speter			strncpy(host, xp, hbsize);
59138032Speter			host[hbsize - 1] = '\0';
59238032Speter			goto cnameloop;
59338032Speter		}
59438032Speter	}
59538032Speter
59638032Speter	/*
59738032Speter	**  Build the search list.
59838032Speter	**	If there is at least one dot in name, start with a null
59938032Speter	**	domain to search the unmodified name first.
60038032Speter	**	If name does not end with a dot and search up local domain
60138032Speter	**	tree desired, append each local domain component to the
60238032Speter	**	search list; if name contains no dots and default domain
60338032Speter	**	name is desired, append default domain name to search list;
60438032Speter	**	else if name ends in a dot, remove that dot.
60538032Speter	*/
60638032Speter
60738032Speter	dp = searchlist;
60838032Speter	if (n > 0)
60938032Speter		*dp++ = "";
61038032Speter	if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options))
61138032Speter	{
61238032Speter		for (domain = _res.dnsrch; *domain != NULL; )
61338032Speter			*dp++ = *domain++;
61438032Speter	}
61538032Speter	else if (n == 0 && bitset(RES_DEFNAMES, _res.options))
61638032Speter	{
61738032Speter		*dp++ = _res.defdname;
61838032Speter	}
61938032Speter	else if (*cp == '.')
62038032Speter	{
62138032Speter		*cp = '\0';
62238032Speter	}
62338032Speter	*dp = NULL;
62438032Speter
62538032Speter	/*
62638032Speter	**  Now loop through the search list, appending each domain in turn
62738032Speter	**  name and searching for a match.
62838032Speter	*/
62938032Speter
63038032Speter	mxmatch = NULL;
63138032Speter	qtype = T_ANY;
63238032Speter
63338032Speter	for (dp = searchlist; *dp != NULL; )
63438032Speter	{
63538032Speter		if (qtype == T_ANY)
63638032Speter			gotmx = FALSE;
63738032Speter		if (tTd(8, 5))
63838032Speter			printf("dns_getcanonname: trying %s.%s (%s)\n",
63938032Speter				host, *dp,
64038032Speter				qtype == T_ANY ? "ANY" : qtype == T_A ? "A" :
64138032Speter				qtype == T_MX ? "MX" : "???");
64238032Speter		ret = res_querydomain(host, *dp, C_IN, qtype,
64338032Speter				      answer.qb2, sizeof(answer.qb2));
64438032Speter		if (ret <= 0)
64538032Speter		{
64638032Speter			if (tTd(8, 7))
64738032Speter				printf("\tNO: errno=%d, h_errno=%d\n",
64838032Speter					errno, h_errno);
64938032Speter
65038032Speter			if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
65138032Speter			{
65238032Speter				/* the name server seems to be down */
65338032Speter				h_errno = TRY_AGAIN;
65438032Speter				*statp = EX_TEMPFAIL;
65538032Speter				return FALSE;
65638032Speter			}
65738032Speter
65838032Speter			if (h_errno != HOST_NOT_FOUND)
65938032Speter			{
66038032Speter				/* might have another type of interest */
66138032Speter				if (qtype == T_ANY)
66238032Speter				{
66338032Speter					qtype = T_A;
66438032Speter					continue;
66538032Speter				}
66638032Speter				else if (qtype == T_A && !gotmx && trymx)
66738032Speter				{
66838032Speter					qtype = T_MX;
66938032Speter					continue;
67038032Speter				}
67138032Speter			}
67238032Speter
67338032Speter			/* definite no -- try the next domain */
67438032Speter			dp++;
67538032Speter			qtype = T_ANY;
67638032Speter			continue;
67738032Speter		}
67838032Speter		else if (tTd(8, 7))
67938032Speter			printf("\tYES\n");
68038032Speter
68138032Speter		/* avoid problems after truncation in tcp packets */
68238032Speter		if (ret > sizeof(answer))
68338032Speter			ret = sizeof(answer);
68438032Speter
68538032Speter		/*
68638032Speter		**  Appear to have a match.  Confirm it by searching for A or
68738032Speter		**  CNAME records.  If we don't have a local domain
68838032Speter		**  wild card MX record, we will accept MX as well.
68938032Speter		*/
69038032Speter
69138032Speter		hp = (HEADER *) &answer;
69238032Speter		ap = (u_char *) &answer + HFIXEDSZ;
69338032Speter		eom = (u_char *) &answer + ret;
69438032Speter
69538032Speter		/* skip question part of response -- we know what we asked */
69638032Speter		for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ)
69738032Speter		{
69838032Speter			if ((ret = dn_skipname(ap, eom)) < 0)
69938032Speter			{
70038032Speter				if (tTd(8, 20))
70138032Speter					printf("qdcount failure (%d)\n",
70238032Speter						ntohs(hp->qdcount));
70338032Speter				*statp = EX_SOFTWARE;
70438032Speter				return FALSE;		/* ???XXX??? */
70538032Speter			}
70638032Speter		}
70738032Speter
70838032Speter		amatch = FALSE;
70938032Speter		for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom;
71038032Speter									ap += n)
71138032Speter		{
71238032Speter			n = dn_expand((u_char *) &answer, eom, ap,
71338032Speter				      (RES_UNC_T) nbuf, sizeof nbuf);
71438032Speter			if (n < 0)
71538032Speter				break;
71638032Speter			ap += n;
71738032Speter			GETSHORT(type, ap);
71838032Speter			ap += INT16SZ + INT32SZ;
71938032Speter			GETSHORT(n, ap);
72038032Speter			switch (type)
72138032Speter			{
72238032Speter			  case T_MX:
72338032Speter				gotmx = TRUE;
72438032Speter				if (**dp != '\0' && HasWildcardMX)
72538032Speter				{
72638032Speter					/*
72738032Speter					**  If we are using MX matches and have
72838032Speter					**  not yet gotten one, save this one
72938032Speter					**  but keep searching for an A or
73038032Speter					**  CNAME match.
73138032Speter					*/
73238032Speter
73338032Speter					if (trymx && mxmatch == NULL)
73438032Speter						mxmatch = *dp;
73538032Speter					continue;
73638032Speter				}
73738032Speter
73838032Speter				/*
73938032Speter				**  If we did not append a domain name, this
74038032Speter				**  must have been a canonical name to start
74138032Speter				**  with.  Even if we did append a domain name,
74238032Speter				**  in the absence of a wildcard MX this must
74338032Speter				**  still be a real MX match.
74438032Speter				**  Such MX matches are as good as an A match,
74538032Speter				**  fall through.
74638032Speter				*/
74738032Speter
74838032Speter			  case T_A:
74938032Speter				/* Flag that a good match was found */
75038032Speter				amatch = TRUE;
75138032Speter
75238032Speter				/* continue in case a CNAME also exists */
75338032Speter				continue;
75438032Speter
75538032Speter			  case T_CNAME:
75638032Speter				if (DontExpandCnames)
75738032Speter				{
75838032Speter					/* got CNAME -- guaranteed canonical */
75938032Speter					amatch = TRUE;
76038032Speter					break;
76138032Speter				}
76238032Speter
76338032Speter				if (loopcnt++ > MAXCNAMEDEPTH)
76438032Speter				{
76538032Speter					/*XXX should notify postmaster XXX*/
76638032Speter					message("DNS failure: CNAME loop for %s",
76738032Speter						host);
76838032Speter					if (CurEnv->e_message == NULL)
76938032Speter					{
77038032Speter						char ebuf[MAXLINE];
77138032Speter
77238032Speter						snprintf(ebuf, sizeof ebuf,
77338032Speter							"Deferred: DNS failure: CNAME loop for %.100s",
77438032Speter							host);
77538032Speter						CurEnv->e_message = newstr(ebuf);
77638032Speter					}
77738032Speter					h_errno = NO_RECOVERY;
77838032Speter					*statp = EX_CONFIG;
77938032Speter					return FALSE;
78038032Speter				}
78138032Speter
78238032Speter				/* value points at name */
78338032Speter				if ((ret = dn_expand((u_char *)&answer,
78438032Speter				    eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0)
78538032Speter					break;
78638032Speter				(void)strncpy(host, nbuf, hbsize); /* XXX */
78738032Speter				host[hbsize - 1] = '\0';
78838032Speter
78938032Speter				/*
79038032Speter				**  RFC 1034 section 3.6 specifies that CNAME
79138032Speter				**  should point at the canonical name -- but
79238032Speter				**  urges software to try again anyway.
79338032Speter				*/
79438032Speter
79538032Speter				goto cnameloop;
79638032Speter
79738032Speter			  default:
79838032Speter				/* not a record of interest */
79938032Speter				continue;
80038032Speter			}
80138032Speter		}
80238032Speter
80338032Speter		if (amatch)
80438032Speter		{
80538032Speter			/*
80638032Speter			**  Got a good match -- either an A, CNAME, or an
80738032Speter			**  exact MX record.  Save it and get out of here.
80838032Speter			*/
80938032Speter
81038032Speter			mxmatch = *dp;
81138032Speter			break;
81238032Speter		}
81338032Speter
81438032Speter		/*
81538032Speter		**  Nothing definitive yet.
81638032Speter		**	If this was a T_ANY query, we don't really know what
81738032Speter		**		was returned -- it might have been a T_NS,
81838032Speter		**		for example.  Try T_A to be more specific
81938032Speter		**		during the next pass.
82038032Speter		**	If this was a T_A query and we haven't yet found a MX
82138032Speter		**		match, try T_MX if allowed to do so.
82238032Speter		**	Otherwise, try the next domain.
82338032Speter		*/
82438032Speter
82538032Speter		if (qtype == T_ANY)
82638032Speter			qtype = T_A;
82738032Speter		else if (qtype == T_A && !gotmx && trymx)
82838032Speter			qtype = T_MX;
82938032Speter		else
83038032Speter		{
83138032Speter			qtype = T_ANY;
83238032Speter			dp++;
83338032Speter		}
83438032Speter	}
83538032Speter
83638032Speter	/* if nothing was found, we are done */
83738032Speter	if (mxmatch == NULL)
83838032Speter	{
83938032Speter		*statp = EX_NOHOST;
84038032Speter		return FALSE;
84138032Speter	}
84238032Speter
84338032Speter	/*
84438032Speter	**  Create canonical name and return.
84538032Speter	**  If saved domain name is null, name was already canonical.
84638032Speter	**  Otherwise append the saved domain name.
84738032Speter	*/
84838032Speter
84938032Speter	(void) snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host,
85038032Speter			*mxmatch == '\0' ? "" : ".",
85138032Speter			MAXDNAME, mxmatch);
85238032Speter	strncpy(host, nbuf, hbsize);
85338032Speter	host[hbsize - 1] = '\0';
85438032Speter	if (tTd(8, 5))
85538032Speter		printf("dns_getcanonname: %s\n", host);
85638032Speter	*statp = EX_OK;
85738032Speter	return TRUE;
85838032Speter}
85938032Speter
86038032Speter
86138032Speter
86238032Speterchar *
86338032Spetergethostalias(host)
86438032Speter	char *host;
86538032Speter{
86638032Speter	char *fname;
86738032Speter	FILE *fp;
86838032Speter	register char *p = NULL;
86938032Speter	int sff = SFF_REGONLY;
87038032Speter	char buf[MAXLINE];
87138032Speter	static char hbuf[MAXDNAME];
87238032Speter
87338032Speter	if (DontLockReadFiles)
87438032Speter		sff |= SFF_NOLOCK;
87538032Speter	fname = getenv("HOSTALIASES");
87638032Speter	if (fname == NULL ||
87738032Speter	    (fp = safefopen(fname, O_RDONLY, 0, sff)) == NULL)
87838032Speter		return NULL;
87938032Speter	while (fgets(buf, sizeof buf, fp) != NULL)
88038032Speter	{
88138032Speter		for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++)
88238032Speter			continue;
88338032Speter		if (*p == 0)
88438032Speter		{
88538032Speter			/* syntax error */
88638032Speter			continue;
88738032Speter		}
88838032Speter		*p++ = '\0';
88938032Speter		if (strcasecmp(buf, host) == 0)
89038032Speter			break;
89138032Speter	}
89238032Speter
89338032Speter	if (feof(fp))
89438032Speter	{
89538032Speter		/* no match */
89638032Speter		fclose(fp);
89738032Speter		return NULL;
89838032Speter	}
89938032Speter	fclose(fp);
90038032Speter
90138032Speter	/* got a match; extract the equivalent name */
90238032Speter	while (*p != '\0' && isascii(*p) && isspace(*p))
90338032Speter		p++;
90438032Speter	host = p;
90538032Speter	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
90638032Speter		p++;
90738032Speter	*p = '\0';
90838032Speter	strncpy(hbuf, host, sizeof hbuf - 1);
90938032Speter	hbuf[sizeof hbuf - 1] = '\0';
91038032Speter	return hbuf;
91138032Speter}
91238032Speter
91338032Speter#endif /* NAMED_BIND */
914