fix compile problems on various platforms
[unix-history] / usr / src / usr.sbin / sendmail / src / domain.c
index 96b4142..988a9a0 100644 (file)
@@ -1,7 +1,7 @@
 /*
 /*
- * Copyright (c) 1986 Eric P. Allman
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1986, 1995 Eric P. Allman
+ * Copyright (c) 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * %sccs.include.redist.c%
  */
  *
  * %sccs.include.redist.c%
  */
@@ -9,27 +9,25 @@
 #include "sendmail.h"
 
 #ifndef lint
 #include "sendmail.h"
 
 #ifndef lint
-#ifdef NAMED_BIND
-static char sccsid[] = "@(#)domain.c   6.5 (Berkeley) %G% (with name server)";
+#if NAMED_BIND
+static char sccsid[] = "@(#)domain.c   8.38 (Berkeley) %G% (with name server)";
 #else
 #else
-static char sccsid[] = "@(#)domain.c   6.5 (Berkeley) %G% (without name server)";
+static char sccsid[] = "@(#)domain.c   8.38 (Berkeley) %G% (without name server)";
 #endif
 #endif /* not lint */
 
 #endif
 #endif /* not lint */
 
-#ifdef NAMED_BIND
+#if NAMED_BIND
 
 #include <errno.h>
 
 #include <errno.h>
-#include <arpa/nameser.h>
 #include <resolv.h>
 #include <resolv.h>
-#include <netdb.h>
 
 typedef union
 {
        HEADER  qb1;
 
 typedef union
 {
        HEADER  qb1;
-       char    qb2[PACKETSZ];
+       u_char  qb2[PACKETSZ];
 } querybuf;
 
 } querybuf;
 
-static char    hostbuf[MAXMXHOSTS*PACKETSZ];
+static char    MXHostBuf[MAXMXHOSTS*PACKETSZ];
 
 #ifndef MAXDNSRCH
 #define MAXDNSRCH      6       /* number of possible domains to search */
 
 #ifndef MAXDNSRCH
 #define MAXDNSRCH      6       /* number of possible domains to search */
@@ -39,22 +37,100 @@ static char        hostbuf[MAXMXHOSTS*PACKETSZ];
 #define MAX(a, b)      ((a) > (b) ? (a) : (b))
 #endif
 
 #define MAX(a, b)      ((a) > (b) ? (a) : (b))
 #endif
 
