diff options
Diffstat (limited to 'usr.sbin/sendmail/src/domain.c')
| -rw-r--r-- | usr.sbin/sendmail/src/domain.c | 325 |
1 files changed, 213 insertions, 112 deletions
diff --git a/usr.sbin/sendmail/src/domain.c b/usr.sbin/sendmail/src/domain.c index da2a79587a62..8058330f962a 100644 --- a/usr.sbin/sendmail/src/domain.c +++ b/usr.sbin/sendmail/src/domain.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1986 Eric P. Allman + * Copyright (c) 1986, 1995 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * @@ -36,23 +36,21 @@ #ifndef lint #if NAMED_BIND -static char sccsid[] = "@(#)domain.c 8.19.1.1 (Berkeley) 3/6/95 (with name server)"; +static char sccsid[] = "@(#)domain.c 8.54 (Berkeley) 9/28/95 (with name server)"; #else -static char sccsid[] = "@(#)domain.c 8.19.1.1 (Berkeley) 3/6/95 (without name server)"; +static char sccsid[] = "@(#)domain.c 8.54 (Berkeley) 9/28/95 (without name server)"; #endif #endif /* not lint */ #if NAMED_BIND #include <errno.h> -#include <arpa/nameser.h> #include <resolv.h> -#include <netdb.h> typedef union { HEADER qb1; - char qb2[PACKETSZ]; + u_char qb2[PACKETSZ]; } querybuf; static char MXHostBuf[MAXMXHOSTS*PACKETSZ]; @@ -69,15 +67,17 @@ static char MXHostBuf[MAXMXHOSTS*PACKETSZ]; # define NO_DATA NO_ADDRESS #endif -#ifndef HEADERSZ -# define HEADERSZ sizeof(HEADER) +#ifndef HFIXEDSZ +# define HFIXEDSZ 12 /* sizeof(HEADER) */ #endif -/* don't use sizeof because sizeof(long) is different on 64-bit machines */ -#define SHORTSIZE 2 /* size of a short (really, must be 2) */ -#define LONGSIZE 4 /* size of a long (really, must be 4) */ - #define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ + +#if defined(__RES) && (__RES >= 19940415) +# define RES_UNC_T char * +#else +# define RES_UNC_T u_char * +#endif /* ** GETMXRR -- get MX resource records for a domain ** @@ -96,13 +96,13 @@ static char MXHostBuf[MAXMXHOSTS*PACKETSZ]; ** and 1 is returned. */ +int getmxrr(host, mxhosts, droplocalhost, rcode) char *host; char **mxhosts; bool droplocalhost; int *rcode; { - extern int h_errno; register u_char *eom, *cp; register int i, j, n; int nmx = 0; @@ -111,11 +111,13 @@ getmxrr(host, mxhosts, droplocalhost, rcode) querybuf answer; int ancount, qdcount, buflen; bool seenlocal = FALSE; - u_short pref, localpref, type; + u_short pref, type; + u_short localpref = 256; char *fallbackMX = FallBackMX; static bool firsttime = TRUE; - STAB *st; bool trycanon = FALSE; + int (*resfunc)(); + extern int res_query(), res_search(); u_short prefer[MAXMXHOSTS]; int weight[MAXMXHOSTS]; extern bool getcanonname(); @@ -125,15 +127,14 @@ getmxrr(host, mxhosts, droplocalhost, rcode) if (fallbackMX != NULL) { - if (firsttime && res_query(FallBackMX, C_IN, T_A, - (char *) &answer, sizeof answer) < 0) + if (firsttime && + res_query(FallBackMX, C_IN, T_A, + (u_char *) &answer, sizeof answer) < 0) { /* this entry is bogus */ fallbackMX = FallBackMX = NULL; } - else if (droplocalhost && - (st = stab(fallbackMX, ST_CLASS, ST_FIND)) != NULL && - bitnset('w', st->s_class)) + else if (droplocalhost && wordinclass(fallbackMX, 'w')) { /* don't use fallback for this pass */ fallbackMX = NULL; @@ -141,12 +142,29 @@ getmxrr(host, mxhosts, droplocalhost, rcode) firsttime = FALSE; } + *rcode = EX_OK; + /* efficiency hack -- numeric or non-MX lookups */ if (host[0] == '[') goto punt; + /* + ** If we don't have MX records in our host switch, don't + ** try for MX records. Note that this really isn't "right", + ** since we might be set up to try NIS first and then DNS; + ** if the host is found in NIS we really shouldn't be doing + ** MX lookups. However, that should be a degenerate case. + */ + + if (!UseNameServer) + goto punt; + if (HasWildcardMX && ConfigLevel >= 6) + resfunc = res_query; + else + resfunc = res_search; + errno = 0; - n = res_search(host, C_IN, T_MX, (char *)&answer, sizeof(answer)); + n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer)); if (n < 0) { if (tTd(8, 1)) @@ -163,25 +181,22 @@ getmxrr(host, mxhosts, droplocalhost, rcode) goto punt; case HOST_NOT_FOUND: -#ifdef BROKEN_RES_SEARCH - /* Ultrix resolver returns failure w/ h_errno=0 */ - case 0: +#if BROKEN_RES_SEARCH + case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ #endif - /* the host just doesn't exist */ + /* host doesn't exist in DNS; might be in /etc/hosts */ + trycanon = TRUE; *rcode = EX_NOHOST; - - if (!UseNameServer) - { - /* might exist in /etc/hosts */ - goto punt; - } - break; + goto punt; case TRY_AGAIN: /* couldn't connect to the name server */ - if (!UseNameServer && errno == ECONNREFUSED) - goto punt; - + if (fallbackMX != NULL) + { + /* name server is hosed -- push to fallback */ + mxhosts[nmx++] = fallbackMX; + return nmx; + } /* it might come up later; better queue it up */ *rcode = EX_TEMPFAIL; break; @@ -199,7 +214,7 @@ getmxrr(host, mxhosts, droplocalhost, rcode) /* find first satisfactory answer */ hp = (HEADER *)&answer; - cp = (u_char *)&answer + HEADERSZ; + cp = (u_char *)&answer + HFIXEDSZ; eom = (u_char *)&answer + n; for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) if ((n = dn_skipname(cp, eom)) < 0) @@ -210,11 +225,11 @@ getmxrr(host, mxhosts, droplocalhost, rcode) while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) { if ((n = dn_expand((u_char *)&answer, - eom, cp, (u_char *)bp, buflen)) < 0) + eom, cp, (RES_UNC_T) bp, buflen)) < 0) break; cp += n; GETSHORT(type, cp); - cp += SHORTSIZE + LONGSIZE; + cp += INT16SZ + INT32SZ; GETSHORT(n, cp); if (type != T_MX) { @@ -226,22 +241,25 @@ getmxrr(host, mxhosts, droplocalhost, rcode) } GETSHORT(pref, cp); if ((n = dn_expand((u_char *)&answer, eom, cp, - (u_char *)bp, buflen)) < 0) + (RES_UNC_T) bp, buflen)) < 0) break; cp += n; - if (droplocalhost && - (st = stab(bp, ST_CLASS, ST_FIND)) != NULL && - bitnset('w', st->s_class)) + if (wordinclass(bp, 'w')) { if (tTd(8, 3)) printf("found localhost (%s) in MX list, pref=%d\n", bp, pref); - if (!seenlocal || pref < localpref) - localpref = pref; - seenlocal = TRUE; - continue; + if (droplocalhost) + { + if (!seenlocal || pref < localpref) + localpref = pref; + seenlocal = TRUE; + continue; + } + weight[nmx] = 0; } - weight[nmx] = mxrand(bp); + else + weight[nmx] = mxrand(bp); prefer[nmx] = pref; mxhosts[nmx++] = bp; n = strlen(bp); @@ -284,11 +302,25 @@ getmxrr(host, mxhosts, droplocalhost, rcode) } } + /* delete duplicates from list (yes, some bozos have duplicates) */ + for (i = 0; i < nmx - 1; ) + { + if (strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) + i++; + else + { + /* compress out duplicate */ + for (j = i + 1; j < nmx; j++) + mxhosts[j] = mxhosts[j + 1]; + nmx--; + } + } + if (nmx == 0) { punt: if (seenlocal && - (!TryNullMXList || gethostbyname(host) == NULL)) + (!TryNullMXList || sm_gethostbyname(host) == NULL)) { /* ** If we have deleted all MX entries, this is @@ -336,8 +368,8 @@ punt: *bp++ = '.'; *bp = '\0'; } + nmx = 1; } - nmx = 1; } /* if we have a default lowest preference, include that */ @@ -364,6 +396,7 @@ punt: ** none. */ +int mxrand(host) register char *host; { @@ -391,13 +424,43 @@ mxrand(host) } hfunc &= 0xff; + hfunc++; if (tTd(17, 9)) printf(" = %d\n", hfunc); return hfunc; } /* -** GETCANONNAME -- get the canonical name for named host +** BESTMX -- find the best MX for a name +** +** This is really a hack, but I don't see any obvious way +** to generalize it at the moment. +*/ + +char * +bestmx_map_lookup(map, name, av, statp) + MAP *map; + char *name; + char **av; + int *statp; +{ + int nmx; + auto int rcode; + int saveopts = _res.options; + char *mxhosts[MAXMXHOSTS + 1]; + + _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); + nmx = getmxrr(name, mxhosts, FALSE, &rcode); + _res.options = saveopts; + if (nmx <= 0) + return NULL; + if (bitset(MF_MATCHONLY, map->map_mflags)) + return map_rewrite(map, name, strlen(name), NULL); + else + return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); +} +/* +** DNS_GETCANONNAME -- get the canonical name for named host using DNS ** ** This algorithm tries to be smart about wildcard MX records. ** This is hard to do because DNS doesn't tell is if we matched @@ -419,6 +482,7 @@ mxrand(host) ** This is a value-result parameter. ** hbsize -- the size of the host buffer. ** trymx -- if set, try MX records as well as A and CNAME. +** statp -- pointer to place to store status. ** ** Returns: ** TRUE -- if the host matched. @@ -426,12 +490,12 @@ mxrand(host) */ bool -getcanonname(host, hbsize, trymx) +dns_getcanonname(host, hbsize, trymx, statp) char *host; int hbsize; bool trymx; + int *statp; { - extern int h_errno; register u_char *eom, *ap; register char *cp; register int n; @@ -444,7 +508,7 @@ getcanonname(host, hbsize, trymx) char **dp; char *mxmatch; bool amatch; - bool gotmx; + bool gotmx = FALSE; int qtype; int loopcnt; char *xp; @@ -453,10 +517,13 @@ getcanonname(host, hbsize, trymx) extern char *gethostalias(); if (tTd(8, 2)) - printf("getcanonname(%s)\n", host); + printf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); if ((_res.options & RES_INIT) == 0 && res_init() == -1) - return (FALSE); + { + *statp = EX_UNAVAILABLE; + return FALSE; + } /* ** Initialize domain search list. If there is at least one @@ -470,10 +537,15 @@ getcanonname(host, hbsize, trymx) loopcnt = 0; cnameloop: - for (cp = host, n = 0; *cp; cp++) + /* Check for dots in the name */ + for (cp = host, n = 0; *cp != '\0'; cp++) if (*cp == '.') n++; + /* + ** If this is a simple name, determine whether it matches an + ** alias in the file defined by the environment variable HOSTALIASES. + */ if (n == 0 && (xp = gethostalias(host)) != NULL) { if (loopcnt++ > MAXCNAMEDEPTH) @@ -488,6 +560,17 @@ cnameloop: } } + /* + ** Build the search list. + ** If there is at least one dot in name, start with a null + ** domain to search the unmodified name first. + ** If name does not end with a dot and search up local domain + ** tree desired, append each local domain component to the + ** search list; if name contains no dots and default domain + ** name is desired, append default domain name to search list; + ** else if name ends in a dot, remove that dot. + */ + dp = searchlist; if (n > 0) *dp++ = ""; @@ -507,7 +590,8 @@ cnameloop: *dp = NULL; /* - ** Now run through the search list for the name in question. + ** Now loop through the search list, appending each domain in turn + ** name and searching for a match. */ mxmatch = NULL; @@ -518,11 +602,12 @@ cnameloop: if (qtype == T_ANY) gotmx = FALSE; if (tTd(8, 5)) - printf("getcanonname: trying %s.%s (%s)\n", host, *dp, + printf("dns_getcanonname: trying %s.%s (%s)\n", + host, *dp, qtype == T_ANY ? "ANY" : qtype == T_A ? "A" : qtype == T_MX ? "MX" : "???"); ret = res_querydomain(host, *dp, C_IN, qtype, - (u_char *) &answer, sizeof(answer)); + answer.qb2, sizeof(answer.qb2)); if (ret <= 0) { if (tTd(8, 7)) @@ -533,6 +618,7 @@ cnameloop: { /* the name server seems to be down */ h_errno = TRY_AGAIN; + *statp = EX_TEMPFAIL; return FALSE; } @@ -551,13 +637,7 @@ cnameloop: } } - if (mxmatch != NULL) - { - /* we matched before -- use that one */ - break; - } - - /* otherwise, try the next name */ + /* definite no -- try the next domain */ dp++; qtype = T_ANY; continue; @@ -566,13 +646,13 @@ cnameloop: printf("\tYES\n"); /* - ** This might be a bogus match. Search for A or - ** CNAME records. If we don't have a matching + ** Appear to have a match. Confirm it by searching for A or + ** CNAME records. If we don't have a local domain ** wild card MX record, we will accept MX as well. */ hp = (HEADER *) &answer; - ap = (u_char *) &answer + HEADERSZ; + ap = (u_char *) &answer + HFIXEDSZ; eom = (u_char *) &answer + ret; /* skip question part of response -- we know what we asked */ @@ -583,44 +663,66 @@ cnameloop: if (tTd(8, 20)) printf("qdcount failure (%d)\n", ntohs(hp->qdcount)); + *statp = EX_SOFTWARE; return FALSE; /* ???XXX??? */ } } amatch = FALSE; - for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n) + for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; + ap += n) { n = dn_expand((u_char *) &answer, eom, ap, - (u_char *) nbuf, sizeof nbuf); + (RES_UNC_T) nbuf, sizeof nbuf); if (n < 0) break; ap += n; GETSHORT(type, ap); - ap += SHORTSIZE + LONGSIZE; + ap += INT16SZ + INT32SZ; GETSHORT(n, ap); switch (type) { case T_MX: gotmx = TRUE; - if (**dp != '\0') + if (**dp != '\0' && HasWildcardMX) { - /* got a match -- save that info */ + /* + ** If we are using MX matches and have + ** not yet gotten one, save this one + ** but keep searching for an A or + ** CNAME match. + */ + if (trymx && mxmatch == NULL) mxmatch = *dp; continue; } - /* exact MX matches are as good as an A match */ - /* fall through */ + /* + ** If we did not append a domain name, this + ** must have been a canonical name to start + ** with. Even if we did append a domain name, + ** in the absence of a wildcard MX this must + ** still be a real MX match. + ** Such MX matches are as good as an A match, + ** fall through. + */ case T_A: - /* good show */ + /* Flag that a good match was found */ amatch = TRUE; /* continue in case a CNAME also exists */ continue; case T_CNAME: + if (DontExpandCnames) + { + /* got CNAME -- guaranteed canonical */ + amatch = TRUE; + break; + } + if (loopcnt++ > MAXCNAMEDEPTH) { /*XXX should notify postmaster XXX*/ @@ -630,17 +732,18 @@ cnameloop: { char ebuf[MAXLINE]; - sprintf(ebuf, "Deferred: DNS failure: CNAME loop for %s", + sprintf(ebuf, "Deferred: DNS failure: CNAME loop for %.100s", host); CurEnv->e_message = newstr(ebuf); } h_errno = NO_RECOVERY; + *statp = EX_CONFIG; return FALSE; } /* value points at name */ if ((ret = dn_expand((u_char *)&answer, - eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0) + eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0) break; (void)strncpy(host, nbuf, hbsize); /* XXX */ host[hbsize - 1] = '\0'; @@ -661,14 +764,24 @@ cnameloop: if (amatch) { - /* got an A record and no CNAME */ + /* + ** Got a good match -- either an A, CNAME, or an + ** exact MX record. Save it and get out of here. + */ + mxmatch = *dp; break; } /* - ** If this was a T_ANY query, we may have the info but - ** need an explicit query. Try T_A, then T_MX. + ** Nothing definitive yet. + ** If this was a T_ANY query, we don't really know what + ** was returned -- it might have been a T_NS, + ** for example. Try T_A to be more specific + ** during the next pass. + ** If this was a T_A query and we haven't yet found a MX + ** match, try T_MX if allowed to do so. + ** Otherwise, try the next domain. */ if (qtype == T_ANY) @@ -677,39 +790,51 @@ cnameloop: qtype = T_MX; else { - /* really nothing in this domain; try the next */ qtype = T_ANY; dp++; } } + /* if nothing was found, we are done */ if (mxmatch == NULL) + { + *statp = EX_NOHOST; return FALSE; + } + + /* + ** Create canonical name and return. + ** If saved domain name is null, name was already canonical. + ** Otherwise append the saved domain name. + */ - /* create matching name and return */ (void) sprintf(nbuf, "%.*s%s%.*s", MAXDNAME, host, *mxmatch == '\0' ? "" : ".", MAXDNAME, mxmatch); strncpy(host, nbuf, hbsize); host[hbsize - 1] = '\0'; + if (tTd(8, 5)) + printf("dns_getcanonname: %s\n", host); + *statp = EX_OK; return TRUE; } + char * gethostalias(host) char *host; { char *fname; FILE *fp; - register char *p; + register char *p = NULL; char buf[MAXLINE]; static char hbuf[MAXDNAME]; fname = getenv("HOSTALIASES"); - if (fname == NULL || (fp = fopen(fname, "r")) == NULL) + if (fname == NULL || + (fp = safefopen(fname, O_RDONLY, 0, SFF_REGONLY)) == NULL) return NULL; - setbuf(fp, NULL); while (fgets(buf, sizeof buf, fp) != NULL) { for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++) @@ -743,28 +868,4 @@ gethostalias(host) return hbuf; } - -#else /* not NAMED_BIND */ - -#include <netdb.h> - -bool -getcanonname(host, hbsize, trymx) - char *host; - int hbsize; - bool trymx; -{ - struct hostent *hp; - - hp = gethostbyname(host); - if (hp == NULL) - return (FALSE); - - if (strlen(hp->h_name) >= hbsize) - return (FALSE); - - (void) strcpy(host, hp->h_name); - return (TRUE); -} - -#endif /* not NAMED_BIND */ +#endif /* NAMED_BIND */ |
