📄 udb.c
字号:
/*
* Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*
*/
#include <sendmail.h>
#ifndef lint
# if USERDB
static char id[] = "@(#)$Id: udb.c,v 8.111 1999/11/16 02:04:04 gshapiro Exp $ (with USERDB)";
# else /* USERDB */
static char id[] = "@(#)$Id: udb.c,v 8.111 1999/11/16 02:04:04 gshapiro Exp $ (without USERDB)";
# endif /* USERDB */
#endif /* ! lint */
#if USERDB
# ifdef NEWDB
# include <db.h>
# ifndef DB_VERSION_MAJOR
# define DB_VERSION_MAJOR 1
# endif /* ! DB_VERSION_MAJOR */
# else /* NEWDB */
# define DBT struct _data_base_thang_
DBT
{
void *data; /* pointer to data */
size_t size; /* length of data */
};
# endif /* NEWDB */
/*
** 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 */
pid_t udb_pid; /* PID of process which opened db */
char *udb_default; /* default host for outgoing mail */
union
{
# if NETINET || NETINET6
/* type UE_REMOTE -- do remote call for lookup */
struct
{
SOCKADDR _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
# endif /* NETINET || NETINET6 */
/* 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
# ifdef NEWDB
/* 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
# endif /* NEWDB */
} 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 udb_option
{
char *udbo_name;
char *udbo_val;
};
# ifdef HESIOD
static int hes_udb_get __P((DBT *, DBT *));
# endif /* HESIOD */
static char *udbmatch __P((char *, char *));
static int _udbx_init __P((ENVELOPE *));
static int _udb_parsespec __P((char *, struct udb_option [], int));
/*
** UDBEXPAND -- look up user in database and expand
**
** Parameters:
** a -- address to expand.
** sendq -- pointer to head of sendq to put the expansions in.
** aliaslevel -- the current alias nesting depth.
** e -- the current envelope.
**
** Returns:
** 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.
*/
static struct udbent UdbEnts[MAXUDBENT + 1];
static bool UdbInitialized = FALSE;
int
udbexpand(a, sendq, aliaslevel, e)
register ADDRESS *a;
ADDRESS **sendq;
int aliaslevel;
register ENVELOPE *e;
{
int i;
DBT key;
DBT info;
bool breakout;
register struct udbent *up;
int keylen;
int naddrs;
char *user;
char keybuf[MAXKEY];
memset(&key, '\0', sizeof key);
memset(&info, '\0', sizeof info);
if (tTd(28, 1))
dprintf("udbexpand(%s)\n", a->q_paddr);
/* make certain we are supposed to send to this address */
if (!QS_IS_SENDABLE(a->q_state))
return EX_OK;
e->e_to = a->q_paddr;
/* on first call, locate the database */
if (!UdbInitialized)
{
if (_udbx_init(e) == EX_TEMPFAIL)
return EX_TEMPFAIL;
}
/* short circuit the process if no chance of a match */
if (UdbSpec == NULL || UdbSpec[0] == '\0')
return EX_OK;
/* extract user to do userdb matching on */
user = a->q_user;
/* short circuit name begins with '\\' since it can't possibly match */
/* (might want to treat this as unquoted instead) */
if (user[0] == '\\')
return EX_OK;
/* if name is too long, assume it won't match */
if (strlen(user) > (SIZE_T) sizeof keybuf - 12)
return EX_OK;
/* if name begins with a colon, it indicates our metadata */
if (user[0] == ':')
return EX_OK;
/* build actual database key */
(void) strlcpy(keybuf, user, sizeof keybuf);
(void) strlcat(keybuf, ":maildrop", sizeof keybuf);
keylen = strlen(keybuf);
breakout = FALSE;
for (up = UdbEnts; !breakout; up++)
{
int usersize;
int userleft;
char userbuf[MEMCHUNKSIZE];
# if defined(HESIOD) && defined(HES_GETMAILHOST)
char pobuf[MAXNAME];
# endif /* defined(HESIOD) && defined(HES_GETMAILHOST) */
# if defined(NEWDB) && DB_VERSION_MAJOR > 1
DBC *dbc = NULL;
# endif /* defined(NEWDB) && DB_VERSION_MAJOR > 1 */
user = userbuf;
userbuf[0] = '\0';
usersize = sizeof userbuf;
userleft = sizeof userbuf - 1;
/*
** 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)
{
# ifdef NEWDB
case UDB_DBFETCH:
key.data = keybuf;
key.size = keylen;
if (tTd(28, 80))
dprintf("udbexpand: trying %s (%d) via db\n",
keybuf, keylen);
# if DB_VERSION_MAJOR < 2
i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
# else /* DB_VERSION_MAJOR < 2 */
i = 0;
if (dbc == NULL &&
# if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6
(errno = (*up->udb_dbp->cursor)(up->udb_dbp,
NULL, &dbc, 0)) != 0)
# else /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
(errno = (*up->udb_dbp->cursor)(up->udb_dbp,
NULL, &dbc)) != 0)
# endif /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
i = -1;
if (i != 0 || dbc == NULL ||
(errno = dbc->c_get(dbc, &key,
&info, DB_SET)) != 0)
i = 1;
# endif /* DB_VERSION_MAJOR < 2 */
if (i > 0 || info.size <= 0)
{
if (tTd(28, 2))
dprintf("udbexpand: no match on %s (%d)\n",
keybuf, keylen);
# if DB_VERSION_MAJOR > 1
if (dbc != NULL)
{
(void) dbc->c_close(dbc);
dbc = NULL;
}
# endif /* DB_VERSION_MAJOR > 1 */
break;
}
if (tTd(28, 80))
dprintf("udbexpand: match %.*s: %.*s\n",
(int) key.size, (char *) key.data,
(int) info.size, (char *) info.data);
a->q_flags &= ~QSELFREF;
while (i == 0 && key.size == keylen &&
memcmp(key.data, keybuf, keylen) == 0)
{
char *p;
if (bitset(EF_VRFYONLY, e->e_flags))
{
a->q_state = QS_VERIFIED;
# if DB_VERSION_MAJOR > 1
if (dbc != NULL)
{
(void) dbc->c_close(dbc);
dbc = NULL;
}
# endif /* DB_VERSION_MAJOR > 1 */
return EX_OK;
}
breakout = TRUE;
if (info.size >= userleft - 1)
{
char *nuser;
int size = MEMCHUNKSIZE;
if (info.size > MEMCHUNKSIZE)
size = info.size;
nuser = xalloc(usersize + size);
memmove(nuser, user, usersize);
if (user != userbuf)
free(user);
user = nuser;
usersize += size;
userleft += size;
}
p = &user[strlen(user)];
if (p != user)
{
*p++ = ',';
userleft--;
}
memmove(p, info.data, info.size);
p[info.size] = '\0';
userleft -= info.size;
/* get the next record */
# if DB_VERSION_MAJOR < 2
i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
# else /* DB_VERSION_MAJOR < 2 */
i = 0;
if ((errno = dbc->c_get(dbc, &key,
&info, DB_NEXT)) != 0)
i = 1;
# endif /* DB_VERSION_MAJOR < 2 */
}
# if DB_VERSION_MAJOR > 1
if (dbc != NULL)
{
(void) dbc->c_close(dbc);
dbc = NULL;
}
# endif /* DB_VERSION_MAJOR > 1 */
/* if nothing ever matched, try next database */
if (!breakout)
break;
message("expanded to %s", user);
if (LogLevel > 10)
sm_syslog(LOG_INFO, e->e_id,
"expand %.100s => %s",
e->e_to,
shortenstring(user, MAXSHORTSTR));
naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
{
if (tTd(28, 5))
{
dprintf("udbexpand: QS_EXPANDED ");
printaddr(a, FALSE);
}
a->q_state = QS_EXPANDED;
}
if (i < 0)
{
syserr("udbexpand: db-get %.*s stat %d",
(int) key.size, (char *) key.data, i);
return EX_TEMPFAIL;
}
/*
** If this address has a -request address, reflect
** it into the envelope.
*/
memset(&key, '\0', sizeof key);
memset(&info, '\0', sizeof info);
(void) strlcpy(keybuf, a->q_user, sizeof keybuf);
(void) strlcat(keybuf, ":mailsender", sizeof keybuf);
keylen = strlen(keybuf);
key.data = keybuf;
key.size = keylen;
# if DB_VERSION_MAJOR < 2
i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
# else /* DB_VERSION_MAJOR < 2 */
i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
&key, &info, 0);
# endif /* DB_VERSION_MAJOR < 2 */
if (i != 0 || info.size <= 0)
break;
a->q_owner = xalloc(info.size + 1);
memmove(a->q_owner, info.data, 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;
a->q_flags |= QDELIVERED|QEXPANDED;
break;
# endif /* NEWDB */
# ifdef HESIOD
case UDB_HESIOD:
key.data = keybuf;
key.size = keylen;
if (tTd(28, 80))
dprintf("udbexpand: trying %s (%d) via hesiod\n",
keybuf, keylen);
/* look up the key via hesiod */
i = hes_udb_get(&key, &info);
if (i < 0)
{
syserr("udbexpand: hesiod-get %.*s stat %d",
(int) key.size, (char *) key.data, i);
return EX_TEMPFAIL;
}
else if (i > 0 || info.size <= 0)
{
# if HES_GETMAILHOST
struct hes_postoffice *hp;
# endif /* HES_GETMAILHOST */
if (tTd(28, 2))
dprintf("udbexpand: no match on %s (%d)\n",
(char *) keybuf, (int) keylen);
# if HES_GETMAILHOST
if (tTd(28, 8))
dprintf(" ... trying hes_getmailhost(%s)\n",
a->q_user);
hp = hes_getmailhost(a->q_user);
if (hp == NULL)
{
if (hes_error() == HES_ER_NET)
{
syserr("udbexpand: hesiod-getmail %s stat %d",
a->q_user, hes_error());
return EX_TEMPFAIL;
}
if (tTd(28, 2))
dprintf("hes_getmailhost(%s): %d\n",
a->q_user, hes_error());
break;
}
if (strlen(hp->po_name) + strlen(hp->po_host) >
sizeof pobuf - 2)
{
if (tTd(28, 2))
dprintf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -