129432Sbloom /* 268839Seric * Copyright (c) 1986, 1995 Eric P. Allman 362524Sbostic * Copyright (c) 1988, 1993 462524Sbostic * The Regents of the University of California. All rights reserved. 533778Sbostic * 642826Sbostic * %sccs.include.redist.c% 733778Sbostic */ 829432Sbloom 940961Sbostic #include "sendmail.h" 1035653Seric 1129432Sbloom #ifndef lint 1266334Seric #if NAMED_BIND 13*69852Seric static char sccsid[] = "@(#)domain.c 8.43 (Berkeley) 06/10/95 (with name server)"; 1435653Seric #else 15*69852Seric static char sccsid[] = "@(#)domain.c 8.43 (Berkeley) 06/10/95 (without name server)"; 1635653Seric #endif 1733778Sbostic #endif /* not lint */ 1829432Sbloom 1966334Seric #if NAMED_BIND 2035653Seric 2135653Seric #include <errno.h> 2233929Sbostic #include <resolv.h> 2329432Sbloom 2457454Seric typedef union 2557454Seric { 2657454Seric HEADER qb1; 2768513Seric u_char qb2[PACKETSZ]; 2829432Sbloom } querybuf; 2929432Sbloom 3059075Seric static char MXHostBuf[MAXMXHOSTS*PACKETSZ]; 3129432Sbloom 3257943Seric #ifndef MAXDNSRCH 3357943Seric #define MAXDNSRCH 6 /* number of possible domains to search */ 3457943Seric #endif 3557943Seric 3658010Seric #ifndef MAX 3758010Seric #define MAX(a, b) ((a) > (b) ? (a) : (b)) 3858010Seric #endif 3958010Seric 4063969Seric #ifndef NO_DATA 4163969Seric # define NO_DATA NO_ADDRESS 4263969Seric #endif 4363969Seric 4468513Seric #ifndef HFIXEDSZ 4568513Seric # define HFIXEDSZ 12 /* sizeof(HEADER) */ 4665751Seric #endif 4765751Seric 4868513Seric #define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ 4968495Seric 5068513Seric #if defined(__RES) && (__RES >= 19940415) 5168513Seric # define RES_UNC_T char * 5268513Seric #else 5368513Seric # define RES_UNC_T u_char * 5468513Seric #endif 5558248Seric /* 5658248Seric ** GETMXRR -- get MX resource records for a domain 5758248Seric ** 5858248Seric ** Parameters: 5958248Seric ** host -- the name of the host to MX. 6058248Seric ** mxhosts -- a pointer to a return buffer of MX records. 6159273Seric ** droplocalhost -- If TRUE, all MX records less preferred 6259273Seric ** than the local host (as determined by $=w) will 6359273Seric ** be discarded. 6458248Seric ** rcode -- a pointer to an EX_ status code. 6558248Seric ** 6658248Seric ** Returns: 6758248Seric ** The number of MX records found. 6858248Seric ** -1 if there is an internal failure. 6958248Seric ** If no MX records are found, mxhosts[0] is set to host 7058248Seric ** and 1 is returned. 7158248Seric */ 7258248Seric 7369748Seric int 7459273Seric getmxrr(host, mxhosts, droplocalhost, rcode) 7559075Seric char *host; 7659075Seric char **mxhosts; 7759273Seric bool droplocalhost; 7833929Sbostic int *rcode; 7929432Sbloom { 8033929Sbostic extern int h_errno; 8133929Sbostic register u_char *eom, *cp; 8263840Seric register int i, j, n; 8363840Seric int nmx = 0; 8433929Sbostic register char *bp; 8529432Sbloom HEADER *hp; 8633929Sbostic querybuf answer; 8758857Seric int ancount, qdcount, buflen; 8863840Seric bool seenlocal = FALSE; 8968513Seric u_short pref, type; 9068513Seric u_short localpref = 256; 9158891Seric char *fallbackMX = FallBackMX; 9258891Seric static bool firsttime = TRUE; 9364015Seric bool trycanon = FALSE; 9458891Seric u_short prefer[MAXMXHOSTS]; 9557454Seric int weight[MAXMXHOSTS]; 9663840Seric extern bool getcanonname(); 9729432Sbloom 9866145Seric if (tTd(8, 2)) 9966145Seric printf("getmxrr(%s, droplocalhost=%d)\n", host, droplocalhost); 10066145Seric 10158891Seric if (fallbackMX != NULL) 10258891Seric { 10368513Seric if (firsttime && 10468513Seric res_query(FallBackMX, C_IN, T_A, 10568513Seric (u_char *) &answer, sizeof answer) < 0) 10658891Seric { 10758891Seric /* this entry is bogus */ 10858891Seric fallbackMX = FallBackMX = NULL; 10958891Seric } 11068513Seric else if (droplocalhost && wordinclass(fallbackMX, 'w')) 11158891Seric { 11258891Seric /* don't use fallback for this pass */ 11358891Seric fallbackMX = NULL; 11458891Seric } 11558891Seric firsttime = FALSE; 11658891Seric } 11758891Seric 11869661Seric *rcode = EX_OK; 11969661Seric 12064676Seric /* efficiency hack -- numeric or non-MX lookups */ 12164676Seric if (host[0] == '[') 12264676Seric goto punt; 12364676Seric 12468513Seric /* 12568513Seric ** If we don't have MX records in our host switch, don't 12668513Seric ** try for MX records. Note that this really isn't "right", 12768513Seric ** since we might be set up to try NIS first and then DNS; 12868513Seric ** if the host is found in NIS we really shouldn't be doing 12968513Seric ** MX lookups. However, that should be a degenerate case. 13068513Seric */ 13168513Seric 13268513Seric if (!UseNameServer) 13368513Seric goto punt; 13468513Seric 13536483Sbostic errno = 0; 13668513Seric n = res_search(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer)); 13735653Seric if (n < 0) 13835653Seric { 13933929Sbostic if (tTd(8, 1)) 14052852Seric printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", 14152852Seric (host == NULL) ? "<NULL>" : host, errno, h_errno); 14235653Seric switch (h_errno) 14335653Seric { 14435653Seric case NO_DATA: 14564015Seric trycanon = TRUE; 14664015Seric /* fall through */ 14764015Seric 14835653Seric case NO_RECOVERY: 14935653Seric /* no MX data on this host */ 15033929Sbostic goto punt; 15135653Seric 15235653Seric case HOST_NOT_FOUND: 15369562Seric #if BROKEN_RES_SEARCH 15468513Seric case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ 15566318Seric #endif 15668513Seric /* host doesn't exist in DNS; might be in /etc/hosts */ 15733929Sbostic *rcode = EX_NOHOST; 15868513Seric goto punt; 15964950Seric 16035653Seric case TRY_AGAIN: 16135653Seric /* couldn't connect to the name server */ 16235653Seric /* it might come up later; better queue it up */ 16333929Sbostic *rcode = EX_TEMPFAIL; 16433929Sbostic break; 16566253Seric 16666253Seric default: 16766253Seric syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n", 16866253Seric host, h_errno); 16966253Seric *rcode = EX_OSERR; 17066253Seric break; 17129432Sbloom } 17235653Seric 17335653Seric /* irreconcilable differences */ 17435653Seric return (-1); 17529432Sbloom } 17633929Sbostic 17733929Sbostic /* find first satisfactory answer */ 17833929Sbostic hp = (HEADER *)&answer; 17968513Seric cp = (u_char *)&answer + HFIXEDSZ; 18033929Sbostic eom = (u_char *)&answer + n; 18133929Sbostic for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 18250957Skarels if ((n = dn_skipname(cp, eom)) < 0) 18333929Sbostic goto punt; 18459075Seric buflen = sizeof(MXHostBuf) - 1; 18559075Seric bp = MXHostBuf; 18633929Sbostic ancount = ntohs(hp->ancount); 18758848Seric while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) 18856336Seric { 18946928Sbostic if ((n = dn_expand((u_char *)&answer, 19068513Seric eom, cp, (RES_UNC_T) bp, buflen)) < 0) 19129432Sbloom break; 19229432Sbloom cp += n; 19333929Sbostic GETSHORT(type, cp); 19468513Seric cp += INT16SZ + INT32SZ; 19533929Sbostic GETSHORT(n, cp); 19656336Seric if (type != T_MX) 19756336Seric { 19857943Seric if (tTd(8, 8) || _res.options & RES_DEBUG) 19929432Sbloom printf("unexpected answer type %d, size %d\n", 20033929Sbostic type, n); 20129432Sbloom cp += n; 20229432Sbloom continue; 20329432Sbloom } 20433929Sbostic GETSHORT(pref, cp); 20556336Seric if ((n = dn_expand((u_char *)&answer, eom, cp, 20668513Seric (RES_UNC_T) bp, buflen)) < 0) 20729432Sbloom break; 20829551Sbloom cp += n; 20968513Seric if (droplocalhost && wordinclass(bp, 'w')) 21056336Seric { 21166145Seric if (tTd(8, 3)) 21266145Seric printf("found localhost (%s) in MX list, pref=%d\n", 21366145Seric bp, pref); 21458857Seric if (!seenlocal || pref < localpref) 21533929Sbostic localpref = pref; 21658857Seric seenlocal = TRUE; 21729551Sbloom continue; 21829551Sbloom } 21957454Seric weight[nmx] = mxrand(bp); 22029432Sbloom prefer[nmx] = pref; 22129432Sbloom mxhosts[nmx++] = bp; 22256336Seric n = strlen(bp); 22333929Sbostic bp += n; 22456336Seric if (bp[-1] != '.') 22556336Seric { 22656336Seric *bp++ = '.'; 22756336Seric n++; 22856336Seric } 22956336Seric *bp++ = '\0'; 23056336Seric buflen -= n + 1; 23129432Sbloom } 23263840Seric 23363840Seric /* sort the records */ 23463840Seric for (i = 0; i < nmx; i++) 23557454Seric { 23663840Seric for (j = i + 1; j < nmx; j++) 23758668Seric { 23863840Seric if (prefer[i] > prefer[j] || 23963840Seric (prefer[i] == prefer[j] && weight[i] > weight[j])) 24063840Seric { 24163840Seric register int temp; 24263840Seric register char *temp1; 24363840Seric 24463840Seric temp = prefer[i]; 24563840Seric prefer[i] = prefer[j]; 24663840Seric prefer[j] = temp; 24763840Seric temp1 = mxhosts[i]; 24863840Seric mxhosts[i] = mxhosts[j]; 24963840Seric mxhosts[j] = temp1; 25063840Seric temp = weight[i]; 25163840Seric weight[i] = weight[j]; 25263840Seric weight[j] = temp; 25363840Seric } 25458668Seric } 25563840Seric if (seenlocal && prefer[i] >= localpref) 25663840Seric { 25763840Seric /* truncate higher preference part of list */ 25863840Seric nmx = i; 25963840Seric } 26029551Sbloom } 26163840Seric 26263840Seric if (nmx == 0) 26357454Seric { 26463840Seric punt: 26563840Seric if (seenlocal && 26668693Seric (!TryNullMXList || sm_gethostbyname(host) == NULL)) 26757454Seric { 26863840Seric /* 26963840Seric ** If we have deleted all MX entries, this is 27063840Seric ** an error -- we should NEVER send to a host that 27163840Seric ** has an MX, and this should have been caught 27263840Seric ** earlier in the config file. 27363840Seric ** 27463840Seric ** Some sites prefer to go ahead and try the 27563840Seric ** A record anyway; that case is handled by 27663840Seric ** setting TryNullMXList. I believe this is a 27763840Seric ** bad idea, but it's up to you.... 27863840Seric */ 27929432Sbloom 28063840Seric *rcode = EX_CONFIG; 28166256Seric syserr("MX list for %s points back to %s", 28266256Seric host, MyHostName); 28363840Seric return -1; 28463840Seric } 28564676Seric strcpy(MXHostBuf, host); 28664676Seric mxhosts[0] = MXHostBuf; 28764676Seric if (host[0] == '[') 28864676Seric { 28964676Seric register char *p; 29064676Seric 29164676Seric /* this may be an MX suppression-style address */ 29264676Seric p = strchr(MXHostBuf, ']'); 29364676Seric if (p != NULL) 29464676Seric { 29564676Seric *p = '\0'; 29664676Seric if (inet_addr(&MXHostBuf[1]) != -1) 29764676Seric *p = ']'; 29864676Seric else 29964677Seric { 30064677Seric trycanon = TRUE; 30164676Seric mxhosts[0]++; 30264677Seric } 30364676Seric } 30464676Seric } 30564677Seric if (trycanon && 30664677Seric getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE)) 30763840Seric { 30864950Seric bp = &MXHostBuf[strlen(MXHostBuf)]; 30963840Seric if (bp[-1] != '.') 31057454Seric { 31163840Seric *bp++ = '.'; 31263840Seric *bp = '\0'; 31329551Sbloom } 31469567Seric nmx = 1; 31529551Sbloom } 31629432Sbloom } 31758848Seric 31858848Seric /* if we have a default lowest preference, include that */ 31964950Seric if (fallbackMX != NULL && !seenlocal) 32064950Seric mxhosts[nmx++] = fallbackMX; 32158848Seric 32257454Seric return (nmx); 32329432Sbloom } 32457135Seric /* 32557454Seric ** MXRAND -- create a randomizer for equal MX preferences 32657454Seric ** 32757454Seric ** If two MX hosts have equal preferences we want to randomize 32857454Seric ** the selection. But in order for signatures to be the same, 32957454Seric ** we need to randomize the same way each time. This function 33057454Seric ** computes a pseudo-random hash function from the host name. 33157454Seric ** 33257454Seric ** Parameters: 33357454Seric ** host -- the name of the host. 33457454Seric ** 33557454Seric ** Returns: 33657454Seric ** A random but repeatable value based on the host name. 33757454Seric ** 33857454Seric ** Side Effects: 33957454Seric ** none. 34057454Seric */ 34157454Seric 34269748Seric int 34357454Seric mxrand(host) 34457454Seric register char *host; 34557454Seric { 34657454Seric int hfunc; 34757454Seric static unsigned int seed; 34857454Seric 34957454Seric if (seed == 0) 35057454Seric { 35157454Seric seed = (int) curtime() & 0xffff; 35257454Seric if (seed == 0) 35357454Seric seed++; 35457454Seric } 35557454Seric 35657454Seric if (tTd(17, 9)) 35757454Seric printf("mxrand(%s)", host); 35857454Seric 35957454Seric hfunc = seed; 36057454Seric while (*host != '\0') 36157454Seric { 36257454Seric int c = *host++; 36357454Seric 36457454Seric if (isascii(c) && isupper(c)) 36557454Seric c = tolower(c); 36665952Seric hfunc = ((hfunc << 1) ^ c) % 2003; 36757454Seric } 36857454Seric 36957454Seric hfunc &= 0xff; 37057454Seric 37157454Seric if (tTd(17, 9)) 37257454Seric printf(" = %d\n", hfunc); 37357454Seric return hfunc; 37457454Seric } 37557454Seric /* 37669401Seric ** DNS_GETCANONNAME -- get the canonical name for named host using DNS 37757135Seric ** 37857943Seric ** This algorithm tries to be smart about wildcard MX records. 37957943Seric ** This is hard to do because DNS doesn't tell is if we matched 38057943Seric ** against a wildcard or a specific MX. 38157943Seric ** 38257943Seric ** We always prefer A & CNAME records, since these are presumed 38357943Seric ** to be specific. 38457943Seric ** 38557943Seric ** If we match an MX in one pass and lose it in the next, we use 38657943Seric ** the old one. For example, consider an MX matching *.FOO.BAR.COM. 38757943Seric ** A hostname bletch.foo.bar.com will match against this MX, but 38857943Seric ** will stop matching when we try bletch.bar.com -- so we know 38957943Seric ** that bletch.foo.bar.com must have been right. This fails if 39057943Seric ** there was also an MX record matching *.BAR.COM, but there are 39157943Seric ** some things that just can't be fixed. 39257943Seric ** 39357135Seric ** Parameters: 39457135Seric ** host -- a buffer containing the name of the host. 39557135Seric ** This is a value-result parameter. 39657135Seric ** hbsize -- the size of the host buffer. 39763840Seric ** trymx -- if set, try MX records as well as A and CNAME. 39869401Seric ** statp -- pointer to place to store status. 39957135Seric ** 40057135Seric ** Returns: 40157135Seric ** TRUE -- if the host matched. 40257135Seric ** FALSE -- otherwise. 40357135Seric */ 40429653Sbloom 40551314Seric bool 40669401Seric dns_getcanonname(host, hbsize, trymx, statp) 40729653Sbloom char *host; 40829653Sbloom int hbsize; 40963840Seric bool trymx; 41069401Seric int *statp; 41129653Sbloom { 41240277Sbostic extern int h_errno; 41351324Seric register u_char *eom, *ap; 41451324Seric register char *cp; 41533929Sbostic register int n; 41629653Sbloom HEADER *hp; 41733929Sbostic querybuf answer; 41861707Seric int ancount, qdcount; 41951324Seric int ret; 42051324Seric char **domain; 42151324Seric int type; 42257943Seric char **dp; 42357943Seric char *mxmatch; 42457943Seric bool amatch; 42568513Seric bool gotmx = FALSE; 42658404Seric int qtype; 42762524Sbostic int loopcnt; 42865193Seric char *xp; 42958010Seric char nbuf[MAX(PACKETSZ, MAXDNAME*2+2)]; 43058039Seric char *searchlist[MAXDNSRCH+2]; 43165193Seric extern char *gethostalias(); 43229653Sbloom 43351324Seric if (tTd(8, 2)) 43469825Seric printf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); 43551324Seric 43651324Seric if ((_res.options & RES_INIT) == 0 && res_init() == -1) 43769401Seric { 43869401Seric *statp = EX_UNAVAILABLE; 43969401Seric return FALSE; 44069401Seric } 44151324Seric 44236483Sbostic /* 44357943Seric ** Initialize domain search list. If there is at least one 44457943Seric ** dot in the name, search the unmodified name first so we 44557943Seric ** find "vse.CS" in Czechoslovakia instead of in the local 44657943Seric ** domain (e.g., vse.CS.Berkeley.EDU). 44757943Seric ** 44857943Seric ** Older versions of the resolver could create this 44957943Seric ** list by tearing apart the host name. 45057205Seric */ 45157205Seric 45262524Sbostic loopcnt = 0; 45359268Seric cnameloop: 45459268Seric for (cp = host, n = 0; *cp; cp++) 45559268Seric if (*cp == '.') 45659268Seric n++; 45759268Seric 45865193Seric if (n == 0 && (xp = gethostalias(host)) != NULL) 45965193Seric { 46065193Seric if (loopcnt++ > MAXCNAMEDEPTH) 46165193Seric { 46265193Seric syserr("loop in ${HOSTALIASES} file"); 46365193Seric } 46465193Seric else 46565193Seric { 46665193Seric strncpy(host, xp, hbsize); 46765193Seric host[hbsize - 1] = '\0'; 46865193Seric goto cnameloop; 46965193Seric } 47065193Seric } 47165193Seric 47257943Seric dp = searchlist; 47357943Seric if (n > 0) 47457943Seric *dp++ = ""; 47558411Seric if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) 47651324Seric { 47757943Seric for (domain = _res.dnsrch; *domain != NULL; ) 47857943Seric *dp++ = *domain++; 47957205Seric } 48058411Seric else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) 48158411Seric { 48258411Seric *dp++ = _res.defdname; 48358411Seric } 48466040Seric else if (*cp == '.') 48566040Seric { 48666040Seric *cp = '\0'; 48766040Seric } 48857943Seric *dp = NULL; 48957205Seric 49057205Seric /* 49157943Seric ** Now run through the search list for the name in question. 49257205Seric */ 49357205Seric 49457943Seric mxmatch = NULL; 49558404Seric qtype = T_ANY; 49657943Seric 49758404Seric for (dp = searchlist; *dp != NULL; ) 49857205Seric { 49958411Seric if (qtype == T_ANY) 50058411Seric gotmx = FALSE; 50157943Seric if (tTd(8, 5)) 50269825Seric printf("dns_getcanonname: trying %s.%s (%s)\n", 50369825Seric host, *dp, 50458508Seric qtype == T_ANY ? "ANY" : qtype == T_A ? "A" : 50558508Seric qtype == T_MX ? "MX" : "???"); 50658404Seric ret = res_querydomain(host, *dp, C_IN, qtype, 50768513Seric answer.qb2, sizeof(answer.qb2)); 50857943Seric if (ret <= 0) 50951324Seric { 51058796Seric if (tTd(8, 7)) 51158082Seric printf("\tNO: errno=%d, h_errno=%d\n", 51258082Seric errno, h_errno); 51351324Seric 51458082Seric if (errno == ECONNREFUSED || h_errno == TRY_AGAIN) 51557205Seric { 51657943Seric /* the name server seems to be down */ 51751324Seric h_errno = TRY_AGAIN; 51869401Seric *statp = EX_TEMPFAIL; 51951910Seric return FALSE; 52051324Seric } 52157943Seric 52258501Seric if (h_errno != HOST_NOT_FOUND) 52358404Seric { 52458501Seric /* might have another type of interest */ 52558501Seric if (qtype == T_ANY) 52658501Seric { 52758501Seric qtype = T_A; 52858501Seric continue; 52958501Seric } 53063840Seric else if (qtype == T_A && !gotmx && trymx) 53158501Seric { 53258501Seric qtype = T_MX; 53358501Seric continue; 53458501Seric } 53558404Seric } 53658404Seric 53757943Seric if (mxmatch != NULL) 53851324Seric { 53957943Seric /* we matched before -- use that one */ 54051324Seric break; 54151324Seric } 54258501Seric 54358501Seric /* otherwise, try the next name */ 54458501Seric dp++; 54558501Seric qtype = T_ANY; 54657943Seric continue; 54751324Seric } 54858796Seric else if (tTd(8, 7)) 54957943Seric printf("\tYES\n"); 55057943Seric 55151910Seric /* 55257943Seric ** This might be a bogus match. Search for A or 55357943Seric ** CNAME records. If we don't have a matching 55457943Seric ** wild card MX record, we will accept MX as well. 55551910Seric */ 55651910Seric 55757943Seric hp = (HEADER *) &answer; 55868513Seric ap = (u_char *) &answer + HFIXEDSZ; 55957943Seric eom = (u_char *) &answer + ret; 56057943Seric 56157943Seric /* skip question part of response -- we know what we asked */ 56257943Seric for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) 56351324Seric { 56457943Seric if ((ret = dn_skipname(ap, eom)) < 0) 56557943Seric { 56657943Seric if (tTd(8, 20)) 56757943Seric printf("qdcount failure (%d)\n", 56857943Seric ntohs(hp->qdcount)); 56969401Seric *statp = EX_SOFTWARE; 57057943Seric return FALSE; /* ???XXX??? */ 57157943Seric } 57251324Seric } 57357943Seric 57457943Seric amatch = FALSE; 57557943Seric for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n) 57651324Seric { 57757943Seric n = dn_expand((u_char *) &answer, eom, ap, 57868513Seric (RES_UNC_T) nbuf, sizeof nbuf); 57957943Seric if (n < 0) 58057943Seric break; 58157943Seric ap += n; 58257943Seric GETSHORT(type, ap); 58368513Seric ap += INT16SZ + INT32SZ; 58457943Seric GETSHORT(n, ap); 58557943Seric switch (type) 58657943Seric { 58757943Seric case T_MX: 58858411Seric gotmx = TRUE; 58964030Seric if (**dp != '\0') 59057943Seric { 59157943Seric /* got a match -- save that info */ 59264030Seric if (trymx && mxmatch == NULL) 59357943Seric mxmatch = *dp; 59457943Seric continue; 59557943Seric } 59633929Sbostic 59757943Seric /* exact MX matches are as good as an A match */ 59857943Seric /* fall through */ 59957205Seric 60057943Seric case T_A: 60157943Seric /* good show */ 60257943Seric amatch = TRUE; 60333929Sbostic 60457943Seric /* continue in case a CNAME also exists */ 60557943Seric continue; 60657943Seric 60757943Seric case T_CNAME: 608*69852Seric if (DontExpandCnames) 609*69852Seric { 610*69852Seric /* got CNAME -- guaranteed canonical */ 611*69852Seric amatch = TRUE; 612*69852Seric break; 613*69852Seric } 614*69852Seric 61562524Sbostic if (loopcnt++ > MAXCNAMEDEPTH) 61662524Sbostic { 61766323Seric /*XXX should notify postmaster XXX*/ 61866323Seric message("DNS failure: CNAME loop for %s", 61962524Sbostic host); 62066323Seric if (CurEnv->e_message == NULL) 62166323Seric { 62266323Seric char ebuf[MAXLINE]; 62366323Seric 62466323Seric sprintf(ebuf, "Deferred: DNS failure: CNAME loop for %s", 62566323Seric host); 62666323Seric CurEnv->e_message = newstr(ebuf); 62766323Seric } 62866323Seric h_errno = NO_RECOVERY; 62969401Seric *statp = EX_CONFIG; 63066323Seric return FALSE; 63162524Sbostic } 63262524Sbostic 63357943Seric /* value points at name */ 63457943Seric if ((ret = dn_expand((u_char *)&answer, 63568513Seric eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0) 63657943Seric break; 63757943Seric (void)strncpy(host, nbuf, hbsize); /* XXX */ 63857943Seric host[hbsize - 1] = '\0'; 63957943Seric 64058844Seric /* 64158844Seric ** RFC 1034 section 3.6 specifies that CNAME 64258844Seric ** should point at the canonical name -- but 64358844Seric ** urges software to try again anyway. 64458844Seric */ 64558844Seric 64658844Seric goto cnameloop; 64758844Seric 64857943Seric default: 64957943Seric /* not a record of interest */ 65057943Seric continue; 65157943Seric } 65251324Seric } 65333929Sbostic 65457943Seric if (amatch) 65557943Seric { 65657943Seric /* got an A record and no CNAME */ 65757943Seric mxmatch = *dp; 65829653Sbloom break; 65929653Sbloom } 66058404Seric 66158404Seric /* 66258404Seric ** If this was a T_ANY query, we may have the info but 66358404Seric ** need an explicit query. Try T_A, then T_MX. 66458404Seric */ 66558404Seric 66658404Seric if (qtype == T_ANY) 66758404Seric qtype = T_A; 66863840Seric else if (qtype == T_A && !gotmx && trymx) 66958404Seric qtype = T_MX; 67058404Seric else 67158404Seric { 67258404Seric /* really nothing in this domain; try the next */ 67358404Seric qtype = T_ANY; 67458404Seric dp++; 67558404Seric } 67629653Sbloom } 67757943Seric 67857943Seric if (mxmatch == NULL) 67969401Seric { 68069401Seric *statp = EX_NOHOST; 68157943Seric return FALSE; 68269401Seric } 68357943Seric 68457943Seric /* create matching name and return */ 68557943Seric (void) sprintf(nbuf, "%.*s%s%.*s", MAXDNAME, host, 68657943Seric *mxmatch == '\0' ? "" : ".", 68757943Seric MAXDNAME, mxmatch); 68857943Seric strncpy(host, nbuf, hbsize); 68957943Seric host[hbsize - 1] = '\0'; 69069401Seric *statp = EX_OK; 69157943Seric return TRUE; 69229653Sbloom } 69335653Seric 69465193Seric 69565193Seric char * 69665193Seric gethostalias(host) 69765193Seric char *host; 69865193Seric { 69965193Seric char *fname; 70065193Seric FILE *fp; 70168513Seric register char *p = NULL; 70265193Seric char buf[MAXLINE]; 70365193Seric static char hbuf[MAXDNAME]; 70465193Seric 70565193Seric fname = getenv("HOSTALIASES"); 70668513Seric if (fname == NULL || 70768513Seric (fp = safefopen(fname, O_RDONLY, 0, SFF_REGONLY)) == NULL) 70865193Seric return NULL; 70965193Seric while (fgets(buf, sizeof buf, fp) != NULL) 71065193Seric { 71165193Seric for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++) 71265193Seric continue; 71365193Seric if (*p == 0) 71465193Seric { 71565193Seric /* syntax error */ 71665193Seric continue; 71765193Seric } 71865193Seric *p++ = '\0'; 71965193Seric if (strcasecmp(buf, host) == 0) 72065193Seric break; 72165193Seric } 72265193Seric 72365193Seric if (feof(fp)) 72465193Seric { 72565193Seric /* no match */ 72665193Seric fclose(fp); 72765193Seric return NULL; 72865193Seric } 72965193Seric 73065193Seric /* got a match; extract the equivalent name */ 73165193Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 73265193Seric p++; 73365193Seric host = p; 73465193Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 73565193Seric p++; 73665193Seric *p = '\0'; 73765193Seric strncpy(hbuf, host, sizeof hbuf - 1); 73865193Seric hbuf[sizeof hbuf - 1] = '\0'; 73965193Seric return hbuf; 74065193Seric } 74165193Seric 74269401Seric #endif /* NAMED_BIND */ 743