BSD 4_4_Lite1 release
[unix-history] / usr / src / usr.sbin / sendmail / src / udb.c
index 28b2671..7887cb3 100644 (file)
@@ -1,31 +1,59 @@
 /*
  * Copyright (c) 1983 Eric P. Allman
 /*
  * Copyright (c) 1983 Eric P. Allman
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  *
- * %sccs.include.redist.c%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
  */
 
+#include "sendmail.h"
+
 #ifndef lint
 #ifdef USERDB
 #ifndef lint
 #ifdef USERDB
-static char sccsid [] = "@(#)udb.c     5.5 (Berkeley) %G% (with USERDB)";
+static char sccsid [] = "@(#)udb.c     8.8 (Berkeley) 4/14/94 (with USERDB)";
 #else
 #else
-static char sccsid [] = "@(#)udb.c     5.5 (Berkeley) %G% (without USERDB)";
+static char sccsid [] = "@(#)udb.c     8.8 (Berkeley) 4/14/94 (without USERDB)";
 #endif
 #endif
 
 #endif
 #endif
 
-#include "sendmail.h"
-
 #ifdef USERDB
 
 #ifdef USERDB
 
-#include <sys/file.h>
-#include <sys/time.h>
-#include <fcntl.h>
+#include <errno.h>
 #include <netdb.h>
 #include <db.h>
 
 #include <netdb.h>
 #include <db.h>
 
+#ifdef HESIOD
+#include <hesiod.h>
+#endif /* HESIOD */
+
 /*
 /*
-**  UDBEXPAND.C -- interface between sendmail and Berkeley User Data Base.
+**  UDB.C -- interface between sendmail and Berkeley User Data Base.
 **
 **     This depends on the 4.4BSD db package.
 */
 **
 **     This depends on the 4.4BSD db package.
 */
