domain.c revision 38032
138032Speter/* 238032Speter * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 338032Speter * Copyright (c) 1986, 1995-1997 Eric P. Allman. All rights reserved. 438032Speter * Copyright (c) 1988, 1993 538032Speter * The Regents of the University of California. All rights reserved. 638032Speter * 738032Speter * By using this file, you agree to the terms and conditions set 838032Speter * forth in the LICENSE file which can be found at the top level of 938032Speter * the sendmail distribution. 1038032Speter * 1138032Speter */ 1238032Speter 1338032Speter#include "sendmail.h" 1438032Speter 1538032Speter#ifndef lint 1638032Speter#if NAMED_BIND 1738032Speterstatic char sccsid[] = "@(#)domain.c 8.77 (Berkeley) 6/4/98 (with name server)"; 1838032Speter#else 1938032Speterstatic char sccsid[] = "@(#)domain.c 8.77 (Berkeley) 6/4/98 (without name server)"; 2038032Speter#endif 2138032Speter#endif /* not lint */ 2238032Speter 2338032Speter#if NAMED_BIND 2438032Speter 2538032Speter#include <errno.h> 2638032Speter#include <resolv.h> 2738032Speter#include <arpa/inet.h> 2838032Speter 2938032Speter/* 3038032Speter** The standard udp packet size PACKETSZ (512) is not sufficient for some 3138032Speter** nameserver answers containing very many resource records. The resolver 3238032Speter** may switch to tcp and retry if it detects udp packet overflow. 3338032Speter** Also note that the resolver routines res_query and res_search return 3438032Speter** the size of the *un*truncated answer in case the supplied answer buffer 3538032Speter** it not big enough to accommodate the entire answer. 3638032Speter*/ 3738032Speter 3838032Speter#ifndef MAXPACKET 3938032Speter# define MAXPACKET 8192 /* max packet size used internally by BIND */ 4038032Speter#endif 4138032Speter 4238032Spetertypedef union 4338032Speter{ 4438032Speter HEADER qb1; 4538032Speter u_char qb2[MAXPACKET]; 4638032Speter} querybuf; 4738032Speter 4838032Speter#ifndef MXHOSTBUFSIZE 4938032Speter# define MXHOSTBUFSIZE (128 * MAXMXHOSTS) 5038032Speter#endif 5138032Speter 5238032Speterstatic char MXHostBuf[MXHOSTBUFSIZE]; 5338032Speter 5438032Speter#ifndef MAXDNSRCH 5538032Speter# define MAXDNSRCH 6 /* number of possible domains to search */ 5638032Speter#endif 5738032Speter 5838032Speter#ifndef MAX 5938032Speter# define MAX(a, b) ((a) > (b) ? (a) : (b)) 6038032Speter#endif 6138032Speter 6238032Speter#ifndef NO_DATA 6338032Speter# define NO_DATA NO_ADDRESS 6438032Speter#endif 6538032Speter 6638032Speter#ifndef HFIXEDSZ 6738032Speter# define HFIXEDSZ 12 /* sizeof(HEADER) */ 6838032Speter#endif 6938032Speter 7038032Speter#define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ 7138032Speter 7238032Speter#if defined(__RES) && (__RES >= 19940415) 7338032Speter# define RES_UNC_T char * 7438032Speter#else 7538032Speter# define RES_UNC_T u_char * 7638032Speter#endif 7738032Speter/* 7838032Speter** GETMXRR -- get MX resource records for a domain 7938032Speter** 8038032Speter** Parameters: 8138032Speter** host -- the name of the host to MX. 8238032Speter** mxhosts -- a pointer to a return buffer of MX records. 8338032Speter** droplocalhost -- If TRUE, all MX records less preferred 8438032Speter** than the local host (as determined by $=w) will 8538032Speter** be discarded. 8638032Speter** rcode -- a pointer to an EX_ status code. 8738032Speter** 8838032Speter** Returns: 8938032Speter** The number of MX records found. 9038032Speter** -1 if there is an internal failure. 9138032Speter** If no MX records are found, mxhosts[0] is set to host 9238032Speter** and 1 is returned. 9338032Speter*/ 9438032Speter 9538032Speterint 9638032Spetergetmxrr(host, mxhosts, droplocalhost, rcode) 9738032Speter char *host; 9838032Speter char **mxhosts; 9938032Speter bool droplocalhost; 10038032Speter int *rcode; 10138032Speter{ 10238032Speter register u_char *eom, *cp; 10338032Speter register int i, j, n; 10438032Speter int nmx = 0; 10538032Speter register char *bp; 10638032Speter HEADER *hp; 10738032Speter querybuf answer; 10838032Speter int ancount, qdcount, buflen; 10938032Speter bool seenlocal = FALSE; 11038032Speter u_short pref, type; 11138032Speter u_short localpref = 256; 11238032Speter char *fallbackMX = FallBackMX; 11338032Speter bool trycanon = FALSE; 11438032Speter int (*resfunc)(); 11538032Speter extern int res_query(), res_search(); 11638032Speter u_short prefer[MAXMXHOSTS]; 11738032Speter int weight[MAXMXHOSTS]; 11838032Speter extern int mxrand __P((char *)); 11938032Speter 12038032Speter if (tTd(8, 2)) 12138032Speter printf("getmxrr(%s, droplocalhost=%d)\n", host, droplocalhost); 12238032Speter 12338032Speter if (fallbackMX != NULL && droplocalhost && 12438032Speter wordinclass(fallbackMX, 'w')) 12538032Speter { 12638032Speter /* don't use fallback for this pass */ 12738032Speter fallbackMX = NULL; 12838032Speter } 12938032Speter 13038032Speter *rcode = EX_OK; 13138032Speter 13238032Speter /* efficiency hack -- numeric or non-MX lookups */ 13338032Speter if (host[0] == '[') 13438032Speter goto punt; 13538032Speter 13638032Speter /* 13738032Speter ** If we don't have MX records in our host switch, don't 13838032Speter ** try for MX records. Note that this really isn't "right", 13938032Speter ** since we might be set up to try NIS first and then DNS; 14038032Speter ** if the host is found in NIS we really shouldn't be doing 14138032Speter ** MX lookups. However, that should be a degenerate case. 14238032Speter */ 14338032Speter 14438032Speter if (!UseNameServer) 14538032Speter goto punt; 14638032Speter if (HasWildcardMX && ConfigLevel >= 6) 14738032Speter resfunc = res_query; 14838032Speter else 14938032Speter resfunc = res_search; 15038032Speter 15138032Speter errno = 0; 15238032Speter n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer)); 15338032Speter if (n < 0) 15438032Speter { 15538032Speter if (tTd(8, 1)) 15638032Speter printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", 15738032Speter (host == NULL) ? "<NULL>" : host, errno, h_errno); 15838032Speter switch (h_errno) 15938032Speter { 16038032Speter case NO_DATA: 16138032Speter trycanon = TRUE; 16238032Speter /* fall through */ 16338032Speter 16438032Speter case NO_RECOVERY: 16538032Speter /* no MX data on this host */ 16638032Speter goto punt; 16738032Speter 16838032Speter case HOST_NOT_FOUND: 16938032Speter#if BROKEN_RES_SEARCH 17038032Speter case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ 17138032Speter#endif 17238032Speter /* host doesn't exist in DNS; might be in /etc/hosts */ 17338032Speter trycanon = TRUE; 17438032Speter *rcode = EX_NOHOST; 17538032Speter goto punt; 17638032Speter 17738032Speter case TRY_AGAIN: 17838032Speter case -1: 17938032Speter /* couldn't connect to the name server */ 18038032Speter if (fallbackMX != NULL) 18138032Speter { 18238032Speter /* name server is hosed -- push to fallback */ 18338032Speter mxhosts[nmx++] = fallbackMX; 18438032Speter return nmx; 18538032Speter } 18638032Speter /* it might come up later; better queue it up */ 18738032Speter *rcode = EX_TEMPFAIL; 18838032Speter break; 18938032Speter 19038032Speter default: 19138032Speter syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n", 19238032Speter host, h_errno); 19338032Speter *rcode = EX_OSERR; 19438032Speter break; 19538032Speter } 19638032Speter 19738032Speter /* irreconcilable differences */ 19838032Speter return (-1); 19938032Speter } 20038032Speter 20138032Speter /* avoid problems after truncation in tcp packets */ 20238032Speter if (n > sizeof(answer)) 20338032Speter n = sizeof(answer); 20438032Speter 20538032Speter /* find first satisfactory answer */ 20638032Speter hp = (HEADER *)&answer; 20738032Speter cp = (u_char *)&answer + HFIXEDSZ; 20838032Speter eom = (u_char *)&answer + n; 20938032Speter for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 21038032Speter if ((n = dn_skipname(cp, eom)) < 0) 21138032Speter goto punt; 21238032Speter buflen = sizeof(MXHostBuf) - 1; 21338032Speter bp = MXHostBuf; 21438032Speter ancount = ntohs(hp->ancount); 21538032Speter while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) 21638032Speter { 21738032Speter if ((n = dn_expand((u_char *)&answer, 21838032Speter eom, cp, (RES_UNC_T) bp, buflen)) < 0) 21938032Speter break; 22038032Speter cp += n; 22138032Speter GETSHORT(type, cp); 22238032Speter cp += INT16SZ + INT32SZ; 22338032Speter GETSHORT(n, cp); 22438032Speter if (type != T_MX) 22538032Speter { 22638032Speter if (tTd(8, 8) || _res.options & RES_DEBUG) 22738032Speter printf("unexpected answer type %d, size %d\n", 22838032Speter type, n); 22938032Speter cp += n; 23038032Speter continue; 23138032Speter } 23238032Speter GETSHORT(pref, cp); 23338032Speter if ((n = dn_expand((u_char *)&answer, eom, cp, 23438032Speter (RES_UNC_T) bp, buflen)) < 0) 23538032Speter break; 23638032Speter cp += n; 23738032Speter if (wordinclass(bp, 'w')) 23838032Speter { 23938032Speter if (tTd(8, 3)) 24038032Speter printf("found localhost (%s) in MX list, pref=%d\n", 24138032Speter bp, pref); 24238032Speter if (droplocalhost) 24338032Speter { 24438032Speter if (!seenlocal || pref < localpref) 24538032Speter localpref = pref; 24638032Speter seenlocal = TRUE; 24738032Speter continue; 24838032Speter } 24938032Speter weight[nmx] = 0; 25038032Speter } 25138032Speter else 25238032Speter weight[nmx] = mxrand(bp); 25338032Speter prefer[nmx] = pref; 25438032Speter mxhosts[nmx++] = bp; 25538032Speter n = strlen(bp); 25638032Speter bp += n; 25738032Speter if (bp[-1] != '.') 25838032Speter { 25938032Speter *bp++ = '.'; 26038032Speter n++; 26138032Speter } 26238032Speter *bp++ = '\0'; 26338032Speter buflen -= n + 1; 26438032Speter } 26538032Speter 26638032Speter /* sort the records */ 26738032Speter for (i = 0; i < nmx; i++) 26838032Speter { 26938032Speter for (j = i + 1; j < nmx; j++) 27038032Speter { 27138032Speter if (prefer[i] > prefer[j] || 27238032Speter (prefer[i] == prefer[j] && weight[i] > weight[j])) 27338032Speter { 27438032Speter register int temp; 27538032Speter register char *temp1; 27638032Speter 27738032Speter temp = prefer[i]; 27838032Speter prefer[i] = prefer[j]; 27938032Speter prefer[j] = temp; 28038032Speter temp1 = mxhosts[i]; 28138032Speter mxhosts[i] = mxhosts[j]; 28238032Speter mxhosts[j] = temp1; 28338032Speter temp = weight[i]; 28438032Speter weight[i] = weight[j]; 28538032Speter weight[j] = temp; 28638032Speter } 28738032Speter } 28838032Speter if (seenlocal && prefer[i] >= localpref) 28938032Speter { 29038032Speter /* truncate higher preference part of list */ 29138032Speter nmx = i; 29238032Speter } 29338032Speter } 29438032Speter 29538032Speter /* delete duplicates from list (yes, some bozos have duplicates) */ 29638032Speter for (i = 0; i < nmx - 1; ) 29738032Speter { 29838032Speter if (strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) 29938032Speter i++; 30038032Speter else 30138032Speter { 30238032Speter /* compress out duplicate */ 30338032Speter for (j = i + 1; j < nmx; j++) 30438032Speter mxhosts[j] = mxhosts[j + 1]; 30538032Speter nmx--; 30638032Speter } 30738032Speter } 30838032Speter 30938032Speter if (nmx == 0) 31038032Speter { 31138032Speterpunt: 31238032Speter if (seenlocal && 31338032Speter (!TryNullMXList || sm_gethostbyname(host) == NULL)) 31438032Speter { 31538032Speter /* 31638032Speter ** If we have deleted all MX entries, this is 31738032Speter ** an error -- we should NEVER send to a host that 31838032Speter ** has an MX, and this should have been caught 31938032Speter ** earlier in the config file. 32038032Speter ** 32138032Speter ** Some sites prefer to go ahead and try the 32238032Speter ** A record anyway; that case is handled by 32338032Speter ** setting TryNullMXList. I believe this is a 32438032Speter ** bad idea, but it's up to you.... 32538032Speter */ 32638032Speter 32738032Speter *rcode = EX_CONFIG; 32838032Speter syserr("MX list for %s points back to %s", 32938032Speter host, MyHostName); 33038032Speter return -1; 33138032Speter } 33238032Speter if (strlen(host) >= (SIZE_T) sizeof MXHostBuf) 33338032Speter { 33438032Speter *rcode = EX_CONFIG; 33538032Speter syserr("Host name %s too long", 33638032Speter shortenstring(host, MAXSHORTSTR)); 33738032Speter return -1; 33838032Speter } 33938032Speter snprintf(MXHostBuf, sizeof MXHostBuf, "%s", host); 34038032Speter mxhosts[0] = MXHostBuf; 34138032Speter if (host[0] == '[') 34238032Speter { 34338032Speter register char *p; 34438032Speter 34538032Speter /* this may be an MX suppression-style address */ 34638032Speter p = strchr(MXHostBuf, ']'); 34738032Speter if (p != NULL) 34838032Speter { 34938032Speter *p = '\0'; 35038032Speter if (inet_addr(&MXHostBuf[1]) != INADDR_NONE) 35138032Speter { 35238032Speter nmx++; 35338032Speter *p = ']'; 35438032Speter } 35538032Speter else 35638032Speter { 35738032Speter trycanon = TRUE; 35838032Speter mxhosts[0]++; 35938032Speter } 36038032Speter } 36138032Speter } 36238032Speter if (trycanon && 36338032Speter getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE)) 36438032Speter { 36538032Speter bp = &MXHostBuf[strlen(MXHostBuf)]; 36638032Speter if (bp[-1] != '.') 36738032Speter { 36838032Speter *bp++ = '.'; 36938032Speter *bp = '\0'; 37038032Speter } 37138032Speter nmx = 1; 37238032Speter } 37338032Speter } 37438032Speter 37538032Speter /* if we have a default lowest preference, include that */ 37638032Speter if (fallbackMX != NULL && !seenlocal) 37738032Speter mxhosts[nmx++] = fallbackMX; 37838032Speter 37938032Speter return (nmx); 38038032Speter} 38138032Speter/* 38238032Speter** MXRAND -- create a randomizer for equal MX preferences 38338032Speter** 38438032Speter** If two MX hosts have equal preferences we want to randomize 38538032Speter** the selection. But in order for signatures to be the same, 38638032Speter** we need to randomize the same way each time. This function 38738032Speter** computes a pseudo-random hash function from the host name. 38838032Speter** 38938032Speter** Parameters: 39038032Speter** host -- the name of the host. 39138032Speter** 39238032Speter** Returns: 39338032Speter** A random but repeatable value based on the host name. 39438032Speter** 39538032Speter** Side Effects: 39638032Speter** none. 39738032Speter*/ 39838032Speter 39938032Speterint 40038032Spetermxrand(host) 40138032Speter register char *host; 40238032Speter{ 40338032Speter int hfunc; 40438032Speter static unsigned int seed; 40538032Speter 40638032Speter if (seed == 0) 40738032Speter { 40838032Speter seed = (int) curtime() & 0xffff; 40938032Speter if (seed == 0) 41038032Speter seed++; 41138032Speter } 41238032Speter 41338032Speter if (tTd(17, 9)) 41438032Speter printf("mxrand(%s)", host); 41538032Speter 41638032Speter hfunc = seed; 41738032Speter while (*host != '\0') 41838032Speter { 41938032Speter int c = *host++; 42038032Speter 42138032Speter if (isascii(c) && isupper(c)) 42238032Speter c = tolower(c); 42338032Speter hfunc = ((hfunc << 1) ^ c) % 2003; 42438032Speter } 42538032Speter 42638032Speter hfunc &= 0xff; 42738032Speter hfunc++; 42838032Speter 42938032Speter if (tTd(17, 9)) 43038032Speter printf(" = %d\n", hfunc); 43138032Speter return hfunc; 43238032Speter} 43338032Speter/* 43438032Speter** BESTMX -- find the best MX for a name 43538032Speter** 43638032Speter** This is really a hack, but I don't see any obvious way 43738032Speter** to generalize it at the moment. 43838032Speter*/ 43938032Speter 44038032Speter/* ARGSUSED3 */ 44138032Speterchar * 44238032Speterbestmx_map_lookup(map, name, av, statp) 44338032Speter MAP *map; 44438032Speter char *name; 44538032Speter char **av; 44638032Speter int *statp; 44738032Speter{ 44838032Speter int nmx; 44938032Speter auto int rcode; 45038032Speter int saveopts = _res.options; 45138032Speter int i, len = 0; 45238032Speter char *p; 45338032Speter char *mxhosts[MAXMXHOSTS + 1]; 45438032Speter char buf[MXHOSTBUFSIZE + 1]; 45538032Speter 45638032Speter _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); 45738032Speter nmx = getmxrr(name, mxhosts, FALSE, &rcode); 45838032Speter _res.options = saveopts; 45938032Speter if (nmx <= 0) 46038032Speter return NULL; 46138032Speter if (bitset(MF_MATCHONLY, map->map_mflags)) 46238032Speter return map_rewrite(map, name, strlen(name), NULL); 46338032Speter if ((map->map_coldelim == '\0') || (nmx == 1)) 46438032Speter return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); 46538032Speter 46638032Speter /* 46738032Speter ** We were given a -z flag (return all MXs) and there are multiple 46838032Speter ** ones. We need to build them all into a list. 46938032Speter */ 47038032Speter p = buf; 47138032Speter for (i = 0; i < nmx; i++) 47238032Speter { 47338032Speter int slen; 47438032Speter 47538032Speter if (strchr(mxhosts[i], map->map_coldelim) != NULL) 47638032Speter { 47738032Speter syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X", 47838032Speter mxhosts[i], map->map_coldelim); 47938032Speter return NULL; 48038032Speter } 48138032Speter slen = strlen(mxhosts[i]); 48238032Speter if (len + slen + 2 > sizeof buf) 48338032Speter break; 48438032Speter if (i > 0) 48538032Speter { 48638032Speter *p++ = map->map_coldelim; 48738032Speter len++; 48838032Speter } 48938032Speter strcpy(p, mxhosts[i]); 49038032Speter p += slen; 49138032Speter len += slen; 49238032Speter } 49338032Speter return map_rewrite(map, buf, len, av); 49438032Speter} 49538032Speter/* 49638032Speter** DNS_GETCANONNAME -- get the canonical name for named host using DNS 49738032Speter** 49838032Speter** This algorithm tries to be smart about wildcard MX records. 49938032Speter** This is hard to do because DNS doesn't tell is if we matched 50038032Speter** against a wildcard or a specific MX. 50138032Speter** 50238032Speter** We always prefer A & CNAME records, since these are presumed 50338032Speter** to be specific. 50438032Speter** 50538032Speter** If we match an MX in one pass and lose it in the next, we use 50638032Speter** the old one. For example, consider an MX matching *.FOO.BAR.COM. 50738032Speter** A hostname bletch.foo.bar.com will match against this MX, but 50838032Speter** will stop matching when we try bletch.bar.com -- so we know 50938032Speter** that bletch.foo.bar.com must have been right. This fails if 51038032Speter** there was also an MX record matching *.BAR.COM, but there are 51138032Speter** some things that just can't be fixed. 51238032Speter** 51338032Speter** Parameters: 51438032Speter** host -- a buffer containing the name of the host. 51538032Speter** This is a value-result parameter. 51638032Speter** hbsize -- the size of the host buffer. 51738032Speter** trymx -- if set, try MX records as well as A and CNAME. 51838032Speter** statp -- pointer to place to store status. 51938032Speter** 52038032Speter** Returns: 52138032Speter** TRUE -- if the host matched. 52238032Speter** FALSE -- otherwise. 52338032Speter*/ 52438032Speter 52538032Speterbool 52638032Speterdns_getcanonname(host, hbsize, trymx, statp) 52738032Speter char *host; 52838032Speter int hbsize; 52938032Speter bool trymx; 53038032Speter int *statp; 53138032Speter{ 53238032Speter register u_char *eom, *ap; 53338032Speter register char *cp; 53438032Speter register int n; 53538032Speter HEADER *hp; 53638032Speter querybuf answer; 53738032Speter int ancount, qdcount; 53838032Speter int ret; 53938032Speter char **domain; 54038032Speter int type; 54138032Speter char **dp; 54238032Speter char *mxmatch; 54338032Speter bool amatch; 54438032Speter bool gotmx = FALSE; 54538032Speter int qtype; 54638032Speter int loopcnt; 54738032Speter char *xp; 54838032Speter char nbuf[MAX(MAXPACKET, MAXDNAME*2+2)]; 54938032Speter char *searchlist[MAXDNSRCH+2]; 55038032Speter extern char *gethostalias __P((char *)); 55138032Speter 55238032Speter if (tTd(8, 2)) 55338032Speter printf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); 55438032Speter 55538032Speter if ((_res.options & RES_INIT) == 0 && res_init() == -1) 55638032Speter { 55738032Speter *statp = EX_UNAVAILABLE; 55838032Speter return FALSE; 55938032Speter } 56038032Speter 56138032Speter /* 56238032Speter ** Initialize domain search list. If there is at least one 56338032Speter ** dot in the name, search the unmodified name first so we 56438032Speter ** find "vse.CS" in Czechoslovakia instead of in the local 56538032Speter ** domain (e.g., vse.CS.Berkeley.EDU). 56638032Speter ** 56738032Speter ** Older versions of the resolver could create this 56838032Speter ** list by tearing apart the host name. 56938032Speter */ 57038032Speter 57138032Speter loopcnt = 0; 57238032Spetercnameloop: 57338032Speter /* Check for dots in the name */ 57438032Speter for (cp = host, n = 0; *cp != '\0'; cp++) 57538032Speter if (*cp == '.') 57638032Speter n++; 57738032Speter 57838032Speter /* 57938032Speter ** If this is a simple name, determine whether it matches an 58038032Speter ** alias in the file defined by the environment variable HOSTALIASES. 58138032Speter */ 58238032Speter if (n == 0 && (xp = gethostalias(host)) != NULL) 58338032Speter { 58438032Speter if (loopcnt++ > MAXCNAMEDEPTH) 58538032Speter { 58638032Speter syserr("loop in ${HOSTALIASES} file"); 58738032Speter } 58838032Speter else 58938032Speter { 59038032Speter strncpy(host, xp, hbsize); 59138032Speter host[hbsize - 1] = '\0'; 59238032Speter goto cnameloop; 59338032Speter } 59438032Speter } 59538032Speter 59638032Speter /* 59738032Speter ** Build the search list. 59838032Speter ** If there is at least one dot in name, start with a null 59938032Speter ** domain to search the unmodified name first. 60038032Speter ** If name does not end with a dot and search up local domain 60138032Speter ** tree desired, append each local domain component to the 60238032Speter ** search list; if name contains no dots and default domain 60338032Speter ** name is desired, append default domain name to search list; 60438032Speter ** else if name ends in a dot, remove that dot. 60538032Speter */ 60638032Speter 60738032Speter dp = searchlist; 60838032Speter if (n > 0) 60938032Speter *dp++ = ""; 61038032Speter if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) 61138032Speter { 61238032Speter for (domain = _res.dnsrch; *domain != NULL; ) 61338032Speter *dp++ = *domain++; 61438032Speter } 61538032Speter else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) 61638032Speter { 61738032Speter *dp++ = _res.defdname; 61838032Speter } 61938032Speter else if (*cp == '.') 62038032Speter { 62138032Speter *cp = '\0'; 62238032Speter } 62338032Speter *dp = NULL; 62438032Speter 62538032Speter /* 62638032Speter ** Now loop through the search list, appending each domain in turn 62738032Speter ** name and searching for a match. 62838032Speter */ 62938032Speter 63038032Speter mxmatch = NULL; 63138032Speter qtype = T_ANY; 63238032Speter 63338032Speter for (dp = searchlist; *dp != NULL; ) 63438032Speter { 63538032Speter if (qtype == T_ANY) 63638032Speter gotmx = FALSE; 63738032Speter if (tTd(8, 5)) 63838032Speter printf("dns_getcanonname: trying %s.%s (%s)\n", 63938032Speter host, *dp, 64038032Speter qtype == T_ANY ? "ANY" : qtype == T_A ? "A" : 64138032Speter qtype == T_MX ? "MX" : "???"); 64238032Speter ret = res_querydomain(host, *dp, C_IN, qtype, 64338032Speter answer.qb2, sizeof(answer.qb2)); 64438032Speter if (ret <= 0) 64538032Speter { 64638032Speter if (tTd(8, 7)) 64738032Speter printf("\tNO: errno=%d, h_errno=%d\n", 64838032Speter errno, h_errno); 64938032Speter 65038032Speter if (errno == ECONNREFUSED || h_errno == TRY_AGAIN) 65138032Speter { 65238032Speter /* the name server seems to be down */ 65338032Speter h_errno = TRY_AGAIN; 65438032Speter *statp = EX_TEMPFAIL; 65538032Speter return FALSE; 65638032Speter } 65738032Speter 65838032Speter if (h_errno != HOST_NOT_FOUND) 65938032Speter { 66038032Speter /* might have another type of interest */ 66138032Speter if (qtype == T_ANY) 66238032Speter { 66338032Speter qtype = T_A; 66438032Speter continue; 66538032Speter } 66638032Speter else if (qtype == T_A && !gotmx && trymx) 66738032Speter { 66838032Speter qtype = T_MX; 66938032Speter continue; 67038032Speter } 67138032Speter } 67238032Speter 67338032Speter /* definite no -- try the next domain */ 67438032Speter dp++; 67538032Speter qtype = T_ANY; 67638032Speter continue; 67738032Speter } 67838032Speter else if (tTd(8, 7)) 67938032Speter printf("\tYES\n"); 68038032Speter 68138032Speter /* avoid problems after truncation in tcp packets */ 68238032Speter if (ret > sizeof(answer)) 68338032Speter ret = sizeof(answer); 68438032Speter 68538032Speter /* 68638032Speter ** Appear to have a match. Confirm it by searching for A or 68738032Speter ** CNAME records. If we don't have a local domain 68838032Speter ** wild card MX record, we will accept MX as well. 68938032Speter */ 69038032Speter 69138032Speter hp = (HEADER *) &answer; 69238032Speter ap = (u_char *) &answer + HFIXEDSZ; 69338032Speter eom = (u_char *) &answer + ret; 69438032Speter 69538032Speter /* skip question part of response -- we know what we asked */ 69638032Speter for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) 69738032Speter { 69838032Speter if ((ret = dn_skipname(ap, eom)) < 0) 69938032Speter { 70038032Speter if (tTd(8, 20)) 70138032Speter printf("qdcount failure (%d)\n", 70238032Speter ntohs(hp->qdcount)); 70338032Speter *statp = EX_SOFTWARE; 70438032Speter return FALSE; /* ???XXX??? */ 70538032Speter } 70638032Speter } 70738032Speter 70838032Speter amatch = FALSE; 70938032Speter for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; 71038032Speter ap += n) 71138032Speter { 71238032Speter n = dn_expand((u_char *) &answer, eom, ap, 71338032Speter (RES_UNC_T) nbuf, sizeof nbuf); 71438032Speter if (n < 0) 71538032Speter break; 71638032Speter ap += n; 71738032Speter GETSHORT(type, ap); 71838032Speter ap += INT16SZ + INT32SZ; 71938032Speter GETSHORT(n, ap); 72038032Speter switch (type) 72138032Speter { 72238032Speter case T_MX: 72338032Speter gotmx = TRUE; 72438032Speter if (**dp != '\0' && HasWildcardMX) 72538032Speter { 72638032Speter /* 72738032Speter ** If we are using MX matches and have 72838032Speter ** not yet gotten one, save this one 72938032Speter ** but keep searching for an A or 73038032Speter ** CNAME match. 73138032Speter */ 73238032Speter 73338032Speter if (trymx && mxmatch == NULL) 73438032Speter mxmatch = *dp; 73538032Speter continue; 73638032Speter } 73738032Speter 73838032Speter /* 73938032Speter ** If we did not append a domain name, this 74038032Speter ** must have been a canonical name to start 74138032Speter ** with. Even if we did append a domain name, 74238032Speter ** in the absence of a wildcard MX this must 74338032Speter ** still be a real MX match. 74438032Speter ** Such MX matches are as good as an A match, 74538032Speter ** fall through. 74638032Speter */ 74738032Speter 74838032Speter case T_A: 74938032Speter /* Flag that a good match was found */ 75038032Speter amatch = TRUE; 75138032Speter 75238032Speter /* continue in case a CNAME also exists */ 75338032Speter continue; 75438032Speter 75538032Speter case T_CNAME: 75638032Speter if (DontExpandCnames) 75738032Speter { 75838032Speter /* got CNAME -- guaranteed canonical */ 75938032Speter amatch = TRUE; 76038032Speter break; 76138032Speter } 76238032Speter 76338032Speter if (loopcnt++ > MAXCNAMEDEPTH) 76438032Speter { 76538032Speter /*XXX should notify postmaster XXX*/ 76638032Speter message("DNS failure: CNAME loop for %s", 76738032Speter host); 76838032Speter if (CurEnv->e_message == NULL) 76938032Speter { 77038032Speter char ebuf[MAXLINE]; 77138032Speter 77238032Speter snprintf(ebuf, sizeof ebuf, 77338032Speter "Deferred: DNS failure: CNAME loop for %.100s", 77438032Speter host); 77538032Speter CurEnv->e_message = newstr(ebuf); 77638032Speter } 77738032Speter h_errno = NO_RECOVERY; 77838032Speter *statp = EX_CONFIG; 77938032Speter return FALSE; 78038032Speter } 78138032Speter 78238032Speter /* value points at name */ 78338032Speter if ((ret = dn_expand((u_char *)&answer, 78438032Speter eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0) 78538032Speter break; 78638032Speter (void)strncpy(host, nbuf, hbsize); /* XXX */ 78738032Speter host[hbsize - 1] = '\0'; 78838032Speter 78938032Speter /* 79038032Speter ** RFC 1034 section 3.6 specifies that CNAME 79138032Speter ** should point at the canonical name -- but 79238032Speter ** urges software to try again anyway. 79338032Speter */ 79438032Speter 79538032Speter goto cnameloop; 79638032Speter 79738032Speter default: 79838032Speter /* not a record of interest */ 79938032Speter continue; 80038032Speter } 80138032Speter } 80238032Speter 80338032Speter if (amatch) 80438032Speter { 80538032Speter /* 80638032Speter ** Got a good match -- either an A, CNAME, or an 80738032Speter ** exact MX record. Save it and get out of here. 80838032Speter */ 80938032Speter 81038032Speter mxmatch = *dp; 81138032Speter break; 81238032Speter } 81338032Speter 81438032Speter /* 81538032Speter ** Nothing definitive yet. 81638032Speter ** If this was a T_ANY query, we don't really know what 81738032Speter ** was returned -- it might have been a T_NS, 81838032Speter ** for example. Try T_A to be more specific 81938032Speter ** during the next pass. 82038032Speter ** If this was a T_A query and we haven't yet found a MX 82138032Speter ** match, try T_MX if allowed to do so. 82238032Speter ** Otherwise, try the next domain. 82338032Speter */ 82438032Speter 82538032Speter if (qtype == T_ANY) 82638032Speter qtype = T_A; 82738032Speter else if (qtype == T_A && !gotmx && trymx) 82838032Speter qtype = T_MX; 82938032Speter else 83038032Speter { 83138032Speter qtype = T_ANY; 83238032Speter dp++; 83338032Speter } 83438032Speter } 83538032Speter 83638032Speter /* if nothing was found, we are done */ 83738032Speter if (mxmatch == NULL) 83838032Speter { 83938032Speter *statp = EX_NOHOST; 84038032Speter return FALSE; 84138032Speter } 84238032Speter 84338032Speter /* 84438032Speter ** Create canonical name and return. 84538032Speter ** If saved domain name is null, name was already canonical. 84638032Speter ** Otherwise append the saved domain name. 84738032Speter */ 84838032Speter 84938032Speter (void) snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host, 85038032Speter *mxmatch == '\0' ? "" : ".", 85138032Speter MAXDNAME, mxmatch); 85238032Speter strncpy(host, nbuf, hbsize); 85338032Speter host[hbsize - 1] = '\0'; 85438032Speter if (tTd(8, 5)) 85538032Speter printf("dns_getcanonname: %s\n", host); 85638032Speter *statp = EX_OK; 85738032Speter return TRUE; 85838032Speter} 85938032Speter 86038032Speter 86138032Speter 86238032Speterchar * 86338032Spetergethostalias(host) 86438032Speter char *host; 86538032Speter{ 86638032Speter char *fname; 86738032Speter FILE *fp; 86838032Speter register char *p = NULL; 86938032Speter int sff = SFF_REGONLY; 87038032Speter char buf[MAXLINE]; 87138032Speter static char hbuf[MAXDNAME]; 87238032Speter 87338032Speter if (DontLockReadFiles) 87438032Speter sff |= SFF_NOLOCK; 87538032Speter fname = getenv("HOSTALIASES"); 87638032Speter if (fname == NULL || 87738032Speter (fp = safefopen(fname, O_RDONLY, 0, sff)) == NULL) 87838032Speter return NULL; 87938032Speter while (fgets(buf, sizeof buf, fp) != NULL) 88038032Speter { 88138032Speter for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++) 88238032Speter continue; 88338032Speter if (*p == 0) 88438032Speter { 88538032Speter /* syntax error */ 88638032Speter continue; 88738032Speter } 88838032Speter *p++ = '\0'; 88938032Speter if (strcasecmp(buf, host) == 0) 89038032Speter break; 89138032Speter } 89238032Speter 89338032Speter if (feof(fp)) 89438032Speter { 89538032Speter /* no match */ 89638032Speter fclose(fp); 89738032Speter return NULL; 89838032Speter } 89938032Speter fclose(fp); 90038032Speter 90138032Speter /* got a match; extract the equivalent name */ 90238032Speter while (*p != '\0' && isascii(*p) && isspace(*p)) 90338032Speter p++; 90438032Speter host = p; 90538032Speter while (*p != '\0' && !(isascii(*p) && isspace(*p))) 90638032Speter p++; 90738032Speter *p = '\0'; 90838032Speter strncpy(hbuf, host, sizeof hbuf - 1); 90938032Speter hbuf[sizeof hbuf - 1] = '\0'; 91038032Speter return hbuf; 91138032Speter} 91238032Speter 91338032Speter#endif /* NAMED_BIND */ 914