domain.c revision 73188
1139823Simp/*
21541Srgrimes * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
31541Srgrimes *	All rights reserved.
41541Srgrimes * Copyright (c) 1986, 1995-1997 Eric P. Allman.  All rights reserved.
51541Srgrimes * Copyright (c) 1988, 1993
61541Srgrimes *	The Regents of the University of California.  All rights reserved.
71541Srgrimes *
81541Srgrimes * By using this file, you agree to the terms and conditions set
91541Srgrimes * forth in the LICENSE file which can be found at the top level of
101541Srgrimes * the sendmail distribution.
111541Srgrimes *
121541Srgrimes */
131541Srgrimes
141541Srgrimes#include <sendmail.h>
151541Srgrimes
161541Srgrimes#ifndef lint
171541Srgrimes# if NAMED_BIND
181541Srgrimesstatic char id[] = "@(#)$Id: domain.c,v 8.114.6.1.2.8 2001/02/12 21:40:19 gshapiro Exp $ (with name server)";
191541Srgrimes# else /* NAMED_BIND */
201541Srgrimesstatic char id[] = "@(#)$Id: domain.c,v 8.114.6.1.2.8 2001/02/12 21:40:19 gshapiro Exp $ (without name server)";
211541Srgrimes# endif /* NAMED_BIND */
221541Srgrimes#endif /* ! lint */
231541Srgrimes
241541Srgrimes
251541Srgrimes#if NAMED_BIND
261541Srgrimes
271541Srgrimes# include <arpa/inet.h>
281541Srgrimes
2910942Swollman/*
3050477Speter**  The standard udp packet size PACKETSZ (512) is not sufficient for some
311541Srgrimes**  nameserver answers containing very many resource records. The resolver
321541Srgrimes**  may switch to tcp and retry if it detects udp packet overflow.
332169Spaul**  Also note that the resolver routines res_query and res_search return
3418940Sbde**  the size of the *un*truncated answer in case the supplied answer buffer
352169Spaul**  it not big enough to accommodate the entire answer.
3678064Sume*/
3778064Sume
381541Srgrimes# ifndef MAXPACKET
391541Srgrimes#  define MAXPACKET 8192	/* max packet size used internally by BIND */
401541Srgrimes# endif /* ! MAXPACKET */
411541Srgrimes
4238513Sdfrtypedef union
431541Srgrimes{
4419183Sfenner	HEADER	qb1;
451541Srgrimes	u_char	qb2[MAXPACKET];
461541Srgrimes} querybuf;
471541Srgrimes
481541Srgrimes# ifndef MXHOSTBUFSIZE
49100419Srwatson#  define MXHOSTBUFSIZE	(128 * MAXMXHOSTS)
501541Srgrimes# endif /* ! MXHOSTBUFSIZE */
511541Srgrimes
521541Srgrimesstatic char	MXHostBuf[MXHOSTBUFSIZE];
531541Srgrimes
541541Srgrimes# ifndef MAXDNSRCH
551541Srgrimes#  define MAXDNSRCH	6	/* number of possible domains to search */
561541Srgrimes# endif /* ! MAXDNSRCH */
5774362Sphk
581541Srgrimes# ifndef RES_DNSRCH_VARIABLE
591541Srgrimes#  define RES_DNSRCH_VARIABLE	_res.dnsrch
601541Srgrimes# endif /* ! RES_DNSRCH_VARIABLE */
6138513Sdfr
621541Srgrimes# ifndef MAX
63111244Ssilby#  define MAX(a, b)	((a) > (b) ? (a) : (b))
64168365Sandre# endif /* ! MAX */
651541Srgrimes
66100419Srwatson# ifndef NO_DATA
671541Srgrimes#  define NO_DATA	NO_ADDRESS
681541Srgrimes# endif /* ! NO_DATA */
69152608Sandre
70152608Sandre# ifndef HFIXEDSZ
71152608Sandre#  define HFIXEDSZ	12	/* sizeof(HEADER) */
72152608Sandre# endif /* ! HFIXEDSZ */
73152608Sandre
74152608Sandre# define MAXCNAMEDEPTH	10	/* maximum depth of CNAME recursion */
75152608Sandre
76152608Sandre# if defined(__RES) && (__RES >= 19940415)
77152608Sandre#  define RES_UNC_T	char *
78152608Sandre# else /* defined(__RES) && (__RES >= 19940415) */
79152608Sandre#  define RES_UNC_T	u_char *
80152608Sandre# endif /* defined(__RES) && (__RES >= 19940415) */
81152608Sandre
821541Srgrimesstatic char	*gethostalias __P((char *));
831541Srgrimesstatic int	mxrand __P((char *));
84170613Sbms
851541Srgrimes/*
861541Srgrimes**  GETMXRR -- get MX resource records for a domain
871541Srgrimes**
8878064Sume**	Parameters:
89158563Sbms**		host -- the name of the host to MX.
901541Srgrimes**		mxhosts -- a pointer to a return buffer of MX records.
911541Srgrimes**		mxprefs -- a pointer to a return buffer of MX preferences.
921541Srgrimes**			If NULL, don't try to populate.
93158563Sbms**		droplocalhost -- If TRUE, all MX records less preferred
94158563Sbms**			than the local host (as determined by $=w) will
95170613Sbms**			be discarded.
961541Srgrimes**		rcode -- a pointer to an EX_ status code.
971541Srgrimes**
981541Srgrimes**	Returns:
991541Srgrimes**		The number of MX records found.
1001541Srgrimes**		-1 if there is an internal failure.
1011541Srgrimes**		If no MX records are found, mxhosts[0] is set to host
1021541Srgrimes**			and 1 is returned.
1031541Srgrimes*/
1041541Srgrimes
1051541Srgrimesint
1061541Srgrimesgetmxrr(host, mxhosts, mxprefs, droplocalhost, rcode)
1071541Srgrimes	char *host;
1081541Srgrimes	char **mxhosts;
10936192Sdg	u_short *mxprefs;
1101541Srgrimes	bool droplocalhost;
1111541Srgrimes	int *rcode;
1121541Srgrimes{
1131541Srgrimes	register u_char *eom, *cp;
1141541Srgrimes	register int i, j, n;
1151541Srgrimes	int nmx = 0;
1161541Srgrimes	register char *bp;
11713765Smpp	HEADER *hp;
1181541Srgrimes	querybuf answer;
1191541Srgrimes	int ancount, qdcount, buflen;
1201541Srgrimes	bool seenlocal = FALSE;
1211541Srgrimes	u_short pref, type;
1221541Srgrimes	u_short localpref = 256;
1231541Srgrimes	char *fallbackMX = FallBackMX;
12419183Sfenner	bool trycanon = FALSE;
12521932Swollman	u_short *prefs;
12652904Sshin	int (*resfunc)();
12778064Sume	u_short prefer[MAXMXHOSTS];
1281541Srgrimes	int weight[MAXMXHOSTS];
1291541Srgrimes	extern int res_query(), res_search();
13055205Speter
13137625Sbde	if (tTd(8, 2))
132195699Srwatson		dprintf("getmxrr(%s, droplocalhost=%d)\n",
133195699Srwatson			host, droplocalhost);
134190951Srwatson
135190951Srwatson	if (fallbackMX != NULL && droplocalhost &&
136190951Srwatson	    wordinclass(fallbackMX, 'w'))
137190951Srwatson	{
138190951Srwatson		/* don't use fallback for this pass */
139170613Sbms		fallbackMX = NULL;
140170613Sbms	}
141170613Sbms
142170613Sbms	*rcode = EX_OK;
143170613Sbms
144168365Sandre	if (mxprefs != NULL)
145168365Sandre		prefs = mxprefs;
1461541Srgrimes	else
147168365Sandre		prefs = prefer;
148168365Sandre
149168365Sandre
150126239Smlaier	/* efficiency hack -- numeric or non-MX lookups */
151126239Smlaier	if (host[0] == '[')
152147744Sthompsa		goto punt;
153147744Sthompsa
154147744Sthompsa	/*
155147744Sthompsa	**  If we don't have MX records in our host switch, don't
156147744Sthompsa	**  try for MX records.  Note that this really isn't "right",
157147744Sthompsa	**  since we might be set up to try NIS first and then DNS;
15838482Swollman	**  if the host is found in NIS we really shouldn't be doing
15919669Sbde	**  MX lookups.  However, that should be a degenerate case.
16018940Sbde	*/
16138482Swollman
16218940Sbde	if (!UseNameServer)
163195699Srwatson		goto punt;
164195699Srwatson	if (HasWildcardMX && ConfigLevel >= 6)
165195699Srwatson		resfunc = res_query;
166195699Srwatson	else
167122723Sandre		resfunc = res_search;
168195699Srwatson
169122723Sandre	errno = 0;
170195699Srwatson	n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer));
171195699Srwatson	if (n < 0)
172195699Srwatson	{
173193502Sluigi		if (tTd(8, 1))
174195727Srwatson			dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
175195727Srwatson			    (host == NULL) ? "<NULL>" : host, errno, h_errno);
176195727Srwatson		switch (h_errno)
177195727Srwatson		{
178195699Srwatson		  case NO_DATA:
179195727Srwatson			trycanon = TRUE;
180195699Srwatson			/* FALLTHROUGH */
181195727Srwatson
182195727Srwatson		  case NO_RECOVERY:
183195727Srwatson			/* no MX data on this host */
184195699Srwatson			goto punt;
185185937Sbz
18692723Salfred		  case HOST_NOT_FOUND:
18792723Salfred# if BROKEN_RES_SEARCH
18822900Swollman		  case 0:	/* Ultrix resolver retns failure w/ h_errno=0 */
1891541Srgrimes# endif /* BROKEN_RES_SEARCH */
190170613Sbms			/* host doesn't exist in DNS; might be in /etc/hosts */
191170613Sbms			trycanon = TRUE;
192170613Sbms			*rcode = EX_NOHOST;
193170613Sbms			goto punt;
194168365Sandre
195168365Sandre		  case TRY_AGAIN:
196168365Sandre		  case -1:
197168365Sandre			/* couldn't connect to the name server */
198118622Shsu			if (fallbackMX != NULL)
199168365Sandre			{
200168365Sandre				/* name server is hosed -- push to fallback */
201168365Sandre				if (nmx > 0)
202168365Sandre					prefs[nmx] = prefs[nmx - 1] + 1;
203168365Sandre				else
204168365Sandre					prefs[nmx] = 0;
205105194Ssam				mxhosts[nmx++] = fallbackMX;
206105194Ssam				return nmx;
207168365Sandre			}
208168365Sandre			/* it might come up later; better queue it up */
209133920Sandre			*rcode = EX_TEMPFAIL;
210168365Sandre			break;
21187120Sru
212178888Sjulian		  default:
213168365Sandre			syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n",
214168365Sandre				host, h_errno);
215168365Sandre			*rcode = EX_OSERR;
216133720Sdwmalone			break;
21798663Sluigi		}
21898663Sluigi
21998663Sluigi		/* irreconcilable differences */
220193731Szec		return -1;
221193731Szec	}
222193731Szec
22398663Sluigi	/* avoid problems after truncation in tcp packets */
22498663Sluigi	if (n > sizeof(answer))
22592723Salfred		n = sizeof(answer);
22692723Salfred
22792723Salfred	/* find first satisfactory answer */
22892723Salfred	hp = (HEADER *)&answer;
229106968Sluigi	cp = (u_char *)&answer + HFIXEDSZ;
230106968Sluigi	eom = (u_char *)&answer + n;
231106968Sluigi	for (qdcount = ntohs((u_short)hp->qdcount);
2327083Swollman	     qdcount--;
233134383Sandre	     cp += n + QFIXEDSZ)
234120386Ssam	{
23560765Sjlemon		if ((n = dn_skipname(cp, eom)) < 0)
23660765Sjlemon			goto punt;
237193502Sluigi	}
238193502Sluigi	buflen = sizeof(MXHostBuf) - 1;
239193502Sluigi	bp = MXHostBuf;
240193502Sluigi	ancount = ntohs((u_short)hp->ancount);
241193502Sluigi	while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
242193502Sluigi	{
243193502Sluigi		if ((n = dn_expand((u_char *)&answer,
244195699Srwatson		    eom, cp, (RES_UNC_T) bp, buflen)) < 0)
245195699Srwatson			break;
246195727Srwatson		cp += n;
247195699Srwatson		GETSHORT(type, cp);
248195699Srwatson		cp += INT16SZ + INT32SZ;
249195699Srwatson		GETSHORT(n, cp);
25055205Speter		if (type != T_MX)
25117072Sjulian		{
25237625Sbde			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				SM_SET_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					SM_SET_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				SM_SET_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				if (WorkAroundBrokenAAAA)
773				{
774					/*
775					**  Only return if not TRY_AGAIN as an
776					**  attempt with a different qtype may
777					**  succeed (res_querydomain() calls
778					**  res_query() calls res_send() which
779					**  sets errno to ETIMEDOUT if the
780					**  nameservers could be contacted but
781					**  didn't give an answer).
782					*/
783
784					if (qtype != T_ANY &&
785					    errno != ETIMEDOUT)
786						return FALSE;
787				}
788#else /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */
789				if (qtype != T_ANY)
790					return FALSE;
791#endif /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */
792			}
793
794			if (h_errno != HOST_NOT_FOUND)
795			{
796				/* might have another type of interest */
797				if (qtype == T_ANY)
798				{
799# if NETINET6
800					qtype = T_AAAA;
801# else /* NETINET6 */
802					qtype = T_A;
803# endif /* NETINET6 */
804					continue;
805				}
806# if NETINET6
807				else if (qtype == T_AAAA)
808				{
809					qtype = T_A;
810					continue;
811				}
812# endif /* NETINET6 */
813				else if (qtype == T_A && !gotmx &&
814					 (trymx || **dp == '\0'))
815				{
816					qtype = T_MX;
817					continue;
818				}
819			}
820
821			/* definite no -- try the next domain */
822			dp++;
823			qtype = T_ANY;
824			continue;
825		}
826		else if (tTd(8, 7))
827			dprintf("\tYES\n");
828
829		/* avoid problems after truncation in tcp packets */
830		if (ret > sizeof(answer))
831			ret = sizeof(answer);
832
833		/*
834		**  Appear to have a match.  Confirm it by searching for A or
835		**  CNAME records.  If we don't have a local domain
836		**  wild card MX record, we will accept MX as well.
837		*/
838
839		hp = (HEADER *) &answer;
840		ap = (u_char *) &answer + HFIXEDSZ;
841		eom = (u_char *) &answer + ret;
842
843		/* skip question part of response -- we know what we asked */
844		for (qdcount = ntohs((u_short)hp->qdcount);
845		     qdcount--;
846		     ap += ret + QFIXEDSZ)
847		{
848			if ((ret = dn_skipname(ap, eom)) < 0)
849			{
850				if (tTd(8, 20))
851					dprintf("qdcount failure (%d)\n",
852						ntohs((u_short)hp->qdcount));
853				*statp = EX_SOFTWARE;
854				return FALSE;		/* ???XXX??? */
855			}
856		}
857
858		amatch = FALSE;
859		for (ancount = ntohs((u_short)hp->ancount);
860		     --ancount >= 0 && ap < eom;
861		     ap += n)
862		{
863			n = dn_expand((u_char *) &answer, eom, ap,
864				      (RES_UNC_T) nbuf, sizeof nbuf);
865			if (n < 0)
866				break;
867			ap += n;
868			GETSHORT(type, ap);
869			ap += INT16SZ + INT32SZ;
870			GETSHORT(n, ap);
871			switch (type)
872			{
873			  case T_MX:
874				gotmx = TRUE;
875				if (**dp != '\0' && HasWildcardMX)
876				{
877					/*
878					**  If we are using MX matches and have
879					**  not yet gotten one, save this one
880					**  but keep searching for an A or
881					**  CNAME match.
882					*/
883
884					if (trymx && mxmatch == NULL)
885						mxmatch = *dp;
886					continue;
887				}
888
889				/*
890				**  If we did not append a domain name, this
891				**  must have been a canonical name to start
892				**  with.  Even if we did append a domain name,
893				**  in the absence of a wildcard MX this must
894				**  still be a real MX match.
895				**  Such MX matches are as good as an A match,
896				**  fall through.
897				*/
898				/* FALLTHROUGH */
899
900# if NETINET6
901			  case T_AAAA:
902				/* Flag that a good match was found */
903				amatch = TRUE;
904
905				/* continue in case a CNAME also exists */
906				continue;
907# endif /* NETINET6 */
908
909			  case T_A:
910				/* Flag that a good match was found */
911				amatch = TRUE;
912
913				/* continue in case a CNAME also exists */
914				continue;
915
916			  case T_CNAME:
917				if (DontExpandCnames)
918				{
919					/* got CNAME -- guaranteed canonical */
920					amatch = TRUE;
921					break;
922				}
923
924				if (loopcnt++ > MAXCNAMEDEPTH)
925				{
926					/*XXX should notify postmaster XXX*/
927					message("DNS failure: CNAME loop for %s",
928						host);
929					if (CurEnv->e_message == NULL)
930					{
931						char ebuf[MAXLINE];
932
933						snprintf(ebuf, sizeof ebuf,
934							"Deferred: DNS failure: CNAME loop for %.100s",
935							host);
936						CurEnv->e_message = newstr(ebuf);
937					}
938					SM_SET_H_ERRNO(NO_RECOVERY);
939					*statp = EX_CONFIG;
940					return FALSE;
941				}
942
943				/* value points at name */
944				if ((ret = dn_expand((u_char *)&answer,
945				    eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0)
946					break;
947				(void)strlcpy(host, nbuf, hbsize);
948
949				/*
950				**  RFC 1034 section 3.6 specifies that CNAME
951				**  should point at the canonical name -- but
952				**  urges software to try again anyway.
953				*/
954
955				goto cnameloop;
956
957			  default:
958				/* not a record of interest */
959				continue;
960			}
961		}
962
963		if (amatch)
964		{
965			/*
966			**  Got a good match -- either an A, CNAME, or an
967			**  exact MX record.  Save it and get out of here.
968			*/
969
970			mxmatch = *dp;
971			break;
972		}
973
974		/*
975		**  Nothing definitive yet.
976		**	If this was a T_ANY query, we don't really know what
977		**		was returned -- it might have been a T_NS,
978		**		for example.  Try T_A to be more specific
979		**		during the next pass.
980		**	If this was a T_A query and we haven't yet found a MX
981		**		match, try T_MX if allowed to do so.
982		**	Otherwise, try the next domain.
983		*/
984
985		if (qtype == T_ANY)
986		{
987# if NETINET6
988			qtype = T_AAAA;
989# else /* NETINET6 */
990			qtype = T_A;
991# endif /* NETINET6 */
992		}
993# if NETINET6
994		else if (qtype == T_AAAA)
995			qtype = T_A;
996# endif /* NETINET6 */
997		else if (qtype == T_A && !gotmx && (trymx || **dp == '\0'))
998			qtype = T_MX;
999		else
1000		{
1001			qtype = T_ANY;
1002			dp++;
1003		}
1004	}
1005
1006	/* if nothing was found, we are done */
1007	if (mxmatch == NULL)
1008	{
1009		if (*statp == EX_OK)
1010			*statp = EX_NOHOST;
1011		return FALSE;
1012	}
1013
1014	/*
1015	**  Create canonical name and return.
1016	**  If saved domain name is null, name was already canonical.
1017	**  Otherwise append the saved domain name.
1018	*/
1019
1020	(void) snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host,
1021			*mxmatch == '\0' ? "" : ".",
1022			MAXDNAME, mxmatch);
1023	(void) strlcpy(host, nbuf, hbsize);
1024	if (tTd(8, 5))
1025		dprintf("dns_getcanonname: %s\n", host);
1026	*statp = EX_OK;
1027	return TRUE;
1028}
1029
1030static char *
1031gethostalias(host)
1032	char *host;
1033{
1034	char *fname;
1035	FILE *fp;
1036	register char *p = NULL;
1037	long sff = SFF_REGONLY;
1038	char buf[MAXLINE];
1039	static char hbuf[MAXDNAME];
1040
1041	if (DontLockReadFiles)
1042		sff |= SFF_NOLOCK;
1043	fname = getenv("HOSTALIASES");
1044	if (fname == NULL ||
1045	    (fp = safefopen(fname, O_RDONLY, 0, sff)) == NULL)
1046		return NULL;
1047	while (fgets(buf, sizeof buf, fp) != NULL)
1048	{
1049		for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++)
1050			continue;
1051		if (*p == 0)
1052		{
1053			/* syntax error */
1054			continue;
1055		}
1056		*p++ = '\0';
1057		if (strcasecmp(buf, host) == 0)
1058			break;
1059	}
1060
1061	if (feof(fp))
1062	{
1063		/* no match */
1064		(void) fclose(fp);
1065		return NULL;
1066	}
1067	(void) fclose(fp);
1068
1069	/* got a match; extract the equivalent name */
1070	while (*p != '\0' && isascii(*p) && isspace(*p))
1071		p++;
1072	host = p;
1073	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1074		p++;
1075	*p = '\0';
1076	(void) strlcpy(hbuf, host, sizeof hbuf);
1077	return hbuf;
1078}
1079#endif /* NAMED_BIND */
1080