BSD 4_4_Lite1 release
[unix-history] / usr / src / usr.sbin / sendmail / src / udb.c
index 45153e2..7887cb3 100644 (file)
@@ -1,28 +1,57 @@
 /*
  * 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     6.14 (Berkeley) %G% (with USERDB)";
+static char sccsid [] = "@(#)udb.c     8.8 (Berkeley) 4/14/94 (with USERDB)";
 #else
 #else
-static char sccsid [] = "@(#)udb.c     6.14 (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/time.h>
 #include <errno.h>
 #include <netdb.h>
 #include <db.h>
 
 #include <errno.h>
 #include <netdb.h>
 #include <db.h>
 
+#ifdef HESIOD
+#include <hesiod.h>
+#endif /* HESIOD */
+
 /*
 **  UDB.C -- interface between sendmail and Berkeley User Data Base.
 **
 /*
 **  UDB.C -- interface between sendmail and Berkeley User Data Base.
 **
@@ -69,6 +98,7 @@ struct udbent
 #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_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 */
 
 
 #define MAXUDBENT      10      /* maximum number of UDB entries */
 
@@ -139,6 +169,10 @@ udbexpand(a, sendq, e)
        if (UdbSpec == NULL || UdbSpec[0] == '\0')
                return EX_OK;
 
        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 is too long, assume it won't match */
        if (strlen(a->q_user) > sizeof keybuf - 12)
                return EX_OK;
@@ -170,13 +204,20 @@ udbexpand(a, sendq, e)
                  case UDB_DBFETCH:
                        key.data = keybuf;
                        key.size = keylen;
                  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))
                        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\n", keybuf);
+                                       printf("udbexpand: no match on %s (%d)\n",
+                                               keybuf, keylen);
                                continue;
                        }
                                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;
 
                        naddrs = 0;
                        a->q_flags &= ~QSELFREF;
@@ -186,6 +227,7 @@ udbexpand(a, sendq, e)
                                if (bitset(EF_VRFYONLY, e->e_flags))
                                {
                                        a->q_flags |= QVERIFIED;
                                if (bitset(EF_VRFYONLY, e->e_flags))
                                {
                                        a->q_flags |= QVERIFIED;
+                                       e->e_nrcpts++;
                                        return EX_OK;
                                }
 
                                        return EX_OK;
                                }
 
@@ -213,6 +255,11 @@ udbexpand(a, sendq, e)
                                /* 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);
                        }
+
+                       /* if nothing ever matched, try next database */
+                       if (!breakout)
+                               continue;
+
                        if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
                        {
                                if (tTd(28, 5))
                        if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
                        {
                                if (tTd(28, 5))
@@ -228,7 +275,120 @@ udbexpand(a, sendq, e)
                                        key.size, key.data, i);
                                return EX_TEMPFAIL;
                        }
                                        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;
                        break;
+#endif /* HESIOD */
 
                  case UDB_REMOTE:
                        /* not yet implemented */
 
                  case UDB_REMOTE:
                        /* not yet implemented */
