BSD 4_4_Lite1 release
[unix-history] / usr / src / usr.sbin / sendmail / src / udb.c
index b12b6c3..7887cb3 100644 (file)
 /*
  * 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
 #ifndef lint
-static char sccsid [] = "@(#)udb.c     5.2 (Berkeley) %G%";
+#ifdef USERDB
+static char sccsid [] = "@(#)udb.c     8.8 (Berkeley) 4/14/94 (with USERDB)";
+#else
+static char sccsid [] = "@(#)udb.c     8.8 (Berkeley) 4/14/94 (without USERDB)";
+#endif
 #endif
 #endif
-
-#include "sendmail.h"
 
 #ifdef USERDB
 
 
 #ifdef USERDB
 
-#include <sys/file.h>
+#include <errno.h>
+#include <netdb.h>
 #include <db.h>
 
 #include <db.h>
 
+#ifdef HESIOD
+#include <hesiod.h>
+#endif /* HESIOD */
+
 /*
 /*
+**  UDB.C -- interface between sendmail and Berkeley User Data Base.
+**
+**     This depends on the 4.4BSD db package.
+*/
+
+
+struct udbent
+{
+       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 */
+               struct
+               {
+                       struct sockaddr_in _udb_addr;   /* address */
+                       int             _udb_timeout;   /* timeout */
+               } udb_remote;
+#define udb_addr       udb_u.udb_remote._udb_addr
+#define udb_timeout    udb_u.udb_remote._udb_timeout
+
+               /* type UE_FORWARD -- forward message to remote */
+               struct
+               {
+                       char    *_udb_fwdhost;  /* name of forward host */
+               } udb_forward;
+#define udb_fwdhost    udb_u.udb_forward._udb_fwdhost
+
+               /* type UE_FETCH -- lookup in local database */
+               struct
+               {
+                       char    *_udb_dbname;   /* pathname of database */
+                       DB      *_udb_dbp;      /* open database ptr */
+               } udb_lookup;
+#define udb_dbname     udb_u.udb_lookup._udb_dbname
+#define udb_dbp                udb_u.udb_lookup._udb_dbp
+       } udb_u;
+};
+
+#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_DBFETCH    3       /* look up in local database */
+#define UDB_FORWARD    4       /* forward to remote host */
+#define UDB_HESIOD     5       /* look up via hesiod */
+
+#define MAXUDBENT      10      /* maximum number of UDB entries */
+
+
+struct option
+{
+       char    *name;
+       char    *val;
+};
+\f/*
 **  UDBEXPAND -- look up user in database and expand
 **
 **     Parameters:
 **  UDBEXPAND -- look up user in database and expand
 **
 **     Parameters:
@@ -25,95 +116,870 @@ static char sccsid [] = "@(#)udb.c        5.2 (Berkeley) %G%";
 **             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.
 */
 
-void
-udbexpand(a, sendq)
+int    UdbPort = 1616;
+int    UdbTimeout = 10;
+
+struct udbent  UdbEnts[MAXUDBENT + 1];
+int            UdbSock = -1;
+bool           UdbInitialized = FALSE;
+
+int
+udbexpand(a, sendq, e)
        register ADDRESS *a;
        ADDRESS **sendq;
        register ADDRESS *a;
        ADDRESS **sendq;