-getmxrr(host, mxhosts, localhost, rcode)
-       char *host, **mxhosts, *localhost;
+#ifndef NO_DATA
+# define NO_DATA       NO_ADDRESS
+#endif
+
+#ifndef HFIXEDSZ
+# define HFIXEDSZ      12      /* sizeof(HEADER) */
+#endif
+
+#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
+\f/*
+**  GETMXRR -- get MX resource records for a domain
+**
+**     Parameters:
+**             host -- the name of the host to MX.
+**             mxhosts -- a pointer to a return buffer of MX records.
+**             droplocalhost -- If TRUE, all MX records less preferred
+**                     than the local host (as determined by $=w) will
+**                     be discarded.
+**             rcode -- a pointer to an EX_ status code.
+**
+**     Returns:
+**             The number of MX records found.
+**             -1 if there is an internal failure.
+**             If no MX records are found, mxhosts[0] is set to host
+**                     and 1 is returned.
+*/
+
+getmxrr(host, mxhosts, droplocalhost, rcode)
+       char *host;
+       char **mxhosts;
+       bool droplocalhost;
        int *rcode;
 {
        extern int h_errno;
        register u_char *eom, *cp;
        int *rcode;
 {
        extern int h_errno;
        register u_char *eom, *cp;
-       register int i, j, n, nmx;
+       register int i, j, n;
+       int nmx = 0;
        register char *bp;
        HEADER *hp;
        querybuf answer;
        register char *bp;
        HEADER *hp;
        querybuf answer;
-       int ancount, qdcount, buflen, seenlocal;
-       u_short pref, localpref, type, prefer[MAXMXHOSTS];
+       int ancount, qdcount, buflen;
+       bool seenlocal = FALSE;
+       u_short pref, type;
+       u_short localpref = 256;
+       char *fallbackMX = FallBackMX;
+       static bool firsttime = TRUE;
+       bool trycanon = FALSE;
+       u_short prefer[MAXMXHOSTS];
        int weight[MAXMXHOSTS];
        int weight[MAXMXHOSTS];
+       extern bool getcanonname();
+
+       if (tTd(8, 2))
+               printf("getmxrr(%s, droplocalhost=%d)\n", host, droplocalhost);
+
+       if (fallbackMX != NULL)
+       {
+               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 && wordinclass(fallbackMX, 'w'))
+               {
+                       /* don't use fallback for this pass */
+                       fallbackMX = NULL;
+               }
+               firsttime = FALSE;
+       }
+
+       /* 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;
 
        errno = 0;
 
        errno = 0;
-       n = res_search(host, C_IN, T_MX, (char *)&answer, sizeof(answer));
+       n = res_search(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer));
        if (n < 0)
        {
                if (tTd(8, 1))
        if (n < 0)
        {
                if (tTd(8, 1))
@@ -63,23 +139,32 @@ getmxrr(host, mxhosts, localhost, rcode)
                switch (h_errno)
                {
                  case NO_DATA:
                switch (h_errno)
                {
                  case NO_DATA:
+                       trycanon = TRUE;
+                       /* fall through */
+
                  case NO_RECOVERY:
                        /* no MX data on this host */
                        goto punt;
 
                  case HOST_NOT_FOUND:
                  case NO_RECOVERY:
                        /* no MX data on this host */
                        goto punt;
 
                  case HOST_NOT_FOUND:
-                       /* the host just doesn't exist */
+#if BROKEN_RES_SEARCH
+                 case 0:       /* Ultrix resolver retns failure w/ h_errno=0 */
+#endif
+                       /* host doesn't exist in DNS; might be in /etc/hosts */
                        *rcode = EX_NOHOST;
                        *rcode = EX_NOHOST;
-                       break;
+                       goto punt;
 
                  case TRY_AGAIN:
                        /* couldn't connect to the name server */
 
                  case TRY_AGAIN:
                        /* couldn't connect to the name server */
-                       if (!UseNameServer && errno == ECONNREFUSED)
-                               goto punt;
-
                        /* it might come up later; better queue it up */
                        *rcode = EX_TEMPFAIL;
                        break;
                        /* it might come up later; better queue it up */
                        *rcode = EX_TEMPFAIL;
                        break;
+
+                 default:
+                       syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n",
+                               host, h_errno);
+                       *rcode = EX_OSERR;
+                       break;
                }
 
                /* irreconcilable differences */
                }
 
                /* irreconcilable differences */
@@ -88,24 +173,22 @@ getmxrr(host, mxhosts, localhost, rcode)
 
        /* find first satisfactory answer */
        hp = (HEADER *)&answer;
 
        /* find first satisfactory answer */
        hp = (HEADER *)&answer;
-       cp = (u_char *)&answer + sizeof(HEADER);
+       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)
                        goto punt;
        eom = (u_char *)&answer + n;
        for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ)
                if ((n = dn_skipname(cp, eom)) < 0)
                        goto punt;
-       nmx = 0;
-       seenlocal = 0;
-       buflen = sizeof(hostbuf) - 1;
-       bp = hostbuf;
+       buflen = sizeof(MXHostBuf) - 1;
+       bp = MXHostBuf;
        ancount = ntohs(hp->ancount);
        ancount = ntohs(hp->ancount);
