1N/A * Copyright (c) 1998-2004, 2006, 2010 Sendmail, Inc. and its suppliers. 1N/A * All rights reserved. 1N/A * Copyright (c) 1986, 1995-1997 Eric P. Allman. All rights reserved. 1N/A * Copyright (c) 1988, 1993 1N/A * The Regents of the University of California. All rights reserved. 1N/A * By using this file, you agree to the terms and conditions set 1N/A * forth in the LICENSE file which can be found at the top level of 1N/A * the sendmail distribution. 1N/A#
else /* NAMED_BIND */ 1N/A#
endif /* NAMED_BIND */ 1N/A#
endif /* ! MXHOSTBUFSIZE */ 1N/A#
endif /* (MXHOSTBUFSIZE < 2) || (MXHOSTBUFSIZE >= INT_MAX/2) */ 1N/A#
define MAXDNSRCH 6 /* number of possible domains to search */ 1N/A#
endif /* ! MAXDNSRCH */ 1N/A#
endif /* ! RES_DNSRCH_VARIABLE */ 1N/A#
endif /* ! NO_DATA */ 1N/A#
endif /* ! HFIXEDSZ */ 1N/A#
else /* defined(__RES) && (__RES >= 19940415) */ 1N/A#
endif /* defined(__RES) && (__RES >= 19940415) */ 1N/A** GETFALLBACKMXRR -- get MX resource records for fallback MX host. 1N/A** We have to initialize this once before doing anything else. 1N/A** Moreover, we have to repeat this from time to time to avoid 1N/A** stale data, e.g., in persistent queue runners. 1N/A** This should be done in a parent process so the child 1N/A** processes have the right data. 1N/A** host -- the name of the fallback MX host. 1N/A** number of MX records. 1N/A** Populates NumFallbackMXHosts and fbhosts. 1N/A** Sets renewal time (based on TTL). 1N/A /* This is currently done before this function is called. */ 1N/A** FALLBACKMXRR -- add MX resource records for fallback MX host to list. 1N/A** nmx -- current number of MX records. 1N/A** prefs -- array of preferences. 1N/A** mxhosts -- array of MX hosts (maximum size: MAXMXHOSTS) 1N/A** new number of MX records. 1N/A** If FallbackMX was set, it appends the MX records for 1N/A** that host to mxhosts (and modifies prefs accordingly). 1N/A** GETMXRR -- get MX resource records for a domain 1N/A** host -- the name of the host to MX. 1N/A** mxhosts -- a pointer to a return buffer of MX records. 1N/A** mxprefs -- a pointer to a return buffer of MX preferences. 1N/A** If NULL, don't try to populate. 1N/A** droplocalhost -- If true, all MX records less preferred 1N/A** than the local host (as determined by $=w) will 1N/A** rcode -- a pointer to an EX_ status code. 1N/A** tryfallback -- add also fallback MX host? 1N/A** pttl -- pointer to return TTL (can be NULL). 1N/A** The number of MX records found. 1N/A** -1 if there is an internal failure. 1N/A** If no MX records are found, mxhosts[0] is set to host 1N/A** and 1 is returned. 1N/A** The entries made for mxhosts point to a static array 1N/A** MXHostBuf[MXHOSTBUFSIZE], so the data needs to be copied, 1N/A** if it must be preserved across calls to this function. 1N/A register int i, j, n;
1N/A /* don't use fallback for this pass */ 1N/A /* efficiency hack -- numeric or non-MX lookups */ 1N/A ** If we don't have MX records in our host switch, don't 1N/A ** try for MX records. Note that this really isn't "right", 1N/A ** since we might be set up to try NIS first and then DNS; 1N/A ** if the host is found in NIS we really shouldn't be doing 1N/A ** MX lookups. However, that should be a degenerate case. 1N/A sm_dprintf(
"getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
1N/A /* no MX data on this host */ 1N/A case 0:
/* Ultrix resolver retns failure w/ h_errno=0 */ 1N/A#
endif /* BROKEN_RES_SEARCH */ 1N/A /* host doesn't exist in DNS; might be in /etc/hosts */ 1N/A /* couldn't connect to the name server */ 1N/A /* name server is hosed -- push to fallback */ 1N/A /* it might come up later; better queue it up */ 1N/A syserr(
"getmxrr: res_search (%s) failed with impossible h_errno (%d)",
1N/A /* irreconcilable differences */ 1N/A /* avoid problems after truncation in tcp packets */ 1N/A /* find first satisfactory answer */ 1N/A /* NOTE: see definition of MXHostBuf! */ 1N/A /* See RFC 1035 for layout of RRs. */ 1N/A /* XXX leave room for FallbackMX ? */ 1N/A /* Can this happen? */ 1N/A "MX records for %s contain empty string",
1N/A /* don't want to wrap buflen */ 1N/A /* return only one TTL entry, that should be sufficient */ 1N/A /* sort the records */ 1N/A /* truncate higher preference part of list */ 1N/A /* delete duplicates from list (yes, some bozos have duplicates) */ 1N/A /* compress out duplicate */ 1N/A ** If we have deleted all MX entries, this is 1N/A ** an error -- we should NEVER send to a host that 1N/A ** has an MX, and this should have been caught 1N/A ** earlier in the config file. 1N/A ** Some sites prefer to go ahead and try the 1N/A ** A record anyway; that case is handled by 1N/A ** setting TryNullMXList. I believe this is a 1N/A ** bad idea, but it's up to you.... 1N/A#
endif /* NETINET6 */ 1N/A#
endif /* NETINET6 */ 1N/A#
endif /* NETINET6 */ 1N/A /* this may be an MX suppression-style address */ 1N/A#
endif /* NETINET6 */ 1N/A /* XXX MXHostBuf == "" ? is that possible? */ 1N/A /* if we have a default lowest preference, include that */ 1N/A** MXRAND -- create a randomizer for equal MX preferences 1N/A** If two MX hosts have equal preferences we want to randomize 1N/A** the selection. But in order for signatures to be the same, 1N/A** we need to randomize the same way each time. This function 1N/A** computes a pseudo-random hash function from the host name. 1N/A** host -- the name of the host. 1N/A** A random but repeatable value based on the host name. 1N/A** BESTMX -- find the best MX for a name 1N/A** This is really a hack, but I don't see any obvious way 1N/A** to generalize it at the moment. 1N/A#
else /* _FFR_BESTMX_BETTER_TRUNCATION */ 1N/A#
endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 1N/A ** We were given a -z flag (return all MXs) and there are multiple 1N/A ** ones. We need to build them all into a list. 1N/A syserr(
"bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
1N/A /* Cleanly truncate for rulesets */ 1N/A#
else /* _FFR_BESTMX_BETTER_TRUNCATION */ 1N/A syserr(
"bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
1N/A#
endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 1N/A#
endif /* _FFR_BESTMX_BETTER_TRUNCATION */ 1N/A** DNS_GETCANONNAME -- get the canonical name for named host using DNS 1N/A** This algorithm tries to be smart about wildcard MX records. 1N/A** This is hard to do because DNS doesn't tell is if we matched 1N/A** against a wildcard or a specific MX. 1N/A** We always prefer A & CNAME records, since these are presumed 1N/A** If we match an MX in one pass and lose it in the next, we use 1N/A** the old one. For example, consider an MX matching *.FOO.BAR.COM. 1N/A** there was also an MX record matching *.BAR.COM, but there are 1N/A** some things that just can't be fixed. 1N/A** host -- a buffer containing the name of the host. 1N/A** This is a value-result parameter. 1N/A** hbsize -- the size of the host buffer. 1N/A** trymx -- if set, try MX records as well as A and CNAME. 1N/A** statp -- pointer to place to store status. 1N/A** pttl -- pointer to return TTL (can be NULL). 1N/A** true -- if the host matched. 1N/A** false -- otherwise. 1N/A ** Initialize domain search list. If there is at least one 1N/A ** dot in the name, search the unmodified name first so we 1N/A ** find "vse.CS" in Czechoslovakia instead of in the local 1N/A ** domain (e.g., vse.CS.Berkeley.EDU). Note that there is no 1N/A ** longer a country named Czechoslovakia but this type of problem 1N/A ** is still present. 1N/A ** Older versions of the resolver could create this 1N/A ** list by tearing apart the host name. 1N/A /* Check for dots in the name */ 1N/A ** Build the search list. 1N/A ** If there is at least one dot in name, start with a null 1N/A ** domain to search the unmodified name first. 1N/A ** If name does not end with a dot and search up local domain 1N/A ** tree desired, append each local domain component to the 1N/A ** search list; if name contains no dots and default domain 1N/A ** name is desired, append default domain name to search list; 1N/A ** else if name ends in a dot, remove that dot. 1N/A /* make sure there are less than MAXDNSRCH domains */ 1N/A ** Now loop through the search list, appending each domain in turn 1N/A ** name and searching for a match. 1N/A#
endif /* NETINET6 */ 1N/A#
endif /* NETINET6 */ 1N/A ** the name server seems to be down or broken. 1N/A ** Only return if not TRY_AGAIN as an 1N/A ** attempt with a different qtype may 1N/A ** succeed (res_querydomain() calls 1N/A ** res_query() calls res_send() which 1N/A ** sets errno to ETIMEDOUT if the 1N/A ** nameservers could be contacted but 1N/A ** didn't give an answer). 1N/A /* might have another type of interest */ 1N/A#
endif /* NETINET6 */ 1N/A /* definite no -- try the next domain */ 1N/A /* avoid problems after truncation in tcp packets */ 1N/A ** Appear to have a match. Confirm it by searching for A or 1N/A ** CNAME records. If we don't have a local domain 1N/A ** wild card MX record, we will accept MX as well. 1N/A /* skip question part of response -- we know what we asked */ 1N/A return false;
/* ???XXX??? */ 1N/A ** If we are using MX matches and have 1N/A ** not yet gotten one, save this one 1N/A ** but keep searching for an A or 1N/A ** If we did not append a domain name, this 1N/A ** must have been a canonical name to start 1N/A ** with. Even if we did append a domain name, 1N/A ** in the absence of a wildcard MX this must 1N/A ** still be a real MX match. 1N/A ** Such MX matches are as good as an A match, 1N/A#
endif /* NETINET6 */ 1N/A /* Flag that a good match was found */ 1N/A /* continue in case a CNAME also exists */ 1N/A /* got CNAME -- guaranteed canonical */ 1N/A /*XXX should notify postmaster XXX*/ 1N/A "Deferred: DNS failure: CNAME loop for %.100s",
1N/A /* value points at name */ 1N/A ** RFC 1034 section 3.6 specifies that CNAME 1N/A ** should point at the canonical name -- but 1N/A ** urges software to try again anyway. 1N/A /* not a record of interest */ 1N/A ** Got a good match -- either an A, CNAME, or an 1N/A ** exact MX record. Save it and get out of here. 1N/A ** Nothing definitive yet. 1N/A ** If this was a T_A query and we haven't yet found a MX 1N/A ** match, try T_MX if allowed to do so. 1N/A ** Otherwise, try the next domain. 1N/A#
endif /* NETINET6 */ 1N/A /* if nothing was found, we are done */ 1N/A ** Create canonical name and return. 1N/A ** If saved domain name is null, name was already canonical. 1N/A ** Otherwise append the saved domain name. 1N/A /* return only one TTL entry, that should be sufficient */ 1N/A#
endif /* NAMED_BIND */