@@ -291,6 +451,17 @@ udbexpand(a, sendq, e)
 char *
 udbsender(sender)
        char *sender;
 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;
 {
        register char *p;
        register struct udbent *up;
@@ -300,7 +471,7 @@ udbsender(sender)
        char keybuf[MAXKEY];
 
        if (tTd(28, 1))
        char keybuf[MAXKEY];
 
        if (tTd(28, 1))
-               printf("udbsender(%s)\n", sender);
+               printf("udbmatch(%s, %s)\n", user, field);
 
        if (!UdbInitialized)
        {
 
        if (!UdbInitialized)
        {
@@ -312,17 +483,22 @@ udbsender(sender)
        if (UdbSpec == NULL || UdbSpec[0] == '\0')
                return NULL;
 
        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 */
        /* long names can never match and are a pain to deal with */
-       if (strlen(sender) > sizeof keybuf - 12)
+       if ((strlen(user) + strlen(field)) > sizeof keybuf - 4)
                return NULL;
 
        /* names beginning with colons indicate metadata */
                return NULL;
 
        /* names beginning with colons indicate metadata */
-       if (sender[0] == ':')
+       if (user[0] == ':')
                return NULL;
 
        /* build database key */
                return NULL;
 
        /* build database key */
-       (void) strcpy(keybuf, sender);
-       (void) strcat(keybuf, ":mailname");
+       (void) strcpy(keybuf, user);
+       (void) strcat(keybuf, ":");
+       (void) strcat(keybuf, field);
        keylen = strlen(keybuf);
 
        for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
        keylen = strlen(keybuf);
 
        for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
@@ -340,8 +516,8 @@ udbsender(sender)
                        if (i != 0 || info.size <= 0)
                        {
                                if (tTd(28, 2))
                        if (i != 0 || info.size <= 0)
                        {
                                if (tTd(28, 2))
-                                       printf("udbsender: no match on %s\n",
-                                                       keybuf);
+                                       printf("udbmatch: no match on %s (%d) via db\n",
+                                                       keybuf, keylen);
                                continue;
                        }
 
                                continue;
                        }
 
@@ -349,11 +525,38 @@ udbsender(sender)
                        bcopy(info.data, p, info.size);
                        p[info.size] = '\0';
                        if (tTd(28, 1))
                        bcopy(info.data, p, info.size);
                        p[info.size] = '\0';
                        if (tTd(28, 1))
-                               printf("udbsender ==> %s\n", p);
+                               printf("udbmatch ==> %s\n", p);
                        return 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
        /*
        **  Nothing yet.  Search again for a default case.  But only
        **  use it if we also have a forward (:maildrop) pointer already
@@ -361,7 +564,7 @@ udbsender(sender)
        */
 
        /* build database key */
        */
 
        /* build database key */
-       (void) strcpy(keybuf, sender);
+       (void) strcpy(keybuf, user);
        (void) strcat(keybuf, ":maildrop");
        keylen = strlen(keybuf);
 
        (void) strcat(keybuf, ":maildrop");
        keylen = strlen(keybuf);
 
@@ -402,13 +605,61 @@ udbsender(sender)
                        }
 
                        /* they exist -- build the actual address */
                        }
 
                        /* they exist -- build the actual address */
-                       p = xalloc(strlen(sender) + strlen(up->udb_default) + 2);
-                       (void) strcpy(p, sender);
+                       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))
                        (void) strcat(p, "@");
                        (void) strcat(p, up->udb_default);
                        if (tTd(28, 1))
-                               printf("udbsender ==> %s\n", p);
+                               printf("udbmatch ==> %s\n", p);
                        return 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 */
                }
        }
 
                }
        }
 
@@ -500,8 +751,8 @@ _udbx_init()
                  case '*':     /* search remote database (expand MX) */
                        if (*spec == '*')
                        {
                  case '*':     /* search remote database (expand MX) */
                        if (*spec == '*')
                        {
-#ifdef NAMED_BIND
-                               nmx = getmxrr(spec + 1, mxhosts, "", &rcode);
+#if NAMED_BIND
+                               nmx = getmxrr(spec + 1, mxhosts, FALSE, &rcode);
 #else
                                mxhosts[0] = spec + 1;
                                nmx = 1;
 #else
                                mxhosts[0] = spec + 1;
                                nmx = 1;
@@ -530,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++;
@@ -553,6 +803,16 @@ _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 */
                        up->udb_dbname = spec;
                        errno = 0;
                  case '/':     /* look up remote name */
                        up->udb_dbname = spec;
                        errno = 0;
@@ -561,6 +821,11 @@ _udbx_init()
                        {
                                if (errno != ENOENT && errno != EACCES)
                                {
                        {
                                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;
                                }
                                        up->udb_type = UDB_EOLIST;
                                        goto tempfail;
                                }
@@ -581,7 +846,7 @@ _udbx_init()
                        {
                          case UDB_REMOTE:
                                printf("REMOTE: addr %s, timeo %d\n",
                        {
                          case UDB_REMOTE:
                                printf("REMOTE: addr %s, timeo %d\n",
-                                       anynet_ntoa(&up->udb_addr),
+                                       anynet_ntoa((SOCKADDR *) &up->udb_addr),
                                        up->udb_timeout);
                                break;
 
                                        up->udb_timeout);
                                break;
 
@@ -595,6 +860,10 @@ _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;
@@ -651,6 +920,57 @@ _udb_parsespec(udbspec, opt, maxopts)
        return optnum;
 }
 
        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 */
 
 int
 #else /* not USERDB */
 
 int