-       while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS)
+       while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
        {
                if ((n = dn_expand((u_char *)&answer,
        {
                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);
                        break;
                cp += n;
                GETSHORT(type, cp);
-               cp += sizeof(u_short) + sizeof(u_long);
+               cp += INT16SZ + INT32SZ;
                GETSHORT(n, cp);
                if (type != T_MX)
                {
                GETSHORT(n, cp);
                if (type != T_MX)
                {
@@ -117,16 +200,21 @@ getmxrr(host, mxhosts, localhost, rcode)
                }
                GETSHORT(pref, cp);
                if ((n = dn_expand((u_char *)&answer, eom, cp,
                }
                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;
                        break;
                cp += n;
-               if (!strcasecmp(bp, localhost))
+               if (droplocalhost && wordinclass(bp, 'w'))
                {
                {
-                       if (seenlocal == 0 || pref < localpref)
+                       if (tTd(8, 3))
+                               printf("found localhost (%s) in MX list, pref=%d\n",
+                                       bp, pref);
+                       if (!seenlocal || pref < localpref)
                                localpref = pref;
                                localpref = pref;
-                       seenlocal = 1;
+                       seenlocal = TRUE;
                        continue;
                }
                        continue;
                }
+               if (fallbackMX != NULL && strcasecmp(bp, fallbackMX) == 0)
+                       fallbackMX = NULL;
                weight[nmx] = mxrand(bp);
                prefer[nmx] = pref;
                mxhosts[nmx++] = bp;
                weight[nmx] = mxrand(bp);
                prefer[nmx] = pref;
                mxhosts[nmx++] = bp;
@@ -140,11 +228,6 @@ getmxrr(host, mxhosts, localhost, rcode)
                *bp++ = '\0';
                buflen -= n + 1;
        }
                *bp++ = '\0';
                buflen -= n + 1;
        }
-       if (nmx == 0)
-       {
-punt:          mxhosts[0] = strcpy(hostbuf, host);
-               return (1);
-       }
 
        /* sort the records */
        for (i = 0; i < nmx; i++)
 
        /* sort the records */
        for (i = 0; i < nmx; i++)
@@ -169,21 +252,85 @@ punt:             mxhosts[0] = strcpy(hostbuf, host);
                        }
                }
                if (seenlocal && prefer[i] >= localpref)
                        }
                }
                if (seenlocal && prefer[i] >= localpref)
+               {
+                       /* truncate higher preference part of list */
+                       nmx = i;
+               }
+       }
+
+       if (nmx == 0)
+       {
+punt:
+               if (seenlocal &&
+                   (!TryNullMXList || sm_gethostbyname(host) == NULL))
                {
                        /*
                {
                        /*
-                        * truncate higher pref part of list; if we're
-                        * the best choice left, we should have realized
-                        * awhile ago that this was a local delivery.
-                        */
-                       if (i == 0)
+                       **  If we have deleted all MX entries, this is
+                       **  an error -- we should NEVER send to a host that
+                       **  has an MX, and this should have been caught
+                       **  earlier in the config file.
+                       **
+                       **  Some sites prefer to go ahead and try the
+                       **  A record anyway; that case is handled by
+                       **  setting TryNullMXList.  I believe this is a
+                       **  bad idea, but it's up to you....
+                       */
+
+                       *rcode = EX_CONFIG;
+                       syserr("MX list for %s points back to %s",
+                               host, MyHostName);
+                       return -1;
+               }
+               strcpy(MXHostBuf, host);
+               mxhosts[0] = MXHostBuf;
+               if (host[0] == '[')
+               {
+                       register char *p;
+
+                       /* this may be an MX suppression-style address */
+                       p = strchr(MXHostBuf, ']');
+                       if (p != NULL)
+                       {
+                               *p = '\0';
+                               if (inet_addr(&MXHostBuf[1]) != -1)
+                                       *p = ']';
+                               else
+                               {
+                                       trycanon = TRUE;
+                                       mxhosts[0]++;
+                               }
+                       }
+               }
+               n = strlen(MXHostBuf);
+               bp = &MXHostBuf[n];
+               buflen = sizeof MXHostBuf - n - 1;
+               if (trycanon &&
+                   getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE))
+               {
+                       if (bp[-1] != '.')
                        {
                        {
-                               *rcode = EX_CONFIG;
-                               return (-1);
+                               *bp++ = '.';
+                               *bp = '\0';
+                               buflen--;
                        }
                        }
-                       nmx = i;
-                       break;
                }
                }
+               bp++;
+               nmx = 1;
        }
        }