@@ -35,6 +63,7 @@ struct udbent
 {
        char    *udb_spec;              /* string version of spec */
        int     udb_type;               /* type of entry */
 {
        char    *udb_spec;              /* string version of spec */
        int     udb_type;               /* type of entry */
+       char    *udb_default;           /* default host for outgoing mail */
        union
        {
                /* type UE_REMOTE -- do remote call for lookup */
        union
        {
                /* type UE_REMOTE -- do remote call for lookup */
@@ -53,7 +82,7 @@ struct udbent
                } udb_forward;
 #define udb_fwdhost    udb_u.udb_forward._udb_fwdhost
 
                } udb_forward;
 #define udb_fwdhost    udb_u.udb_forward._udb_fwdhost
 
-               /* type UE_LOOKUP -- lookup in local database */
+               /* type UE_FETCH -- lookup in local database */
                struct
                {
                        char    *_udb_dbname;   /* pathname of database */
                struct
                {
                        char    *_udb_dbname;   /* pathname of database */
@@ -67,8 +96,9 @@ struct udbent
 #define UDB_EOLIST     0       /* end of list */
 #define UDB_SKIP       1       /* skip this entry */
 #define UDB_REMOTE     2       /* look up in remote database */
 #define UDB_EOLIST     0       /* end of list */
 #define UDB_SKIP       1       /* skip this entry */
 #define UDB_REMOTE     2       /* look up in remote database */
-#define UDB_LOOKUP     3       /* look up in local database */
+#define UDB_DBFETCH    3       /* look up in local database */
 #define UDB_FORWARD    4       /* forward to remote host */
 #define UDB_FORWARD    4       /* forward to remote host */
+#define UDB_HESIOD     5       /* look up via hesiod */
 
 #define MAXUDBENT      10      /* maximum number of UDB entries */
 
 
 #define MAXUDBENT      10      /* maximum number of UDB entries */
 
@@ -86,7 +116,9 @@ struct option
 **             sendq -- pointer to head of sendq to put the expansions in.
 **
 **     Returns:
 **             sendq -- pointer to head of sendq to put the expansions in.
 **
 **     Returns:
-**             none.
+**             EX_TEMPFAIL -- if something "odd" happened -- probably due
+**                     to accessing a file on an NFS server that is down.
+**             EX_OK -- otherwise.
 **
 **     Side Effects:
 **             Modifies sendq.
 **
 **     Side Effects:
 **             Modifies sendq.
@@ -97,48 +129,57 @@ int        UdbTimeout = 10;
 
 struct udbent  UdbEnts[MAXUDBENT + 1];
 int            UdbSock = -1;
 
 struct udbent  UdbEnts[MAXUDBENT + 1];
 int            UdbSock = -1;
+bool           UdbInitialized = FALSE;
 
 
-void
-udbexpand(a, sendq)
+int
+udbexpand(a, sendq, e)
        register ADDRESS *a;
        ADDRESS **sendq;
        register ADDRESS *a;
        ADDRESS **sendq;
+       register ENVELOPE *e;
 {
        int i;
        register char *p;
        DBT key;
        DBT info;
 {
        int i;
        register char *p;
        DBT key;
        DBT info;
-       static bool firstcall = TRUE;
        bool breakout;
        register struct udbent *up;
        int keylen;
        bool breakout;
        register struct udbent *up;
        int keylen;
-       char keybuf[128];
-       char buf[8192];
+       int naddrs;
+       char keybuf[MAXKEY];
+       char buf[BUFSIZ];
 
        if (tTd(28, 1))
 
        if (tTd(28, 1))
-               printf("expand(%s)\n", a->q_paddr);
+               printf("udbexpand(%s)\n", a->q_paddr);
 
        /* make certain we are supposed to send to this address */
 
        /* make certain we are supposed to send to this address */
-       if (bitset(QDONTSEND, a->q_flags) ||
-           UdbSpec == NULL || UdbSpec[0] == '\0')
-               return;
-       CurEnv->e_to = a->q_paddr;
+       if (bitset(QDONTSEND|QVERIFIED, a->q_flags))
+               return EX_OK;
+       e->e_to = a->q_paddr;
 
        /* on first call, locate the database */
 
        /* on first call, locate the database */
-       if (firstcall)
+       if (!UdbInitialized)
        {
        {
-               extern void _udbx_init();
+               extern int _udbx_init();
 
 
-               _udbx_init();
-               firstcall = FALSE;
+               if (_udbx_init() == EX_TEMPFAIL)
+                       return EX_TEMPFAIL;
        }
 
        }
 
+       /* short circuit the process if no chance of a match */
+       if (UdbSpec == NULL || UdbSpec[0] == '\0')
+               return EX_OK;
+
+       /* short circuit name begins with '\\' since it can't possibly match */
+       if (a->q_user[0] == '\\')
+               return EX_OK;
+
        /* if name is too long, assume it won't match */
        if (strlen(a->q_user) > sizeof keybuf - 12)
        /* if name is too long, assume it won't match */
        if (strlen(a->q_user) > sizeof keybuf - 12)
-               return;
+               return EX_OK;
 
        /* if name begins with a colon, it indicates our metadata */
        if (a->q_user[0] == ':')
 
        /* if name begins with a colon, it indicates our metadata */
        if (a->q_user[0] == ':')
-               return;
+               return EX_OK;
 
        /* build actual database key */
        (void) strcpy(keybuf, a->q_user);
 
        /* build actual database key */
        (void) strcpy(keybuf, a->q_user);
@@ -149,8 +190,6 @@ udbexpand(a, sendq)
        for (up = UdbEnts; !breakout; up++)
        {
                char *user;
        for (up = UdbEnts; !breakout; up++)
        {
                char *user;
-               struct timeval timeout;
-               fd_set fdset;
 
                /*
                **  Select action based on entry type.
 
                /*
                **  Select action based on entry type.
@@ -162,23 +201,37 @@ udbexpand(a, sendq)
 
                switch (up->udb_type)
                {
 
                switch (up->udb_type)
                {
-                 case UDB_LOOKUP:
+                 case UDB_DBFETCH:
                        key.data = keybuf;
                        key.size = keylen;
                        key.data = keybuf;
                        key.size = keylen;
+                       if (tTd(28, 80))
+                               printf("udbexpand: trying %s (%d) via db\n",
+                                       keybuf, keylen);
                        i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
                        i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
-                       if (i != 0 || info.size <= 0)
+                       if (i > 0 || info.size <= 0)
                        {
                        {
-                               if (i < 0)
-                                       syserr("udbexpand: db-get stat %s");
                                if (tTd(28, 2))
                                if (tTd(28, 2))
-                                       printf("expand: no match on %s\n", keybuf);
+                                       printf("udbexpand: no match on %s (%d)\n",
+                                               keybuf, keylen);
                                continue;
                        }
                                continue;
                        }
-
-                       /* there is at least one match -- start processing */
-                       breakout = TRUE;
-                       do
+                       if (tTd(28, 80))
+                               printf("udbexpand: match %.*s: %.*s\n",
+                                       key.size, key.data, info.size, info.data);
+
+                       naddrs = 0;
+                       a->q_flags &= ~QSELFREF;
+                       while (i == 0 && key.size == keylen &&
+                                       bcmp(key.data, keybuf, keylen) == 0)
                        {
                        {
+                               if (bitset(EF_VRFYONLY, e->e_flags))
+                               {
+                                       a->q_flags |= QVERIFIED;
+                                       e->e_nrcpts++;
+                                       return EX_OK;
+                               }
+
+                               breakout = TRUE;
                                if (info.size < sizeof buf)
                                        user = buf;
                                else
                                if (info.size < sizeof buf)
                                        user = buf;
                                else
@@ -186,9 +239,14 @@ udbexpand(a, sendq)
                                bcopy(info.data, user, info.size);
                                user[info.size] = '\0';
 
                                bcopy(info.data, user, info.size);
                                user[info.size] = '\0';
 
-                               message(Arpa_Info, "expanded to %s", user);
+                               message("expanded to %s", user);
+#ifdef LOG
+                               if (LogLevel >= 10)
+                                       syslog(LOG_INFO, "%s: expand %s => %s",
+                                               e->e_id, e->e_to, user);
+#endif
                                AliasLevel++;
                                AliasLevel++;
-                               sendtolist(user, a, sendq);
+                               naddrs += sendtolist(user, a, sendq, e);
                                AliasLevel--;
 
                                if (user != buf)
                                AliasLevel--;
 
                                if (user != buf)
@@ -196,62 +254,169 @@ udbexpand(a, sendq)
 
                                /* get the next record */
                                i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
 
                                /* get the next record */
                                i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
-                       } while (i == 0 && key.size == keylen &&
-                                       bcmp(key.data, keybuf, keylen) == 0);
+                       }
+
+                       /* if nothing ever matched, try next database */
+                       if (!breakout)
+                               continue;
+
+                       if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
+                       {
+                               if (tTd(28, 5))
+                               {
+                                       printf("udbexpand: QDONTSEND ");
+                                       printaddr(a, FALSE);
+                               }
+                               a->q_flags |= QDONTSEND;
+                       }
+                       if (i < 0)
+                       {
+                               syserr("udbexpand: db-get %.*s stat %d",
+                                       key.size, key.data, i);
+                               return EX_TEMPFAIL;
+                       }
+
+                       /*
+                       **  If this address has a -request address, reflect
+                       **  it into the envelope.
+                       */
+
+                       (void) strcpy(keybuf, a->q_user);
+                       (void) strcat(keybuf, ":mailsender");
+                       keylen = strlen(keybuf);
+                       key.data = keybuf;
+                       key.size = keylen;
+                       i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
+                       if (i != 0 || info.size <= 0)
+                               break;
+                       a->q_owner = xalloc(info.size + 1);
+                       bcopy(info.data, a->q_owner, info.size);
+                       a->q_owner[info.size] = '\0';
+
+                       /* announce delivery; NORECEIPT bit set later */
+                       if (e->e_xfp != NULL)
+                       {
+                               fprintf(e->e_xfp,
+                                       "Message delivered to mailing list %s\n",
+                                       a->q_paddr);
+                               e->e_flags |= EF_SENDRECEIPT;
+                       }
                        break;
 
                        break;
 
-                 case UDB_REMOTE:
-                       if (sendto(UdbSock, keybuf, keylen, 0,
-                                  (struct sockaddr *) &up->udb_addr,
-                                  sizeof up->udb_addr) < 0)
+#ifdef HESIOD
+                 case UDB_HESIOD:
+                       key.data = keybuf;
+                       key.size = keylen;
+                       if (tTd(28, 80))
+                               printf("udbexpand: trying %s (%d) via hesiod\n",
+                                       keybuf, keylen);
+                       /* look up the key via hesiod */
+                       i = hes_udb_get(&key, &info);
+                       if (i > 0 || info.size <= 0)
                        {
                        {
+                               if (tTd(28, 2))
+                               printf("udbexpand: no match on %s (%d)\n",
+                                       keybuf, keylen);
                                continue;
                        }
                                continue;
                        }
-                       timeout.tv_sec = up->udb_timeout / 10;
-                       timeout.tv_usec = (up->udb_timeout % 10) * 100000;
-                       do
+                       if (tTd(28, 80))
+                               printf("udbexpand: match %.*s: %.*s\n",
+                                       key.size, key.data, info.size, info.data);
+                       a->q_flags &= ~QSELFREF;
+
+                       if (bitset(EF_VRFYONLY, e->e_flags))
                        {
                        {
-                               FD_ZERO(&fdset);
-                               FD_SET(UdbSock, &fdset);
-                               i = select(FD_SETSIZE, &fdset, NULL, NULL, &timeout);
-                       } while (i > 0 && !FD_ISSET(UdbSock, &fdset));
-                       if (i <= 0)
-                               continue;
-                       i = recvfrom(UdbSock, buf, sizeof buf - 1, 0, NULL, NULL);
-                       if (i < 0)
-                               continue;
-                       if (buf[0] != ' ' && buf[0] != '-')
-                               continue;
+                               a->q_flags |= QVERIFIED;
+                               e->e_nrcpts++;
+                               free(info.data);
+                               return EX_OK;
+                       }
+
                        breakout = TRUE;
                        breakout = TRUE;
-                       while (buf[0] == ' ' || buf[0] == '-')
-                       {
-                               user = &buf[1];
-                               buf[i] = '\0';
-                               message(Arpa_Info, "expanded to %s", user);
-                               AliasLevel++;
-                               sendtolist(user, a, sendq);
-                               AliasLevel--;
+                       if (info.size < sizeof buf)
+                               user = buf;
+                       else
+                               user = xalloc(info.size + 1);
+                       bcopy(info.data, user, info.size);
+                       user[info.size] = '\0';
+                       free(info.data);
+
+                       message("hesioded to %s", user);
+#ifdef LOG
+                       if (LogLevel >= 10)
+                               syslog(LOG_INFO, "%s: hesiod %s => %s",
+                                       e->e_id, e->e_to, user);
+#endif
+                       AliasLevel++;
+                       naddrs = sendtolist(user, a, sendq, e);
+                       AliasLevel--;
 
 
-                               /* try for next record */
-                               if (buf[0] == ' ')
-                                       break;
-                               i = recvfrom(UdbSock, buf, sizeof buf - 1, 0, NULL, NULL);
-                               if (i < 0)
-                                       break;
+                       if (user != buf)
+                               free(user);
+
+                       if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
+                       {
+                               if (tTd(28, 5))
+                               {
+                                       printf("udbexpand: QDONTSEND ");
+                                       printaddr(a, FALSE);
+                               }
+                               a->q_flags |= QDONTSEND;
+                       }
+                       if (i < 0)
+                       {
+                               syserr("udbexpand: hesiod-get %.*s stat %d",
+                                       key.size, key.data, i);
+                               return EX_TEMPFAIL;
                        }
                        }
+
+                       /*
+                       **  If this address has a -request address, reflect
+                       **  it into the envelope.
+                       */
+
+                       (void) strcpy(keybuf, a->q_user);
+                       (void) strcat(keybuf, ":mailsender");
+                       keylen = strlen(keybuf);
+                       key.data = keybuf;
+                       key.size = keylen;
+                       i = hes_udb_get(&key, &info);
+                       if (i != 0 || info.size <= 0)
+                               break;
+                       a->q_owner = xalloc(info.size + 1);
+                       bcopy(info.data, a->q_owner, info.size);
+                       a->q_owner[info.size] = '\0';
+                       free(info.data);
                        break;
                        break;
+#endif /* HESIOD */
+
+                 case UDB_REMOTE:
+                       /* not yet implemented */
+                       continue;
 
                  case UDB_FORWARD:
 
                  case UDB_FORWARD:
+                       if (bitset(EF_VRFYONLY, e->e_flags))
+                               return EX_OK;
                        i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1;
                        if (i < sizeof buf)
                                user = buf;
                        else
                                user = xalloc(i + 1);
                        (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost);
                        i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1;
                        if (i < sizeof buf)
                                user = buf;
                        else
                                user = xalloc(i + 1);
                        (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost);
-                       message(Arpa_Info, "expanded to %s", user);
+                       message("expanded to %s", user);
+                       a->q_flags &= ~QSELFREF;
                        AliasLevel++;
                        AliasLevel++;
-                       sendtolist(user, a, sendq);
+                       naddrs = sendtolist(user, a, sendq, e);
                        AliasLevel--;
                        AliasLevel--;
+                       if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
+                       {
+                               if (tTd(28, 5))
+                               {
+                                       printf("udbexpand: QDONTSEND ");
+                                       printaddr(a, FALSE);
+                               }
+                               a->q_flags |= QDONTSEND;
+                       }
                        if (user != buf)
                                free(user);
                        breakout = TRUE;
                        if (user != buf)
                                free(user);
                        breakout = TRUE;
@@ -266,21 +431,278 @@ udbexpand(a, sendq)
                        continue;
                }
        }
                        continue;
                }
        }
+       return EX_OK;
 }
 }
+\f/*
+**  UDBSENDER -- return canonical external name of sender, given local name
+**
+**     Parameters:
+**             sender -- the name of the sender on the local machine.
+**
+**     Returns:
+**             The external name for this sender, if derivable from the
+**                     database.
+**             NULL -- if nothing is changed from the database.
+**
+**     Side Effects:
+**             none.
+*/
+
+char *
+udbsender(sender)
+       char *sender;
+{
+       extern char *udbmatch();
+
+       return udbmatch(sender, "mailname");
+}
+
+
+char *
+udbmatch(user, field)
+       char *user;
+       char *field;
+{
+       register char *p;
+       register struct udbent *up;
+       int i;
+       int keylen;
+       DBT key, info;
+       char keybuf[MAXKEY];
+
+       if (tTd(28, 1))
+               printf("udbmatch(%s, %s)\n", user, field);
+
+       if (!UdbInitialized)
+       {
+               if (_udbx_init() == EX_TEMPFAIL)
+                       return NULL;
+       }
+
+       /* short circuit if no spec */
+       if (UdbSpec == NULL || UdbSpec[0] == '\0')
+               return NULL;
+
+       /* short circuit name begins with '\\' since it can't possibly match */
+       if (user[0] == '\\')
+               return NULL;
+
+       /* long names can never match and are a pain to deal with */
+       if ((strlen(user) + strlen(field)) > sizeof keybuf - 4)
+               return NULL;
+
+       /* names beginning with colons indicate metadata */
+       if (user[0] == ':')
+               return NULL;
+
+       /* build database key */
+       (void) strcpy(keybuf, user);
+       (void) strcat(keybuf, ":");
+       (void) strcat(keybuf, field);
+       keylen = strlen(keybuf);
+
+       for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
+       {
+               /*
+               **  Select action based on entry type.
+               */
+
+               switch (up->udb_type)
+               {
+                 case UDB_DBFETCH:
+                       key.data = keybuf;
+                       key.size = keylen;
+                       i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
+                       if (i != 0 || info.size <= 0)
+                       {
+                               if (tTd(28, 2))
+                                       printf("udbmatch: no match on %s (%d) via db\n",
+                                                       keybuf, keylen);
+                               continue;
+                       }
+
+                       p = xalloc(info.size + 1);
+                       bcopy(info.data, p, info.size);
+                       p[info.size] = '\0';
+                       if (tTd(28, 1))
+                               printf("udbmatch ==> %s\n", p);
+                       return p;
+                       break;
+
+#ifdef HESIOD
+                 case UDB_HESIOD:
+                       key.data = keybuf;
+                       key.size = keylen;
+                       i = hes_udb_get(&key, &info); 
+                       if (i != 0 || info.size <= 0)
+                       {
+                               if (tTd(28, 2))
+                                       printf("udbmatch: no match on %s (%d) via hesiod\n",
+                                                       keybuf, keylen);
+                               continue;
+                       }
+
+                       p = xalloc(info.size + 1);
+                       bcopy(info.data, p, info.size);
+                       p[info.size] = '\0';
+                       free(info.data);
+                       if (tTd(28, 1))
+                               printf("udbmatch ==> %s\n", p);
+                       return p;
+                       break;
+#endif /* HESIOD */
+               }
+       }
+
+       if (strcmp(field, "mailname") != 0)
+               return NULL;
+
+       /*
+       **  Nothing yet.  Search again for a default case.  But only
+       **  use it if we also have a forward (:maildrop) pointer already
+       **  in the database.
+       */
+
+       /* build database key */
+       (void) strcpy(keybuf, user);
+       (void) strcat(keybuf, ":maildrop");
+       keylen = strlen(keybuf);
+
+       for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
+       {
+               switch (up->udb_type)
+               {
+                 case UDB_DBFETCH:
+                       /* get the default case for this database */
+                       if (up->udb_default == NULL)
+                       {
+                               key.data = ":default:mailname";
+                               key.size = strlen(key.data);
+                               i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
+                               if (i != 0 || info.size <= 0)
+                               {
+                                       /* no default case */
+                                       up->udb_default = "";
+                                       continue;
+                               }
+
+                               /* save the default case */
+                               up->udb_default = xalloc(info.size + 1);
+                               bcopy(info.data, up->udb_default, info.size);
+                               up->udb_default[info.size] = '\0';
+                       }
+                       else if (up->udb_default[0] == '\0')
+                               continue;
+
+                       /* we have a default case -- verify user:maildrop */
+                       key.data = keybuf;
+                       key.size = keylen;
+                       i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
+                       if (i != 0 || info.size <= 0)
+                       {
+                               /* nope -- no aliasing for this user */
+                               continue;
+                       }
+
+                       /* they exist -- build the actual address */
+                       p = xalloc(strlen(user) + strlen(up->udb_default) + 2);
+                       (void) strcpy(p, user);
+                       (void) strcat(p, "@");
+                       (void) strcat(p, up->udb_default);
+                       if (tTd(28, 1))
+                               printf("udbmatch ==> %s\n", p);
+                       return p;
+                       break;
+
+#ifdef HESIOD
+                 case UDB_HESIOD:
+                       /* get the default case for this database */
+                       if (up->udb_default == NULL)
+                       {
+                               key.data = ":default:mailname";
+                               key.size = strlen(key.data);
+                               i = hes_udb_get(&key, &info); 
+
+                               if (i != 0 || info.size <= 0)
+                               {
+                                       /* no default case */
+                                       up->udb_default = "";
+                                       continue;
+                               }
+
+                               /* save the default case */
+                               up->udb_default = xalloc(info.size + 1);
+                               bcopy(info.data, up->udb_default, info.size);
+                               up->udb_default[info.size] = '\0';
+                               free(info.data);
+                       }
+                       else if (up->udb_default[0] == '\0')
+                               continue;
+
+                       /* we have a default case -- verify user:maildrop */
+                       key.data = keybuf;
+                       key.size = keylen;
+                       i = hes_udb_get(&key, &info);
+                       if (i != 0 || info.size <= 0)
+                       {
+                               /* nope -- no aliasing for this user */
+                               continue;
+                       }
+
+                       free(info.data);
+                       /* they exist -- build the actual address */
+                       p = xalloc(strlen(user) + strlen(up->udb_default) + 2);
+                       (void) strcpy(p, user);
+                       (void) strcat(p, "@");
+                       (void) strcat(p, up->udb_default);
+                       if (tTd(28, 1))
+                               printf("udbmatch ==> %s\n", p);
+                       return p;
+                       break;
+#endif /* HESIOD */
+               }
+       }
+
+       /* still nothing....  too bad */
+       return NULL;
+}
+\f/*
+**  _UDBX_INIT -- parse the UDB specification, opening any valid entries.
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             EX_TEMPFAIL -- if it appeared it couldn't get hold of a
+**                     database due to a host being down or some similar
+**                     (recoverable) situation.
+**             EX_OK -- otherwise.
+**
+**     Side Effects:
+**             Fills in the UdbEnts structure from UdbSpec.
+*/
 
 #define MAXUDBOPTS     27
 
 
 #define MAXUDBOPTS     27
 
-void
+int
 _udbx_init()
 {
        register char *p;
        int i;
        register struct udbent *up;
 _udbx_init()
 {
        register char *p;
        int i;
        register struct udbent *up;
-       char buf[8192];
+       char buf[BUFSIZ];
+
+       if (UdbInitialized)
+               return EX_OK;
+
+# ifdef UDB_DEFAULT_SPEC
+       if (UdbSpec == NULL)
+               UdbSpec = UDB_DEFAULT_SPEC;
+# endif
 
        p = UdbSpec;
        up = UdbEnts;
 
        p = UdbSpec;
        up = UdbEnts;
-       for (;;)
+       while (p != NULL)
        {
                char *spec;
                auto int rcode;
        {
                char *spec;
                auto int rcode;
@@ -295,8 +717,8 @@ _udbx_init()
                if (*p == '\0')
                        break;
                spec = p;
                if (*p == '\0')
                        break;
                spec = p;
-               p = index(p, ',');
-               if (*p != '\0')
+               p = strchr(p, ',');
+               if (p != NULL)
                        *p++ = '\0';
 
                /* extract options */
                        *p++ = '\0';
 
                /* extract options */
@@ -329,7 +751,13 @@ _udbx_init()
                  case '*':     /* search remote database (expand MX) */
                        if (*spec == '*')
                        {
                  case '*':     /* search remote database (expand MX) */
                        if (*spec == '*')
                        {
-                               nmx = getmxrr(spec + 1, mxhosts, "", &rcode);
+#if NAMED_BIND
+                               nmx = getmxrr(spec + 1, mxhosts, FALSE, &rcode);
+#else
+                               mxhosts[0] = spec + 1;
+                               nmx = 1;
+                               rcode = 0;
+#endif
                                if (tTd(28, 16))
                                {
                                        int i;
                                if (tTd(28, 16))
                                {
                                        int i;
@@ -353,10 +781,9 @@ _udbx_init()
                                        continue;
                                up->udb_type = UDB_REMOTE;
                                up->udb_addr.sin_family = h->h_addrtype;
                                        continue;
                                up->udb_type = UDB_REMOTE;
                                up->udb_addr.sin_family = h->h_addrtype;
-                               up->udb_addr.sin_len = h->h_length;
                                bcopy(h->h_addr_list[0],
                                      (char *) &up->udb_addr.sin_addr,
                                bcopy(h->h_addr_list[0],
                                      (char *) &up->udb_addr.sin_addr,
-                                     h->h_length);
+                                     sizeof up->udb_addr.sin_addr);
                                up->udb_addr.sin_port = UdbPort;
                                up->udb_timeout = UdbTimeout;
                                up++;
                                up->udb_addr.sin_port = UdbPort;
                                up->udb_timeout = UdbTimeout;
                                up++;
@@ -376,11 +803,35 @@ _udbx_init()
                        up++;
                        break;
 
                        up++;
                        break;
 
+                 case 'h':     /* use hesiod */
+                 case 'H':
+#ifdef HESIOD
+                       if (strcasecmp(spec, "hesiod") != 0)
+                               break;
+                       up->udb_type = UDB_HESIOD;
+                       up++;
+#endif /* HESIOD */
+                       break;
+
                  case '/':     /* look up remote name */
                  case '/':     /* look up remote name */
+                       up->udb_dbname = spec;
+                       errno = 0;
                        up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL);
                        if (up->udb_dbp == NULL)
                        up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL);
                        if (up->udb_dbp == NULL)
+                       {
+                               if (errno != ENOENT && errno != EACCES)
+                               {
+#ifdef LOG
+                                       if (LogLevel > 2)
+                                               syslog(LOG_ERR, "dbopen(%s): %s",
+                                                       spec, errstring(errno));
+#endif
+                                       up->udb_type = UDB_EOLIST;
+                                       goto tempfail;
+                               }
                                break;
                                break;
-                       up->udb_type = UDB_LOOKUP;
+                       }
+                       up->udb_type = UDB_DBFETCH;
                        up++;
                        break;
                }
                        up++;
                        break;
                }
@@ -389,21 +840,19 @@ _udbx_init()
 
        if (tTd(28, 4))
        {
 
        if (tTd(28, 4))
        {
-               for (up = UdbEnts; ; up++)
+               for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
                {
                        switch (up->udb_type)
                        {
                {
                        switch (up->udb_type)
                        {
-                         case UDB_EOLIST:
-                               return;
-
                          case UDB_REMOTE:
                                printf("REMOTE: addr %s, timeo %d\n",
                          case UDB_REMOTE:
                                printf("REMOTE: addr %s, timeo %d\n",
-                                       inet_ntoa(up->udb_addr.sin_addr),
+                                       anynet_ntoa((SOCKADDR *) &up->udb_addr),
                                        up->udb_timeout);
                                break;
 
                                        up->udb_timeout);
                                break;
 
-                         case UDB_LOOKUP:
-                               printf("LOOKUP\n");
+                         case UDB_DBFETCH:
+                               printf("FETCH: file %s\n",
+                                       up->udb_dbname);
                                break;
 
                          case UDB_FORWARD:
                                break;
 
                          case UDB_FORWARD:
@@ -411,12 +860,34 @@ _udbx_init()
                                        up->udb_fwdhost);
                                break;
 
                                        up->udb_fwdhost);
                                break;
 
+                         case UDB_HESIOD:
+                               printf("HESIOD\n");
+                               break;
+
                          default:
                                printf("UNKNOWN\n");
                                break;
                        }
                }
        }
                          default:
                                printf("UNKNOWN\n");
                                break;
                        }
                }
        }
+
+       UdbInitialized = TRUE;
+       errno = 0;
+       return EX_OK;
+
+       /*
+       **  On temporary failure, back out anything we've already done
+       */
+
+  tempfail:
+       for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
+       {
+               if (up->udb_type == UDB_DBFETCH)
+               {
+                       (*up->udb_dbp->close)(up->udb_dbp);
+               }
+       }
+       return EX_TEMPFAIL;
 }
 
 int
 }
 
 int
@@ -429,34 +900,86 @@ _udb_parsespec(udbspec, opt, maxopts)
        register char *spec_end;
        register int optnum;
 
        register char *spec_end;
        register int optnum;
 
-       spec_end = index(udbspec, ':');
+       spec_end = strchr(udbspec, ':');
        for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++)
        {
                register char *p;
 
        for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++)
        {
                register char *p;
 
-               while (isspace(*spec))
+               while (isascii(*spec) && isspace(*spec))
                        spec++;
                        spec++;
-               spec_end = index(spec, ':');
+               spec_end = strchr(spec, ':');
                if (spec_end != NULL)
                        *spec_end++ = '\0';
 
                opt[optnum].name = spec;
                opt[optnum].val = NULL;
                if (spec_end != NULL)
                        *spec_end++ = '\0';
 
                opt[optnum].name = spec;
                opt[optnum].val = NULL;
-               p = index(spec, '=');
+               p = strchr(spec, '=');
                if (p != NULL)
                        opt[optnum].val = ++p;
        }
        return optnum;
 }
 
                if (p != NULL)
                        opt[optnum].val = ++p;
        }
        return optnum;
 }
 
