diff options
Diffstat (limited to 'contrib/sendmail/src/domain.c')
-rw-r--r-- | contrib/sendmail/src/domain.c | 1248 |
1 files changed, 0 insertions, 1248 deletions
diff --git a/contrib/sendmail/src/domain.c b/contrib/sendmail/src/domain.c deleted file mode 100644 index f086d80854310..0000000000000 --- a/contrib/sendmail/src/domain.c +++ /dev/null @@ -1,1248 +0,0 @@ -/* - * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers. - * All rights reserved. - * Copyright (c) 1986, 1995-1997 Eric P. Allman. All rights reserved. - * Copyright (c) 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * By using this file, you agree to the terms and conditions set - * forth in the LICENSE file which can be found at the top level of - * the sendmail distribution. - * - */ - -#include <sendmail.h> - -#if NAMED_BIND -SM_RCSID("@(#)$Id: domain.c,v 8.181.2.1 2002/06/27 16:55:04 ca Exp $ (with name server)") -#else /* NAMED_BIND */ -SM_RCSID("@(#)$Id: domain.c,v 8.181.2.1 2002/06/27 16:55:04 ca Exp $ (without name server)") -#endif /* NAMED_BIND */ - -#if NAMED_BIND - -# include <arpa/inet.h> - - -/* -** The standard udp packet size PACKETSZ (512) is not sufficient for some -** nameserver answers containing very many resource records. The resolver -** may switch to tcp and retry if it detects udp packet overflow. -** Also note that the resolver routines res_query and res_search return -** the size of the *un*truncated answer in case the supplied answer buffer -** it not big enough to accommodate the entire answer. -*/ - -# ifndef MAXPACKET -# define MAXPACKET 8192 /* max packet size used internally by BIND */ -# endif /* ! MAXPACKET */ - -typedef union -{ - HEADER qb1; - unsigned char qb2[MAXPACKET]; -} querybuf; - -# ifndef MXHOSTBUFSIZE -# define MXHOSTBUFSIZE (128 * MAXMXHOSTS) -# endif /* ! MXHOSTBUFSIZE */ - -static char MXHostBuf[MXHOSTBUFSIZE]; -#if (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) - ERROR: _MXHOSTBUFSIZE is out of range -#endif /* (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) */ - -# ifndef MAXDNSRCH -# define MAXDNSRCH 6 /* number of possible domains to search */ -# endif /* ! MAXDNSRCH */ - -# ifndef RES_DNSRCH_VARIABLE -# define RES_DNSRCH_VARIABLE _res.dnsrch -# endif /* ! RES_DNSRCH_VARIABLE */ - -# ifndef NO_DATA -# define NO_DATA NO_ADDRESS -# endif /* ! NO_DATA */ - -# ifndef HFIXEDSZ -# define HFIXEDSZ 12 /* sizeof(HEADER) */ -# endif /* ! HFIXEDSZ */ - -# define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ - -# if defined(__RES) && (__RES >= 19940415) -# define RES_UNC_T char * -# else /* defined(__RES) && (__RES >= 19940415) */ -# define RES_UNC_T unsigned char * -# endif /* defined(__RES) && (__RES >= 19940415) */ - -static char *gethostalias __P((char *)); -static int mxrand __P((char *)); -static int fallbackmxrr __P((int, unsigned short *, char **)); - -/* -** GETFALLBACKMXRR -- get MX resource records for fallback MX host. -** -** We have to initialize this once before doing anything else. -** Moreover, we have to repeat this from time to time to avoid -** stale data, e.g., in persistent queue runners. -** This should be done in a parent process so the child -** processes have the right data. -** -** Parameters: -** host -- the name of the fallback MX host. -** -** Returns: -** number of MX records. -** -** Side Effects: -** Populates NumFallBackMXHosts and fbhosts. -** Sets renewal time (based on TTL). -*/ - -int NumFallBackMXHosts = 0; /* Number of fallback MX hosts (after MX expansion) */ -static char *fbhosts[MAXMXHOSTS + 1]; - -int -getfallbackmxrr(host) - char *host; -{ - int i, rcode; - int ttl; - static time_t renew = 0; - -#if 0 - /* This is currently done before this function is called. */ - if (host == NULL || *host == '\0') - return 0; -#endif /* 0 */ - if (NumFallBackMXHosts > 0 && renew > curtime()) - return NumFallBackMXHosts; - if (host[0] == '[') - { - fbhosts[0] = host; - NumFallBackMXHosts = 1; - } - else - { - /* free old data */ - for (i = 0; i < NumFallBackMXHosts; i++) - sm_free(fbhosts[i]); - - /* get new data */ - NumFallBackMXHosts = getmxrr(host, fbhosts, NULL, false, - &rcode, false, &ttl); - renew = curtime() + ttl; - for (i = 0; i < NumFallBackMXHosts; i++) - fbhosts[i] = newstr(fbhosts[i]); - } - return NumFallBackMXHosts; -} - -/* -** FALLBACKMXRR -- add MX resource records for fallback MX host to list. -** -** Parameters: -** nmx -- current number of MX records. -** prefs -- array of preferences. -** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS) -** -** Returns: -** new number of MX records. -** -** Side Effects: -** If FallBackMX was set, it appends the MX records for -** that host to mxhosts (and modifies prefs accordingly). -*/ - -static int -fallbackmxrr(nmx, prefs, mxhosts) - int nmx; - unsigned short *prefs; - char **mxhosts; -{ - int i; - - for (i = 0; i < NumFallBackMXHosts && nmx < MAXMXHOSTS; i++) - { - if (nmx > 0) - prefs[nmx] = prefs[nmx - 1] + 1; - else - prefs[nmx] = 0; - mxhosts[nmx++] = fbhosts[i]; - } - return nmx; -} - -/* -** GETMXRR -- get MX resource records for a domain -** -** Parameters: -** host -- the name of the host to MX. -** mxhosts -- a pointer to a return buffer of MX records. -** mxprefs -- a pointer to a return buffer of MX preferences. -** If NULL, don't try to populate. -** droplocalhost -- If true, all MX records less preferred -** than the local host (as determined by $=w) will -** be discarded. -** rcode -- a pointer to an EX_ status code. -** tryfallback -- add also fallback MX host? -** pttl -- pointer to return TTL (can be NULL). -** -** Returns: -** The number of MX records found. -** -1 if there is an internal failure. -** If no MX records are found, mxhosts[0] is set to host -** and 1 is returned. -** -** Side Effects: -** The entries made for mxhosts point to a static array -** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied, -** if it must be preserved across calls to this function. -*/ - -int -getmxrr(host, mxhosts, mxprefs, droplocalhost, rcode, tryfallback, pttl) - char *host; - char **mxhosts; - unsigned short *mxprefs; - bool droplocalhost; - int *rcode; - bool tryfallback; - int *pttl; -{ - register unsigned char *eom, *cp; - register int i, j, n; - int nmx = 0; - register char *bp; - HEADER *hp; - querybuf answer; - int ancount, qdcount, buflen; - bool seenlocal = false; - unsigned short pref, type; - unsigned short localpref = 256; - char *fallbackMX = FallBackMX; - bool trycanon = false; - unsigned short *prefs; - int (*resfunc)(); - unsigned short prefer[MAXMXHOSTS]; - int weight[MAXMXHOSTS]; - int ttl = 0; - extern int res_query(), res_search(); - - if (tTd(8, 2)) - sm_dprintf("getmxrr(%s, droplocalhost=%d)\n", - host, droplocalhost); - - if ((fallbackMX != NULL && droplocalhost && - wordinclass(fallbackMX, 'w')) || !tryfallback) - { - /* don't use fallback for this pass */ - fallbackMX = NULL; - } - - *rcode = EX_OK; - - if (mxprefs != NULL) - prefs = mxprefs; - else - prefs = prefer; - - /* efficiency hack -- numeric or non-MX lookups */ - if (host[0] == '[') - goto punt; - - /* - ** If we don't have MX records in our host switch, don't - ** try for MX records. Note that this really isn't "right", - ** since we might be set up to try NIS first and then DNS; - ** if the host is found in NIS we really shouldn't be doing - ** MX lookups. However, that should be a degenerate case. - */ - - if (!UseNameServer) - goto punt; - if (HasWildcardMX && ConfigLevel >= 6) - resfunc = res_query; - else - resfunc = res_search; - - errno = 0; - n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer, - sizeof(answer)); - if (n < 0) - { - if (tTd(8, 1)) - sm_dprintf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", - host == NULL ? "<NULL>" : host, errno, h_errno); - switch (h_errno) - { - case NO_DATA: - trycanon = true; - /* FALLTHROUGH */ - - case NO_RECOVERY: - /* no MX data on this host */ - goto punt; - - case HOST_NOT_FOUND: -# if BROKEN_RES_SEARCH - case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ -# endif /* BROKEN_RES_SEARCH */ - /* host doesn't exist in DNS; might be in /etc/hosts */ - trycanon = true; - *rcode = EX_NOHOST; - goto punt; - - case TRY_AGAIN: - case -1: - /* couldn't connect to the name server */ - if (fallbackMX != NULL) - { - /* name server is hosed -- push to fallback */ - return fallbackmxrr(nmx, prefs, mxhosts); - } - /* it might come up later; better queue it up */ - *rcode = EX_TEMPFAIL; - break; - - default: - syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)", - host, h_errno); - *rcode = EX_OSERR; - break; - } - - /* irreconcilable differences */ - return -1; - } - - /* avoid problems after truncation in tcp packets */ - if (n > sizeof(answer)) - n = sizeof(answer); - - /* find first satisfactory answer */ - hp = (HEADER *)&answer; - cp = (unsigned char *)&answer + HFIXEDSZ; - eom = (unsigned char *)&answer + n; - for (qdcount = ntohs((unsigned short) hp->qdcount); - qdcount--; - cp += n + QFIXEDSZ) - { - if ((n = dn_skipname(cp, eom)) < 0) - goto punt; - } - - /* NOTE: see definition of MXHostBuf! */ - buflen = sizeof(MXHostBuf) - 1; - SM_ASSERT(buflen > 0); - bp = MXHostBuf; - ancount = ntohs((unsigned short) hp->ancount); - - /* See RFC 1035 for layout of RRs. */ - /* XXX leave room for FallBackMX ? */ - while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) - { - if ((n = dn_expand((unsigned char *)&answer, eom, cp, - (RES_UNC_T) bp, buflen)) < 0) - break; - cp += n; - GETSHORT(type, cp); - cp += INT16SZ; /* skip over class */ - GETLONG(ttl, cp); - GETSHORT(n, cp); /* rdlength */ - if (type != T_MX) - { - if (tTd(8, 8) || _res.options & RES_DEBUG) - sm_dprintf("unexpected answer type %d, size %d\n", - type, n); - cp += n; - continue; - } - GETSHORT(pref, cp); - if ((n = dn_expand((unsigned char *)&answer, eom, cp, - (RES_UNC_T) bp, buflen)) < 0) - break; - cp += n; - n = strlen(bp); -# if 0 - /* Can this happen? */ - if (n == 0) - { - if (LogLevel > 4) - sm_syslog(LOG_ERR, NOQID, - "MX records for %s contain empty string", - host); - continue; - } -# endif /* 0 */ - if (wordinclass(bp, 'w')) - { - if (tTd(8, 3)) - sm_dprintf("found localhost (%s) in MX list, pref=%d\n", - bp, pref); - if (droplocalhost) - { - if (!seenlocal || pref < localpref) - localpref = pref; - seenlocal = true; - continue; - } - weight[nmx] = 0; - } - else - weight[nmx] = mxrand(bp); - prefs[nmx] = pref; - mxhosts[nmx++] = bp; - bp += n; - if (bp[-1] != '.') - { - *bp++ = '.'; - n++; - } - *bp++ = '\0'; - if (buflen < n + 1) - { - /* don't want to wrap buflen */ - break; - } - buflen -= n + 1; - } - - /* return only one TTL entry, that should be sufficient */ - if (ttl > 0 && pttl != NULL) - *pttl = ttl; - - /* sort the records */ - for (i = 0; i < nmx; i++) - { - for (j = i + 1; j < nmx; j++) - { - if (prefs[i] > prefs[j] || - (prefs[i] == prefs[j] && weight[i] > weight[j])) - { - register int temp; - register char *temp1; - - temp = prefs[i]; - prefs[i] = prefs[j]; - prefs[j] = temp; - temp1 = mxhosts[i]; - mxhosts[i] = mxhosts[j]; - mxhosts[j] = temp1; - temp = weight[i]; - weight[i] = weight[j]; - weight[j] = temp; - } - } - if (seenlocal && prefs[i] >= localpref) - { - /* truncate higher preference part of list */ - nmx = i; - } - } - - /* delete duplicates from list (yes, some bozos have duplicates) */ - for (i = 0; i < nmx - 1; ) - { - if (sm_strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) - i++; - else - { - /* compress out duplicate */ - for (j = i + 1; j < nmx; j++) - { - mxhosts[j] = mxhosts[j + 1]; - prefs[j] = prefs[j + 1]; - } - nmx--; - } - } - - if (nmx == 0) - { -punt: - if (seenlocal) - { - struct hostent *h = NULL; - - /* - ** If we have deleted all MX entries, this is - ** an error -- we should NEVER send to a host that - ** has an MX, and this should have been caught - ** earlier in the config file. - ** - ** Some sites prefer to go ahead and try the - ** A record anyway; that case is handled by - ** setting TryNullMXList. I believe this is a - ** bad idea, but it's up to you.... - */ - - if (TryNullMXList) - { - SM_SET_H_ERRNO(0); - errno = 0; - h = sm_gethostbyname(host, AF_INET); - if (h == NULL) - { - if (errno == ETIMEDOUT || - h_errno == TRY_AGAIN || - (errno == ECONNREFUSED && - UseNameServer)) - { - *rcode = EX_TEMPFAIL; - return -1; - } -# if NETINET6 - SM_SET_H_ERRNO(0); - errno = 0; - h = sm_gethostbyname(host, AF_INET6); - if (h == NULL && - (errno == ETIMEDOUT || - h_errno == TRY_AGAIN || - (errno == ECONNREFUSED && - UseNameServer))) - { - *rcode = EX_TEMPFAIL; - return -1; - } -# endif /* NETINET6 */ - } - } - - if (h == NULL) - { - *rcode = EX_CONFIG; - syserr("MX list for %s points back to %s", - host, MyHostName); - return -1; - } -# if NETINET6 - freehostent(h); - hp = NULL; -# endif /* NETINET6 */ - } - if (strlen(host) >= sizeof MXHostBuf) - { - *rcode = EX_CONFIG; - syserr("Host name %s too long", - shortenstring(host, MAXSHORTSTR)); - return -1; - } - (void) sm_strlcpy(MXHostBuf, host, sizeof MXHostBuf); - mxhosts[0] = MXHostBuf; - prefs[0] = 0; - if (host[0] == '[') - { - register char *p; -# if NETINET6 - struct sockaddr_in6 tmp6; -# endif /* NETINET6 */ - - /* this may be an MX suppression-style address */ - p = strchr(MXHostBuf, ']'); - if (p != NULL) - { - *p = '\0'; - - if (inet_addr(&MXHostBuf[1]) != INADDR_NONE) - { - nmx++; - *p = ']'; - } -# if NETINET6 - else if (anynet_pton(AF_INET6, &MXHostBuf[1], - &tmp6.sin6_addr) == 1) - { - nmx++; - *p = ']'; - } -# endif /* NETINET6 */ - else - { - trycanon = true; - mxhosts[0]++; - } - } - } - if (trycanon && - getcanonname(mxhosts[0], sizeof MXHostBuf - 2, false, pttl)) - { - /* XXX MXHostBuf == "" ? is that possible? */ - bp = &MXHostBuf[strlen(MXHostBuf)]; - if (bp[-1] != '.') - { - *bp++ = '.'; - *bp = '\0'; - } - nmx = 1; - } - } - - /* if we have a default lowest preference, include that */ - if (fallbackMX != NULL && !seenlocal) - { - nmx = fallbackmxrr(nmx, prefs, mxhosts); - } - return nmx; -} -/* -** MXRAND -- create a randomizer for equal MX preferences -** -** If two MX hosts have equal preferences we want to randomize -** the selection. But in order for signatures to be the same, -** we need to randomize the same way each time. This function -** computes a pseudo-random hash function from the host name. -** -** Parameters: -** host -- the name of the host. -** -** Returns: -** A random but repeatable value based on the host name. -*/ - -static int -mxrand(host) - register char *host; -{ - int hfunc; - static unsigned int seed; - - if (seed == 0) - { - seed = (int) curtime() & 0xffff; - if (seed == 0) - seed++; - } - - if (tTd(17, 9)) - sm_dprintf("mxrand(%s)", host); - - hfunc = seed; - while (*host != '\0') - { - int c = *host++; - - if (isascii(c) && isupper(c)) - c = tolower(c); - hfunc = ((hfunc << 1) ^ c) % 2003; - } - - hfunc &= 0xff; - hfunc++; - - if (tTd(17, 9)) - sm_dprintf(" = %d\n", hfunc); - return hfunc; -} -/* -** BESTMX -- find the best MX for a name -** -** This is really a hack, but I don't see any obvious way -** to generalize it at the moment. -*/ - -/* ARGSUSED3 */ -char * -bestmx_map_lookup(map, name, av, statp) - MAP *map; - char *name; - char **av; - int *statp; -{ - int nmx; - int saveopts = _res.options; - int i; - ssize_t len = 0; - char *result; - char *mxhosts[MAXMXHOSTS + 1]; -#if _FFR_BESTMX_BETTER_TRUNCATION - char *buf; -#else /* _FFR_BESTMX_BETTER_TRUNCATION */ - char *p; - char buf[PSBUFSIZE / 2]; -#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ - - _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); - nmx = getmxrr(name, mxhosts, NULL, false, statp, false, NULL); - _res.options = saveopts; - if (nmx <= 0) - return NULL; - if (bitset(MF_MATCHONLY, map->map_mflags)) - return map_rewrite(map, name, strlen(name), NULL); - if ((map->map_coldelim == '\0') || (nmx == 1)) - return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); - - /* - ** We were given a -z flag (return all MXs) and there are multiple - ** ones. We need to build them all into a list. - */ - -#if _FFR_BESTMX_BETTER_TRUNCATION - for (i = 0; i < nmx; i++) - { - if (strchr(mxhosts[i], map->map_coldelim) != NULL) - { - syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", - mxhosts[i], map->map_coldelim); - return NULL; - } - len += strlen(mxhosts[i]) + 1; - if (len < 0) - { - len -= strlen(mxhosts[i]) + 1; - break; - } - } - buf = (char *) sm_malloc(len); - if (buf == NULL) - { - *statp = EX_UNAVAILABLE; - return NULL; - } - *buf = '\0'; - for (i = 0; i < nmx; i++) - { - int end; - - end = sm_strlcat(buf, mxhosts[i], len); - if (i != nmx && end + 1 < len) - { - buf[end] = map->map_coldelim; - buf[end + 1] = '\0'; - } - } - - /* Cleanly truncate for rulesets */ - truncate_at_delim(buf, PSBUFSIZE / 2, map->map_coldelim); -#else /* _FFR_BESTMX_BETTER_TRUNCATION */ - p = buf; - for (i = 0; i < nmx; i++) - { - size_t slen; - - if (strchr(mxhosts[i], map->map_coldelim) != NULL) - { - syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", - mxhosts[i], map->map_coldelim); - return NULL; - } - slen = strlen(mxhosts[i]); - if (len + slen + 2 > sizeof buf) - break; - if (i > 0) - { - *p++ = map->map_coldelim; - len++; - } - (void) sm_strlcpy(p, mxhosts[i], sizeof buf - len); - p += slen; - len += slen; - } -#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ - - result = map_rewrite(map, buf, len, av); -#if _FFR_BESTMX_BETTER_TRUNCATION - sm_free(buf); -#endif /* _FFR_BESTMX_BETTER_TRUNCATION */ - return result; -} -/* -** DNS_GETCANONNAME -- get the canonical name for named host using DNS -** -** This algorithm tries to be smart about wildcard MX records. -** This is hard to do because DNS doesn't tell is if we matched -** against a wildcard or a specific MX. -** -** We always prefer A & CNAME records, since these are presumed -** to be specific. -** -** If we match an MX in one pass and lose it in the next, we use -** the old one. For example, consider an MX matching *.FOO.BAR.COM. -** A hostname bletch.foo.bar.com will match against this MX, but -** will stop matching when we try bletch.bar.com -- so we know -** that bletch.foo.bar.com must have been right. This fails if -** there was also an MX record matching *.BAR.COM, but there are -** some things that just can't be fixed. -** -** Parameters: -** host -- a buffer containing the name of the host. -** This is a value-result parameter. -** hbsize -- the size of the host buffer. -** trymx -- if set, try MX records as well as A and CNAME. -** statp -- pointer to place to store status. -** pttl -- pointer to return TTL (can be NULL). -** -** Returns: -** true -- if the host matched. -** false -- otherwise. -*/ - -# if NETINET6 -# define SM_T_INITIAL T_AAAA -# else /* NETINET6 */ -# define SM_T_INITIAL T_A -# endif /* NETINET6 */ - -bool -dns_getcanonname(host, hbsize, trymx, statp, pttl) - char *host; - int hbsize; - bool trymx; - int *statp; - int *pttl; -{ - register unsigned char *eom, *ap; - register char *cp; - register int n; - HEADER *hp; - querybuf answer; - int ancount, qdcount; - int ret; - char **domain; - int type; - int ttl = 0; - char **dp; - char *mxmatch; - bool amatch; - bool gotmx = false; - int qtype; - int loopcnt; - char *xp; - char nbuf[SM_MAX(MAXPACKET, MAXDNAME*2+2)]; - char *searchlist[MAXDNSRCH + 2]; - - if (tTd(8, 2)) - sm_dprintf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); - - if ((_res.options & RES_INIT) == 0 && res_init() == -1) - { - *statp = EX_UNAVAILABLE; - return false; - } - - *statp = EX_OK; - - /* - ** Initialize domain search list. If there is at least one - ** dot in the name, search the unmodified name first so we - ** find "vse.CS" in Czechoslovakia instead of in the local - ** domain (e.g., vse.CS.Berkeley.EDU). Note that there is no - ** longer a country named Czechoslovakia but this type of problem - ** is still present. - ** - ** Older versions of the resolver could create this - ** list by tearing apart the host name. - */ - - loopcnt = 0; -cnameloop: - /* Check for dots in the name */ - for (cp = host, n = 0; *cp != '\0'; cp++) - if (*cp == '.') - n++; - - /* - ** If this is a simple name, determine whether it matches an - ** alias in the file defined by the environment variable HOSTALIASES. - */ - - if (n == 0 && (xp = gethostalias(host)) != NULL) - { - if (loopcnt++ > MAXCNAMEDEPTH) - { - syserr("loop in ${HOSTALIASES} file"); - } - else - { - (void) sm_strlcpy(host, xp, hbsize); - goto cnameloop; - } - } - - /* - ** Build the search list. - ** If there is at least one dot in name, start with a null - ** domain to search the unmodified name first. - ** If name does not end with a dot and search up local domain - ** tree desired, append each local domain component to the - ** search list; if name contains no dots and default domain - ** name is desired, append default domain name to search list; - ** else if name ends in a dot, remove that dot. - */ - - dp = searchlist; - if (n > 0) - *dp++ = ""; - if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) - { - /* make sure there are less than MAXDNSRCH domains */ - for (domain = RES_DNSRCH_VARIABLE, ret = 0; - *domain != NULL && ret < MAXDNSRCH; - ret++) - *dp++ = *domain++; - } - else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) - { - *dp++ = _res.defdname; - } - else if (*cp == '.') - { - *cp = '\0'; - } - *dp = NULL; - - /* - ** Now loop through the search list, appending each domain in turn - ** name and searching for a match. - */ - - mxmatch = NULL; - qtype = SM_T_INITIAL; - - for (dp = searchlist; *dp != NULL; ) - { - if (qtype == SM_T_INITIAL) - gotmx = false; - if (tTd(8, 5)) - sm_dprintf("dns_getcanonname: trying %s.%s (%s)\n", - host, *dp, -# if NETINET6 - qtype == T_AAAA ? "AAAA" : -# endif /* NETINET6 */ - qtype == T_A ? "A" : - qtype == T_MX ? "MX" : - "???"); - errno = 0; - ret = res_querydomain(host, *dp, C_IN, qtype, - answer.qb2, sizeof(answer.qb2)); - if (ret <= 0) - { - int save_errno = errno; - - if (tTd(8, 7)) - sm_dprintf("\tNO: errno=%d, h_errno=%d\n", - save_errno, h_errno); - - if (save_errno == ECONNREFUSED || h_errno == TRY_AGAIN) - { - /* - ** the name server seems to be down or broken. - */ - - SM_SET_H_ERRNO(TRY_AGAIN); -# if _FFR_DONT_STOP_LOOKING - if (**dp == '\0') - { - if (*statp == EX_OK) - *statp = EX_TEMPFAIL; - goto nexttype; - } -# endif /* _FFR_DONT_STOP_LOOKING */ - *statp = EX_TEMPFAIL; - - if (WorkAroundBrokenAAAA) - { - /* - ** Only return if not TRY_AGAIN as an - ** attempt with a different qtype may - ** succeed (res_querydomain() calls - ** res_query() calls res_send() which - ** sets errno to ETIMEDOUT if the - ** nameservers could be contacted but - ** didn't give an answer). - */ - - if (save_errno != ETIMEDOUT) - return false; - } - else - return false; - } - -# if _FFR_DONT_STOP_LOOKING -nexttype: -# endif /* _FFR_DONT_STOP_LOOKING */ - if (h_errno != HOST_NOT_FOUND) - { - /* might have another type of interest */ -# if NETINET6 - if (qtype == T_AAAA) - { - qtype = T_A; - continue; - } - else -# endif /* NETINET6 */ - if (qtype == T_A && !gotmx && - (trymx || **dp == '\0')) - { - qtype = T_MX; - continue; - } - } - - /* definite no -- try the next domain */ - dp++; - qtype = SM_T_INITIAL; - continue; - } - else if (tTd(8, 7)) - sm_dprintf("\tYES\n"); - - /* avoid problems after truncation in tcp packets */ - if (ret > sizeof(answer)) - ret = sizeof(answer); - if (ret < 0) - { - *statp = EX_SOFTWARE; - return false; - } - - /* - ** Appear to have a match. Confirm it by searching for A or - ** CNAME records. If we don't have a local domain - ** wild card MX record, we will accept MX as well. - */ - - hp = (HEADER *) &answer; - ap = (unsigned char *) &answer + HFIXEDSZ; - eom = (unsigned char *) &answer + ret; - - /* skip question part of response -- we know what we asked */ - for (qdcount = ntohs((unsigned short) hp->qdcount); - qdcount--; - ap += ret + QFIXEDSZ) - { - if ((ret = dn_skipname(ap, eom)) < 0) - { - if (tTd(8, 20)) - sm_dprintf("qdcount failure (%d)\n", - ntohs((unsigned short) hp->qdcount)); - *statp = EX_SOFTWARE; - return false; /* ???XXX??? */ - } - } - - amatch = false; - for (ancount = ntohs((unsigned short) hp->ancount); - --ancount >= 0 && ap < eom; - ap += n) - { - n = dn_expand((unsigned char *) &answer, eom, ap, - (RES_UNC_T) nbuf, sizeof nbuf); - if (n < 0) - break; - ap += n; - GETSHORT(type, ap); - ap += INT16SZ; /* skip over class */ - GETLONG(ttl, ap); - GETSHORT(n, ap); /* rdlength */ - switch (type) - { - case T_MX: - gotmx = true; - if (**dp != '\0' && HasWildcardMX) - { - /* - ** If we are using MX matches and have - ** not yet gotten one, save this one - ** but keep searching for an A or - ** CNAME match. - */ - - if (trymx && mxmatch == NULL) - mxmatch = *dp; - continue; - } - - /* - ** If we did not append a domain name, this - ** must have been a canonical name to start - ** with. Even if we did append a domain name, - ** in the absence of a wildcard MX this must - ** still be a real MX match. - ** Such MX matches are as good as an A match, - ** fall through. - */ - /* FALLTHROUGH */ - -# if NETINET6 - case T_AAAA: - /* Flag that a good match was found */ - amatch = true; - - /* continue in case a CNAME also exists */ - continue; -# endif /* NETINET6 */ - - case T_A: - /* Flag that a good match was found */ - amatch = true; - - /* continue in case a CNAME also exists */ - continue; - - case T_CNAME: - if (DontExpandCnames) - { - /* got CNAME -- guaranteed canonical */ - amatch = true; - break; - } - - if (loopcnt++ > MAXCNAMEDEPTH) - { - /*XXX should notify postmaster XXX*/ - message("DNS failure: CNAME loop for %s", - host); - if (CurEnv->e_message == NULL) - { - char ebuf[MAXLINE]; - - (void) sm_snprintf(ebuf, - sizeof ebuf, - "Deferred: DNS failure: CNAME loop for %.100s", - host); - CurEnv->e_message = - sm_rpool_strdup_x( - CurEnv->e_rpool, ebuf); - } - SM_SET_H_ERRNO(NO_RECOVERY); - *statp = EX_CONFIG; - return false; - } - - /* value points at name */ - if ((ret = dn_expand((unsigned char *)&answer, - eom, ap, (RES_UNC_T) nbuf, - sizeof(nbuf))) < 0) - break; - (void) sm_strlcpy(host, nbuf, hbsize); - - /* - ** RFC 1034 section 3.6 specifies that CNAME - ** should point at the canonical name -- but - ** urges software to try again anyway. - */ - - goto cnameloop; - - default: - /* not a record of interest */ - continue; - } - } - - if (amatch) - { - /* - ** Got a good match -- either an A, CNAME, or an - ** exact MX record. Save it and get out of here. - */ - - mxmatch = *dp; - break; - } - - /* - ** Nothing definitive yet. - ** If this was a T_A query and we haven't yet found a MX - ** match, try T_MX if allowed to do so. - ** Otherwise, try the next domain. - */ - -# if NETINET6 - if (qtype == T_AAAA) - qtype = T_A; - else -# endif /* NETINET6 */ - if (qtype == T_A && !gotmx && (trymx || **dp == '\0')) - qtype = T_MX; - else - { - qtype = SM_T_INITIAL; - dp++; - } - } - - /* if nothing was found, we are done */ - if (mxmatch == NULL) - { - if (*statp == EX_OK) - *statp = EX_NOHOST; - return false; - } - - /* - ** Create canonical name and return. - ** If saved domain name is null, name was already canonical. - ** Otherwise append the saved domain name. - */ - - (void) sm_snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host, - *mxmatch == '\0' ? "" : ".", - MAXDNAME, mxmatch); - (void) sm_strlcpy(host, nbuf, hbsize); - if (tTd(8, 5)) - sm_dprintf("dns_getcanonname: %s\n", host); - *statp = EX_OK; - - /* return only one TTL entry, that should be sufficient */ - if (ttl > 0 && pttl != NULL) - *pttl = ttl; - return true; -} - -static char * -gethostalias(host) - char *host; -{ - char *fname; - SM_FILE_T *fp; - register char *p = NULL; - long sff = SFF_REGONLY; - char buf[MAXLINE]; - static char hbuf[MAXDNAME]; - - if (ResNoAliases) - return NULL; - if (DontLockReadFiles) - sff |= SFF_NOLOCK; - fname = getenv("HOSTALIASES"); - if (fname == NULL || - (fp = safefopen(fname, O_RDONLY, 0, sff)) == NULL) - return NULL; - while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) - { - for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++) - continue; - if (*p == 0) - { - /* syntax error */ - continue; - } - *p++ = '\0'; - if (sm_strcasecmp(buf, host) == 0) - break; - } - - if (sm_io_eof(fp)) - { - /* no match */ - (void) sm_io_close(fp, SM_TIME_DEFAULT); - return NULL; - } - (void) sm_io_close(fp, SM_TIME_DEFAULT); - - /* got a match; extract the equivalent name */ - while (*p != '\0' && isascii(*p) && isspace(*p)) - p++; - host = p; - while (*p != '\0' && !(isascii(*p) && isspace(*p))) - p++; - *p = '\0'; - (void) sm_strlcpy(hbuf, host, sizeof hbuf); - return hbuf; -} -#endif /* NAMED_BIND */ |