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*69954Seric static char sccsid[] = "@(#)domain.c 8.47 (Berkeley) 06/20/95 (with name server)";
1435653Seric #else
15*69954Seric static char sccsid[] = "@(#)domain.c 8.47 (Berkeley) 06/20/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
getmxrr(host,mxhosts,droplocalhost,rcode)7459273Seric getmxrr(host, mxhosts, droplocalhost, rcode)
7559075Seric char *host;
7659075Seric char **mxhosts;
7759273Seric bool droplocalhost;
7833929Sbostic int *rcode;
7929432Sbloom {
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;
9369936Seric int (*resfunc)();
9469936Seric extern int res_query(), res_search();
9558891Seric u_short prefer[MAXMXHOSTS];
9657454Seric int weight[MAXMXHOSTS];
9763840Seric extern bool getcanonname();
9829432Sbloom
9966145Seric if (tTd(8, 2))
10066145Seric printf("getmxrr(%s, droplocalhost=%d)\n", host, droplocalhost);
10166145Seric
10258891Seric if (fallbackMX != NULL)
10358891Seric {
10468513Seric if (firsttime &&
10568513Seric res_query(FallBackMX, C_IN, T_A,
10668513Seric (u_char *) &answer, sizeof answer) < 0)
10758891Seric {
10858891Seric /* this entry is bogus */
10958891Seric fallbackMX = FallBackMX = NULL;
11058891Seric }
11168513Seric else if (droplocalhost && wordinclass(fallbackMX, 'w'))
11258891Seric {
11358891Seric /* don't use fallback for this pass */
11458891Seric fallbackMX = NULL;
11558891Seric }
11658891Seric firsttime = FALSE;
11758891Seric }
11858891Seric
11969661Seric *rcode = EX_OK;
12069661Seric
12164676Seric /* efficiency hack -- numeric or non-MX lookups */
12264676Seric if (host[0] == '[')
12364676Seric goto punt;
12464676Seric
12568513Seric /*
12668513Seric ** If we don't have MX records in our host switch, don't
12768513Seric ** try for MX records. Note that this really isn't "right",
12868513Seric ** since we might be set up to try NIS first and then DNS;
12968513Seric ** if the host is found in NIS we really shouldn't be doing
13068513Seric ** MX lookups. However, that should be a degenerate case.
13168513Seric */
13268513Seric
13368513Seric if (!UseNameServer)
13468513Seric goto punt;
13569936Seric if (HasWildcardMX && ConfigLevel >= 6)
13669936Seric resfunc = res_query;
13769936Seric else
13869936Seric resfunc = res_search;
13968513Seric
14036483Sbostic errno = 0;
14169936Seric n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer));
14235653Seric if (n < 0)
14335653Seric {
14433929Sbostic if (tTd(8, 1))
14552852Seric printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
14652852Seric (host == NULL) ? "<NULL>" : host, errno, h_errno);
14735653Seric switch (h_errno)
14835653Seric {
14935653Seric case NO_DATA:
15064015Seric trycanon = TRUE;
15164015Seric /* fall through */
15264015Seric
15335653Seric case NO_RECOVERY:
15435653Seric /* no MX data on this host */
15533929Sbostic goto punt;
15635653Seric
15735653Seric case HOST_NOT_FOUND:
15869562Seric #if BROKEN_RES_SEARCH
15968513Seric case 0: /* Ultrix resolver retns failure w/ h_errno=0 */
16066318Seric #endif
16168513Seric /* host doesn't exist in DNS; might be in /etc/hosts */
16233929Sbostic *rcode = EX_NOHOST;
16368513Seric goto punt;
16464950Seric
16535653Seric case TRY_AGAIN:
16635653Seric /* couldn't connect to the name server */
16735653Seric /* it might come up later; better queue it up */
16833929Sbostic *rcode = EX_TEMPFAIL;
16933929Sbostic break;
17066253Seric
17166253Seric default:
17266253Seric syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n",
17366253Seric host, h_errno);
17466253Seric *rcode = EX_OSERR;
17566253Seric break;
17629432Sbloom }
17735653Seric
17835653Seric /* irreconcilable differences */
17935653Seric return (-1);
18029432Sbloom }
18133929Sbostic
18233929Sbostic /* find first satisfactory answer */
18333929Sbostic hp = (HEADER *)&answer;
18468513Seric cp = (u_char *)&answer + HFIXEDSZ;
18533929Sbostic eom = (u_char *)&answer + n;
18633929Sbostic for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ)
18750957Skarels if ((n = dn_skipname(cp, eom)) < 0)
18833929Sbostic goto punt;
18959075Seric buflen = sizeof(MXHostBuf) - 1;
19059075Seric bp = MXHostBuf;
19133929Sbostic ancount = ntohs(hp->ancount);
19258848Seric while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
19356336Seric {
19446928Sbostic if ((n = dn_expand((u_char *)&answer,
19568513Seric eom, cp, (RES_UNC_T) bp, buflen)) < 0)
19629432Sbloom break;
19729432Sbloom cp += n;
19833929Sbostic GETSHORT(type, cp);
19968513Seric cp += INT16SZ + INT32SZ;
20033929Sbostic GETSHORT(n, cp);
20156336Seric if (type != T_MX)
20256336Seric {
20357943Seric if (tTd(8, 8) || _res.options & RES_DEBUG)
20429432Sbloom printf("unexpected answer type %d, size %d\n",
20533929Sbostic type, n);
20629432Sbloom cp += n;
20729432Sbloom continue;
20829432Sbloom }
20933929Sbostic GETSHORT(pref, cp);
21056336Seric if ((n = dn_expand((u_char *)&answer, eom, cp,
21168513Seric (RES_UNC_T) bp, buflen)) < 0)
21229432Sbloom break;
21329551Sbloom cp += n;
21468513Seric if (droplocalhost && wordinclass(bp, 'w'))
21556336Seric {
21666145Seric if (tTd(8, 3))
21766145Seric printf("found localhost (%s) in MX list, pref=%d\n",
21866145Seric bp, pref);
21958857Seric if (!seenlocal || pref < localpref)
22033929Sbostic localpref = pref;
22158857Seric seenlocal = TRUE;
22229551Sbloom continue;
22329551Sbloom }
22457454Seric weight[nmx] = mxrand(bp);
22529432Sbloom prefer[nmx] = pref;
22629432Sbloom mxhosts[nmx++] = bp;
22756336Seric n = strlen(bp);
22833929Sbostic bp += n;
22956336Seric if (bp[-1] != '.')
23056336Seric {
23156336Seric *bp++ = '.';
23256336Seric n++;
23356336Seric }
23456336Seric *bp++ = '\0';
23556336Seric buflen -= n + 1;
23629432Sbloom }
23763840Seric
23863840Seric /* sort the records */
23963840Seric for (i = 0; i < nmx; i++)
24057454Seric {
24163840Seric for (j = i + 1; j < nmx; j++)
24258668Seric {
24363840Seric if (prefer[i] > prefer[j] ||
24463840Seric (prefer[i] == prefer[j] && weight[i] > weight[j]))
24563840Seric {
24663840Seric register int temp;
24763840Seric register char *temp1;
24863840Seric
24963840Seric temp = prefer[i];
25063840Seric prefer[i] = prefer[j];
25163840Seric prefer[j] = temp;
25263840Seric temp1 = mxhosts[i];
25363840Seric mxhosts[i] = mxhosts[j];
25463840Seric mxhosts[j] = temp1;
25563840Seric temp = weight[i];
25663840Seric weight[i] = weight[j];
25763840Seric weight[j] = temp;
25863840Seric }
25958668Seric }
26063840Seric if (seenlocal && prefer[i] >= localpref)
26163840Seric {
26263840Seric /* truncate higher preference part of list */
26363840Seric nmx = i;
26463840Seric }
26529551Sbloom }
26663840Seric
26763840Seric if (nmx == 0)
26857454Seric {
26963840Seric punt:
27063840Seric if (seenlocal &&
27168693Seric (!TryNullMXList || sm_gethostbyname(host) == NULL))
27257454Seric {
27363840Seric /*
27463840Seric ** If we have deleted all MX entries, this is
27563840Seric ** an error -- we should NEVER send to a host that
27663840Seric ** has an MX, and this should have been caught
27763840Seric ** earlier in the config file.
27863840Seric **
27963840Seric ** Some sites prefer to go ahead and try the
28063840Seric ** A record anyway; that case is handled by
28163840Seric ** setting TryNullMXList. I believe this is a
28263840Seric ** bad idea, but it's up to you....
28363840Seric */
28429432Sbloom
28563840Seric *rcode = EX_CONFIG;
28666256Seric syserr("MX list for %s points back to %s",
28766256Seric host, MyHostName);
28863840Seric return -1;
28963840Seric }
29064676Seric strcpy(MXHostBuf, host);
29164676Seric mxhosts[0] = MXHostBuf;
29264676Seric if (host[0] == '[')
29364676Seric {
29464676Seric register char *p;
29564676Seric
29664676Seric /* this may be an MX suppression-style address */
29764676Seric p = strchr(MXHostBuf, ']');
29864676Seric if (p != NULL)
29964676Seric {
30064676Seric *p = '\0';
30164676Seric if (inet_addr(&MXHostBuf[1]) != -1)
30264676Seric *p = ']';
30364676Seric else
30464677Seric {
30564677Seric trycanon = TRUE;
30664676Seric mxhosts[0]++;
30764677Seric }
30864676Seric }
30964676Seric }
31064677Seric if (trycanon &&
31164677Seric getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE))
31263840Seric {
31364950Seric bp = &MXHostBuf[strlen(MXHostBuf)];
31463840Seric if (bp[-1] != '.')
31557454Seric {
31663840Seric *bp++ = '.';
31763840Seric *bp = '\0';
31829551Sbloom }
31969567Seric nmx = 1;
32029551Sbloom }
32129432Sbloom }
32258848Seric
32358848Seric /* if we have a default lowest preference, include that */
32464950Seric if (fallbackMX != NULL && !seenlocal)
32564950Seric mxhosts[nmx++] = fallbackMX;
32658848Seric
32757454Seric return (nmx);
32829432Sbloom }
32957135Seric /*
33057454Seric ** MXRAND -- create a randomizer for equal MX preferences
33157454Seric **
33257454Seric ** If two MX hosts have equal preferences we want to randomize
33357454Seric ** the selection. But in order for signatures to be the same,
33457454Seric ** we need to randomize the same way each time. This function
33557454Seric ** computes a pseudo-random hash function from the host name.
33657454Seric **
33757454Seric ** Parameters:
33857454Seric ** host -- the name of the host.
33957454Seric **
34057454Seric ** Returns:
34157454Seric ** A random but repeatable value based on the host name.
34257454Seric **
34357454Seric ** Side Effects:
34457454Seric ** none.
34557454Seric */
34657454Seric
34769748Seric int
mxrand(host)34857454Seric mxrand(host)
34957454Seric register char *host;
35057454Seric {
35157454Seric int hfunc;
35257454Seric static unsigned int seed;
35357454Seric
35457454Seric if (seed == 0)
35557454Seric {
35657454Seric seed = (int) curtime() & 0xffff;
35757454Seric if (seed == 0)
35857454Seric seed++;
35957454Seric }
36057454Seric
36157454Seric if (tTd(17, 9))
36257454Seric printf("mxrand(%s)", host);
36357454Seric
36457454Seric hfunc = seed;
36557454Seric while (*host != '\0')
36657454Seric {
36757454Seric int c = *host++;
36857454Seric
36957454Seric if (isascii(c) && isupper(c))
37057454Seric c = tolower(c);
37165952Seric hfunc = ((hfunc << 1) ^ c) % 2003;
37257454Seric }
37357454Seric
37457454Seric hfunc &= 0xff;
37557454Seric
37657454Seric if (tTd(17, 9))
37757454Seric printf(" = %d\n", hfunc);
37857454Seric return hfunc;
37957454Seric }
38057454Seric /*
38169905Seric ** BESTMX -- find the best MX for a name
38269905Seric **
38369905Seric ** This is really a hack, but I don't see any obvious way
38469905Seric ** to generalize it at the moment.
38569905Seric */
38669905Seric
38769905Seric char *
bestmx_map_lookup(map,name,av,statp)38869905Seric bestmx_map_lookup(map, name, av, statp)
38969905Seric MAP *map;
39069905Seric char *name;
39169905Seric char **av;
39269905Seric int *statp;
39369905Seric {
39469905Seric int nmx;
39569905Seric auto int rcode;
39669905Seric int saveopts = _res.options;
39769905Seric char *mxhosts[MAXMXHOSTS + 1];
39869905Seric
39969905Seric _res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
40069905Seric nmx = getmxrr(name, mxhosts, FALSE, &rcode);
40169905Seric _res.options = saveopts;
40269905Seric if (nmx <= 0)
40369905Seric return NULL;
40469905Seric if (bitset(MF_MATCHONLY, map->map_mflags))
40569905Seric return map_rewrite(map, name, strlen(name), NULL);
40669905Seric else
40769905Seric return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
40869905Seric }
40969905Seric /*
41069401Seric ** DNS_GETCANONNAME -- get the canonical name for named host using DNS
41157135Seric **
41257943Seric ** This algorithm tries to be smart about wildcard MX records.
41357943Seric ** This is hard to do because DNS doesn't tell is if we matched
41457943Seric ** against a wildcard or a specific MX.
41557943Seric **
41657943Seric ** We always prefer A & CNAME records, since these are presumed
41757943Seric ** to be specific.
41857943Seric **
41957943Seric ** If we match an MX in one pass and lose it in the next, we use
42057943Seric ** the old one. For example, consider an MX matching *.FOO.BAR.COM.
42157943Seric ** A hostname bletch.foo.bar.com will match against this MX, but
42257943Seric ** will stop matching when we try bletch.bar.com -- so we know
42357943Seric ** that bletch.foo.bar.com must have been right. This fails if
42457943Seric ** there was also an MX record matching *.BAR.COM, but there are
42557943Seric ** some things that just can't be fixed.
42657943Seric **
42757135Seric ** Parameters:
42857135Seric ** host -- a buffer containing the name of the host.
42957135Seric ** This is a value-result parameter.
43057135Seric ** hbsize -- the size of the host buffer.
43163840Seric ** trymx -- if set, try MX records as well as A and CNAME.
43269401Seric ** statp -- pointer to place to store status.
43357135Seric **
43457135Seric ** Returns:
43557135Seric ** TRUE -- if the host matched.
43657135Seric ** FALSE -- otherwise.
43757135Seric */
43829653Sbloom
43951314Seric bool
dns_getcanonname(host,hbsize,trymx,statp)44069401Seric dns_getcanonname(host, hbsize, trymx, statp)
44129653Sbloom char *host;
44229653Sbloom int hbsize;
44363840Seric bool trymx;
44469401Seric int *statp;
44529653Sbloom {
44651324Seric register u_char *eom, *ap;
44751324Seric register char *cp;
44833929Sbostic register int n;
44929653Sbloom HEADER *hp;
45033929Sbostic querybuf answer;
45161707Seric int ancount, qdcount;
45251324Seric int ret;
45351324Seric char **domain;
45451324Seric int type;
45557943Seric char **dp;
45657943Seric char *mxmatch;
45757943Seric bool amatch;
45868513Seric bool gotmx = FALSE;
45958404Seric int qtype;
46062524Sbostic int loopcnt;
46165193Seric char *xp;
46258010Seric char nbuf[MAX(PACKETSZ, MAXDNAME*2+2)];
46358039Seric char *searchlist[MAXDNSRCH+2];
46465193Seric extern char *gethostalias();
46529653Sbloom
46651324Seric if (tTd(8, 2))
46769825Seric printf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
46851324Seric
46951324Seric if ((_res.options & RES_INIT) == 0 && res_init() == -1)
47069401Seric {
47169401Seric *statp = EX_UNAVAILABLE;
47269401Seric return FALSE;
47369401Seric }
47451324Seric
47536483Sbostic /*
47657943Seric ** Initialize domain search list. If there is at least one
47757943Seric ** dot in the name, search the unmodified name first so we
47857943Seric ** find "vse.CS" in Czechoslovakia instead of in the local
47957943Seric ** domain (e.g., vse.CS.Berkeley.EDU).
48057943Seric **
48157943Seric ** Older versions of the resolver could create this
48257943Seric ** list by tearing apart the host name.
48357205Seric */
48457205Seric
48562524Sbostic loopcnt = 0;
48659268Seric cnameloop:
48769899Seric for (cp = host, n = 0; *cp != '\0'; cp++)
48859268Seric if (*cp == '.')
48959268Seric n++;
49059268Seric
49165193Seric if (n == 0 && (xp = gethostalias(host)) != NULL)
49265193Seric {
49365193Seric if (loopcnt++ > MAXCNAMEDEPTH)
49465193Seric {
49565193Seric syserr("loop in ${HOSTALIASES} file");
49665193Seric }
49765193Seric else
49865193Seric {
49965193Seric strncpy(host, xp, hbsize);
50065193Seric host[hbsize - 1] = '\0';
50165193Seric goto cnameloop;
50265193Seric }
50365193Seric }
50465193Seric
50557943Seric dp = searchlist;
50657943Seric if (n > 0)
50757943Seric *dp++ = "";
50858411Seric if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options))
50951324Seric {
51057943Seric for (domain = _res.dnsrch; *domain != NULL; )
51157943Seric *dp++ = *domain++;
51257205Seric }
51358411Seric else if (n == 0 && bitset(RES_DEFNAMES, _res.options))
51458411Seric {
51558411Seric *dp++ = _res.defdname;
51658411Seric }
51766040Seric else if (*cp == '.')
51866040Seric {
51966040Seric *cp = '\0';
52066040Seric }
52157943Seric *dp = NULL;
52257205Seric
52369899Seric /* if we have a wildcard MX and no dots, try MX anyhow */
52469899Seric if (n == 0)
52569899Seric trymx = TRUE;
52669899Seric
52757205Seric /*
52857943Seric ** Now run through the search list for the name in question.
52957205Seric */
53057205Seric
53157943Seric mxmatch = NULL;
53258404Seric qtype = T_ANY;
53357943Seric
53458404Seric for (dp = searchlist; *dp != NULL; )
53557205Seric {
53658411Seric if (qtype == T_ANY)
53758411Seric gotmx = FALSE;
53857943Seric if (tTd(8, 5))
53969825Seric printf("dns_getcanonname: trying %s.%s (%s)\n",
54069825Seric host, *dp,
54158508Seric qtype == T_ANY ? "ANY" : qtype == T_A ? "A" :
54258508Seric qtype == T_MX ? "MX" : "???");
54358404Seric ret = res_querydomain(host, *dp, C_IN, qtype,
54468513Seric answer.qb2, sizeof(answer.qb2));
54557943Seric if (ret <= 0)
54651324Seric {
54758796Seric if (tTd(8, 7))
54858082Seric printf("\tNO: errno=%d, h_errno=%d\n",
54958082Seric errno, h_errno);
55051324Seric
55158082Seric if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
55257205Seric {
55357943Seric /* the name server seems to be down */
55451324Seric h_errno = TRY_AGAIN;
55569401Seric *statp = EX_TEMPFAIL;
55651910Seric return FALSE;
55751324Seric }
55857943Seric
55958501Seric if (h_errno != HOST_NOT_FOUND)
56058404Seric {
56158501Seric /* might have another type of interest */
56258501Seric if (qtype == T_ANY)
56358501Seric {
56458501Seric qtype = T_A;
56558501Seric continue;
56658501Seric }
56763840Seric else if (qtype == T_A && !gotmx && trymx)
56858501Seric {
56958501Seric qtype = T_MX;
57058501Seric continue;
57158501Seric }
57258404Seric }
57358404Seric
57469899Seric /* try the next name */
57558501Seric dp++;
57658501Seric qtype = T_ANY;
57757943Seric continue;
57851324Seric }
57958796Seric else if (tTd(8, 7))
58057943Seric printf("\tYES\n");
58157943Seric
58251910Seric /*
58357943Seric ** This might be a bogus match. Search for A or
58457943Seric ** CNAME records. If we don't have a matching
58557943Seric ** wild card MX record, we will accept MX as well.
58651910Seric */
58751910Seric
58857943Seric hp = (HEADER *) &answer;
58968513Seric ap = (u_char *) &answer + HFIXEDSZ;
59057943Seric eom = (u_char *) &answer + ret;
59157943Seric
59257943Seric /* skip question part of response -- we know what we asked */
59357943Seric for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ)
59451324Seric {
59557943Seric if ((ret = dn_skipname(ap, eom)) < 0)
59657943Seric {
59757943Seric if (tTd(8, 20))
59857943Seric printf("qdcount failure (%d)\n",
59957943Seric ntohs(hp->qdcount));
60069401Seric *statp = EX_SOFTWARE;
60157943Seric return FALSE; /* ???XXX??? */
60257943Seric }
60351324Seric }
60457943Seric
60557943Seric amatch = FALSE;
60657943Seric for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n)
60751324Seric {
60857943Seric n = dn_expand((u_char *) &answer, eom, ap,
60968513Seric (RES_UNC_T) nbuf, sizeof nbuf);
61057943Seric if (n < 0)
61157943Seric break;
61257943Seric ap += n;
61357943Seric GETSHORT(type, ap);
61468513Seric ap += INT16SZ + INT32SZ;
61557943Seric GETSHORT(n, ap);
61657943Seric switch (type)
61757943Seric {
61857943Seric case T_MX:
61958411Seric gotmx = TRUE;
62069899Seric if (**dp != '\0' || !HasWildcardMX)
62157943Seric {
62257943Seric /* got a match -- save that info */
62364030Seric if (trymx && mxmatch == NULL)
62457943Seric mxmatch = *dp;
62557943Seric continue;
62657943Seric }
62733929Sbostic
62857943Seric /* exact MX matches are as good as an A match */
62957943Seric /* fall through */
63057205Seric
63157943Seric case T_A:
63257943Seric /* good show */
63357943Seric amatch = TRUE;
63433929Sbostic
63557943Seric /* continue in case a CNAME also exists */
63657943Seric continue;
63757943Seric
63857943Seric case T_CNAME:
63969852Seric if (DontExpandCnames)
64069852Seric {
64169852Seric /* got CNAME -- guaranteed canonical */
64269852Seric amatch = TRUE;
64369852Seric break;
64469852Seric }
64569852Seric
64662524Sbostic if (loopcnt++ > MAXCNAMEDEPTH)
64762524Sbostic {
64866323Seric /*XXX should notify postmaster XXX*/
64966323Seric message("DNS failure: CNAME loop for %s",
65062524Sbostic host);
65166323Seric if (CurEnv->e_message == NULL)
65266323Seric {
65366323Seric char ebuf[MAXLINE];
65466323Seric
65566323Seric sprintf(ebuf, "Deferred: DNS failure: CNAME loop for %s",
65666323Seric host);
65766323Seric CurEnv->e_message = newstr(ebuf);
65866323Seric }
65966323Seric h_errno = NO_RECOVERY;
66069401Seric *statp = EX_CONFIG;
66166323Seric return FALSE;
66262524Sbostic }
66362524Sbostic
66457943Seric /* value points at name */
66557943Seric if ((ret = dn_expand((u_char *)&answer,
66668513Seric eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0)
66757943Seric break;
66857943Seric (void)strncpy(host, nbuf, hbsize); /* XXX */
66957943Seric host[hbsize - 1] = '\0';
67057943Seric
67158844Seric /*
67258844Seric ** RFC 1034 section 3.6 specifies that CNAME
67358844Seric ** should point at the canonical name -- but
67458844Seric ** urges software to try again anyway.
67558844Seric */
67658844Seric
67758844Seric goto cnameloop;
67858844Seric
67957943Seric default:
68057943Seric /* not a record of interest */
68157943Seric continue;
68257943Seric }
68351324Seric }
68433929Sbostic
68557943Seric if (amatch)
68657943Seric {
68757943Seric /* got an A record and no CNAME */
68857943Seric mxmatch = *dp;
68929653Sbloom break;
69029653Sbloom }
69158404Seric
69258404Seric /*
69358404Seric ** If this was a T_ANY query, we may have the info but
69458404Seric ** need an explicit query. Try T_A, then T_MX.
69558404Seric */
69658404Seric
69758404Seric if (qtype == T_ANY)
69858404Seric qtype = T_A;
69963840Seric else if (qtype == T_A && !gotmx && trymx)
70058404Seric qtype = T_MX;
70158404Seric else
70258404Seric {
70358404Seric /* really nothing in this domain; try the next */
70458404Seric qtype = T_ANY;
70558404Seric dp++;
70658404Seric }
70729653Sbloom }
70857943Seric
70957943Seric if (mxmatch == NULL)
71069401Seric {
71169401Seric *statp = EX_NOHOST;
71257943Seric return FALSE;
71369401Seric }
71457943Seric
71557943Seric /* create matching name and return */
71657943Seric (void) sprintf(nbuf, "%.*s%s%.*s", MAXDNAME, host,
71757943Seric *mxmatch == '\0' ? "" : ".",
71857943Seric MAXDNAME, mxmatch);
71957943Seric strncpy(host, nbuf, hbsize);
72057943Seric host[hbsize - 1] = '\0';
72169401Seric *statp = EX_OK;
72257943Seric return TRUE;
72329653Sbloom }
72435653Seric
72565193Seric
72665193Seric char *
gethostalias(host)72765193Seric gethostalias(host)
72865193Seric char *host;
72965193Seric {
73065193Seric char *fname;
73165193Seric FILE *fp;
73268513Seric register char *p = NULL;
73365193Seric char buf[MAXLINE];
73465193Seric static char hbuf[MAXDNAME];
73565193Seric
73665193Seric fname = getenv("HOSTALIASES");
73768513Seric if (fname == NULL ||
73868513Seric (fp = safefopen(fname, O_RDONLY, 0, SFF_REGONLY)) == NULL)
73965193Seric return NULL;
74065193Seric while (fgets(buf, sizeof buf, fp) != NULL)
74165193Seric {
74265193Seric for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++)
74365193Seric continue;
74465193Seric if (*p == 0)
74565193Seric {
74665193Seric /* syntax error */
74765193Seric continue;
74865193Seric }
74965193Seric *p++ = '\0';
75065193Seric if (strcasecmp(buf, host) == 0)
75165193Seric break;
75265193Seric }
75365193Seric
75465193Seric if (feof(fp))
75565193Seric {
75665193Seric /* no match */
75765193Seric fclose(fp);
75865193Seric return NULL;
75965193Seric }
76065193Seric
76165193Seric /* got a match; extract the equivalent name */
76265193Seric while (*p != '\0' && isascii(*p) && isspace(*p))
76365193Seric p++;
76465193Seric host = p;
76565193Seric while (*p != '\0' && !(isascii(*p) && isspace(*p)))
76665193Seric p++;
76765193Seric *p = '\0';
76865193Seric strncpy(hbuf, host, sizeof hbuf - 1);
76965193Seric hbuf[sizeof hbuf - 1] = '\0';
77065193Seric return hbuf;
77165193Seric }
77265193Seric
77369401Seric #endif /* NAMED_BIND */
774