+#ifdef HESIOD
+
+int
+hes_udb_get(key, info)
+       DBT *key;
+       DBT *info;
+{
+       char *name, *type;
+       char *p, **hp;
+
+       name = key->data;
+       type = strchr(name, ':');
+       if (type == NULL)
+               return 1;
+
+       *type++ = '\0';
+
+       if (tTd(28, 1))
+               printf("hes_udb_get(%s, %s)\n", name, type);
+
+       /* make the hesiod query */
+       hp = hes_resolve(name, type);
+       if (hp == NULL)
+       {
+               /* network problem or timeout */
+               if (hes_error() == HES_ER_NET)
+                       return -1;
+
+               return 1;
+       }
+       else
+       {
+               /*
+               **  If there are multiple matches, just return the
+               **  first one and free the others.
+               **
+               **  XXX These should really be returned; for example,
+               **  XXX it is legal for :maildrop to be multi-valued.
+               */
+
+               for (p = hp[1]; p; p++)
+                       free(p);
+
+               info->data = hp[0];
+               info->size = (size_t) strlen(info->data);
+       }
+
+       return 0;
+}
+#endif /* HESIOD */
+
 #else /* not USERDB */
 
 #else /* not USERDB */
 
-void
-udbexpand(a, sendq)
+int
+udbexpand(a, sendq, e)
        ADDRESS *a;
        ADDRESS **sendq;
        ADDRESS *a;
        ADDRESS **sendq;
+       ENVELOPE *e;
 {
 {
-       return;
+       return EX_OK;
 }
 
 #endif /* USERDB */
 }
 
 #endif /* USERDB */