129432Sbloom /* 234921Sbostic * Copyright (c) 1986 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*68513Seric static char sccsid[] = "@(#)domain.c 8.33 (Berkeley) 03/10/95 (with name server)"; 1435653Seric #else 15*68513Seric static char sccsid[] = "@(#)domain.c 8.33 (Berkeley) 03/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> 2333929Sbostic #include <netdb.h> 2429432Sbloom 2557454Seric typedef union 2657454Seric { 2757454Seric HEADER qb1; 28*68513Seric u_char qb2[PACKETSZ]; 2929432Sbloom } querybuf; 3029432Sbloom 3159075Seric static char MXHostBuf[MAXMXHOSTS*PACKETSZ]; 3229432Sbloom 3357943Seric #ifndef MAXDNSRCH 3457943Seric #define MAXDNSRCH 6 /* number of possible domains to search */ 3557943Seric #endif 3657943Seric 3758010Seric #ifndef MAX 3858010Seric #define MAX(a, b) ((a) > (b) ? (a) : (b)) 3958010Seric #endif 4058010Seric 4163969Seric #ifndef NO_DATA 4263969Seric # define NO_DATA NO_ADDRESS 4363969Seric #endif 4463969Seric 45*68513Seric #ifndef HFIXEDSZ 46*68513Seric # define HFIXEDSZ 12 /* sizeof(HEADER) */ 4765751Seric #endif 4865751Seric 49*68513Seric #define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ 5068495Seric 51*68513Seric #if defined(__RES) && (__RES >= 19940415) 52*68513Seric # define RES_UNC_T char * 53*68513Seric #else 54*68513Seric # define RES_UNC_T u_char * 55*68513Seric #endif 5658248Seric /* 5758248Seric ** GETMXRR -- get MX resource records for a domain 5858248Seric ** 5958248Seric ** Parameters: 6058248Seric ** host -- the name of the host to MX. 6158248Seric ** mxhosts -- a pointer to a return buffer of MX records. 6259273Seric ** droplocalhost -- If TRUE, all MX records less preferred 6359273Seric ** than the local host (as determined by $=w) will 6459273Seric ** be discarded. 6558248Seric ** rcode -- a pointer to an EX_ status code. 6658248Seric ** 6758248Seric ** Returns: 6858248Seric ** The number of MX records found. 6958248Seric ** -1 if there is an internal failure. 7058248Seric ** If no MX records are found, mxhosts[0] is set to host 7158248Seric ** and 1 is returned. 7258248Seric */ 7358248Seric 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; 89*68513Seric u_short pref, type; 90*68513Seric 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 { 103*68513Seric if (firsttime && 104*68513Seric res_query(FallBackMX, C_IN, T_A, 105*68513Seric (u_char *) &answer, sizeof answer) < 0) 10658891Seric { 10758891Seric /* this entry is bogus */ 10858891Seric fallbackMX = FallBackMX = NULL; 10958891Seric } 110*68513Seric else if (droplocalhost && wordinclass(fallbackMX, 'w')) 11158891Seric { 11258891Seric /* don't use fallback for this pass */ 11358891Seric fallbackMX = NULL; 11458891Seric } 11558891Seric firsttime = FALSE; 11658891Seric } 11758891Seric 11864676Seric /* efficiency hack -- numeric or non-MX lookups */ 11964676Seric if (host[0] == '[') 12064676Seric goto punt; 12164676Seric 122*68513Seric /* 123*68513Seric ** If we don't have MX records in our host switch, don't 124*68513Seric ** try for MX records. Note that this really isn't "right", 125*68513Seric ** since we might be set up to try NIS first and then DNS; 126*68513Seric ** if the host is found in NIS we really shouldn't be doing 127*68513Seric ** MX lookups. However, that should be a degenerate case. 128*68513Seric */ 129*68513Seric 130*68513Seric if (!UseNameServer) 131*68513Seric goto punt; 132*68513Seric 13336483Sbostic errno = 0; 134*68513Seric n = res_search(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer)); 13535653Seric if (n < 0) 13635653Seric { 13733929Sbostic if (tTd(8, 1)) 13852852Seric printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", 13952852Seric (host == NULL) ? "<NULL>" : host, errno, h_errno); 14035653Seric switch (h_errno) 14135653Seric { 14235653Seric case NO_DATA: 14364015Seric trycanon = TRUE; 14464015Seric /* fall through */ 14564015Seric 14635653Seric case NO_RECOVERY: 14735653Seric /* no MX data on this host */ 14833929Sbostic goto punt; 14935653Seric 15035653Seric case HOST_NOT_FOUND: 15166318Seric #ifdef BROKEN_RES_SEARCH 152*68513Seric case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ 15366318Seric #endif 154*68513Seric /* host doesn't exist in DNS; might be in /etc/hosts */ 15533929Sbostic *rcode = EX_NOHOST; 156*68513Seric goto punt; 15764950Seric 15835653Seric case TRY_AGAIN: 15935653Seric /* couldn't connect to the name server */ 16035653Seric /* it might come up later; better queue it up */ 16133929Sbostic *rcode = EX_TEMPFAIL; 16233929Sbostic break; 16366253Seric 16466253Seric default: 16566253Seric syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n", 16666253Seric host, h_errno); 16766253Seric *rcode = EX_OSERR; 16866253Seric break; 16929432Sbloom } 17035653Seric 17135653Seric /* irreconcilable differences */ 17235653Seric return (-1); 17329432Sbloom } 17433929Sbostic 17533929Sbostic /* find first satisfactory answer */ 17633929Sbostic hp = (HEADER *)&answer; 177*68513Seric cp = (u_char *)&answer + HFIXEDSZ; 17833929Sbostic eom = (u_char *)&answer + n; 17933929Sbostic for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) 18050957Skarels if ((n = dn_skipname(cp, eom)) < 0) 18133929Sbostic goto punt; 18259075Seric buflen = sizeof(MXHostBuf) - 1; 18359075Seric bp = MXHostBuf; 18433929Sbostic ancount = ntohs(hp->ancount); 18558848Seric while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) 18656336Seric { 18746928Sbostic if ((n = dn_expand((u_char *)&answer, 188*68513Seric eom, cp, (RES_UNC_T) bp, buflen)) < 0) 18929432Sbloom break; 19029432Sbloom cp += n; 19133929Sbostic GETSHORT(type, cp); 192*68513Seric cp += INT16SZ + INT32SZ; 19333929Sbostic GETSHORT(n, cp); 19456336Seric if (type != T_MX) 19556336Seric { 19657943Seric if (tTd(8, 8) || _res.options & RES_DEBUG) 19729432Sbloom printf("unexpected answer type %d, size %d\n", 19833929Sbostic type, n); 19929432Sbloom cp += n; 20029432Sbloom continue; 20129432Sbloom } 20233929Sbostic GETSHORT(pref, cp); 20356336Seric if ((n = dn_expand((u_char *)&answer, eom, cp, 204*68513Seric (RES_UNC_T) bp, buflen)) < 0) 20529432Sbloom break; 20629551Sbloom cp += n; 207*68513Seric if (droplocalhost && wordinclass(bp, 'w')) 20856336Seric { 20966145Seric if (tTd(8, 3)) 21066145Seric printf("found localhost (%s) in MX list, pref=%d\n", 21166145Seric bp, pref); 21258857Seric if (!seenlocal || pref < localpref) 21333929Sbostic localpref = pref; 21458857Seric seenlocal = TRUE; 21529551Sbloom continue; 21629551Sbloom } 21757454Seric weight[nmx] = mxrand(bp); 21829432Sbloom prefer[nmx] = pref; 21929432Sbloom mxhosts[nmx++] = bp; 22056336Seric n = strlen(bp); 22133929Sbostic bp += n; 22256336Seric if (bp[-1] != '.') 22356336Seric { 22456336Seric *bp++ = '.'; 22556336Seric n++; 22656336Seric } 22756336Seric *bp++ = '\0'; 22856336Seric buflen -= n + 1; 22929432Sbloom } 23063840Seric 23163840Seric /* sort the records */ 23263840Seric for (i = 0; i < nmx; i++) 23357454Seric { 23463840Seric for (j = i + 1; j < nmx; j++) 23558668Seric { 23663840Seric if (prefer[i] > prefer[j] || 23763840Seric (prefer[i] == prefer[j] && weight[i] > weight[j])) 23863840Seric { 23963840Seric register int temp; 24063840Seric register char *temp1; 24163840Seric 24263840Seric temp = prefer[i]; 24363840Seric prefer[i] = prefer[j]; 24463840Seric prefer[j] = temp; 24563840Seric temp1 = mxhosts[i]; 24663840Seric mxhosts[i] = mxhosts[j]; 24763840Seric mxhosts[j] = temp1; 24863840Seric temp = weight[i]; 24963840Seric weight[i] = weight[j]; 25063840Seric weight[j] = temp; 25163840Seric } 25258668Seric } 25363840Seric if (seenlocal && prefer[i] >= localpref) 25463840Seric { 25563840Seric /* truncate higher preference part of list */ 25663840Seric nmx = i; 25763840Seric } 25829551Sbloom } 25963840Seric 26063840Seric if (nmx == 0) 26157454Seric { 26263840Seric punt: 26363840Seric if (seenlocal && 26463840Seric (!TryNullMXList || gethostbyname(host) == NULL)) 26557454Seric { 26663840Seric /* 26763840Seric ** If we have deleted all MX entries, this is 26863840Seric ** an error -- we should NEVER send to a host that 26963840Seric ** has an MX, and this should have been caught 27063840Seric ** earlier in the config file. 27163840Seric ** 27263840Seric ** Some sites prefer to go ahead and try the 27363840Seric ** A record anyway; that case is handled by 27463840Seric ** setting TryNullMXList. I believe this is a 27563840Seric ** bad idea, but it's up to you.... 27663840Seric */ 27729432Sbloom 27863840Seric *rcode = EX_CONFIG; 27966256Seric syserr("MX list for %s points back to %s", 28066256Seric host, MyHostName); 28163840Seric return -1; 28263840Seric } 28364676Seric strcpy(MXHostBuf, host); 28464676Seric mxhosts[0] = MXHostBuf; 28564676Seric if (host[0] == '[') 28664676Seric { 28764676Seric register char *p; 28864676Seric 28964676Seric /* this may be an MX suppression-style address */ 29064676Seric p = strchr(MXHostBuf, ']'); 29164676Seric if (p != NULL) 29264676Seric { 29364676Seric *p = '\0'; 29464676Seric if (inet_addr(&MXHostBuf[1]) != -1) 29564676Seric *p = ']'; 29664676Seric else 29764677Seric { 29864677Seric trycanon = TRUE; 29964676Seric mxhosts[0]++; 30064677Seric } 30164676Seric } 30264676Seric } 30364677Seric if (trycanon && 30464677Seric getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE)) 30563840Seric { 30664950Seric bp = &MXHostBuf[strlen(MXHostBuf)]; 30763840Seric if (bp[-1] != '.') 30857454Seric { 30963840Seric *bp++ = '.'; 31063840Seric *bp = '\0'; 31129551Sbloom } 31229551Sbloom } 31363840Seric nmx = 1; 31429432Sbloom } 31558848Seric 31658848Seric /* if we have a default lowest preference, include that */ 31764950Seric if (fallbackMX != NULL && !seenlocal) 31864950Seric mxhosts[nmx++] = fallbackMX; 31958848Seric 32057454Seric return (nmx); 32129432Sbloom } 32257135Seric /* 32357454Seric ** MXRAND -- create a randomizer for equal MX preferences 32457454Seric ** 32557454Seric ** If two MX hosts have equal preferences we want to randomize 32657454Seric ** the selection. But in order for signatures to be the same, 32757454Seric ** we need to randomize the same way each time. This function 32857454Seric ** computes a pseudo-random hash function from the host name. 32957454Seric ** 33057454Seric ** Parameters: 33157454Seric ** host -- the name of the host. 33257454Seric ** 33357454Seric ** Returns: 33457454Seric ** A random but repeatable value based on the host name. 33557454Seric ** 33657454Seric ** Side Effects: 33757454Seric ** none. 33857454Seric */ 33957454Seric 34057454Seric mxrand(host) 34157454Seric register char *host; 34257454Seric { 34357454Seric int hfunc; 34457454Seric static unsigned int seed; 34557454Seric 34657454Seric if (seed == 0) 34757454Seric { 34857454Seric seed = (int) curtime() & 0xffff; 34957454Seric if (seed == 0) 35057454Seric seed++; 35157454Seric } 35257454Seric 35357454Seric if (tTd(17, 9)) 35457454Seric printf("mxrand(%s)", host); 35557454Seric 35657454Seric hfunc = seed; 35757454Seric while (*host != '\0') 35857454Seric { 35957454Seric int c = *host++; 36057454Seric 36157454Seric if (isascii(c) && isupper(c)) 36257454Seric c = tolower(c); 36365952Seric hfunc = ((hfunc << 1) ^ c) % 2003; 36457454Seric } 36557454Seric 36657454Seric hfunc &= 0xff; 36757454Seric 36857454Seric if (tTd(17, 9)) 36957454Seric printf(" = %d\n", hfunc); 37057454Seric return hfunc; 37157454Seric } 37257454Seric /* 37357135Seric ** GETCANONNAME -- get the canonical name for named host 37457135Seric ** 37557943Seric ** This algorithm tries to be smart about wildcard MX records. 37657943Seric ** This is hard to do because DNS doesn't tell is if we matched 37757943Seric ** against a wildcard or a specific MX. 37857943Seric ** 37957943Seric ** We always prefer A & CNAME records, since these are presumed 38057943Seric ** to be specific. 38157943Seric ** 38257943Seric ** If we match an MX in one pass and lose it in the next, we use 38357943Seric ** the old one. For example, consider an MX matching *.FOO.BAR.COM. 38457943Seric ** A hostname bletch.foo.bar.com will match against this MX, but 38557943Seric ** will stop matching when we try bletch.bar.com -- so we know 38657943Seric ** that bletch.foo.bar.com must have been right. This fails if 38757943Seric ** there was also an MX record matching *.BAR.COM, but there are 38857943Seric ** some things that just can't be fixed. 38957943Seric ** 39057135Seric ** Parameters: 39157135Seric ** host -- a buffer containing the name of the host. 39257135Seric ** This is a value-result parameter. 39357135Seric ** hbsize -- the size of the host buffer. 39463840Seric ** trymx -- if set, try MX records as well as A and CNAME. 39557135Seric ** 39657135Seric ** Returns: 39757135Seric ** TRUE -- if the host matched. 39857135Seric ** FALSE -- otherwise. 39957135Seric */ 40029653Sbloom 40151314Seric bool 40263840Seric getcanonname(host, hbsize, trymx) 40329653Sbloom char *host; 40429653Sbloom int hbsize; 40563840Seric bool trymx; 40629653Sbloom { 40740277Sbostic extern int h_errno; 40851324Seric register u_char *eom, *ap; 40951324Seric register char *cp; 41033929Sbostic register int n; 41129653Sbloom HEADER *hp; 41233929Sbostic querybuf answer; 41361707Seric int ancount, qdcount; 41451324Seric int ret; 41551324Seric char **domain; 41651324Seric int type; 41757943Seric char **dp; 41857943Seric char *mxmatch; 41957943Seric bool amatch; 420*68513Seric bool gotmx = FALSE; 42158404Seric int qtype; 42262524Sbostic int loopcnt; 42365193Seric char *xp; 42458010Seric char nbuf[MAX(PACKETSZ, MAXDNAME*2+2)]; 42558039Seric char *searchlist[MAXDNSRCH+2]; 42665193Seric extern char *gethostalias(); 42729653Sbloom 42851324Seric if (tTd(8, 2)) 42951324Seric printf("getcanonname(%s)\n", host); 43051324Seric 43151324Seric if ((_res.options & RES_INIT) == 0 && res_init() == -1) 43251324Seric return (FALSE); 43351324Seric 43436483Sbostic /* 43557943Seric ** Initialize domain search list. If there is at least one 43657943Seric ** dot in the name, search the unmodified name first so we 43757943Seric ** find "vse.CS" in Czechoslovakia instead of in the local 43857943Seric ** domain (e.g., vse.CS.Berkeley.EDU). 43957943Seric ** 44057943Seric ** Older versions of the resolver could create this 44157943Seric ** list by tearing apart the host name. 44257205Seric */ 44357205Seric 44462524Sbostic loopcnt = 0; 44559268Seric cnameloop: 44659268Seric for (cp = host, n = 0; *cp; cp++) 44759268Seric if (*cp == '.') 44859268Seric n++; 44959268Seric 45065193Seric if (n == 0 && (xp = gethostalias(host)) != NULL) 45165193Seric { 45265193Seric if (loopcnt++ > MAXCNAMEDEPTH) 45365193Seric { 45465193Seric syserr("loop in ${HOSTALIASES} file"); 45565193Seric } 45665193Seric else 45765193Seric { 45865193Seric strncpy(host, xp, hbsize); 45965193Seric host[hbsize - 1] = '\0'; 46065193Seric goto cnameloop; 46165193Seric } 46265193Seric } 46365193Seric 46457943Seric dp = searchlist; 46557943Seric if (n > 0) 46657943Seric *dp++ = ""; 46758411Seric if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) 46851324Seric { 46957943Seric for (domain = _res.dnsrch; *domain != NULL; ) 47057943Seric *dp++ = *domain++; 47157205Seric } 47258411Seric else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) 47358411Seric { 47458411Seric *dp++ = _res.defdname; 47558411Seric } 47666040Seric else if (*cp == '.') 47766040Seric { 47866040Seric *cp = '\0'; 47966040Seric } 48057943Seric *dp = NULL; 48157205Seric 48257205Seric /* 48357943Seric ** Now run through the search list for the name in question. 48457205Seric */ 48557205Seric 48657943Seric mxmatch = NULL; 48758404Seric qtype = T_ANY; 48857943Seric 48958404Seric for (dp = searchlist; *dp != NULL; ) 49057205Seric { 49158411Seric if (qtype == T_ANY) 49258411Seric gotmx = FALSE; 49357943Seric if (tTd(8, 5)) 49458508Seric printf("getcanonname: trying %s.%s (%s)\n", host, *dp, 49558508Seric qtype == T_ANY ? "ANY" : qtype == T_A ? "A" : 49658508Seric qtype == T_MX ? "MX" : "???"); 49758404Seric ret = res_querydomain(host, *dp, C_IN, qtype, 498*68513Seric answer.qb2, sizeof(answer.qb2)); 49957943Seric if (ret <= 0) 50051324Seric { 50158796Seric if (tTd(8, 7)) 50258082Seric printf("\tNO: errno=%d, h_errno=%d\n", 50358082Seric errno, h_errno); 50451324Seric 50558082Seric if (errno == ECONNREFUSED || h_errno == TRY_AGAIN) 50657205Seric { 50757943Seric /* the name server seems to be down */ 50851324Seric h_errno = TRY_AGAIN; 50951910Seric return FALSE; 51051324Seric } 51157943Seric 51258501Seric if (h_errno != HOST_NOT_FOUND) 51358404Seric { 51458501Seric /* might have another type of interest */ 51558501Seric if (qtype == T_ANY) 51658501Seric { 51758501Seric qtype = T_A; 51858501Seric continue; 51958501Seric } 52063840Seric else if (qtype == T_A && !gotmx && trymx) 52158501Seric { 52258501Seric qtype = T_MX; 52358501Seric continue; 52458501Seric } 52558404Seric } 52658404Seric 52757943Seric if (mxmatch != NULL) 52851324Seric { 52957943Seric /* we matched before -- use that one */ 53051324Seric break; 53151324Seric } 53258501Seric 53358501Seric /* otherwise, try the next name */ 53458501Seric dp++; 53558501Seric qtype = T_ANY; 53657943Seric continue; 53751324Seric } 53858796Seric else if (tTd(8, 7)) 53957943Seric printf("\tYES\n"); 54057943Seric 54151910Seric /* 54257943Seric ** This might be a bogus match. Search for A or 54357943Seric ** CNAME records. If we don't have a matching 54457943Seric ** wild card MX record, we will accept MX as well. 54551910Seric */ 54651910Seric 54757943Seric hp = (HEADER *) &answer; 548*68513Seric ap = (u_char *) &answer + HFIXEDSZ; 54957943Seric eom = (u_char *) &answer + ret; 55057943Seric 55157943Seric /* skip question part of response -- we know what we asked */ 55257943Seric for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) 55351324Seric { 55457943Seric if ((ret = dn_skipname(ap, eom)) < 0) 55557943Seric { 55657943Seric if (tTd(8, 20)) 55757943Seric printf("qdcount failure (%d)\n", 55857943Seric ntohs(hp->qdcount)); 55957943Seric return FALSE; /* ???XXX??? */ 56057943Seric } 56151324Seric } 56257943Seric 56357943Seric amatch = FALSE; 56457943Seric for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n) 56551324Seric { 56657943Seric n = dn_expand((u_char *) &answer, eom, ap, 567*68513Seric (RES_UNC_T) nbuf, sizeof nbuf); 56857943Seric if (n < 0) 56957943Seric break; 57057943Seric ap += n; 57157943Seric GETSHORT(type, ap); 572*68513Seric ap += INT16SZ + INT32SZ; 57357943Seric GETSHORT(n, ap); 57457943Seric switch (type) 57557943Seric { 57657943Seric case T_MX: 57758411Seric gotmx = TRUE; 57864030Seric if (**dp != '\0') 57957943Seric { 58057943Seric /* got a match -- save that info */ 58164030Seric if (trymx && mxmatch == NULL) 58257943Seric mxmatch = *dp; 58357943Seric continue; 58457943Seric } 58533929Sbostic 58657943Seric /* exact MX matches are as good as an A match */ 58757943Seric /* fall through */ 58857205Seric 58957943Seric case T_A: 59057943Seric /* good show */ 59157943Seric amatch = TRUE; 59233929Sbostic 59357943Seric /* continue in case a CNAME also exists */ 59457943Seric continue; 59557943Seric 59657943Seric case T_CNAME: 59762524Sbostic if (loopcnt++ > MAXCNAMEDEPTH) 59862524Sbostic { 59966323Seric /*XXX should notify postmaster XXX*/ 60066323Seric message("DNS failure: CNAME loop for %s", 60162524Sbostic host); 60266323Seric if (CurEnv->e_message == NULL) 60366323Seric { 60466323Seric char ebuf[MAXLINE]; 60566323Seric 60666323Seric sprintf(ebuf, "Deferred: DNS failure: CNAME loop for %s", 60766323Seric host); 60866323Seric CurEnv->e_message = newstr(ebuf); 60966323Seric } 61066323Seric h_errno = NO_RECOVERY; 61166323Seric return FALSE; 61262524Sbostic } 61362524Sbostic 61457943Seric /* value points at name */ 61557943Seric if ((ret = dn_expand((u_char *)&answer, 616*68513Seric eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0) 61757943Seric break; 61857943Seric (void)strncpy(host, nbuf, hbsize); /* XXX */ 61957943Seric host[hbsize - 1] = '\0'; 62057943Seric 62158844Seric /* 62258844Seric ** RFC 1034 section 3.6 specifies that CNAME 62358844Seric ** should point at the canonical name -- but 62458844Seric ** urges software to try again anyway. 62558844Seric */ 62658844Seric 62758844Seric goto cnameloop; 62858844Seric 62957943Seric default: 63057943Seric /* not a record of interest */ 63157943Seric continue; 63257943Seric } 63351324Seric } 63433929Sbostic 63557943Seric if (amatch) 63657943Seric { 63757943Seric /* got an A record and no CNAME */ 63857943Seric mxmatch = *dp; 63929653Sbloom break; 64029653Sbloom } 64158404Seric 64258404Seric /* 64358404Seric ** If this was a T_ANY query, we may have the info but 64458404Seric ** need an explicit query. Try T_A, then T_MX. 64558404Seric */ 64658404Seric 64758404Seric if (qtype == T_ANY) 64858404Seric qtype = T_A; 64963840Seric else if (qtype == T_A && !gotmx && trymx) 65058404Seric qtype = T_MX; 65158404Seric else 65258404Seric { 65358404Seric /* really nothing in this domain; try the next */ 65458404Seric qtype = T_ANY; 65558404Seric dp++; 65658404Seric } 65729653Sbloom } 65857943Seric 65957943Seric if (mxmatch == NULL) 66057943Seric return FALSE; 66157943Seric 66257943Seric /* create matching name and return */ 66357943Seric (void) sprintf(nbuf, "%.*s%s%.*s", MAXDNAME, host, 66457943Seric *mxmatch == '\0' ? "" : ".", 66557943Seric MAXDNAME, mxmatch); 66657943Seric strncpy(host, nbuf, hbsize); 66757943Seric host[hbsize - 1] = '\0'; 66857943Seric return TRUE; 66929653Sbloom } 67035653Seric 67165193Seric 67265193Seric char * 67365193Seric gethostalias(host) 67465193Seric char *host; 67565193Seric { 67665193Seric char *fname; 67765193Seric FILE *fp; 678*68513Seric register char *p = NULL; 67965193Seric char buf[MAXLINE]; 68065193Seric static char hbuf[MAXDNAME]; 68165193Seric 68265193Seric fname = getenv("HOSTALIASES"); 683*68513Seric if (fname == NULL || 684*68513Seric (fp = safefopen(fname, O_RDONLY, 0, SFF_REGONLY)) == NULL) 68565193Seric return NULL; 68665193Seric while (fgets(buf, sizeof buf, fp) != NULL) 68765193Seric { 68865193Seric for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++) 68965193Seric continue; 69065193Seric if (*p == 0) 69165193Seric { 69265193Seric /* syntax error */ 69365193Seric continue; 69465193Seric } 69565193Seric *p++ = '\0'; 69665193Seric if (strcasecmp(buf, host) == 0) 69765193Seric break; 69865193Seric } 69965193Seric 70065193Seric if (feof(fp)) 70165193Seric { 70265193Seric /* no match */ 70365193Seric fclose(fp); 70465193Seric return NULL; 70565193Seric } 70665193Seric 70765193Seric /* got a match; extract the equivalent name */ 70865193Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 70965193Seric p++; 71065193Seric host = p; 71165193Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 71265193Seric p++; 71365193Seric *p = '\0'; 71465193Seric strncpy(hbuf, host, sizeof hbuf - 1); 71565193Seric hbuf[sizeof hbuf - 1] = '\0'; 71665193Seric return hbuf; 71765193Seric } 71865193Seric 71965193Seric 72036494Sphil #else /* not NAMED_BIND */ 72136494Sphil 72236494Sphil #include <netdb.h> 72336494Sphil 72451314Seric bool 72563840Seric getcanonname(host, hbsize, trymx) 72636494Sphil char *host; 72736494Sphil int hbsize; 72863840Seric bool trymx; 72936494Sphil { 73036494Sphil struct hostent *hp; 731*68513Seric char *p; 73236494Sphil 73336494Sphil hp = gethostbyname(host); 73436494Sphil if (hp == NULL) 73551314Seric return (FALSE); 736*68513Seric p = hp->h_name; 737*68513Seric if (strchr(p, '.') == NULL) 738*68513Seric { 739*68513Seric /* first word is a short name -- try to find a long one */ 740*68513Seric char **ap; 74136494Sphil 742*68513Seric for (ap = hp->h_aliases; *ap != NULL; ap++) 743*68513Seric if (strchr(*ap, '.') != NULL) 744*68513Seric break; 745*68513Seric if (*ap != NULL) 746*68513Seric p = *ap; 747*68513Seric } 748*68513Seric 749*68513Seric if (strlen(p) >= hbsize) 75051314Seric return (FALSE); 75136494Sphil 752*68513Seric (void) strcpy(host, p); 75351314Seric return (TRUE); 75436494Sphil } 75536494Sphil 75636494Sphil #endif /* not NAMED_BIND */ 757