+       register ENVELOPE *e;
 {
        int i;
        register char *p;
 {
        int i;
        register char *p;
-       auto char *class;
-       auto char *list;
        DBT key;
        DBT info;
        DBT key;
        DBT info;
-       static DB *dbp = NULL;
-       register char *bp;
-       char buf[8192];
+       bool breakout;
+       register struct udbent *up;
+       int keylen;
+       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))
-               return;
-       CurEnv->e_to = a->q_paddr;
+       if (bitset(QDONTSEND|QVERIFIED, a->q_flags))
+               return EX_OK;
+       e->e_to = a->q_paddr;
 
 
-       /* if necessary, open the database */
-       if (dbp == NULL)
+       /* on first call, locate the database */
+       if (!UdbInitialized)
        {
        {
-               if (UdbFileName == NULL || UdbFileName[0] == '\0')
+               extern int _udbx_init();
+
+               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)
+               return EX_OK;
+
+       /* if name begins with a colon, it indicates our metadata */
+       if (a->q_user[0] == ':')
+               return EX_OK;
+
+       /* build actual database key */
+       (void) strcpy(keybuf, a->q_user);
+       (void) strcat(keybuf, ":maildrop");
+       keylen = strlen(keybuf);
+
+       breakout = FALSE;
+       for (up = UdbEnts; !breakout; up++)
+       {
+               char *user;
+
+               /*
+               **  Select action based on entry type.
+               **
+               **      On dropping out of this switch, "class" should
+               **      explain the type of the data, and "user" should
+               **      contain the user information.
+               */
+
+               switch (up->udb_type)
                {
                {
-                       if (tTd(28, 4))
-                               printf("no userdb specified\n");
-                       return;
+                 case UDB_DBFETCH:
+                       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);
+                       if (i > 0 || info.size <= 0)
+                       {
+                               if (tTd(28, 2))
+                                       printf("udbexpand: no match on %s (%d)\n",
+                                               keybuf, keylen);
+                               continue;
+                       }
+                       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
+                                       user = xalloc(info.size + 1);
+                               bcopy(info.data, user, info.size);
+                               user[info.size] = '\0';
+
+                               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++;
+                               naddrs += sendtolist(user, a, sendq, e);
+                               AliasLevel--;
+
+                               if (user != buf)
+                                       free(user);
+
+                               /* get the next record */
+                               i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
+                       }
+
+                       /* 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;
+
+#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;
+                       }
+                       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))
+                       {
+                               a->q_flags |= QVERIFIED;
+                               e->e_nrcpts++;
+                               free(info.data);
+                               return EX_OK;
+                       }
+
+                       breakout = TRUE;
+                       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--;
+
+                       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;
+#endif /* HESIOD */
+
+                 case UDB_REMOTE:
+                       /* not yet implemented */
+                       continue;
+
+                 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);
+                       message("expanded to %s", user);
+                       a->q_flags &= ~QSELFREF;
+                       AliasLevel++;
+                       naddrs = sendtolist(user, a, sendq, e);
+                       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;
+                       break;
+
+                 case UDB_EOLIST:
+                       breakout = TRUE;
+                       continue;
+
+                 default:
+                       /* unknown entry type */
+                       continue;
                }
                }
-               dbp = dbopen(UdbFileName, O_RDONLY, 0644, DB_HASH, NULL);
-               if (dbp == NULL)
+       }
+       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)
                {
                {
-                       extern int errno;
+                 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;
 
 
-                       if (tTd(28, 2))
-                               printf("cannot open %s: %d\n", UdbFileName, errno);
-                       return;
+#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 */
                }
        }
 
                }
        }
 
-       key.data = a->q_user;
-       key.size = strlen(key.data);
-       i = dbp->get(dbp, &key, &info, R_NOOVERWRITE);
-       if (i != 0 || info.size <= 0)
+       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++)
        {
        {
-               if (i < 0)
-                       syserr("udbexpand: db-get stat %s");
-               if (tTd(28, 2))
-                       printf("expand: no match on %s\n", key.data);
-               return;
+               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 */
+               }
        }
 
        }
 
