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*69401Seric static char sccsid[] = "@(#)domain.c 8.37 (Berkeley) 05/13/95 (with name server)"; 1435653Seric #else 15*69401Seric static char sccsid[] = "@(#)domain.c 8.37 (Berkeley) 05/13/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 7359273Seric getmxrr(host, mxhosts, droplocalhost, rcode) 7459075Seric char *host; 7559075Seric char **mxhosts; 7659273Seric bool droplocalhost; 7733929Sbostic int *rcode; 7829432Sbloom { 7933929Sbostic extern int h_errno; 8033929Sbostic register u_char *eom, *cp; 8163840Seric register int i, j, n; 8263840Seric int nmx = 0; 8333929Sbostic register char *bp; 8429432Sbloom HEADER *hp; 8533929Sbostic querybuf answer; 8658857Seric int ancount, qdcount, buflen; 8763840Seric bool seenlocal = FALSE; 8868513Seric u_short pref, type; 8968513Seric u_short localpref = 256; 9058891Seric char *fallbackMX = FallBackMX; 9158891Seric static bool firsttime = TRUE; 9264015Seric bool trycanon = FALSE; 9358891Seric u_short prefer[MAXMXHOSTS]; 9457454Seric int weight[MAXMXHOSTS]; 9563840Seric extern bool getcanonname(); 9629432Sbloom 9766145Seric if (tTd(8, 2)) 9866145Seric printf("getmxrr(%s, droplocalhost=%d)\n", host, droplocalhost); 9966145Seric 10058891Seric if (fallbackMX != NULL) 10158891Seric { 10268513Seric if (firsttime && 10368513Seric res_query(FallBackMX, C_IN, T_A, 10468513Seric (u_char *) &answer, sizeof answer) < 0) 10558891Seric { 10658891Seric /* this entry is bogus */ 10758891Seric fallbackMX = FallBackMX = NULL; 10858891Seric } 10968513Seric else if (droplocalhost && wordinclass(fallbackMX, 'w')) 11058891Seric { 11158891Seric /* don't use fallback for this pass */ 11258891Seric fallbackMX = NULL; 11358891Seric } 11458891Seric firsttime = FALSE; 11558891Seric } 11658891Seric 11764676Seric /* efficiency hack -- numeric or non-MX lookups */ 11864676Seric if (host[0] == '[') 11964676Seric goto punt; 12064676Seric 12168513Seric /* 12268513Seric ** If we don't have MX records in our host switch, don't 12368513Seric ** try for MX records. Note that this really isn't "right", 12468513Seric ** since we might be set up to try NIS first and then DNS; 12568513Seric ** if the host is found in NIS we really shouldn't be doing 12668513Seric ** MX lookups. However, that should be a degenerate case. 12768513Seric */ 12868513Seric 12968513Seric if (!UseNameServer) 13068513Seric goto punt; 13168513Seric 13236483Sbostic errno = 0; 13368513Seric n = res_search(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer)); 13435653Seric if (n < 0) 13535653Seric { 13633929Sbostic if (tTd(8, 1)) 13752852Seric printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", 13852852Seric (host == NULL) ? "<NULL>" : host, errno, h_errno); 13935653Seric switch (h_errno) 14035653Seric { 14135653Seric case NO_DATA: 14264015Seric trycanon = TRUE; 14364015Seric /* fall through */ 14464015Seric 14535653Seric case NO_RECOVERY: 14635653Seric /* no MX data on this host */ 14733929Sbostic goto punt; 14835653Seric 14935653Seric case HOST_NOT_FOUND: 15066318Seric #ifdef BROKEN_RES_SEARCH 15168513Seric case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ 15266318Seric #endif 15368513Seric /* host doesn't exist in DNS; might be in /etc/hosts */ 15433929Sbostic *rcode = EX_NOHOST; 15568513Seric goto punt; 15664950Seric 15735653Seric case TRY_AGAIN: 15835653Seric /* couldn't connect to the name server */ 15935653Seric /* it might come up later; better queue it up */ 16033929Sbostic *rcode = EX_TEMPFAIL; 16133929Sbostic break; 16266253Seric 16366253Seric default: 16466253Seric syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n", 16566253Seric host, h_errno); 16666253Seric *rcode = EX_OSERR; 16766253Seric break; 16829432Sbloom } 16935653Seric 17035653Seric /* irreconcilable differences */ 17135653Seric return (-1); 17229432Sbloom } 17333929Sbostic 17433929Sbostic /* find first satisfactory answer */ 17533929Sbostic hp = (HEADER *)&answer; 17668513Seric cp = (u_char *)&answer + HFIXEDSZ; 17733929Sbostic eom = (u_char *)&answer + n; 17833929Sbostic for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 17950957Skarels if ((n = dn_skipname(cp, eom)) < 0) 18033929Sbostic goto punt; 18159075Seric buflen = sizeof(MXHostBuf) - 1; 18259075Seric bp = MXHostBuf; 18333929Sbostic ancount = ntohs(hp->ancount); 18458848Seric while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) 18556336Seric { 18646928Sbostic if ((n = dn_expand((u_char *)&answer, 18768513Seric eom, cp, (RES_UNC_T) bp, buflen)) < 0) 18829432Sbloom break; 18929432Sbloom cp += n; 19033929Sbostic GETSHORT(type, cp); 19168513Seric cp += INT16SZ + INT32SZ; 19233929Sbostic GETSHORT(n, cp); 19356336Seric if (type != T_MX) 19456336Seric { 19557943Seric if (tTd(8, 8) || _res.options & RES_DEBUG) 19629432Sbloom printf("unexpected answer type %d, size %d\n", 19733929Sbostic type, n); 19829432Sbloom cp += n; 19929432Sbloom continue; 20029432Sbloom } 20133929Sbostic GETSHORT(pref, cp); 20256336Seric if ((n = dn_expand((u_char *)&answer, eom, cp, 20368513Seric (RES_UNC_T) bp, buflen)) < 0) 20429432Sbloom break; 20529551Sbloom cp += n; 20668513Seric if (droplocalhost && wordinclass(bp, 'w')) 20756336Seric { 20866145Seric if (tTd(8, 3)) 20966145Seric printf("found localhost (%s) in MX list, pref=%d\n", 21066145Seric bp, pref); 21158857Seric if (!seenlocal || pref < localpref) 21233929Sbostic localpref = pref; 21358857Seric seenlocal = TRUE; 21429551Sbloom continue; 21529551Sbloom } 21657454Seric weight[nmx] = mxrand(bp); 21729432Sbloom prefer[nmx] = pref; 21829432Sbloom mxhosts[nmx++] = bp; 21956336Seric n = strlen(bp); 22033929Sbostic bp += n; 22156336Seric if (bp[-1] != '.') 22256336Seric { 22356336Seric *bp++ = '.'; 22456336Seric n++; 22556336Seric } 22656336Seric *bp++ = '\0'; 22756336Seric buflen -= n + 1; 22829432Sbloom } 22963840Seric 23063840Seric /* sort the records */ 23163840Seric for (i = 0; i < nmx; i++) 23257454Seric { 23363840Seric for (j = i + 1; j < nmx; j++) 23458668Seric { 23563840Seric if (prefer[i] > prefer[j] || 23663840Seric (prefer[i] == prefer[j] && weight[i] > weight[j])) 23763840Seric { 23863840Seric register int temp; 23963840Seric register char *temp1; 24063840Seric 24163840Seric temp = prefer[i]; 24263840Seric prefer[i] = prefer[j]; 24363840Seric prefer[j] = temp; 24463840Seric temp1 = mxhosts[i]; 24563840Seric mxhosts[i] = mxhosts[j]; 24663840Seric mxhosts[j] = temp1; 24763840Seric temp = weight[i]; 24863840Seric weight[i] = weight[j]; 24963840Seric weight[j] = temp; 25063840Seric } 25158668Seric } 25263840Seric if (seenlocal && prefer[i] >= localpref) 25363840Seric { 25463840Seric /* truncate higher preference part of list */ 25563840Seric nmx = i; 25663840Seric } 25729551Sbloom } 25863840Seric 25963840Seric if (nmx == 0) 26057454Seric { 26163840Seric punt: 26263840Seric if (seenlocal && 26368693Seric (!TryNullMXList || sm_gethostbyname(host) == NULL)) 26457454Seric { 26563840Seric /* 26663840Seric ** If we have deleted all MX entries, this is 26763840Seric ** an error -- we should NEVER send to a host that 26863840Seric ** has an MX, and this should have been caught 26963840Seric ** earlier in the config file. 27063840Seric ** 27163840Seric ** Some sites prefer to go ahead and try the 27263840Seric ** A record anyway; that case is handled by 27363840Seric ** setting TryNullMXList. I believe this is a 27463840Seric ** bad idea, but it's up to you.... 27563840Seric */ 27629432Sbloom 27763840Seric *rcode = EX_CONFIG; 27866256Seric syserr("MX list for %s points back to %s", 27966256Seric host, MyHostName); 28063840Seric return -1; 28163840Seric } 28264676Seric strcpy(MXHostBuf, host); 28364676Seric mxhosts[0] = MXHostBuf; 28464676Seric if (host[0] == '[') 28564676Seric { 28664676Seric register char *p; 28764676Seric 28864676Seric /* this may be an MX suppression-style address */ 28964676Seric p = strchr(MXHostBuf, ']'); 29064676Seric if (p != NULL) 29164676Seric { 29264676Seric *p = '\0'; 29364676Seric if (inet_addr(&MXHostBuf[1]) != -1) 29464676Seric *p = ']'; 29564676Seric else 29664677Seric { 29764677Seric trycanon = TRUE; 29864676Seric mxhosts[0]++; 29964677Seric } 30064676Seric } 30164676Seric } 30264677Seric if (trycanon && 30364677Seric getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE)) 30463840Seric { 30564950Seric bp = &MXHostBuf[strlen(MXHostBuf)]; 30663840Seric if (bp[-1] != '.') 30757454Seric { 30863840Seric *bp++ = '.'; 30963840Seric *bp = '\0'; 31029551Sbloom } 31129551Sbloom } 31263840Seric nmx = 1; 31329432Sbloom } 31458848Seric 31558848Seric /* if we have a default lowest preference, include that */ 31664950Seric if (fallbackMX != NULL && !seenlocal) 31764950Seric mxhosts[nmx++] = fallbackMX; 31858848Seric 31957454Seric return (nmx); 32029432Sbloom } 32157135Seric /* 32257454Seric ** MXRAND -- create a randomizer for equal MX preferences 32357454Seric ** 32457454Seric ** If two MX hosts have equal preferences we want to randomize 32557454Seric ** the selection. But in order for signatures to be the same, 32657454Seric ** we need to randomize the same way each time. This function 32757454Seric ** computes a pseudo-random hash function from the host name. 32857454Seric ** 32957454Seric ** Parameters: 33057454Seric ** host -- the name of the host. 33157454Seric ** 33257454Seric ** Returns: 33357454Seric ** A random but repeatable value based on the host name. 33457454Seric ** 33557454Seric ** Side Effects: 33657454Seric ** none. 33757454Seric */ 33857454Seric 33957454Seric mxrand(host) 34057454Seric register char *host; 34157454Seric { 34257454Seric int hfunc; 34357454Seric static unsigned int seed; 34457454Seric 34557454Seric if (seed == 0) 34657454Seric { 34757454Seric seed = (int) curtime() & 0xffff; 34857454Seric if (seed == 0) 34957454Seric seed++; 35057454Seric } 35157454Seric 35257454Seric if (tTd(17, 9)) 35357454Seric printf("mxrand(%s)", host); 35457454Seric 35557454Seric hfunc = seed; 35657454Seric while (*host != '\0') 35757454Seric { 35857454Seric int c = *host++; 35957454Seric 36057454Seric if (isascii(c) && isupper(c)) 36157454Seric c = tolower(c); 36265952Seric hfunc = ((hfunc << 1) ^ c) % 2003; 36357454Seric } 36457454Seric 36557454Seric hfunc &= 0xff; 36657454Seric 36757454Seric if (tTd(17, 9)) 36857454Seric printf(" = %d\n", hfunc); 36957454Seric return hfunc; 37057454Seric } 37157454Seric /* 372*69401Seric ** DNS_GETCANONNAME -- get the canonical name for named host using DNS 37357135Seric ** 37457943Seric ** This algorithm tries to be smart about wildcard MX records. 37557943Seric ** This is hard to do because DNS doesn't tell is if we matched 37657943Seric ** against a wildcard or a specific MX. 37757943Seric ** 37857943Seric ** We always prefer A & CNAME records, since these are presumed 37957943Seric ** to be specific. 38057943Seric ** 38157943Seric ** If we match an MX in one pass and lose it in the next, we use 38257943Seric ** the old one. For example, consider an MX matching *.FOO.BAR.COM. 38357943Seric ** A hostname bletch.foo.bar.com will match against this MX, but 38457943Seric ** will stop matching when we try bletch.bar.com -- so we know 38557943Seric ** that bletch.foo.bar.com must have been right. This fails if 38657943Seric ** there was also an MX record matching *.BAR.COM, but there are 38757943Seric ** some things that just can't be fixed. 38857943Seric ** 38957135Seric ** Parameters: 39057135Seric ** host -- a buffer containing the name of the host. 39157135Seric ** This is a value-result parameter. 39257135Seric ** hbsize -- the size of the host buffer. 39363840Seric ** trymx -- if set, try MX records as well as A and CNAME. 394*69401Seric ** statp -- pointer to place to store status. 39557135Seric ** 39657135Seric ** Returns: 39757135Seric ** TRUE -- if the host matched. 39857135Seric ** FALSE -- otherwise. 39957135Seric */ 40029653Sbloom 40151314Seric bool 402*69401Seric dns_getcanonname(host, hbsize, trymx, statp) 40329653Sbloom char *host; 40429653Sbloom int hbsize; 40563840Seric bool trymx; 406*69401Seric int *statp; 40729653Sbloom { 40840277Sbostic extern int h_errno; 40951324Seric register u_char *eom, *ap; 41051324Seric register char *cp; 41133929Sbostic register int n; 41229653Sbloom HEADER *hp; 41333929Sbostic querybuf answer; 41461707Seric int ancount, qdcount; 41551324Seric int ret; 41651324Seric char **domain; 41751324Seric int type; 41857943Seric char **dp; 41957943Seric char *mxmatch; 42057943Seric bool amatch; 42168513Seric bool gotmx = FALSE; 42258404Seric int qtype; 42362524Sbostic int loopcnt; 42465193Seric char *xp; 42558010Seric char nbuf[MAX(PACKETSZ, MAXDNAME*2+2)]; 42658039Seric char *searchlist[MAXDNSRCH+2]; 42765193Seric extern char *gethostalias(); 42829653Sbloom 42951324Seric if (tTd(8, 2)) 43051324Seric printf("getcanonname(%s)\n", host); 43151324Seric 43251324Seric if ((_res.options & RES_INIT) == 0 && res_init() == -1) 433*69401Seric { 434*69401Seric *statp = EX_UNAVAILABLE; 435*69401Seric return FALSE; 436*69401Seric } 43751324Seric 43836483Sbostic /* 43957943Seric ** Initialize domain search list. If there is at least one 44057943Seric ** dot in the name, search the unmodified name first so we 44157943Seric ** find "vse.CS" in Czechoslovakia instead of in the local 44257943Seric ** domain (e.g., vse.CS.Berkeley.EDU). 44357943Seric ** 44457943Seric ** Older versions of the resolver could create this 44557943Seric ** list by tearing apart the host name. 44657205Seric */ 44757205Seric 44862524Sbostic loopcnt = 0; 44959268Seric cnameloop: 45059268Seric for (cp = host, n = 0; *cp; cp++) 45159268Seric if (*cp == '.') 45259268Seric n++; 45359268Seric 45465193Seric if (n == 0 && (xp = gethostalias(host)) != NULL) 45565193Seric { 45665193Seric if (loopcnt++ > MAXCNAMEDEPTH) 45765193Seric { 45865193Seric syserr("loop in ${HOSTALIASES} file"); 45965193Seric } 46065193Seric else 46165193Seric { 46265193Seric strncpy(host, xp, hbsize); 46365193Seric host[hbsize - 1] = '\0'; 46465193Seric goto cnameloop; 46565193Seric } 46665193Seric } 46765193Seric 46857943Seric dp = searchlist; 46957943Seric if (n > 0) 47057943Seric *dp++ = ""; 47158411Seric if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) 47251324Seric { 47357943Seric for (domain = _res.dnsrch; *domain != NULL; ) 47457943Seric *dp++ = *domain++; 47557205Seric } 47658411Seric else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) 47758411Seric { 47858411Seric *dp++ = _res.defdname; 47958411Seric } 48066040Seric else if (*cp == '.') 48166040Seric { 48266040Seric *cp = '\0'; 48366040Seric } 48457943Seric *dp = NULL; 48557205Seric 48657205Seric /* 48757943Seric ** Now run through the search list for the name in question. 48857205Seric */ 48957205Seric 49057943Seric mxmatch = NULL; 49158404Seric qtype = T_ANY; 49257943Seric 49358404Seric for (dp = searchlist; *dp != NULL; ) 49457205Seric { 49558411Seric if (qtype == T_ANY) 49658411Seric gotmx = FALSE; 49757943Seric if (tTd(8, 5)) 49858508Seric printf("getcanonname: trying %s.%s (%s)\n", host, *dp, 49958508Seric qtype == T_ANY ? "ANY" : qtype == T_A ? "A" : 50058508Seric qtype == T_MX ? "MX" : "???"); 50158404Seric ret = res_querydomain(host, *dp, C_IN, qtype, 50268513Seric answer.qb2, sizeof(answer.qb2)); 50357943Seric if (ret <= 0) 50451324Seric { 50558796Seric if (tTd(8, 7)) 50658082Seric printf("\tNO: errno=%d, h_errno=%d\n", 50758082Seric errno, h_errno); 50851324Seric 50958082Seric if (errno == ECONNREFUSED || h_errno == TRY_AGAIN) 51057205Seric { 51157943Seric /* the name server seems to be down */ 51251324Seric h_errno = TRY_AGAIN; 513*69401Seric *statp = EX_TEMPFAIL; 51451910Seric return FALSE; 51551324Seric } 51657943Seric 51758501Seric if (h_errno != HOST_NOT_FOUND) 51858404Seric { 51958501Seric /* might have another type of interest */ 52058501Seric if (qtype == T_ANY) 52158501Seric { 52258501Seric qtype = T_A; 52358501Seric continue; 52458501Seric } 52563840Seric else if (qtype == T_A && !gotmx && trymx) 52658501Seric { 52758501Seric qtype = T_MX; 52858501Seric continue; 52958501Seric } 53058404Seric } 53158404Seric 53257943Seric if (mxmatch != NULL) 53351324Seric { 53457943Seric /* we matched before -- use that one */ 53551324Seric break; 53651324Seric } 53758501Seric 53858501Seric /* otherwise, try the next name */ 53958501Seric dp++; 54058501Seric qtype = T_ANY; 54157943Seric continue; 54251324Seric } 54358796Seric else if (tTd(8, 7)) 54457943Seric printf("\tYES\n"); 54557943Seric 54651910Seric /* 54757943Seric ** This might be a bogus match. Search for A or 54857943Seric ** CNAME records. If we don't have a matching 54957943Seric ** wild card MX record, we will accept MX as well. 55051910Seric */ 55151910Seric 55257943Seric hp = (HEADER *) &answer; 55368513Seric ap = (u_char *) &answer + HFIXEDSZ; 55457943Seric eom = (u_char *) &answer + ret; 55557943Seric 55657943Seric /* skip question part of response -- we know what we asked */ 55757943Seric for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) 55851324Seric { 55957943Seric if ((ret = dn_skipname(ap, eom)) < 0) 56057943Seric { 56157943Seric if (tTd(8, 20)) 56257943Seric printf("qdcount failure (%d)\n", 56357943Seric ntohs(hp->qdcount)); 564*69401Seric *statp = EX_SOFTWARE; 56557943Seric return FALSE; /* ???XXX??? */ 56657943Seric } 56751324Seric } 56857943Seric 56957943Seric amatch = FALSE; 57057943Seric for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n) 57151324Seric { 57257943Seric n = dn_expand((u_char *) &answer, eom, ap, 57368513Seric (RES_UNC_T) nbuf, sizeof nbuf); 57457943Seric if (n < 0) 57557943Seric break; 57657943Seric ap += n; 57757943Seric GETSHORT(type, ap); 57868513Seric ap += INT16SZ + INT32SZ; 57957943Seric GETSHORT(n, ap); 58057943Seric switch (type) 58157943Seric { 58257943Seric case T_MX: 58358411Seric gotmx = TRUE; 58464030Seric if (**dp != '\0') 58557943Seric { 58657943Seric /* got a match -- save that info */ 58764030Seric if (trymx && mxmatch == NULL) 58857943Seric mxmatch = *dp; 58957943Seric continue; 59057943Seric } 59133929Sbostic 59257943Seric /* exact MX matches are as good as an A match */ 59357943Seric /* fall through */ 59457205Seric 59557943Seric case T_A: 59657943Seric /* good show */ 59757943Seric amatch = TRUE; 59833929Sbostic 59957943Seric /* continue in case a CNAME also exists */ 60057943Seric continue; 60157943Seric 60257943Seric case T_CNAME: 60362524Sbostic if (loopcnt++ > MAXCNAMEDEPTH) 60462524Sbostic { 60566323Seric /*XXX should notify postmaster XXX*/ 60666323Seric message("DNS failure: CNAME loop for %s", 60762524Sbostic host); 60866323Seric if (CurEnv->e_message == NULL) 60966323Seric { 61066323Seric char ebuf[MAXLINE]; 61166323Seric 61266323Seric sprintf(ebuf, "Deferred: DNS failure: CNAME loop for %s", 61366323Seric host); 61466323Seric CurEnv->e_message = newstr(ebuf); 61566323Seric } 61666323Seric h_errno = NO_RECOVERY; 617*69401Seric *statp = EX_CONFIG; 61866323Seric return FALSE; 61962524Sbostic } 62062524Sbostic 62157943Seric /* value points at name */ 62257943Seric if ((ret = dn_expand((u_char *)&answer, 62368513Seric eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0) 62457943Seric break; 62557943Seric (void)strncpy(host, nbuf, hbsize); /* XXX */ 62657943Seric host[hbsize - 1] = '\0'; 62757943Seric 62858844Seric /* 62958844Seric ** RFC 1034 section 3.6 specifies that CNAME 63058844Seric ** should point at the canonical name -- but 63158844Seric ** urges software to try again anyway. 63258844Seric */ 63358844Seric 63458844Seric goto cnameloop; 63558844Seric 63657943Seric default: 63757943Seric /* not a record of interest */ 63857943Seric continue; 63957943Seric } 64051324Seric } 64133929Sbostic 64257943Seric if (amatch) 64357943Seric { 64457943Seric /* got an A record and no CNAME */ 64557943Seric mxmatch = *dp; 64629653Sbloom break; 64729653Sbloom } 64858404Seric 64958404Seric /* 65058404Seric ** If this was a T_ANY query, we may have the info but 65158404Seric ** need an explicit query. Try T_A, then T_MX. 65258404Seric */ 65358404Seric 65458404Seric if (qtype == T_ANY) 65558404Seric qtype = T_A; 65663840Seric else if (qtype == T_A && !gotmx && trymx) 65758404Seric qtype = T_MX; 65858404Seric else 65958404Seric { 66058404Seric /* really nothing in this domain; try the next */ 66158404Seric qtype = T_ANY; 66258404Seric dp++; 66358404Seric } 66429653Sbloom } 66557943Seric 66657943Seric if (mxmatch == NULL) 667*69401Seric { 668*69401Seric *statp = EX_NOHOST; 66957943Seric return FALSE; 670*69401Seric } 67157943Seric 67257943Seric /* create matching name and return */ 67357943Seric (void) sprintf(nbuf, "%.*s%s%.*s", MAXDNAME, host, 67457943Seric *mxmatch == '\0' ? "" : ".", 67557943Seric MAXDNAME, mxmatch); 67657943Seric strncpy(host, nbuf, hbsize); 67757943Seric host[hbsize - 1] = '\0'; 678*69401Seric *statp = EX_OK; 67957943Seric return TRUE; 68029653Sbloom } 68135653Seric 68265193Seric 68365193Seric char * 68465193Seric gethostalias(host) 68565193Seric char *host; 68665193Seric { 68765193Seric char *fname; 68865193Seric FILE *fp; 68968513Seric register char *p = NULL; 69065193Seric char buf[MAXLINE]; 69165193Seric static char hbuf[MAXDNAME]; 69265193Seric 69365193Seric fname = getenv("HOSTALIASES"); 69468513Seric if (fname == NULL || 69568513Seric (fp = safefopen(fname, O_RDONLY, 0, SFF_REGONLY)) == NULL) 69665193Seric return NULL; 69765193Seric while (fgets(buf, sizeof buf, fp) != NULL) 69865193Seric { 69965193Seric for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++) 70065193Seric continue; 70165193Seric if (*p == 0) 70265193Seric { 70365193Seric /* syntax error */ 70465193Seric continue; 70565193Seric } 70665193Seric *p++ = '\0'; 70765193Seric if (strcasecmp(buf, host) == 0) 70865193Seric break; 70965193Seric } 71065193Seric 71165193Seric if (feof(fp)) 71265193Seric { 71365193Seric /* no match */ 71465193Seric fclose(fp); 71565193Seric return NULL; 71665193Seric } 71765193Seric 71865193Seric /* got a match; extract the equivalent name */ 71965193Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 72065193Seric p++; 72165193Seric host = p; 72265193Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 72365193Seric p++; 72465193Seric *p = '\0'; 72565193Seric strncpy(hbuf, host, sizeof hbuf - 1); 72665193Seric hbuf[sizeof hbuf - 1] = '\0'; 72765193Seric return hbuf; 72865193Seric } 72965193Seric 730*69401Seric #endif /* NAMED_BIND */ 731