+
+       /* if we have a default lowest preference, include that */
+       if (fallbackMX != NULL && !seenlocal && strlen(fallbackMX) < buflen)
+       {
+               strcpy(bp, fallbackMX);
+               mxhosts[nmx++] = bp;
+               bp += strlen(bp);
+               if (bp[-1] != '.')
+               {
+                       *bp++ = '.';
+                       *bp = '\0';
+               }
+       }
+
        return (nmx);
 }
 \f/*
        return (nmx);
 }
 \f/*
@@ -227,7 +374,7 @@ mxrand(host)
 
                if (isascii(c) && isupper(c))
                        c = tolower(c);
 
                if (isascii(c) && isupper(c))
                        c = tolower(c);
-               hfunc = ((hfunc << 1) + c) % 2003;
+               hfunc = ((hfunc << 1) ^ c) % 2003;
        }
 
        hfunc &= 0xff;
        }
 
        hfunc &= 0xff;
@@ -237,7 +384,7 @@ mxrand(host)
        return hfunc;
 }
 \f/*
        return hfunc;
 }
 \f/*
-**  GETCANONNAME -- get the canonical name for named host
+**  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
 **
 **     This algorithm tries to be smart about wildcard MX records.
 **     This is hard to do because DNS doesn't tell is if we matched
@@ -258,6 +405,8 @@ mxrand(host)
 **             host -- a buffer containing the name of the host.
 **                     This is a value-result parameter.
 **             hbsize -- the size of the host buffer.
 **             host -- a buffer containing the name of the 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.
 **
 **     Returns:
 **             TRUE -- if the host matched.
@@ -265,9 +414,11 @@ mxrand(host)
 */
 
 bool
 */
 
 bool
-getcanonname(host, hbsize)
+dns_getcanonname(host, hbsize, trymx, statp)
        char *host;
        int hbsize;
        char *host;
        int hbsize;
+       bool trymx;
+       int *statp;
 {
        extern int h_errno;
        register u_char *eom, *ap;
 {
        extern int h_errno;
        register u_char *eom, *ap;
@@ -275,25 +426,29 @@ getcanonname(host, hbsize)
        register int n; 
        HEADER *hp;
        querybuf answer;
        register int n; 
        HEADER *hp;
        querybuf answer;
-       int first, ancount, qdcount;
+       int ancount, qdcount;
        int ret;
        char **domain;
        int type;
        char **dp;
        char *mxmatch;
        bool amatch;
        int ret;
        char **domain;
        int type;
        char **dp;
        char *mxmatch;
        bool amatch;
+       bool gotmx = FALSE;
+       int qtype;
+       int loopcnt;
+       char *xp;
        char nbuf[MAX(PACKETSZ, MAXDNAME*2+2)];
        char *searchlist[MAXDNSRCH+2];
        char nbuf[MAX(PACKETSZ, MAXDNAME*2+2)];
        char *searchlist[MAXDNSRCH+2];
+       extern char *gethostalias();
 
        if (tTd(8, 2))
                printf("getcanonname(%s)\n", host);
 
        if ((_res.options & RES_INIT) == 0 && res_init() == -1)
 
        if (tTd(8, 2))
                printf("getcanonname(%s)\n", host);
 
        if ((_res.options & RES_INIT) == 0 && res_init() == -1)
-               return (FALSE);
-
-       for (cp = host, n = 0; *cp; cp++)
-               if (*cp == '.')
-                       n++;
+       {
+               *statp = EX_UNAVAILABLE;
+               return FALSE;
+       }
 
        /*
        **  Initialize domain search list.  If there is at least one
 
        /*
        **  Initialize domain search list.  If there is at least one
@@ -305,49 +460,102 @@ getcanonname(host, hbsize)
        **  list by tearing apart the host name.
        */
 
        **  list by tearing apart the host name.
        */
 
+       loopcnt = 0;
+cnameloop:
+       for (cp = host, n = 0; *cp; cp++)
+               if (*cp == '.')
+                       n++;
+
+       if (n == 0 && (xp = gethostalias(host)) != NULL)
+       {
+               if (loopcnt++ > MAXCNAMEDEPTH)
+               {
+                       syserr("loop in ${HOSTALIASES} file");
+               }
+               else
+               {
+                       strncpy(host, xp, hbsize);
+                       host[hbsize - 1] = '\0';
+                       goto cnameloop;
+               }
+       }
+
        dp = searchlist;
        if (n > 0)
                *dp++ = "";
        dp = searchlist;
        if (n > 0)
                *dp++ = "";
-       if (n == 0 || n > 0 && *--cp != '.')
+       if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options))
        {
                for (domain = _res.dnsrch; *domain != NULL; )
                        *dp++ = *domain++;
        }
        {
                for (domain = _res.dnsrch; *domain != NULL; )
                        *dp++ = *domain++;
        }
