xref: /freebsd/contrib/sendmail/src/domain.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
1 /*
2  * Copyright (c) 1998-2001 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
18 static char id[] = "@(#)$Id: domain.c,v 8.114.6.1.2.8 2001/02/12 21:40:19 gshapiro Exp $ (with name server)";
19 # else /* NAMED_BIND */
20 static char id[] = "@(#)$Id: domain.c,v 8.114.6.1.2.8 2001/02/12 21:40:19 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 
42 typedef 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 
52 static 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 
82 static char	*gethostalias __P((char *));
83 static 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 
105 int
106 getmxrr(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 	{
340 punt:
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 
487 static int
488 mxrand(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 */
529 char *
530 bestmx_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 
612 bool
613 dns_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;
662 cnameloop:
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 
1030 static char *
1031 gethostalias(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