-       /* extract the class (first string) and data (second string) */
-       i = strlen((char *) info.data) + 1;
-       p = (char *) info.data + i;
-       i = info.size - i;
+       /* 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
 
 
-       /* use internal buffer if it will fit; otherwise malloc */
-       if (i < sizeof buf)
-               bp = buf;
-       else
-               bp = xalloc(i + 1);
-       bcopy(p, bp, i);
-       bp[i] = '\0';
+int
+_udbx_init()
+{
+       register char *p;
+       int i;
+       register struct udbent *up;
+       char buf[BUFSIZ];
+
+       if (UdbInitialized)
+               return EX_OK;
+
+# ifdef UDB_DEFAULT_SPEC
+       if (UdbSpec == NULL)
+               UdbSpec = UDB_DEFAULT_SPEC;
+# endif
+
+       p = UdbSpec;
+       up = UdbEnts;
+       while (p != NULL)
+       {
+               char *spec;
+               auto int rcode;
+               int nopts;
+               int nmx;
+               register struct hostent *h;
+               char *mxhosts[MAXMXHOSTS + 1];
+               struct option opts[MAXUDBOPTS + 1];
+
+               while (*p == ' ' || *p == '\t' || *p == ',')
+                       p++;
+               if (*p == '\0')
+                       break;
+               spec = p;
+               p = strchr(p, ',');
+               if (p != NULL)
+                       *p++ = '\0';
+
+               /* extract options */
+               nopts = _udb_parsespec(spec, opts, MAXUDBOPTS);
+
+               /*
+               **  Decode database specification.
+               **
+               **      In the sendmail tradition, the leading character
+               **      defines the semantics of the rest of the entry.
+               **
+               **      +hostname --    send a datagram to the udb server
+               **                      on host "hostname" asking for the
+               **                      home mail server for this user.
+               **      *hostname --    similar to +hostname, except that the
+               **                      hostname is searched as an MX record;
+               **                      resulting hosts are searched as for
+               **                      +mxhostname.  If no MX host is found,
+               **                      this is the same as +hostname.
+               **      @hostname --    forward email to the indicated host.
+               **                      This should be the last in the list,
+               **                      since it always matches the input.
+               **      /dbname  --     search the named database on the local
+               **                      host using the Berkeley db package.
+               */
+
+               switch (*spec)
+               {
+                 case '+':     /* search remote database */
+                 case '*':     /* search remote database (expand MX) */
+                       if (*spec == '*')
+                       {
+#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;
+
+                                       printf("getmxrr(%s): %d", spec + 1, nmx);
+                                       for (i = 0; i <= nmx; i++)
+                                               printf(" %s", mxhosts[i]);
+                                       printf("\n");
+                               }
+                       }
+                       else
+                       {
+                               nmx = 1;
+                               mxhosts[0] = spec + 1;
+                       }
+
+                       for (i = 0; i < nmx; i++)
+                       {
+                               h = gethostbyname(mxhosts[i]);
+                               if (h == NULL)
+                                       continue;
+                               up->udb_type = UDB_REMOTE;
+                               up->udb_addr.sin_family = h->h_addrtype;
+                               bcopy(h->h_addr_list[0],
+                                     (char *) &up->udb_addr.sin_addr,
+                                     sizeof up->udb_addr.sin_addr);
+                               up->udb_addr.sin_port = UdbPort;
+                               up->udb_timeout = UdbTimeout;
+                               up++;
+                       }
+
+                       /* set up a datagram socket */
+                       if (UdbSock < 0)
+                       {
+                               UdbSock = socket(AF_INET, SOCK_DGRAM, 0);
+                               (void) fcntl(UdbSock, F_SETFD, 1);
+                       }
+                       break;
+
+                 case '@':     /* forward to remote host */
+                       up->udb_type = UDB_FORWARD;
+                       up->udb_fwdhost = spec + 1;
+                       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 */
+                       up->udb_dbname = spec;
+                       errno = 0;
+                       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;
+                       }
+                       up->udb_type = UDB_DBFETCH;
+                       up++;
+                       break;
+               }
+       }
+       up->udb_type = UDB_EOLIST;
+
+       if (tTd(28, 4))
+       {
+               for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
+               {
+                       switch (up->udb_type)
+                       {
+                         case UDB_REMOTE:
+                               printf("REMOTE: addr %s, timeo %d\n",
+                                       anynet_ntoa((SOCKADDR *) &up->udb_addr),
+                                       up->udb_timeout);
+                               break;
+
+                         case UDB_DBFETCH:
+                               printf("FETCH: file %s\n",
+                                       up->udb_dbname);
+                               break;
+
+                         case UDB_FORWARD:
+                               printf("FORWARD: host %s\n",
+                                       up->udb_fwdhost);
+                               break;
+
+                         case UDB_HESIOD:
+                               printf("HESIOD\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
+_udb_parsespec(udbspec, opt, maxopts)
+       char *udbspec;
+       struct option opt[];
+       int maxopts;
+{
+       register char *spec;
+       register char *spec_end;
+       register int optnum;
+
+       spec_end = strchr(udbspec, ':');
+       for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++)
+       {
+               register char *p;
+
+               while (isascii(*spec) && isspace(*spec))
+                       spec++;
+               spec_end = strchr(spec, ':');
+               if (spec_end != NULL)
+                       *spec_end++ = '\0';
+
+               opt[optnum].name = spec;
+               opt[optnum].val = NULL;
+               p = strchr(spec, '=');
+               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))
 
        if (tTd(28, 1))
-               printf("Class %s: %s\n", info.data, bp);
+               printf("hes_udb_get(%s, %s)\n", name, type);
 
 
-       /* do special processing based on class */
-       if (strcmp((char *) info.data, "user") == 0)
+       /* make the hesiod query */
+       hp = hes_resolve(name, type);
+       if (hp == NULL)
        {
        {
-               message(Arpa_Info, "expanded to (%s) %s", info.data, bp);
-               AliasLevel++;
-               sendtolist(bp, a, sendq);
-               AliasLevel--;
+               /* 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);
        }
 
        }
 
-       /* free memory if we allocated it */
-       if (bp != buf)
-               free(bp);
+       return 0;
+}
+#endif /* HESIOD */
+
+#else /* not USERDB */
+
+int
+udbexpand(a, sendq, e)
+       ADDRESS *a;
+       ADDRESS **sendq;
+       ENVELOPE *e;
+{
+       return EX_OK;
 }
 
 #endif /* USERDB */
 }
 
 #endif /* USERDB */