+       else if (n == 0 && bitset(RES_DEFNAMES, _res.options))
+       {
+               *dp++ = _res.defdname;
+       }
+       else if (*cp == '.')
+       {
+               *cp = '\0';
+       }
        *dp = NULL;
 
        /*
        **  Now run through the search list for the name in question.
        */
 
        *dp = NULL;
 
        /*
        **  Now run through the search list for the name in question.
        */
 
-       dp = searchlist;
        mxmatch = NULL;
        mxmatch = NULL;
+       qtype = T_ANY;
 
 
-       for (dp = searchlist; *dp != NULL; dp++)
+       for (dp = searchlist; *dp != NULL; )
        {
        {
+               if (qtype == T_ANY)
+                       gotmx = FALSE;
                if (tTd(8, 5))
                if (tTd(8, 5))
-                       printf("getcanonname: trying %s.%s\n", host, *dp);
-               ret = res_querydomain(host, *dp, C_IN, T_ANY,
-                                     &answer, sizeof(answer));
+                       printf("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,
+                                     answer.qb2, sizeof(answer.qb2));
                if (ret <= 0)
                {
                if (ret <= 0)
                {
-                       if (tTd(8, 8))
-                               printf("\tNO: h_errno=%d\n", h_errno);
+                       if (tTd(8, 7))
+                               printf("\tNO: errno=%d, h_errno=%d\n",
+                                       errno, h_errno);
 
 
-                       if (errno == ECONNREFUSED)
+                       if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
                        {
                                /* the name server seems to be down */
                                h_errno = TRY_AGAIN;
                        {
                                /* the name server seems to be down */
                                h_errno = TRY_AGAIN;
+                               *statp = EX_TEMPFAIL;
                                return FALSE;
                        }
 
                                return FALSE;
                        }
 
+                       if (h_errno != HOST_NOT_FOUND)
+                       {
+                               /* might have another type of interest */
+                               if (qtype == T_ANY)
+                               {
+                                       qtype = T_A;
+                                       continue;
+                               }
+                               else if (qtype == T_A && !gotmx && trymx)
+                               {
+                                       qtype = T_MX;
+                                       continue;
+                               }
+                       }
+
                        if (mxmatch != NULL)
                        {
                                /* we matched before -- use that one */
                                break;
                        }
                        if (mxmatch != NULL)
                        {
                                /* we matched before -- use that one */
                                break;
                        }
+
+                       /* otherwise, try the next name */
+                       dp++;
+                       qtype = T_ANY;
                        continue;
                }
                        continue;
                }
-               if (tTd(8, 8))
+               else if (tTd(8, 7))
                        printf("\tYES\n");
 
                /*
                        printf("\tYES\n");
 
                /*
@@ -357,7 +565,7 @@ getcanonname(host, hbsize)
                */
 
                hp = (HEADER *) &answer;
                */
 
                hp = (HEADER *) &answer;
-               ap = (u_char *) &answer + sizeof(HEADER);
+               ap = (u_char *) &answer + HFIXEDSZ;
                eom = (u_char *) &answer + ret;
 
                /* skip question part of response -- we know what we asked */
                eom = (u_char *) &answer + ret;
 
                /* skip question part of response -- we know what we asked */
@@ -368,6 +576,7 @@ getcanonname(host, hbsize)
                                if (tTd(8, 20))
                                        printf("qdcount failure (%d)\n",
                                                ntohs(hp->qdcount));
                                if (tTd(8, 20))
                                        printf("qdcount failure (%d)\n",
                                                ntohs(hp->qdcount));
+                               *statp = EX_SOFTWARE;
                                return FALSE;           /* ???XXX??? */
                        }
                }
                                return FALSE;           /* ???XXX??? */
                        }
                }
