domain.c revision 71345
1/*
2 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1986, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#include <sendmail.h>
15
16#ifndef lint
17# if NAMED_BIND
18static char id[] = "@(#)$Id: domain.c,v 8.114.6.1.2.6 2000/12/19 02:50:33 gshapiro Exp $ (with name server)";
19# else /* NAMED_BIND */
20static char id[] = "@(#)$Id: domain.c,v 8.114.6.1.2.6 2000/12/19 02:50:33 gshapiro Exp $ (without name server)";
21# endif /* NAMED_BIND */
22#endif /* ! lint */
23
24
25#if NAMED_BIND
26
27# include <arpa/inet.h>
28
29/*
30**  The standard udp packet size PACKETSZ (512) is not sufficient for some
31**  nameserver answers containing very many resource records. The resolver
32**  may switch to tcp and retry if it detects udp packet overflow.
33**  Also note that the resolver routines res_query and res_search return
34**  the size of the *un*truncated answer in case the supplied answer buffer
35**  it not big enough to accommodate the entire answer.
36*/
37
38# ifndef MAXPACKET
39#  define MAXPACKET 8192	/* max packet size used internally by BIND */
40# endif /* ! MAXPACKET */
41
42typedef union
43{
44	HEADER	qb1;
45	u_char	qb2[MAXPACKET];
46} querybuf;
47
48# ifndef MXHOSTBUFSIZE
49#  define MXHOSTBUFSIZE	(128 * MAXMXHOSTS)
50# endif /* ! MXHOSTBUFSIZE */
51
52static char	MXHostBuf[MXHOSTBUFSIZE];
53
54# ifndef MAXDNSRCH
55#  define MAXDNSRCH	6	/* number of possible domains to search */
56# endif /* ! MAXDNSRCH */
57
58# ifndef RES_DNSRCH_VARIABLE
59#  define RES_DNSRCH_VARIABLE	_res.dnsrch
60# endif /* ! RES_DNSRCH_VARIABLE */
61
62# ifndef MAX
63#  define MAX(a, b)	((a) > (b) ? (a) : (b))
64# endif /* ! MAX */
65
66# ifndef NO_DATA
67#  define NO_DATA	NO_ADDRESS
68# endif /* ! NO_DATA */
69
70# ifndef HFIXEDSZ
71#  define HFIXEDSZ	12	/* sizeof(HEADER) */
72# endif /* ! HFIXEDSZ */
73
74# define MAXCNAMEDEPTH	10	/* maximum depth of CNAME recursion */
75
76# if defined(__RES) && (__RES >= 19940415)
77#  define RES_UNC_T	char *
78# else /* defined(__RES) && (__RES >= 19940415) */
79#  define RES_UNC_T	u_char *
80# endif /* defined(__RES) && (__RES >= 19940415) */
81
82static char	*gethostalias __P((char *));
83static int	mxrand __P((char *));
84
85/*
86**  GETMXRR -- get MX resource records for a domain
87**
88**	Parameters:
89**		host -- the name of the host to MX.
90**		mxhosts -- a pointer to a return buffer of MX records.
91**		mxprefs -- a pointer to a return buffer of MX preferences.
92**			If NULL, don't try to populate.
93**		droplocalhost -- If TRUE, all MX records less preferred
94**			than the local host (as determined by $=w) will
95**			be discarded.
96**		rcode -- a pointer to an EX_ status code.
97**
98**	Returns:
99**		The number of MX records found.
100**		-1 if there is an internal failure.
101**		If no MX records are found, mxhosts[0] is set to host
102**			and 1 is returned.
103*/
104
105int
106getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode)
107	char *host;
108	char **mxhosts;
109	u_short *mxprefs;
110	bool droplocalhost;
111	int *rcode;
112{
113	register u_char *eom, *cp;
114	register int i, j, n;
115	int nmx = 0;
116	register char *bp;
117	HEADER *hp;
118	querybuf answer;
119	int ancount, qdcount, buflen;
120	bool seenlocal = FALSE;
121	u_short pref, type;
122	u_short localpref = 256;
123	char *fallbackMX = FallBackMX;
124	bool trycanon = FALSE;
125	u_short *prefs;
126	int (*resfunc)();
127	u_short prefer[MAXMXHOSTS];
128	int weight[MAXMXHOSTS];
129	extern int res_query(), res_search();
130
131	if (tTd(8, 2))
132		dprintf("getmxrr(%s, droplocalhost=%d)\n",
133			host, droplocalhost);
134
135	if (fallbackMX != NULL && droplocalhost &&
136	    wordinclass(fallbackMX, 'w'))
137	{
138		/* don't use fallback for this pass */
139		fallbackMX = NULL;
140	}
141
142	*rcode = EX_OK;
143
144	if (mxprefs != NULL)
145		prefs = mxprefs;
146	else
147		prefs = prefer;
148
149
150	/* efficiency hack -- numeric or non-MX lookups */
151	if (host[0] == '[')
152		goto punt;
153
154	/*
155	**  If we don't have MX records in our host switch, don't
156	**  try for MX records.  Note that this really isn't "right",
157	**  since we might be set up to try NIS first and then DNS;
158	**  if the host is found in NIS we really shouldn't be doing
159	**  MX lookups.  However, that should be a degenerate case.
160	*/
161
162	if (!UseNameServer)
163		goto punt;
164	if (HasWildcardMX && ConfigLevel >= 6)
165		resfunc = res_query;
166	else
167		resfunc = res_search;
168
169	errno = 0;
170	n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer));
171	if (n < 0)
172	{
173		if (tTd(8, 1))
174			dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
175			    (host == NULL) ? "<NULL>" : host, errno, h_errno);
176		switch (h_errno)
177		{
178		  case NO_DATA:
179			trycanon = TRUE;
180			/* FALLTHROUGH */
181
182		  case NO_RECOVERY:
183			/* no MX data on this host */
184			goto punt;
185
186		  case HOST_NOT_FOUND:
187# if BROKEN_RES_SEARCH
188		  case 0:	/* Ultrix resolver retns failure w/ h_errno=0 */
189# endif /* BROKEN_RES_SEARCH */
190			/* host doesn't exist in DNS; might be in /etc/hosts */
191			trycanon = TRUE;
192			*rcode = EX_NOHOST;
193			goto punt;
194
195		  case TRY_AGAIN:
196		  case -1:
197			/* couldn't connect to the name server */
198			if (fallbackMX != NULL)
199			{
200				/* name server is hosed -- push to fallback */
201				if (nmx > 0)
202					prefs[nmx] = prefs[nmx - 1] + 1;
203				else
204					prefs[nmx] = 0;
205				mxhosts[nmx++] = fallbackMX;
206				return nmx;
207			}
208			/* it might come up later; better queue it up */
209			*rcode = EX_TEMPFAIL;
210			break;
211
212		  default:
213			syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n",
214				host, h_errno);
215			*rcode = EX_OSERR;
216			break;
217		}
218
219		/* irreconcilable differences */
220		return -1;
221	}
222
223	/* avoid problems after truncation in tcp packets */
224	if (n > sizeof(answer))
225		n = sizeof(answer);
226
227	/* find first satisfactory answer */
228	hp = (HEADER *)&answer;
229	cp = (u_char *)&answer + HFIXEDSZ;
230	eom = (u_char *)&answer + n;
231	for (qdcount = ntohs((u_short)hp->qdcount);
232	     qdcount--;
233	     cp += n + QFIXEDSZ)
234	{
235		if ((n = dn_skipname(cp, eom)) < 0)
236			goto punt;
237	}
238	buflen = sizeof(MXHostBuf) - 1;
239	bp = MXHostBuf;
240	ancount = ntohs((u_short)hp->ancount);
241	while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
242	{
243		if ((n = dn_expand((u_char *)&answer,
244		    eom, cp, (RES_UNC_T) bp, buflen)) < 0)
245			break;
246		cp += n;
247		GETSHORT(type, cp);
248		cp += INT16SZ + INT32SZ;
249		GETSHORT(n, cp);
250		if (type != T_MX)
251		{
252			if (tTd(8, 8) || _res.options & RES_DEBUG)
253				dprintf("unexpected answer type %d, size %d\n",
254					type, n);
255			cp += n;
256			continue;
257		}
258		GETSHORT(pref, cp);
259		if ((n = dn_expand((u_char *)&answer, eom, cp,
260				   (RES_UNC_T) bp, buflen)) < 0)
261			break;
262		cp += n;
263		if (wordinclass(bp, 'w'))
264		{
265			if (tTd(8, 3))
266				dprintf("found localhost (%s) in MX list, pref=%d\n",
267					bp, pref);
268			if (droplocalhost)
269			{
270				if (!seenlocal || pref < localpref)
271					localpref = pref;
272				seenlocal = TRUE;
273				continue;
274			}
275			weight[nmx] = 0;
276		}
277		else
278			weight[nmx] = mxrand(bp);
279		prefs[nmx] = pref;
280		mxhosts[nmx++] = bp;
281		n = strlen(bp);
282		bp += n;
283		if (bp[-1] != '.')
284		{
285			*bp++ = '.';
286			n++;
287		}
288		*bp++ = '\0';
289		buflen -= n + 1;
290	}
291
292	/* sort the records */
293	for (i = 0; i < nmx; i++)
294	{
295		for (j = i + 1; j < nmx; j++)
296		{
297			if (prefs[i] > prefs[j] ||
298			    (prefs[i] == prefs[j] && weight[i] > weight[j]))
299			{
300				register int temp;
301				register char *temp1;
302
303				temp = prefs[i];
304				prefs[i] = prefs[j];
305				prefs[j] = temp;
306				temp1 = mxhosts[i];
307				mxhosts[i] = mxhosts[j];
308				mxhosts[j] = temp1;
309				temp = weight[i];
310				weight[i] = weight[j];
311				weight[j] = temp;
312			}
313		}
314		if (seenlocal && prefs[i] >= localpref)
315		{
316			/* truncate higher preference part of list */
317			nmx = i;
318		}
319	}
320
321	/* delete duplicates from list (yes, some bozos have duplicates) */
322	for (i = 0; i < nmx - 1; )
323	{
324		if (strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0)
325			i++;
326		else
327		{
328			/* compress out duplicate */
329			for (j = i + 1; j < nmx; j++)
330			{
331				mxhosts[j] = mxhosts[j + 1];
332				prefs[j] = prefs[j + 1];
333			}
334			nmx--;
335		}
336	}
337
338	if (nmx == 0)
339	{
340punt:
341		if (seenlocal)
342		{
343			struct hostent *h = NULL;
344
345			/*
346			**  If we have deleted all MX entries, this is
347			**  an error -- we should NEVER send to a host that
348			**  has an MX, and this should have been caught
349			**  earlier in the config file.
350			**
351			**  Some sites prefer to go ahead and try the
352			**  A record anyway; that case is handled by
353			**  setting TryNullMXList.  I believe this is a
354			**  bad idea, but it's up to you....
355			*/
356
357			if (TryNullMXList)
358			{
359				h_errno = 0;
360				errno = 0;
361				h = sm_gethostbyname(host, AF_INET);
362				if (h == NULL)
363				{
364					if (errno == ETIMEDOUT ||
365					    h_errno == TRY_AGAIN ||
366					    (errno == ECONNREFUSED &&
367					     UseNameServer))
368					{
369						*rcode = EX_TEMPFAIL;
370						return -1;
371					}
372# if NETINET6
373					h_errno = 0;
374					errno = 0;
375					h = sm_gethostbyname(host, AF_INET6);
376					if (h == NULL &&
377					    (errno == ETIMEDOUT ||
378					     h_errno == TRY_AGAIN ||
379					     (errno == ECONNREFUSED &&
380					      UseNameServer)))
381					{
382						*rcode = EX_TEMPFAIL;
383						return -1;
384					}
385# endif /* NETINET6 */
386				}
387			}
388
389			if (h == NULL)
390			{
391				*rcode = EX_CONFIG;
392				syserr("MX list for %s points back to %s",
393				       host, MyHostName);
394				return -1;
395			}
396# if _FFR_FREEHOSTENT && NETINET6
397			freehostent(h);
398			hp = NULL;
399# endif /* _FFR_FREEHOSTENT && NETINET6 */
400		}
401		if (strlen(host) >= (SIZE_T) sizeof MXHostBuf)
402		{
403			*rcode = EX_CONFIG;
404			syserr("Host name %s too long",
405			       shortenstring(host, MAXSHORTSTR));
406			return -1;
407		}
408		snprintf(MXHostBuf, sizeof MXHostBuf, "%s", host);
409		mxhosts[0] = MXHostBuf;
410		prefs[0] = 0;
411		if (host[0] == '[')
412		{
413			register char *p;
414# if NETINET6
415			struct sockaddr_in6 tmp6;
416# endif /* NETINET6 */
417
418			/* this may be an MX suppression-style address */
419			p = strchr(MXHostBuf, ']');
420			if (p != NULL)
421			{
422				*p = '\0';
423
424				if (inet_addr(&MXHostBuf[1]) != INADDR_NONE)
425				{
426					nmx++;
427					*p = ']';
428				}
429# if NETINET6
430				else if (inet_pton(AF_INET6, &MXHostBuf[1],
431						   &tmp6.sin6_addr) == 1)
432				{
433					nmx++;
434					*p = ']';
435				}
436# endif /* NETINET6 */
437				else
438				{
439					trycanon = TRUE;
440					mxhosts[0]++;
441				}
442			}
443		}
444		if (trycanon &&
445		    getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE))
446		{
447			bp = &MXHostBuf[strlen(MXHostBuf)];
448			if (bp[-1] != '.')
449			{
450				*bp++ = '.';
451				*bp = '\0';
452			}
453			nmx = 1;
454		}
455	}
456
457	/* if we have a default lowest preference, include that */
458	if (fallbackMX != NULL && !seenlocal)
459	{
460		if (nmx > 0)
461			prefs[nmx] = prefs[nmx - 1] + 1;
462		else
463			prefs[nmx] = 0;
464		mxhosts[nmx++] = fallbackMX;
465	}
466
467	return nmx;
468}
469/*
470**  MXRAND -- create a randomizer for equal MX preferences
471**
472**	If two MX hosts have equal preferences we want to randomize
473**	the selection.  But in order for signatures to be the same,
474**	we need to randomize the same way each time.  This function
475**	computes a pseudo-random hash function from the host name.
476**
477**	Parameters:
478**		host -- the name of the host.
479**
480**	Returns:
481**		A random but repeatable value based on the host name.
482**
483**	Side Effects:
484**		none.
485*/
486
487static int
488mxrand(host)
489	register char *host;
490{
491	int hfunc;
492	static unsigned int seed;
493
494	if (seed == 0)
495	{
496		seed = (int) curtime() & 0xffff;
497		if (seed == 0)
498			seed++;
499	}
500
501	if (tTd(17, 9))
502		dprintf("mxrand(%s)", host);
503
504	hfunc = seed;
505	while (*host != '\0')
506	{
507		int c = *host++;
508
509		if (isascii(c) && isupper(c))
510			c = tolower(c);
511		hfunc = ((hfunc << 1) ^ c) % 2003;
512	}
513
514	hfunc &= 0xff;
515	hfunc++;
516
517	if (tTd(17, 9))
518		dprintf(" = %d\n", hfunc);
519	return hfunc;
520}
521/*
522**  BESTMX -- find the best MX for a name
523**
524**	This is really a hack, but I don't see any obvious way
525**	to generalize it at the moment.
526*/
527
528/* ARGSUSED3 */
529char *
530bestmx_map_lookup(map, name, av, statp)
531	MAP *map;
532	char *name;
533	char **av;
534	int *statp;
535{
536	int nmx;
537	int saveopts = _res.options;
538	int i, len = 0;
539	char *p;
540	char *mxhosts[MAXMXHOSTS + 1];
541	char buf[PSBUFSIZE / 2];
542
543	_res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
544	nmx = getmxrr(name, mxhosts, NULL, FALSE, statp);
545	_res.options = saveopts;
546	if (nmx <= 0)
547		return NULL;
548	if (bitset(MF_MATCHONLY, map->map_mflags))
549		return map_rewrite(map, name, strlen(name), NULL);
550	if ((map->map_coldelim == '\0') || (nmx == 1))
551		return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
552
553	/*
554	**  We were given a -z flag (return all MXs) and there are multiple
555	**  ones.  We need to build them all into a list.
556	*/
557	p = buf;
558	for (i = 0; i < nmx; i++)
559	{
560		int slen;
561
562		if (strchr(mxhosts[i], map->map_coldelim) != NULL)
563		{
564			syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
565			       mxhosts[i], map->map_coldelim);
566			return NULL;
567		}
568		slen = strlen(mxhosts[i]);
569		if (len + slen + 2 > sizeof buf)
570			break;
571		if (i > 0)
572		{
573			*p++ = map->map_coldelim;
574			len++;
575		}
576		(void) strlcpy(p, mxhosts[i], sizeof buf - len);
577		p += slen;
578		len += slen;
579	}
580	return map_rewrite(map, buf, len, av);
581}
582/*
583**  DNS_GETCANONNAME -- get the canonical name for named host using DNS
584**
585**	This algorithm tries to be smart about wildcard MX records.
586**	This is hard to do because DNS doesn't tell is if we matched
587**	against a wildcard or a specific MX.
588**
589**	We always prefer A & CNAME records, since these are presumed
590**	to be specific.
591**
592**	If we match an MX in one pass and lose it in the next, we use
593**	the old one.  For example, consider an MX matching *.FOO.BAR.COM.
594**	A hostname bletch.foo.bar.com will match against this MX, but
595**	will stop matching when we try bletch.bar.com -- so we know
596**	that bletch.foo.bar.com must have been right.  This fails if
597**	there was also an MX record matching *.BAR.COM, but there are
598**	some things that just can't be fixed.
599**
600**	Parameters:
601**		host -- a buffer containing the name of the host.
602**			This is a value-result parameter.
603**		hbsize -- the size of the host buffer.
604**		trymx -- if set, try MX records as well as A and CNAME.
605**		statp -- pointer to place to store status.
606**
607**	Returns:
608**		TRUE -- if the host matched.
609**		FALSE -- otherwise.
610*/
611
612bool
613dns_getcanonname(host, hbsize, trymx, statp)
614	char *host;
615	int hbsize;
616	bool trymx;
617	int *statp;
618{
619	register u_char *eom, *ap;
620	register char *cp;
621	register int n;
622	HEADER *hp;
623	querybuf answer;
624	int ancount, qdcount;
625	int ret;
626	char **domain;
627	int type;
628	char **dp;
629	char *mxmatch;
630	bool amatch;
631	bool gotmx = FALSE;
632	int qtype;
633	int loopcnt;
634	char *xp;
635	char nbuf[MAX(MAXPACKET, MAXDNAME*2+2)];
636	char *searchlist[MAXDNSRCH+2];
637
638	if (tTd(8, 2))
639		dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
640
641	if ((_res.options & RES_INIT) == 0 && res_init() == -1)
642	{
643		*statp = EX_UNAVAILABLE;
644		return FALSE;
645	}
646
647	*statp = EX_OK;
648
649	/*
650	**  Initialize domain search list.  If there is at least one
651	**  dot in the name, search the unmodified name first so we
652	**  find "vse.CS" in Czechoslovakia instead of in the local
653	**  domain (e.g., vse.CS.Berkeley.EDU).  Note that there is no
654	**  longer a country named Czechoslovakia but this type of problem
655	**  is still present.
656	**
657	**  Older versions of the resolver could create this
658	**  list by tearing apart the host name.
659	*/
660
661	loopcnt = 0;
662cnameloop:
663	/* Check for dots in the name */
664	for (cp = host, n = 0; *cp != '\0'; cp++)
665		if (*cp == '.')
666			n++;
667
668	/*
669	**  If this is a simple name, determine whether it matches an
670	**  alias in the file defined by the environment variable HOSTALIASES.
671	*/
672	if (n == 0 && (xp = gethostalias(host)) != NULL)
673	{
674		if (loopcnt++ > MAXCNAMEDEPTH)
675		{
676			syserr("loop in ${HOSTALIASES} file");
677		}
678		else
679		{
680			(void) strlcpy(host, xp, hbsize);
681			goto cnameloop;
682		}
683	}
684
685	/*
686	**  Build the search list.
687	**	If there is at least one dot in name, start with a null
688	**	domain to search the unmodified name first.
689	**	If name does not end with a dot and search up local domain
690	**	tree desired, append each local domain component to the
691	**	search list; if name contains no dots and default domain
692	**	name is desired, append default domain name to search list;
693	**	else if name ends in a dot, remove that dot.
694	*/
695
696	dp = searchlist;
697	if (n > 0)
698		*dp++ = "";
699	if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options))
700	{
701		/* make sure there are less than MAXDNSRCH domains */
702		for (domain = RES_DNSRCH_VARIABLE, ret = 0;
703		     *domain != NULL && ret < MAXDNSRCH;
704		     ret++)
705			*dp++ = *domain++;
706	}
707	else if (n == 0 && bitset(RES_DEFNAMES, _res.options))
708	{
709		*dp++ = _res.defdname;
710	}
711	else if (*cp == '.')
712	{
713		*cp = '\0';
714	}
715	*dp = NULL;
716
717	/*
718	**  Now loop through the search list, appending each domain in turn
719	**  name and searching for a match.
720	*/
721
722	mxmatch = NULL;
723	qtype = T_ANY;
724
725	for (dp = searchlist; *dp != NULL; )
726	{
727		if (qtype == T_ANY)
728			gotmx = FALSE;
729		if (tTd(8, 5))
730			dprintf("dns_getcanonname: trying %s.%s (%s)\n",
731				host, *dp,
732				qtype == T_ANY ? "ANY" :
733# if NETINET6
734				qtype == T_AAAA ? "AAAA" :
735# endif /* NETINET6 */
736				qtype == T_A ? "A" :
737				qtype == T_MX ? "MX" :
738				"???");
739		errno = 0;
740		ret = res_querydomain(host, *dp, C_IN, qtype,
741				      answer.qb2, sizeof(answer.qb2));
742		if (ret <= 0)
743		{
744			if (tTd(8, 7))
745				dprintf("\tNO: errno=%d, h_errno=%d\n",
746					errno, h_errno);
747
748			if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
749			{
750				/*
751				**  the name server seems to be down or
752				**  broken.
753				*/
754
755				h_errno = TRY_AGAIN;
756				*statp = EX_TEMPFAIL;
757
758				/*
759				**  If the ANY query is larger than the
760				**  UDP packet size, the resolver will
761				**  fall back to TCP.  However, some
762				**  misconfigured firewalls block 53/TCP
763				**  so the ANY lookup fails whereas an MX
764				**  or A record might work.  Therefore,
765				**  don't fail on ANY queries.
766				**
767				**  The ANY query is really meant to prime
768				**  the cache so this isn't dangerous.
769				*/
770
771#if _FFR_WORKAROUND_BROKEN_NAMESERVERS
772				/*
773				**  Only return if not TRY_AGAIN as an
774				**  attempt with a different qtype may
775				**  succeed (res_querydomain() calls
776				**  res_query() calls res_send() which
777				**  sets errno to ETIMEDOUT if the
778				**  nameservers could be contacted but
779				**  didn't give an answer).
780				*/
781
782				if (qtype != T_ANY && errno != ETIMEDOUT)
783					return FALSE;
784#else /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */
785				if (qtype != T_ANY)
786					return FALSE;
787#endif /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */
788			}
789
790			if (h_errno != HOST_NOT_FOUND)
791			{
792				/* might have another type of interest */
793				if (qtype == T_ANY)
794				{
795# if NETINET6
796					qtype = T_AAAA;
797# else /* NETINET6 */
798					qtype = T_A;
799# endif /* NETINET6 */
800					continue;
801				}
802# if NETINET6
803				else if (qtype == T_AAAA)
804				{
805					qtype = T_A;
806					continue;
807				}
808# endif /* NETINET6 */
809				else if (qtype == T_A && !gotmx &&
810					 (trymx || **dp == '\0'))
811				{
812					qtype = T_MX;
813					continue;
814				}
815			}
816
817			/* definite no -- try the next domain */
818			dp++;
819			qtype = T_ANY;
820			continue;
821		}
822		else if (tTd(8, 7))
823			dprintf("\tYES\n");
824
825		/* avoid problems after truncation in tcp packets */
826		if (ret > sizeof(answer))
827			ret = sizeof(answer);
828
829		/*
830		**  Appear to have a match.  Confirm it by searching for A or
831		**  CNAME records.  If we don't have a local domain
832		**  wild card MX record, we will accept MX as well.
833		*/
834
835		hp = (HEADER *) &answer;
836		ap = (u_char *) &answer + HFIXEDSZ;
837		eom = (u_char *) &answer + ret;
838
839		/* skip question part of response -- we know what we asked */
840		for (qdcount = ntohs((u_short)hp->qdcount);
841		     qdcount--;
842		     ap += ret + QFIXEDSZ)
843		{
844			if ((ret = dn_skipname(ap, eom)) < 0)
845			{
846				if (tTd(8, 20))
847					dprintf("qdcount failure (%d)\n",
848						ntohs((u_short)hp->qdcount));
849				*statp = EX_SOFTWARE;
850				return FALSE;		/* ???XXX??? */
851			}
852		}
853
854		amatch = FALSE;
855		for (ancount = ntohs((u_short)hp->ancount);
856		     --ancount >= 0 && ap < eom;
857		     ap += n)
858		{
859			n = dn_expand((u_char *) &answer, eom, ap,
860				      (RES_UNC_T) nbuf, sizeof nbuf);
861			if (n < 0)
862				break;
863			ap += n;
864			GETSHORT(type, ap);
865			ap += INT16SZ + INT32SZ;
866			GETSHORT(n, ap);
867			switch (type)
868			{
869			  case T_MX:
870				gotmx = TRUE;
871				if (**dp != '\0' && HasWildcardMX)
872				{
873					/*
874					**  If we are using MX matches and have
875					**  not yet gotten one, save this one
876					**  but keep searching for an A or
877					**  CNAME match.
878					*/
879
880					if (trymx && mxmatch == NULL)
881						mxmatch = *dp;
882					continue;
883				}
884
885				/*
886				**  If we did not append a domain name, this
887				**  must have been a canonical name to start
888				**  with.  Even if we did append a domain name,
889				**  in the absence of a wildcard MX this must
890				**  still be a real MX match.
891				**  Such MX matches are as good as an A match,
892				**  fall through.
893				*/
894				/* FALLTHROUGH */
895
896# if NETINET6
897			  case T_AAAA:
898				/* Flag that a good match was found */
899				amatch = TRUE;
900
901				/* continue in case a CNAME also exists */
902				continue;
903# endif /* NETINET6 */
904
905			  case T_A:
906				/* Flag that a good match was found */
907				amatch = TRUE;
908
909				/* continue in case a CNAME also exists */
910				continue;
911
912			  case T_CNAME:
913				if (DontExpandCnames)
914				{
915					/* got CNAME -- guaranteed canonical */
916					amatch = TRUE;
917					break;
918				}
919
920				if (loopcnt++ > MAXCNAMEDEPTH)
921				{
922					/*XXX should notify postmaster XXX*/
923					message("DNS failure: CNAME loop for %s",
924						host);
925					if (CurEnv->e_message == NULL)
926					{
927						char ebuf[MAXLINE];
928
929						snprintf(ebuf, sizeof ebuf,
930							"Deferred: DNS failure: CNAME loop for %.100s",
931							host);
932						CurEnv->e_message = newstr(ebuf);
933					}
934					h_errno = NO_RECOVERY;
935					*statp = EX_CONFIG;
936					return FALSE;
937				}
938
939				/* value points at name */
940				if ((ret = dn_expand((u_char *)&answer,
941				    eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0)
942					break;
943				(void)strlcpy(host, nbuf, hbsize);
944
945				/*
946				**  RFC 1034 section 3.6 specifies that CNAME
947				**  should point at the canonical name -- but
948				**  urges software to try again anyway.
949				*/
950
951				goto cnameloop;
952
953			  default:
954				/* not a record of interest */
955				continue;
956			}
957		}
958
959		if (amatch)
960		{
961			/*
962			**  Got a good match -- either an A, CNAME, or an
963			**  exact MX record.  Save it and get out of here.
964			*/
965
966			mxmatch = *dp;
967			break;
968		}
969
970		/*
971		**  Nothing definitive yet.
972		**	If this was a T_ANY query, we don't really know what
973		**		was returned -- it might have been a T_NS,
974		**		for example.  Try T_A to be more specific
975		**		during the next pass.
976		**	If this was a T_A query and we haven't yet found a MX
977		**		match, try T_MX if allowed to do so.
978		**	Otherwise, try the next domain.
979		*/
980
981		if (qtype == T_ANY)
982		{
983# if NETINET6
984			qtype = T_AAAA;
985# else /* NETINET6 */
986			qtype = T_A;
987# endif /* NETINET6 */
988		}
989# if NETINET6
990		else if (qtype == T_AAAA)
991			qtype = T_A;
992# endif /* NETINET6 */
993		else if (qtype == T_A && !gotmx && (trymx || **dp == '\0'))
994			qtype = T_MX;
995		else
996		{
997			qtype = T_ANY;
998			dp++;
999		}
1000	}
1001
1002	/* if nothing was found, we are done */
1003	if (mxmatch == NULL)
1004	{
1005		if (*statp == EX_OK)
1006			*statp = EX_NOHOST;
1007		return FALSE;
1008	}
1009
1010	/*
1011	**  Create canonical name and return.
1012	**  If saved domain name is null, name was already canonical.
1013	**  Otherwise append the saved domain name.
1014	*/
1015
1016	(void) snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host,
1017			*mxmatch == '\0' ? "" : ".",
1018			MAXDNAME, mxmatch);
1019	(void) strlcpy(host, nbuf, hbsize);
1020	if (tTd(8, 5))
1021		dprintf("dns_getcanonname: %s\n", host);
1022	*statp = EX_OK;
1023	return TRUE;
1024}
1025
1026static char *
1027gethostalias(host)
1028	char *host;
1029{
1030	char *fname;
1031	FILE *fp;
1032	register char *p = NULL;
1033	long sff = SFF_REGONLY;
1034	char buf[MAXLINE];
1035	static char hbuf[MAXDNAME];
1036
1037	if (DontLockReadFiles)
1038		sff |= SFF_NOLOCK;
1039	fname = getenv("HOSTALIASES");
1040	if (fname == NULL ||
1041	    (fp = safefopen(fname, O_RDONLY, 0, sff)) == NULL)
1042		return NULL;
1043	while (fgets(buf, sizeof buf, fp) != NULL)
1044	{
1045		for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++)
1046			continue;
1047		if (*p == 0)
1048		{
1049			/* syntax error */
1050			continue;
1051		}
1052		*p++ = '\0';
1053		if (strcasecmp(buf, host) == 0)
1054			break;
1055	}
1056
1057	if (feof(fp))
1058	{
1059		/* no match */
1060		(void) fclose(fp);
1061		return NULL;
1062	}
1063	(void) fclose(fp);
1064
1065	/* got a match; extract the equivalent name */
1066	while (*p != '\0' && isascii(*p) && isspace(*p))
1067		p++;
1068	host = p;
1069	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1070		p++;
1071	*p = '\0';
1072	(void) strlcpy(hbuf, host, sizeof hbuf);
1073	return hbuf;
1074}
1075#endif /* NAMED_BIND */
1076