138032Speter/*
2261370Sgshapiro * Copyright (c) 1998-2003, 2006 Proofpoint, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
15168515Sgshapiro#include "map.h"
1638032Speter
1790792Sgshapiro#if USERDB
18266711SgshapiroSM_RCSID("@(#)$Id: udb.c,v 8.166 2013-11-22 20:51:57 ca Exp $ (with USERDB)")
1990792Sgshapiro#else /* USERDB */
20266711SgshapiroSM_RCSID("@(#)$Id: udb.c,v 8.166 2013-11-22 20:51:57 ca Exp $ (without USERDB)")
2190792Sgshapiro#endif /* USERDB */
2238032Speter
2338032Speter#if USERDB
2438032Speter
25168515Sgshapiro#include <sm/sendmail.h>
2690792Sgshapiro# if NEWDB
27110560Sgshapiro#  include "sm/bdb.h"
2864562Sgshapiro# else /* NEWDB */
2964562Sgshapiro#  define DBT	struct _data_base_thang_
3038032SpeterDBT
3138032Speter{
3238032Speter	void	*data;		/* pointer to data */
3338032Speter	size_t	size;		/* length of data */
3438032Speter};
3564562Sgshapiro# endif /* NEWDB */
3638032Speter
3738032Speter/*
3838032Speter**  UDB.C -- interface between sendmail and Berkeley User Data Base.
3938032Speter**
4038032Speter**	This depends on the 4.4BSD db package.
4138032Speter*/
4238032Speter
4338032Speter
4438032Speterstruct udbent
4538032Speter{
4638032Speter	char	*udb_spec;		/* string version of spec */
4738032Speter	int	udb_type;		/* type of entry */
4842575Speter	pid_t	udb_pid;		/* PID of process which opened db */
4938032Speter	char	*udb_default;		/* default host for outgoing mail */
5038032Speter	union
5138032Speter	{
5264562Sgshapiro# if NETINET || NETINET6
5338032Speter		/* type UE_REMOTE -- do remote call for lookup */
5438032Speter		struct
5538032Speter		{
5664562Sgshapiro			SOCKADDR	_udb_addr;	/* address */
5738032Speter			int		_udb_timeout;	/* timeout */
5838032Speter		} udb_remote;
5964562Sgshapiro#  define udb_addr	udb_u.udb_remote._udb_addr
6064562Sgshapiro#  define udb_timeout	udb_u.udb_remote._udb_timeout
6164562Sgshapiro# endif /* NETINET || NETINET6 */
6238032Speter
6338032Speter		/* type UE_FORWARD -- forward message to remote */
6438032Speter		struct
6538032Speter		{
6638032Speter			char	*_udb_fwdhost;	/* name of forward host */
6738032Speter		} udb_forward;
6864562Sgshapiro# define udb_fwdhost	udb_u.udb_forward._udb_fwdhost
6938032Speter
7090792Sgshapiro# if NEWDB
7138032Speter		/* type UE_FETCH -- lookup in local database */
7238032Speter		struct
7338032Speter		{
7438032Speter			char	*_udb_dbname;	/* pathname of database */
7538032Speter			DB	*_udb_dbp;	/* open database ptr */
7638032Speter		} udb_lookup;
7764562Sgshapiro#  define udb_dbname	udb_u.udb_lookup._udb_dbname
7864562Sgshapiro#  define udb_dbp	udb_u.udb_lookup._udb_dbp
7964562Sgshapiro# endif /* NEWDB */
8038032Speter	} udb_u;
8138032Speter};
8238032Speter
8364562Sgshapiro# define UDB_EOLIST	0	/* end of list */
8464562Sgshapiro# define UDB_SKIP	1	/* skip this entry */
8564562Sgshapiro# define UDB_REMOTE	2	/* look up in remote database */
8664562Sgshapiro# define UDB_DBFETCH	3	/* look up in local database */
8764562Sgshapiro# define UDB_FORWARD	4	/* forward to remote host */
8864562Sgshapiro# define UDB_HESIOD	5	/* look up via hesiod */
8938032Speter
9064562Sgshapiro# define MAXUDBENT	10	/* maximum number of UDB entries */
9138032Speter
9238032Speter
9342575Speterstruct udb_option
9438032Speter{
9564562Sgshapiro	char	*udbo_name;
9664562Sgshapiro	char	*udbo_val;
9738032Speter};
9838032Speter
9990792Sgshapiro# if HESIOD
10064562Sgshapirostatic int	hes_udb_get __P((DBT *, DBT *));
10164562Sgshapiro# endif /* HESIOD */
10290792Sgshapirostatic char	*udbmatch __P((char *, char *, SM_RPOOL_T *));
10364562Sgshapirostatic int	_udbx_init __P((ENVELOPE *));
10464562Sgshapirostatic int	_udb_parsespec __P((char *, struct udb_option [], int));
10564562Sgshapiro
10664562Sgshapiro/*
10738032Speter**  UDBEXPAND -- look up user in database and expand
10838032Speter**
10938032Speter**	Parameters:
11038032Speter**		a -- address to expand.
11138032Speter**		sendq -- pointer to head of sendq to put the expansions in.
11238032Speter**		aliaslevel -- the current alias nesting depth.
11338032Speter**		e -- the current envelope.
11438032Speter**
11538032Speter**	Returns:
11638032Speter**		EX_TEMPFAIL -- if something "odd" happened -- probably due
11738032Speter**			to accessing a file on an NFS server that is down.
11838032Speter**		EX_OK -- otherwise.
11938032Speter**
12038032Speter**	Side Effects:
12138032Speter**		Modifies sendq.
12238032Speter*/
12338032Speter
12464562Sgshapirostatic struct udbent	UdbEnts[MAXUDBENT + 1];
12590792Sgshapirostatic bool		UdbInitialized = false;
12638032Speter
12738032Speterint
12838032Speterudbexpand(a, sendq, aliaslevel, e)
12938032Speter	register ADDRESS *a;
13038032Speter	ADDRESS **sendq;
13138032Speter	int aliaslevel;
13238032Speter	register ENVELOPE *e;
13338032Speter{
13438032Speter	int i;
13538032Speter	DBT key;
13638032Speter	DBT info;
13738032Speter	bool breakout;
13838032Speter	register struct udbent *up;
13938032Speter	int keylen;
14038032Speter	int naddrs;
14142575Speter	char *user;
142157001Sgshapiro	char keybuf[MAXUDBKEY];
14338032Speter
144168515Sgshapiro	memset(&key, '\0', sizeof(key));
145168515Sgshapiro	memset(&info, '\0', sizeof(info));
14638032Speter
14738032Speter	if (tTd(28, 1))
14890792Sgshapiro		sm_dprintf("udbexpand(%s)\n", a->q_paddr);
14938032Speter
15038032Speter	/* make certain we are supposed to send to this address */
15164562Sgshapiro	if (!QS_IS_SENDABLE(a->q_state))
15238032Speter		return EX_OK;
15338032Speter	e->e_to = a->q_paddr;
15438032Speter
15538032Speter	/* on first call, locate the database */
15638032Speter	if (!UdbInitialized)
15738032Speter	{
15838032Speter		if (_udbx_init(e) == EX_TEMPFAIL)
15938032Speter			return EX_TEMPFAIL;
16038032Speter	}
16138032Speter
16238032Speter	/* short circuit the process if no chance of a match */
16338032Speter	if (UdbSpec == NULL || UdbSpec[0] == '\0')
16438032Speter		return EX_OK;
16538032Speter
16642575Speter	/* extract user to do userdb matching on */
16742575Speter	user = a->q_user;
16842575Speter
16938032Speter	/* short circuit name begins with '\\' since it can't possibly match */
17042575Speter	/* (might want to treat this as unquoted instead) */
17142575Speter	if (user[0] == '\\')
17238032Speter		return EX_OK;
17338032Speter
17438032Speter	/* if name begins with a colon, it indicates our metadata */
17542575Speter	if (user[0] == ':')
17638032Speter		return EX_OK;
17738032Speter
178168515Sgshapiro	keylen = sm_strlcpyn(keybuf, sizeof(keybuf), 2, user, ":maildrop");
17990792Sgshapiro
18090792Sgshapiro	/* if name is too long, assume it won't match */
181168515Sgshapiro	if (keylen >= sizeof(keybuf))
18290792Sgshapiro		return EX_OK;
18390792Sgshapiro
18438032Speter	/* build actual database key */
18538032Speter
18690792Sgshapiro	breakout = false;
18738032Speter	for (up = UdbEnts; !breakout; up++)
18838032Speter	{
18938032Speter		int usersize;
19038032Speter		int userleft;
19138032Speter		char userbuf[MEMCHUNKSIZE];
192110560Sgshapiro# if HESIOD && HES_GETMAILHOST
19338032Speter		char pobuf[MAXNAME];
194110560Sgshapiro# endif /* HESIOD && HES_GETMAILHOST */
19564562Sgshapiro# if defined(NEWDB) && DB_VERSION_MAJOR > 1
19638032Speter		DBC *dbc = NULL;
19764562Sgshapiro# endif /* defined(NEWDB) && DB_VERSION_MAJOR > 1 */
19838032Speter
19938032Speter		user = userbuf;
20038032Speter		userbuf[0] = '\0';
201168515Sgshapiro		usersize = sizeof(userbuf);
202168515Sgshapiro		userleft = sizeof(userbuf) - 1;
20338032Speter
20438032Speter		/*
20538032Speter		**  Select action based on entry type.
20638032Speter		**
20738032Speter		**	On dropping out of this switch, "class" should
20838032Speter		**	explain the type of the data, and "user" should
20938032Speter		**	contain the user information.
21038032Speter		*/
21138032Speter
21238032Speter		switch (up->udb_type)
21338032Speter		{
21490792Sgshapiro# if NEWDB
21538032Speter		  case UDB_DBFETCH:
21638032Speter			key.data = keybuf;
21738032Speter			key.size = keylen;
21838032Speter			if (tTd(28, 80))
21990792Sgshapiro				sm_dprintf("udbexpand: trying %s (%d) via db\n",
22038032Speter					keybuf, keylen);
22164562Sgshapiro#  if DB_VERSION_MAJOR < 2
22238032Speter			i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
22364562Sgshapiro#  else /* DB_VERSION_MAJOR < 2 */
22438032Speter			i = 0;
22538032Speter			if (dbc == NULL &&
22664562Sgshapiro#   if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6
22738032Speter			    (errno = (*up->udb_dbp->cursor)(up->udb_dbp,
22842575Speter							    NULL, &dbc, 0)) != 0)
22964562Sgshapiro#   else /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
23042575Speter			    (errno = (*up->udb_dbp->cursor)(up->udb_dbp,
23138032Speter							    NULL, &dbc)) != 0)
23264562Sgshapiro#   endif /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */
23338032Speter				i = -1;
23438032Speter			if (i != 0 || dbc == NULL ||
23538032Speter			    (errno = dbc->c_get(dbc, &key,
23638032Speter						&info, DB_SET)) != 0)
23738032Speter				i = 1;
23864562Sgshapiro#  endif /* DB_VERSION_MAJOR < 2 */
23938032Speter			if (i > 0 || info.size <= 0)
24038032Speter			{
24138032Speter				if (tTd(28, 2))
24290792Sgshapiro					sm_dprintf("udbexpand: no match on %s (%d)\n",
24338032Speter						keybuf, keylen);
24464562Sgshapiro#  if DB_VERSION_MAJOR > 1
24564562Sgshapiro				if (dbc != NULL)
24638032Speter				{
24738032Speter					(void) dbc->c_close(dbc);
24838032Speter					dbc = NULL;
24938032Speter				}
25064562Sgshapiro#  endif /* DB_VERSION_MAJOR > 1 */
25138032Speter				break;
25238032Speter			}
25338032Speter			if (tTd(28, 80))
25490792Sgshapiro				sm_dprintf("udbexpand: match %.*s: %.*s\n",
25538032Speter					(int) key.size, (char *) key.data,
25638032Speter					(int) info.size, (char *) info.data);
25738032Speter
25838032Speter			a->q_flags &= ~QSELFREF;
25938032Speter			while (i == 0 && key.size == keylen &&
26064562Sgshapiro			       memcmp(key.data, keybuf, keylen) == 0)
26138032Speter			{
26238032Speter				char *p;
26338032Speter
26438032Speter				if (bitset(EF_VRFYONLY, e->e_flags))
26538032Speter				{
26664562Sgshapiro					a->q_state = QS_VERIFIED;
26764562Sgshapiro#  if DB_VERSION_MAJOR > 1
26838032Speter					if (dbc != NULL)
26938032Speter					{
27038032Speter						(void) dbc->c_close(dbc);
27138032Speter						dbc = NULL;
27238032Speter					}
27364562Sgshapiro#  endif /* DB_VERSION_MAJOR > 1 */
27438032Speter					return EX_OK;
27538032Speter				}
27638032Speter
27790792Sgshapiro				breakout = true;
27838032Speter				if (info.size >= userleft - 1)
27938032Speter				{
28038032Speter					char *nuser;
28138032Speter					int size = MEMCHUNKSIZE;
28238032Speter
28338032Speter					if (info.size > MEMCHUNKSIZE)
28438032Speter						size = info.size;
28590792Sgshapiro					nuser = sm_malloc_x(usersize + size);
28638032Speter
28764562Sgshapiro					memmove(nuser, user, usersize);
28838032Speter					if (user != userbuf)
28990792Sgshapiro						sm_free(user); /* XXX */
29038032Speter					user = nuser;
29138032Speter					usersize += size;
29238032Speter					userleft += size;
29338032Speter				}
29438032Speter				p = &user[strlen(user)];
29538032Speter				if (p != user)
29638032Speter				{
29738032Speter					*p++ = ',';
29838032Speter					userleft--;
29938032Speter				}
30064562Sgshapiro				memmove(p, info.data, info.size);
30138032Speter				p[info.size] = '\0';
30238032Speter				userleft -= info.size;
30338032Speter
30438032Speter				/* get the next record */
30564562Sgshapiro#  if DB_VERSION_MAJOR < 2
30638032Speter				i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
30764562Sgshapiro#  else /* DB_VERSION_MAJOR < 2 */
30838032Speter				i = 0;
30938032Speter				if ((errno = dbc->c_get(dbc, &key,
31038032Speter							&info, DB_NEXT)) != 0)
31138032Speter					i = 1;
31264562Sgshapiro#  endif /* DB_VERSION_MAJOR < 2 */
31338032Speter			}
31438032Speter
31564562Sgshapiro#  if DB_VERSION_MAJOR > 1
31638032Speter			if (dbc != NULL)
31738032Speter			{
31838032Speter				(void) dbc->c_close(dbc);
31938032Speter				dbc = NULL;
32038032Speter			}
32164562Sgshapiro#  endif /* DB_VERSION_MAJOR > 1 */
32238032Speter
32338032Speter			/* if nothing ever matched, try next database */
32438032Speter			if (!breakout)
32538032Speter				break;
32638032Speter
32738032Speter			message("expanded to %s", user);
32864562Sgshapiro			if (LogLevel > 10)
32938032Speter				sm_syslog(LOG_INFO, e->e_id,
33064562Sgshapiro					  "expand %.100s => %s",
33164562Sgshapiro					  e->e_to,
33264562Sgshapiro					  shortenstring(user, MAXSHORTSTR));
33338032Speter			naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
33438032Speter			if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
33538032Speter			{
33638032Speter				if (tTd(28, 5))
33738032Speter				{
33890792Sgshapiro					sm_dprintf("udbexpand: QS_EXPANDED ");
339132943Sgshapiro					printaddr(sm_debug_file(), a, false);
34038032Speter				}
34164562Sgshapiro				a->q_state = QS_EXPANDED;
34238032Speter			}
34338032Speter			if (i < 0)
34438032Speter			{
34538032Speter				syserr("udbexpand: db-get %.*s stat %d",
34638032Speter					(int) key.size, (char *) key.data, i);
34738032Speter				return EX_TEMPFAIL;
34838032Speter			}
34938032Speter
35038032Speter			/*
35138032Speter			**  If this address has a -request address, reflect
35238032Speter			**  it into the envelope.
35338032Speter			*/
35438032Speter
355168515Sgshapiro			memset(&key, '\0', sizeof(key));
356168515Sgshapiro			memset(&info, '\0', sizeof(info));
357168515Sgshapiro			(void) sm_strlcpyn(keybuf, sizeof(keybuf), 2, a->q_user,
35890792Sgshapiro					   ":mailsender");
35938032Speter			keylen = strlen(keybuf);
36038032Speter			key.data = keybuf;
36138032Speter			key.size = keylen;
36238032Speter
36364562Sgshapiro#  if DB_VERSION_MAJOR < 2
36438032Speter			i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
36564562Sgshapiro#  else /* DB_VERSION_MAJOR < 2 */
36638032Speter			i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
36738032Speter							&key, &info, 0);
36864562Sgshapiro#  endif /* DB_VERSION_MAJOR < 2 */
36938032Speter			if (i != 0 || info.size <= 0)
37038032Speter				break;
37190792Sgshapiro			a->q_owner = sm_rpool_malloc_x(e->e_rpool,
37290792Sgshapiro						       info.size + 1);
37364562Sgshapiro			memmove(a->q_owner, info.data, info.size);
37438032Speter			a->q_owner[info.size] = '\0';
37538032Speter
37638032Speter			/* announce delivery; NORECEIPT bit set later */
37738032Speter			if (e->e_xfp != NULL)
37838032Speter			{
37990792Sgshapiro				(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
38090792Sgshapiro						     "Message delivered to mailing list %s\n",
38190792Sgshapiro						     a->q_paddr);
38238032Speter			}
38338032Speter			e->e_flags |= EF_SENDRECEIPT;
38438032Speter			a->q_flags |= QDELIVERED|QEXPANDED;
38538032Speter			break;
38664562Sgshapiro# endif /* NEWDB */
38738032Speter
38890792Sgshapiro# if HESIOD
38938032Speter		  case UDB_HESIOD:
39038032Speter			key.data = keybuf;
39138032Speter			key.size = keylen;
39238032Speter			if (tTd(28, 80))
39390792Sgshapiro				sm_dprintf("udbexpand: trying %s (%d) via hesiod\n",
39438032Speter					keybuf, keylen);
39538032Speter			/* look up the key via hesiod */
39638032Speter			i = hes_udb_get(&key, &info);
39738032Speter			if (i < 0)
39838032Speter			{
39938032Speter				syserr("udbexpand: hesiod-get %.*s stat %d",
40038032Speter					(int) key.size, (char *) key.data, i);
40138032Speter				return EX_TEMPFAIL;
40238032Speter			}
40338032Speter			else if (i > 0 || info.size <= 0)
40438032Speter			{
40564562Sgshapiro#  if HES_GETMAILHOST
40638032Speter				struct hes_postoffice *hp;
40764562Sgshapiro#  endif /* HES_GETMAILHOST */
40838032Speter
40938032Speter				if (tTd(28, 2))
41090792Sgshapiro					sm_dprintf("udbexpand: no match on %s (%d)\n",
41138032Speter						(char *) keybuf, (int) keylen);
41264562Sgshapiro#  if HES_GETMAILHOST
41338032Speter				if (tTd(28, 8))
41490792Sgshapiro					sm_dprintf("  ... trying hes_getmailhost(%s)\n",
41538032Speter						a->q_user);
41638032Speter				hp = hes_getmailhost(a->q_user);
41738032Speter				if (hp == NULL)
41838032Speter				{
41938032Speter					if (hes_error() == HES_ER_NET)
42038032Speter					{
42138032Speter						syserr("udbexpand: hesiod-getmail %s stat %d",
42238032Speter							a->q_user, hes_error());
42338032Speter						return EX_TEMPFAIL;
42438032Speter					}
42538032Speter					if (tTd(28, 2))
42690792Sgshapiro						sm_dprintf("hes_getmailhost(%s): %d\n",
42738032Speter							a->q_user, hes_error());
42838032Speter					break;
42938032Speter				}
43038032Speter				if (strlen(hp->po_name) + strlen(hp->po_host) >
431168515Sgshapiro				    sizeof(pobuf) - 2)
43238032Speter				{
43338032Speter					if (tTd(28, 2))
43490792Sgshapiro						sm_dprintf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n",
43538032Speter							a->q_user,
43638032Speter							hp->po_name,
43738032Speter							hp->po_host);
43838032Speter					break;
43938032Speter				}
44038032Speter				info.data = pobuf;
441168515Sgshapiro				(void) sm_snprintf(pobuf, sizeof(pobuf),
44290792Sgshapiro					"%s@%s", hp->po_name, hp->po_host);
44338032Speter				info.size = strlen(info.data);
44464562Sgshapiro#  else /* HES_GETMAILHOST */
44538032Speter				break;
44664562Sgshapiro#  endif /* HES_GETMAILHOST */
44738032Speter			}
44838032Speter			if (tTd(28, 80))
44990792Sgshapiro				sm_dprintf("udbexpand: match %.*s: %.*s\n",
45038032Speter					(int) key.size, (char *) key.data,
45138032Speter					(int) info.size, (char *) info.data);
45238032Speter			a->q_flags &= ~QSELFREF;
45338032Speter
45438032Speter			if (bitset(EF_VRFYONLY, e->e_flags))
45538032Speter			{
45664562Sgshapiro				a->q_state = QS_VERIFIED;
45738032Speter				return EX_OK;
45838032Speter			}
45938032Speter
46090792Sgshapiro			breakout = true;
46138032Speter			if (info.size >= usersize)
46290792Sgshapiro				user = sm_malloc_x(info.size + 1);
46364562Sgshapiro			memmove(user, info.data, info.size);
46438032Speter			user[info.size] = '\0';
46538032Speter
46638032Speter			message("hesioded to %s", user);
46764562Sgshapiro			if (LogLevel > 10)
46838032Speter				sm_syslog(LOG_INFO, e->e_id,
46964562Sgshapiro					  "hesiod %.100s => %s",
47064562Sgshapiro					  e->e_to,
47164562Sgshapiro					  shortenstring(user, MAXSHORTSTR));
47238032Speter			naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
47338032Speter
47438032Speter			if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
47538032Speter			{
47638032Speter				if (tTd(28, 5))
47738032Speter				{
47890792Sgshapiro					sm_dprintf("udbexpand: QS_EXPANDED ");
479132943Sgshapiro					printaddr(sm_debug_file(), a, false);
48038032Speter				}
48164562Sgshapiro				a->q_state = QS_EXPANDED;
48238032Speter			}
48338032Speter
48438032Speter			/*
48538032Speter			**  If this address has a -request address, reflect
48638032Speter			**  it into the envelope.
48738032Speter			*/
48838032Speter
489168515Sgshapiro			(void) sm_strlcpyn(keybuf, sizeof(keybuf), 2, a->q_user,
49090792Sgshapiro					   ":mailsender");
49138032Speter			keylen = strlen(keybuf);
49238032Speter			key.data = keybuf;
49338032Speter			key.size = keylen;
49438032Speter			i = hes_udb_get(&key, &info);
49538032Speter			if (i != 0 || info.size <= 0)
49638032Speter				break;
49790792Sgshapiro			a->q_owner = sm_rpool_malloc_x(e->e_rpool,
49890792Sgshapiro						       info.size + 1);
49964562Sgshapiro			memmove(a->q_owner, info.data, info.size);
50038032Speter			a->q_owner[info.size] = '\0';
50138032Speter			break;
50264562Sgshapiro# endif /* HESIOD */
50338032Speter
50438032Speter		  case UDB_REMOTE:
50538032Speter			/* not yet implemented */
50638032Speter			break;
50738032Speter
50838032Speter		  case UDB_FORWARD:
50938032Speter			if (bitset(EF_VRFYONLY, e->e_flags))
51064562Sgshapiro			{
51164562Sgshapiro				a->q_state = QS_VERIFIED;
51238032Speter				return EX_OK;
51364562Sgshapiro			}
51438032Speter			i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1;
51538032Speter			if (i >= usersize)
51638032Speter			{
51738032Speter				usersize = i + 1;
51890792Sgshapiro				user = sm_malloc_x(usersize);
51938032Speter			}
52090792Sgshapiro			(void) sm_strlcpyn(user, usersize, 3,
52190792Sgshapiro					a->q_user, "@", up->udb_fwdhost);
52238032Speter			message("expanded to %s", user);
52338032Speter			a->q_flags &= ~QSELFREF;
52438032Speter			naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
52538032Speter			if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
52638032Speter			{
52738032Speter				if (tTd(28, 5))
52838032Speter				{
52990792Sgshapiro					sm_dprintf("udbexpand: QS_EXPANDED ");
530132943Sgshapiro					printaddr(sm_debug_file(), a, false);
53138032Speter				}
53264562Sgshapiro				a->q_state = QS_EXPANDED;
53338032Speter			}
53490792Sgshapiro			breakout = true;
53538032Speter			break;
53638032Speter
53738032Speter		  case UDB_EOLIST:
53890792Sgshapiro			breakout = true;
53938032Speter			break;
54038032Speter
54138032Speter		  default:
54238032Speter			/* unknown entry type */
54338032Speter			break;
54438032Speter		}
54590792Sgshapiro		/* XXX if an exception occurs, there is a storage leak */
54638032Speter		if (user != userbuf)
54790792Sgshapiro			sm_free(user); /* XXX */
54838032Speter	}
54938032Speter	return EX_OK;
55038032Speter}
55190792Sgshapiro/*
55238032Speter**  UDBSENDER -- return canonical external name of sender, given local name
55338032Speter**
55438032Speter**	Parameters:
55538032Speter**		sender -- the name of the sender on the local machine.
55690792Sgshapiro**		rpool -- resource pool from which to allocate result
55738032Speter**
55838032Speter**	Returns:
55938032Speter**		The external name for this sender, if derivable from the
56090792Sgshapiro**			database.  Storage allocated from rpool.
56138032Speter**		NULL -- if nothing is changed from the database.
56238032Speter**
56338032Speter**	Side Effects:
56438032Speter**		none.
56538032Speter*/
56638032Speter
56738032Speterchar *
56890792Sgshapiroudbsender(sender, rpool)
56938032Speter	char *sender;
57090792Sgshapiro	SM_RPOOL_T *rpool;
57138032Speter{
57290792Sgshapiro	return udbmatch(sender, "mailname", rpool);
57338032Speter}
57490792Sgshapiro/*
57564562Sgshapiro**  UDBMATCH -- match user in field, return result of lookup.
57664562Sgshapiro**
57764562Sgshapiro**	Parameters:
57864562Sgshapiro**		user -- the name of the user.
57964562Sgshapiro**		field -- the field to lookup.
58090792Sgshapiro**		rpool -- resource pool from which to allocate result
58164562Sgshapiro**
58264562Sgshapiro**	Returns:
58364562Sgshapiro**		The external name for this sender, if derivable from the
58490792Sgshapiro**			database.  Storage allocated from rpool.
58564562Sgshapiro**		NULL -- if nothing is changed from the database.
58664562Sgshapiro**
58764562Sgshapiro**	Side Effects:
58864562Sgshapiro**		none.
58964562Sgshapiro*/
59038032Speter
59164562Sgshapirostatic char *
59290792Sgshapiroudbmatch(user, field, rpool)
59338032Speter	char *user;
59438032Speter	char *field;
59590792Sgshapiro	SM_RPOOL_T *rpool;
59638032Speter{
59738032Speter	register char *p;
59838032Speter	register struct udbent *up;
59938032Speter	int i;
60038032Speter	int keylen;
60138032Speter	DBT key, info;
602157001Sgshapiro	char keybuf[MAXUDBKEY];
60338032Speter
60438032Speter	if (tTd(28, 1))
60590792Sgshapiro		sm_dprintf("udbmatch(%s, %s)\n", user, field);
60638032Speter
60738032Speter	if (!UdbInitialized)
60838032Speter	{
60938032Speter		if (_udbx_init(CurEnv) == EX_TEMPFAIL)
61038032Speter			return NULL;
61138032Speter	}
61238032Speter
61338032Speter	/* short circuit if no spec */
61438032Speter	if (UdbSpec == NULL || UdbSpec[0] == '\0')
61538032Speter		return NULL;
61638032Speter
61738032Speter	/* short circuit name begins with '\\' since it can't possibly match */
61838032Speter	if (user[0] == '\\')
61938032Speter		return NULL;
62038032Speter
62138032Speter	/* long names can never match and are a pain to deal with */
62238032Speter	i = strlen(field);
623168515Sgshapiro	if (i < sizeof("maildrop"))
624168515Sgshapiro		i = sizeof("maildrop");
625168515Sgshapiro	if ((strlen(user) + i) > sizeof(keybuf) - 4)
62638032Speter		return NULL;
62738032Speter
62838032Speter	/* names beginning with colons indicate metadata */
62938032Speter	if (user[0] == ':')
63038032Speter		return NULL;
63138032Speter
63238032Speter	/* build database key */
633168515Sgshapiro	(void) sm_strlcpyn(keybuf, sizeof(keybuf), 3, user, ":", field);
63438032Speter	keylen = strlen(keybuf);
63538032Speter
63638032Speter	for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
63738032Speter	{
63838032Speter		/*
63938032Speter		**  Select action based on entry type.
64038032Speter		*/
64138032Speter
64238032Speter		switch (up->udb_type)
64338032Speter		{
64490792Sgshapiro# if NEWDB
64538032Speter		  case UDB_DBFETCH:
646168515Sgshapiro			memset(&key, '\0', sizeof(key));
647168515Sgshapiro			memset(&info, '\0', sizeof(info));
64838032Speter			key.data = keybuf;
64938032Speter			key.size = keylen;
65064562Sgshapiro#  if DB_VERSION_MAJOR < 2
65138032Speter			i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
65264562Sgshapiro#  else /* DB_VERSION_MAJOR < 2 */
65338032Speter			i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
65438032Speter							&key, &info, 0);
65564562Sgshapiro#  endif /* DB_VERSION_MAJOR < 2 */
65638032Speter			if (i != 0 || info.size <= 0)
65738032Speter			{
65838032Speter				if (tTd(28, 2))
65990792Sgshapiro					sm_dprintf("udbmatch: no match on %s (%d) via db\n",
66064562Sgshapiro						keybuf, keylen);
66138032Speter				continue;
66238032Speter			}
66338032Speter
66490792Sgshapiro			p = sm_rpool_malloc_x(rpool, info.size + 1);
66564562Sgshapiro			memmove(p, info.data, info.size);
66638032Speter			p[info.size] = '\0';
66738032Speter			if (tTd(28, 1))
66890792Sgshapiro				sm_dprintf("udbmatch ==> %s\n", p);
66938032Speter			return p;
67064562Sgshapiro# endif /* NEWDB */
67138032Speter
67290792Sgshapiro# if HESIOD
67338032Speter		  case UDB_HESIOD:
67438032Speter			key.data = keybuf;
67538032Speter			key.size = keylen;
67664562Sgshapiro			i = hes_udb_get(&key, &info);
67738032Speter			if (i != 0 || info.size <= 0)
67838032Speter			{
67938032Speter				if (tTd(28, 2))
68090792Sgshapiro					sm_dprintf("udbmatch: no match on %s (%d) via hesiod\n",
68164562Sgshapiro						keybuf, keylen);
68238032Speter				continue;
68338032Speter			}
68438032Speter
68590792Sgshapiro			p = sm_rpool_malloc_x(rpool, info.size + 1);
68664562Sgshapiro			memmove(p, info.data, info.size);
68738032Speter			p[info.size] = '\0';
68838032Speter			if (tTd(28, 1))
68990792Sgshapiro				sm_dprintf("udbmatch ==> %s\n", p);
69038032Speter			return p;
69164562Sgshapiro# endif /* HESIOD */
69238032Speter		}
69338032Speter	}
69438032Speter
69538032Speter	if (strcmp(field, "mailname") != 0)
69638032Speter		return NULL;
69738032Speter
69838032Speter	/*
69938032Speter	**  Nothing yet.  Search again for a default case.  But only
70038032Speter	**  use it if we also have a forward (:maildrop) pointer already
70138032Speter	**  in the database.
70238032Speter	*/
70338032Speter
70438032Speter	/* build database key */
705168515Sgshapiro	(void) sm_strlcpyn(keybuf, sizeof(keybuf), 2, user, ":maildrop");
70638032Speter	keylen = strlen(keybuf);
70738032Speter
70838032Speter	for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
70938032Speter	{
71038032Speter		switch (up->udb_type)
71138032Speter		{
71290792Sgshapiro# if NEWDB
71338032Speter		  case UDB_DBFETCH:
71438032Speter			/* get the default case for this database */
71538032Speter			if (up->udb_default == NULL)
71638032Speter			{
717168515Sgshapiro				memset(&key, '\0', sizeof(key));
718168515Sgshapiro				memset(&info, '\0', sizeof(info));
71938032Speter				key.data = ":default:mailname";
72038032Speter				key.size = strlen(key.data);
72164562Sgshapiro#  if DB_VERSION_MAJOR < 2
72238032Speter				i = (*up->udb_dbp->get)(up->udb_dbp,
72338032Speter							&key, &info, 0);
72464562Sgshapiro#  else /* DB_VERSION_MAJOR < 2 */
72538032Speter				i = errno = (*up->udb_dbp->get)(up->udb_dbp,
72638032Speter								NULL, &key,
72738032Speter								&info, 0);
72864562Sgshapiro#  endif /* DB_VERSION_MAJOR < 2 */
72938032Speter				if (i != 0 || info.size <= 0)
73038032Speter				{
73138032Speter					/* no default case */
73238032Speter					up->udb_default = "";
73338032Speter					continue;
73438032Speter				}
73538032Speter
73638032Speter				/* save the default case */
73790792Sgshapiro				up->udb_default = sm_pmalloc_x(info.size + 1);
73864562Sgshapiro				memmove(up->udb_default, info.data, info.size);
73938032Speter				up->udb_default[info.size] = '\0';
74038032Speter			}
74138032Speter			else if (up->udb_default[0] == '\0')
74238032Speter				continue;
74338032Speter
74438032Speter			/* we have a default case -- verify user:maildrop */
745168515Sgshapiro			memset(&key, '\0', sizeof(key));
746168515Sgshapiro			memset(&info, '\0', sizeof(info));
74738032Speter			key.data = keybuf;
74838032Speter			key.size = keylen;
74964562Sgshapiro#  if DB_VERSION_MAJOR < 2
75038032Speter			i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
75164562Sgshapiro#  else /* DB_VERSION_MAJOR < 2 */
75238032Speter			i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
75338032Speter							&key, &info, 0);
75464562Sgshapiro#  endif /* DB_VERSION_MAJOR < 2 */
75538032Speter			if (i != 0 || info.size <= 0)
75638032Speter			{
75738032Speter				/* nope -- no aliasing for this user */
75838032Speter				continue;
75938032Speter			}
76038032Speter
76138032Speter			/* they exist -- build the actual address */
76264562Sgshapiro			i = strlen(user) + strlen(up->udb_default) + 2;
76390792Sgshapiro			p = sm_rpool_malloc_x(rpool, i);
76490792Sgshapiro			(void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default);
76538032Speter			if (tTd(28, 1))
76690792Sgshapiro				sm_dprintf("udbmatch ==> %s\n", p);
76738032Speter			return p;
76864562Sgshapiro# endif /* NEWDB */
76938032Speter
77090792Sgshapiro# if HESIOD
77138032Speter		  case UDB_HESIOD:
77238032Speter			/* get the default case for this database */
77338032Speter			if (up->udb_default == NULL)
77438032Speter			{
77538032Speter				key.data = ":default:mailname";
77638032Speter				key.size = strlen(key.data);
77764562Sgshapiro				i = hes_udb_get(&key, &info);
77838032Speter
77938032Speter				if (i != 0 || info.size <= 0)
78038032Speter				{
78138032Speter					/* no default case */
78238032Speter					up->udb_default = "";
78338032Speter					continue;
78438032Speter				}
78538032Speter
78638032Speter				/* save the default case */
78790792Sgshapiro				up->udb_default = sm_pmalloc_x(info.size + 1);
78864562Sgshapiro				memmove(up->udb_default, info.data, info.size);
78938032Speter				up->udb_default[info.size] = '\0';
79038032Speter			}
79138032Speter			else if (up->udb_default[0] == '\0')
79238032Speter				continue;
79338032Speter
79438032Speter			/* we have a default case -- verify user:maildrop */
79538032Speter			key.data = keybuf;
79638032Speter			key.size = keylen;
79738032Speter			i = hes_udb_get(&key, &info);
79838032Speter			if (i != 0 || info.size <= 0)
79938032Speter			{
80038032Speter				/* nope -- no aliasing for this user */
80138032Speter				continue;
80238032Speter			}
80338032Speter
80438032Speter			/* they exist -- build the actual address */
80564562Sgshapiro			i = strlen(user) + strlen(up->udb_default) + 2;
80690792Sgshapiro			p = sm_rpool_malloc_x(rpool, i);
80790792Sgshapiro			(void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default);
80838032Speter			if (tTd(28, 1))
80990792Sgshapiro				sm_dprintf("udbmatch ==> %s\n", p);
81038032Speter			return p;
81138032Speter			break;
81264562Sgshapiro# endif /* HESIOD */
81338032Speter		}
81438032Speter	}
81538032Speter
81638032Speter	/* still nothing....  too bad */
81738032Speter	return NULL;
81838032Speter}
81990792Sgshapiro/*
82038032Speter**  UDB_MAP_LOOKUP -- look up arbitrary entry in user database map
82138032Speter**
82238032Speter**	Parameters:
82338032Speter**		map -- the map being queried.
82438032Speter**		name -- the name to look up.
82538032Speter**		av -- arguments to the map lookup.
82638032Speter**		statp -- to get any error status.
82738032Speter**
82838032Speter**	Returns:
82938032Speter**		NULL if name not found in map.
83038032Speter**		The rewritten name otherwise.
83138032Speter*/
83238032Speter
83338032Speter/* ARGSUSED3 */
83438032Speterchar *
83538032Speterudb_map_lookup(map, name, av, statp)
83638032Speter	MAP *map;
83738032Speter	char *name;
83838032Speter	char **av;
83938032Speter	int *statp;
84038032Speter{
84138032Speter	char *val;
84238032Speter	char *key;
84390792Sgshapiro	char *SM_NONVOLATILE result = NULL;
84438032Speter	char keybuf[MAXNAME + 1];
84538032Speter
84638032Speter	if (tTd(28, 20) || tTd(38, 20))
84790792Sgshapiro		sm_dprintf("udb_map_lookup(%s, %s)\n", map->map_mname, name);
84838032Speter
84938032Speter	if (bitset(MF_NOFOLDCASE, map->map_mflags))
85038032Speter	{
85138032Speter		key = name;
85238032Speter	}
85338032Speter	else
85438032Speter	{
85538032Speter		int keysize = strlen(name);
85638032Speter
857168515Sgshapiro		if (keysize > sizeof(keybuf) - 1)
858168515Sgshapiro			keysize = sizeof(keybuf) - 1;
85964562Sgshapiro		memmove(keybuf, name, keysize);
86038032Speter		keybuf[keysize] = '\0';
86138032Speter		makelower(keybuf);
86238032Speter		key = keybuf;
86338032Speter	}
86490792Sgshapiro	val = udbmatch(key, map->map_file, NULL);
86538032Speter	if (val == NULL)
86638032Speter		return NULL;
86790792Sgshapiro	SM_TRY
86890792Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
86990792Sgshapiro			result = map_rewrite(map, name, strlen(name), NULL);
87090792Sgshapiro		else
87190792Sgshapiro			result = map_rewrite(map, val, strlen(val), av);
87290792Sgshapiro	SM_FINALLY
87390792Sgshapiro		sm_free(val);
87490792Sgshapiro	SM_END_TRY
87590792Sgshapiro	return result;
87638032Speter}
87790792Sgshapiro/*
87838032Speter**  _UDBX_INIT -- parse the UDB specification, opening any valid entries.
87938032Speter**
88038032Speter**	Parameters:
88138032Speter**		e -- the current envelope.
88238032Speter**
88338032Speter**	Returns:
88438032Speter**		EX_TEMPFAIL -- if it appeared it couldn't get hold of a
88538032Speter**			database due to a host being down or some similar
88638032Speter**			(recoverable) situation.
88738032Speter**		EX_OK -- otherwise.
88838032Speter**
88938032Speter**	Side Effects:
89038032Speter**		Fills in the UdbEnts structure from UdbSpec.
89138032Speter*/
89238032Speter
89364562Sgshapiro# define MAXUDBOPTS	27
89438032Speter
89564562Sgshapirostatic int
89638032Speter_udbx_init(e)
89738032Speter	ENVELOPE *e;
89838032Speter{
89938032Speter	int ents = 0;
90038032Speter	register char *p;
90138032Speter	register struct udbent *up;
90238032Speter
90338032Speter	if (UdbInitialized)
90438032Speter		return EX_OK;
90538032Speter
90638032Speter# ifdef UDB_DEFAULT_SPEC
90738032Speter	if (UdbSpec == NULL)
90838032Speter		UdbSpec = UDB_DEFAULT_SPEC;
90964562Sgshapiro# endif /* UDB_DEFAULT_SPEC */
91038032Speter
91138032Speter	p = UdbSpec;
91238032Speter	up = UdbEnts;
91338032Speter	while (p != NULL)
91438032Speter	{
91538032Speter		char *spec;
91638032Speter		int l;
91742575Speter		struct udb_option opts[MAXUDBOPTS + 1];
91838032Speter
91938032Speter		while (*p == ' ' || *p == '\t' || *p == ',')
92038032Speter			p++;
92138032Speter		if (*p == '\0')
92238032Speter			break;
92338032Speter		spec = p;
92438032Speter		p = strchr(p, ',');
92538032Speter		if (p != NULL)
92638032Speter			*p++ = '\0';
92738032Speter
92838032Speter		if (ents >= MAXUDBENT)
92938032Speter		{
93038032Speter			syserr("Maximum number of UDB entries exceeded");
93138032Speter			break;
93238032Speter		}
93338032Speter
93438032Speter		/* extract options */
93538032Speter		(void) _udb_parsespec(spec, opts, MAXUDBOPTS);
93638032Speter
93738032Speter		/*
93838032Speter		**  Decode database specification.
93938032Speter		**
94038032Speter		**	In the sendmail tradition, the leading character
94138032Speter		**	defines the semantics of the rest of the entry.
94238032Speter		**
94338032Speter		**	@hostname --	forward email to the indicated host.
94438032Speter		**			This should be the last in the list,
94538032Speter		**			since it always matches the input.
94638032Speter		**	/dbname	 --	search the named database on the local
94738032Speter		**			host using the Berkeley db package.
94838032Speter		**	Hesiod --	search the named database with BIND
94938032Speter		**			using the MIT Hesiod package.
95038032Speter		*/
95138032Speter
95238032Speter		switch (*spec)
95338032Speter		{
95438032Speter		  case '@':	/* forward to remote host */
95538032Speter			up->udb_type = UDB_FORWARD;
95690792Sgshapiro			up->udb_pid = CurrentPid;
95738032Speter			up->udb_fwdhost = spec + 1;
95838032Speter			ents++;
95938032Speter			up++;
96038032Speter			break;
96138032Speter
96290792Sgshapiro# if HESIOD
96338032Speter		  case 'h':	/* use hesiod */
96438032Speter		  case 'H':
96590792Sgshapiro			if (sm_strcasecmp(spec, "hesiod") != 0)
96638032Speter				goto badspec;
96738032Speter			up->udb_type = UDB_HESIOD;
96890792Sgshapiro			up->udb_pid = CurrentPid;
96938032Speter			ents++;
97038032Speter			up++;
97138032Speter			break;
97264562Sgshapiro# endif /* HESIOD */
97338032Speter
97490792Sgshapiro# if NEWDB
97538032Speter		  case '/':	/* look up remote name */
97638032Speter			l = strlen(spec);
97738032Speter			if (l > 3 && strcmp(&spec[l - 3], ".db") == 0)
97838032Speter			{
97938032Speter				up->udb_dbname = spec;
98038032Speter			}
98138032Speter			else
98238032Speter			{
98390792Sgshapiro				up->udb_dbname = sm_pmalloc_x(l + 4);
98490792Sgshapiro				(void) sm_strlcpyn(up->udb_dbname, l + 4, 2,
98590792Sgshapiro						   spec, ".db");
98638032Speter			}
98738032Speter			errno = 0;
98864562Sgshapiro#  if DB_VERSION_MAJOR < 2
98938032Speter			up->udb_dbp = dbopen(up->udb_dbname, O_RDONLY,
99038032Speter					     0644, DB_BTREE, NULL);
99164562Sgshapiro#  else /* DB_VERSION_MAJOR < 2 */
99264562Sgshapiro			{
99364562Sgshapiro				int flags = DB_RDONLY;
99464562Sgshapiro#  if DB_VERSION_MAJOR > 2
99564562Sgshapiro				int ret;
99664562Sgshapiro#  endif /* DB_VERSION_MAJOR > 2 */
99764562Sgshapiro
998110560Sgshapiro				SM_DB_FLAG_ADD(flags);
99964562Sgshapiro				up->udb_dbp = NULL;
100064562Sgshapiro#  if DB_VERSION_MAJOR > 2
100164562Sgshapiro				ret = db_create(&up->udb_dbp, NULL, 0);
100264562Sgshapiro				if (ret != 0)
100364562Sgshapiro				{
100464562Sgshapiro					(void) up->udb_dbp->close(up->udb_dbp,
100564562Sgshapiro								  0);
100664562Sgshapiro					up->udb_dbp = NULL;
100764562Sgshapiro				}
100864562Sgshapiro				else
100964562Sgshapiro				{
101064562Sgshapiro					ret = up->udb_dbp->open(up->udb_dbp,
1011110560Sgshapiro								DBTXN
101264562Sgshapiro								up->udb_dbname,
101364562Sgshapiro								NULL,
101464562Sgshapiro								DB_BTREE,
101564562Sgshapiro								flags,
101664562Sgshapiro								0644);
101764562Sgshapiro					if (ret != 0)
101864562Sgshapiro					{
101973188Sgshapiro#ifdef DB_OLD_VERSION
102073188Sgshapiro						if (ret == DB_OLD_VERSION)
102173188Sgshapiro							ret = EINVAL;
102273188Sgshapiro#endif /* DB_OLD_VERSION */
102364562Sgshapiro						(void) up->udb_dbp->close(up->udb_dbp, 0);
102464562Sgshapiro						up->udb_dbp = NULL;
102564562Sgshapiro					}
102664562Sgshapiro				}
102764562Sgshapiro				errno = ret;
102864562Sgshapiro#  else /* DB_VERSION_MAJOR > 2 */
102964562Sgshapiro				errno = db_open(up->udb_dbname, DB_BTREE,
103064562Sgshapiro						flags, 0644, NULL,
103164562Sgshapiro						NULL, &up->udb_dbp);
103264562Sgshapiro#  endif /* DB_VERSION_MAJOR > 2 */
103364562Sgshapiro			}
103464562Sgshapiro#  endif /* DB_VERSION_MAJOR < 2 */
103538032Speter			if (up->udb_dbp == NULL)
103638032Speter			{
103738032Speter				if (tTd(28, 1))
103838032Speter				{
103964562Sgshapiro					int save_errno = errno;
104038032Speter
104164562Sgshapiro#  if DB_VERSION_MAJOR < 2
104290792Sgshapiro					sm_dprintf("dbopen(%s): %s\n",
104364562Sgshapiro#  else /* DB_VERSION_MAJOR < 2 */
104490792Sgshapiro					sm_dprintf("db_open(%s): %s\n",
104564562Sgshapiro#  endif /* DB_VERSION_MAJOR < 2 */
104638032Speter						up->udb_dbname,
104790792Sgshapiro						sm_errstring(errno));
104864562Sgshapiro					errno = save_errno;
104938032Speter				}
105038032Speter				if (errno != ENOENT && errno != EACCES)
105138032Speter				{
105238032Speter					if (LogLevel > 2)
105338032Speter						sm_syslog(LOG_ERR, e->e_id,
105464562Sgshapiro#  if DB_VERSION_MAJOR < 2
105564562Sgshapiro							  "dbopen(%s): %s",
105664562Sgshapiro#  else /* DB_VERSION_MAJOR < 2 */
105764562Sgshapiro							  "db_open(%s): %s",
105864562Sgshapiro#  endif /* DB_VERSION_MAJOR < 2 */
105964562Sgshapiro							  up->udb_dbname,
106090792Sgshapiro							  sm_errstring(errno));
106138032Speter					up->udb_type = UDB_EOLIST;
106238032Speter					if (up->udb_dbname != spec)
106390792Sgshapiro						sm_free(up->udb_dbname); /* XXX */
106438032Speter					goto tempfail;
106538032Speter				}
106638032Speter				if (up->udb_dbname != spec)
106790792Sgshapiro					sm_free(up->udb_dbname); /* XXX */
106838032Speter				break;
106938032Speter			}
107042575Speter			if (tTd(28, 1))
107142575Speter			{
107264562Sgshapiro#  if DB_VERSION_MAJOR < 2
107390792Sgshapiro				sm_dprintf("_udbx_init: dbopen(%s)\n",
107464562Sgshapiro#  else /* DB_VERSION_MAJOR < 2 */
107590792Sgshapiro				sm_dprintf("_udbx_init: db_open(%s)\n",
107664562Sgshapiro#  endif /* DB_VERSION_MAJOR < 2 */
107742575Speter					up->udb_dbname);
107842575Speter			}
107938032Speter			up->udb_type = UDB_DBFETCH;
108090792Sgshapiro			up->udb_pid = CurrentPid;
108138032Speter			ents++;
108238032Speter			up++;
108338032Speter			break;
108464562Sgshapiro# endif /* NEWDB */
108538032Speter
108638032Speter		  default:
108790792Sgshapiro# if HESIOD
108838032Speterbadspec:
108964562Sgshapiro# endif /* HESIOD */
109038032Speter			syserr("Unknown UDB spec %s", spec);
109138032Speter			break;
109238032Speter		}
109338032Speter	}
109438032Speter	up->udb_type = UDB_EOLIST;
109538032Speter
109638032Speter	if (tTd(28, 4))
109738032Speter	{
109838032Speter		for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
109938032Speter		{
110038032Speter			switch (up->udb_type)
110138032Speter			{
110238032Speter			  case UDB_REMOTE:
110390792Sgshapiro				sm_dprintf("REMOTE: addr %s, timeo %d\n",
110490792Sgshapiro					   anynet_ntoa((SOCKADDR *) &up->udb_addr),
110590792Sgshapiro					   up->udb_timeout);
110638032Speter				break;
110738032Speter
110838032Speter			  case UDB_DBFETCH:
110990792Sgshapiro# if NEWDB
111090792Sgshapiro				sm_dprintf("FETCH: file %s\n",
111138032Speter					up->udb_dbname);
111264562Sgshapiro# else /* NEWDB */
111390792Sgshapiro				sm_dprintf("FETCH\n");
111464562Sgshapiro# endif /* NEWDB */
111538032Speter				break;
111638032Speter
111738032Speter			  case UDB_FORWARD:
111890792Sgshapiro				sm_dprintf("FORWARD: host %s\n",
111938032Speter					up->udb_fwdhost);
112038032Speter				break;
112138032Speter
112238032Speter			  case UDB_HESIOD:
112390792Sgshapiro				sm_dprintf("HESIOD\n");
112438032Speter				break;
112538032Speter
112638032Speter			  default:
112790792Sgshapiro				sm_dprintf("UNKNOWN\n");
112838032Speter				break;
112938032Speter			}
113038032Speter		}
113138032Speter	}
113238032Speter
113390792Sgshapiro	UdbInitialized = true;
113438032Speter	errno = 0;
113538032Speter	return EX_OK;
113638032Speter
113738032Speter	/*
113838032Speter	**  On temporary failure, back out anything we've already done
113938032Speter	*/
114038032Speter
114138032Speter  tempfail:
114290792Sgshapiro# if NEWDB
114338032Speter	for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
114438032Speter	{
114538032Speter		if (up->udb_type == UDB_DBFETCH)
114638032Speter		{
114764562Sgshapiro#  if DB_VERSION_MAJOR < 2
114838032Speter			(*up->udb_dbp->close)(up->udb_dbp);
114964562Sgshapiro#  else /* DB_VERSION_MAJOR < 2 */
115038032Speter			errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
115164562Sgshapiro#  endif /* DB_VERSION_MAJOR < 2 */
115242575Speter			if (tTd(28, 1))
115390792Sgshapiro				sm_dprintf("_udbx_init: db->close(%s)\n",
115442575Speter					up->udb_dbname);
115538032Speter		}
115638032Speter	}
115764562Sgshapiro# endif /* NEWDB */
115838032Speter	return EX_TEMPFAIL;
115938032Speter}
116038032Speter
116164562Sgshapirostatic int
116238032Speter_udb_parsespec(udbspec, opt, maxopts)
116338032Speter	char *udbspec;
116442575Speter	struct udb_option opt[];
116538032Speter	int maxopts;
116638032Speter{
116738032Speter	register char *spec;
116838032Speter	register char *spec_end;
116938032Speter	register int optnum;
117038032Speter
117138032Speter	spec_end = strchr(udbspec, ':');
117238032Speter	for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++)
117338032Speter	{
117438032Speter		register char *p;
117538032Speter
117638032Speter		while (isascii(*spec) && isspace(*spec))
117738032Speter			spec++;
117838032Speter		spec_end = strchr(spec, ':');
117938032Speter		if (spec_end != NULL)
118038032Speter			*spec_end++ = '\0';
118138032Speter
118264562Sgshapiro		opt[optnum].udbo_name = spec;
118364562Sgshapiro		opt[optnum].udbo_val = NULL;
118438032Speter		p = strchr(spec, '=');
118538032Speter		if (p != NULL)
118664562Sgshapiro			opt[optnum].udbo_val = ++p;
118738032Speter	}
118838032Speter	return optnum;
118938032Speter}
119090792Sgshapiro/*
119142575Speter**  _UDBX_CLOSE -- close all file based UDB entries.
119242575Speter**
119342575Speter**	Parameters:
119442575Speter**		none
119542575Speter**
119642575Speter**	Returns:
119742575Speter**		none
119842575Speter*/
119942575Spetervoid
120042575Speter_udbx_close()
120142575Speter{
120242575Speter	struct udbent *up;
120338032Speter
120442575Speter	if (!UdbInitialized)
120542575Speter		return;
120642575Speter
120742575Speter	for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
120842575Speter	{
120990792Sgshapiro		if (up->udb_pid != CurrentPid)
121042575Speter			continue;
121164562Sgshapiro
121290792Sgshapiro# if NEWDB
121342575Speter		if (up->udb_type == UDB_DBFETCH)
121442575Speter		{
121564562Sgshapiro#  if DB_VERSION_MAJOR < 2
121642575Speter			(*up->udb_dbp->close)(up->udb_dbp);
121764562Sgshapiro#  else /* DB_VERSION_MAJOR < 2 */
121842575Speter			errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
121964562Sgshapiro#  endif /* DB_VERSION_MAJOR < 2 */
122042575Speter		}
122142575Speter		if (tTd(28, 1))
1222223067Sgshapiro			sm_dprintf("_udbx_close: db->close(%s)\n",
122342575Speter				up->udb_dbname);
122464562Sgshapiro# endif /* NEWDB */
122542575Speter	}
122642575Speter}
122742575Speter
122890792Sgshapiro# if HESIOD
122938032Speter
123064562Sgshapirostatic int
123138032Speterhes_udb_get(key, info)
123238032Speter	DBT *key;
123338032Speter	DBT *info;
123438032Speter{
123538032Speter	char *name, *type;
123638032Speter	char **hp;
1237157001Sgshapiro	char kbuf[MAXUDBKEY + 1];
123838032Speter
1239168515Sgshapiro	if (sm_strlcpy(kbuf, key->data, sizeof(kbuf)) >= sizeof(kbuf))
124038032Speter		return 0;
124138032Speter	name = kbuf;
124238032Speter	type = strrchr(name, ':');
124338032Speter	if (type == NULL)
124438032Speter		return 1;
124538032Speter	*type++ = '\0';
124638032Speter	if (strchr(name, '@') != NULL)
124738032Speter		return 1;
124838032Speter
124938032Speter	if (tTd(28, 1))
125090792Sgshapiro		sm_dprintf("hes_udb_get(%s, %s)\n", name, type);
125138032Speter
125238032Speter	/* make the hesiod query */
125364562Sgshapiro#  ifdef HESIOD_INIT
125438032Speter	if (HesiodContext == NULL && hesiod_init(&HesiodContext) != 0)
125538032Speter		return -1;
125638032Speter	hp = hesiod_resolve(HesiodContext, name, type);
125764562Sgshapiro#  else /* HESIOD_INIT */
125838032Speter	hp = hes_resolve(name, type);
125964562Sgshapiro#  endif /* HESIOD_INIT */
126038032Speter	*--type = ':';
126164562Sgshapiro#  ifdef HESIOD_INIT
126238032Speter	if (hp == NULL)
126338032Speter		return 1;
126438032Speter	if (*hp == NULL)
126538032Speter	{
126638032Speter		hesiod_free_list(HesiodContext, hp);
126738032Speter		if (errno == ECONNREFUSED || errno == EMSGSIZE)
126838032Speter			return -1;
126938032Speter		return 1;
127038032Speter	}
127164562Sgshapiro#  else /* HESIOD_INIT */
127238032Speter	if (hp == NULL || hp[0] == NULL)
127338032Speter	{
127438032Speter		/* network problem or timeout */
127538032Speter		if (hes_error() == HES_ER_NET)
127638032Speter			return -1;
127738032Speter
127838032Speter		return 1;
127938032Speter	}
128064562Sgshapiro#  endif /* HESIOD_INIT */
128138032Speter	else
128238032Speter	{
128338032Speter		/*
128438032Speter		**  If there are multiple matches, just return the
128538032Speter		**  first one.
128638032Speter		**
128738032Speter		**  XXX These should really be returned; for example,
128838032Speter		**  XXX it is legal for :maildrop to be multi-valued.
128938032Speter		*/
129038032Speter
129138032Speter		info->data = hp[0];
129238032Speter		info->size = (size_t) strlen(info->data);
129338032Speter	}
129438032Speter
129538032Speter	if (tTd(28, 80))
129690792Sgshapiro		sm_dprintf("hes_udb_get => %s\n", *hp);
129738032Speter
129838032Speter	return 0;
129938032Speter}
130064562Sgshapiro# endif /* HESIOD */
130138032Speter
130264562Sgshapiro#else /* USERDB */
130338032Speter
130438032Speterint
130538032Speterudbexpand(a, sendq, aliaslevel, e)
130638032Speter	ADDRESS *a;
130738032Speter	ADDRESS **sendq;
130838032Speter	int aliaslevel;
130938032Speter	ENVELOPE *e;
131038032Speter{
131138032Speter	return EX_OK;
131238032Speter}
131338032Speter
131438032Speter#endif /* USERDB */
1315