@@ -376,20 +585,21 @@ getcanonname(host, hbsize)
                for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n)
                {
                        n = dn_expand((u_char *) &answer, eom, ap,
                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);
                        if (n < 0)
                                break;
                        ap += n;
                        GETSHORT(type, ap);
-                       ap += sizeof(u_short) + sizeof(u_long);
+                       ap += INT16SZ + INT32SZ;
                        GETSHORT(n, ap);
                        switch (type)
                        {
                          case T_MX:
                        GETSHORT(n, ap);
                        switch (type)
                        {
                          case T_MX:
+                               gotmx = TRUE;
                                if (**dp != '\0')
                                {
                                        /* got a match -- save that info */
                                if (**dp != '\0')
                                {
                                        /* got a match -- save that info */
-                                       if (mxmatch == NULL)
+                                       if (trymx && mxmatch == NULL)
                                                mxmatch = *dp;
                                        continue;
                                }
                                                mxmatch = *dp;
                                        continue;
                                }
@@ -405,13 +615,38 @@ getcanonname(host, hbsize)
                                continue;
 
                          case T_CNAME:
                                continue;
 
                          case T_CNAME:
+                               if (loopcnt++ > MAXCNAMEDEPTH)
+                               {
+                                       /*XXX should notify postmaster XXX*/
+                                       message("DNS failure: CNAME loop for %s",
+                                               host);
+                                       if (CurEnv->e_message == NULL)
+                                       {
+                                               char ebuf[MAXLINE];
+
+                                               sprintf(ebuf, "Deferred: DNS failure: CNAME loop for %s",
+                                                       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,
                                /* 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';
                                        break;
                                (void)strncpy(host, nbuf, hbsize); /* XXX */
                                host[hbsize - 1] = '\0';
-                               return TRUE;
+
+                               /*
+                               **  RFC 1034 section 3.6 specifies that CNAME
+                               **  should point at the canonical name -- but
+                               **  urges software to try again anyway.
+                               */
+
+                               goto cnameloop;
 
                          default:
                                /* not a record of interest */
 
                          default:
                                /* not a record of interest */
@@ -425,10 +660,29 @@ getcanonname(host, hbsize)
                        mxmatch = *dp;
                        break;
                }
                        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.
+               */
+
+               if (qtype == T_ANY)
+                       qtype = T_A;
+               else if (qtype == T_A && !gotmx && trymx)
+                       qtype = T_MX;
+               else
+               {
+                       /* really nothing in this domain; try the next */
+                       qtype = T_ANY;
+                       dp++;
+               }
        }
 
        if (mxmatch == NULL)
        }
 
        if (mxmatch == NULL)
+       {
+               *statp = EX_NOHOST;
                return FALSE;
                return FALSE;
+       }
 
        /* create matching name and return */
        (void) sprintf(nbuf, "%.*s%s%.*s", MAXDNAME, host,
 
        /* create matching name and return */
        (void) sprintf(nbuf, "%.*s%s%.*s", MAXDNAME, host,
@@ -436,29 +690,181 @@ getcanonname(host, hbsize)
                        MAXDNAME, mxmatch);
        strncpy(host, nbuf, hbsize);
        host[hbsize - 1] = '\0';
                        MAXDNAME, mxmatch);
        strncpy(host, nbuf, hbsize);
        host[hbsize - 1] = '\0';
+       *statp = EX_OK;
        return TRUE;
 }
 
        return TRUE;
 }
 
-#else /* not NAMED_BIND */
 
 
-#include <netdb.h>
-
-bool
-getcanonname(host, hbsize)
+char *
+gethostalias(host)
        char *host;
        char *host;
-       int hbsize;
 {
 {
-       struct hostent *hp;
-
-       hp = gethostbyname(host);
-       if (hp == NULL)
-               return (FALSE);
+       char *fname;
+       FILE *fp;
+       register char *p = NULL;
+       char buf[MAXLINE];
+       static char hbuf[MAXDNAME];
+
+       fname = getenv("HOSTALIASES");
+       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++)
+                       continue;
+               if (*p == 0)
+               {
+                       /* syntax error */
+                       continue;
+               }
+               *p++ = '\0';
+               if (strcasecmp(buf, host) == 0)
+                       break;
+       }
 
 
-       if (strlen(hp->h_name) >= hbsize)
-               return (FALSE);
+       if (feof(fp))
+       {
+               /* no match */
+               fclose(fp);
+               return NULL;
+       }
 
 
-       (void) strcpy(host, hp->h_name);
-       return (TRUE);
+       /* got a match; extract the equivalent name */
+       while (*p != '\0' && isascii(*p) && isspace(*p))
+               p++;
+       host = p;
+       while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+               p++;
+       *p = '\0';
+       strncpy(hbuf, host, sizeof hbuf - 1);
+       hbuf[sizeof hbuf - 1] = '\0';
+       return hbuf;
 }
 
 }
 
-#endif /* not NAMED_BIND */
+\f/*
+**  MAILB_LOOKUP -- do DNS mailbox lookup
+*/
+
+#ifdef DNS_MAILB
+
+mailb_lookup(addr)
+       char *addr;
+{
+       /*
+       **  Convert addr to DNS form (user.host).
+       */
+
+       /* figure out how much space it needs */
+       atp = strchr(addr, '@');
+       if (atp == NULL)
+               atp = &addr(strlen(addr));
+       i = strlen(addr);
+       for (p = addr; (p = strchr(p, '.')) != NULL; p++)
+       {
+               if (p > atp)
+                       break;
+               i++;
+       }
+       if (i < sizeof abuf)
+               bufp = abuf;
+       else
+               bufp = xalloc(i + 1);
+
+       lhsmode = TRUE;
+       for (p = addr, q = bufp; (c = *p++) != '\0'; )
+       {
+               if (c == '.' && lhsmode)
+                       *q++ = '\\';
+               if (c == '@')
+                       lhsmode = FALSE;
+               *q++ = c;
+       }
+       *q = '\0';
+
+       /*
+       **  Now do a MAILB lookup.
+       */
+
+retry:
+       if (res_query(bufp, C_IN, T_MAILB, (char *) &answer, sizeof answer < 0)
+       {
+               /* no match -- just continue as usual */
+               return FALSE;
+       }
+
+       /* find first satisfactory answer */
+       hp = (HEADER *)&answer;
+       ap = (u_char *)&answer + sizeof(HEADER);
+       eom = (u_char *)&answer + n;
+       for (qdcount = ntohs(hp->qdcount); qdcount--; ap += n + QFIXEDSZ)
+               if ((n = dn_skipname(ap, eom)) < 0)
+                       return FALSE;
+       for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n)
+       {
+               n = dn_expand((u_char *)&answer, eom, ap, (u_char *)bp, buflen);
+               if (n < 0)
+                       break;
+               ap += n;
+               GETSHORT(type, ap);
+               ap += SHORTSIZE + LONGSIZE;
+               GETSHORT(n, ap);
+               switch (type)
+               {
+                 case T_MR:
+                       /* rename: try again */
+                       i = dn_expand((u_char *) &answer, eom, ap,
+                                       (u_char) abuf, sizeof abuf);
+                       if (i < 0)
+                               break;
+                       if (bufp != abuf)
+                       {
+                               free(bufp);
+                               bufp = abuf;
+                       }
+                       goto retry;
+
+                 case T_MB:
+                       i = dn_expand((u_char *) &answer, eom, ap,
+                                       (u_char) hbuf, sizeof hbuf);
+                       if (i < 0)
+                               break;
+
+                       /* hbuf now has the host to deliver to */
+                       break;
+
+                 case T_MG:
+                       i = dn_expand((u_char *) &answer, eom, ap,
+                                       (u_char) gbuf, sizeof gbuf);
+                       if (i < 0)
+                               break;
+                       AliasLevel++;
+                       naddrs += sendtolist(ubuf, a, sendq, e);
+                       AliasLevel--;
+                       break;
+
+                 case T_MINFO:
+                       /* bleach */
+                       XXX;
+               }
+
+
+
+               if (type != T_MX)
+               {
+                       if (tTd(8, 8) || _res.options & RES_DEBUG)
+                               printf("unexpected answer type %d, size %d\n",
+                                   type, n);
+                       cp += n;
+                       continue;
+               }
+               GETSHORT(pref, cp);
+               if ((n = dn_expand((u_char *)&answer, eom, cp,
+                                  (u_char *)bp, buflen)) < 0)
+                       break;
+               cp += n;
+
+
+#endif /* DNS_MAILB */
+#endif /* NAMED_BIND */