map.c revision 132943
138032Speter/*
2120256Sgshapiro * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1992, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1992, 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>
1538032Speter
16132943SgshapiroSM_RCSID("@(#)$Id: map.c,v 8.664 2004/06/28 17:46:13 ca Exp $")
1764562Sgshapiro
1890792Sgshapiro#if LDAPMAP
1990792Sgshapiro# include <sm/ldap.h>
2090792Sgshapiro#endif /* LDAPMAP */
2190792Sgshapiro
2290792Sgshapiro#if NDBM
2338032Speter# include <ndbm.h>
2438032Speter# ifdef R_FIRST
2538032Speter  ERROR README:	You are running the Berkeley DB version of ndbm.h.  See
2638032Speter  ERROR README:	the README file about tweaking Berkeley DB so it can
2738032Speter  ERROR README:	coexist with NDBM, or delete -DNDBM from the Makefile
2838032Speter  ERROR README: and use -DNEWDB instead.
2964562Sgshapiro# endif /* R_FIRST */
3064562Sgshapiro#endif /* NDBM */
3190792Sgshapiro#if NEWDB
32110560Sgshapiro# include "sm/bdb.h"
3364562Sgshapiro#endif /* NEWDB */
3490792Sgshapiro#if NIS
3538032Speter  struct dom_binding;	/* forward reference needed on IRIX */
3638032Speter# include <rpcsvc/ypclnt.h>
3790792Sgshapiro# if NDBM
3838032Speter#  define NDBM_YP_COMPAT	/* create YP-compatible NDBM files */
3964562Sgshapiro# endif /* NDBM */
4064562Sgshapiro#endif /* NIS */
4138032Speter
4290792Sgshapiro#if NEWDB
4364562Sgshapiro# if DB_VERSION_MAJOR < 2
4464562Sgshapirostatic bool	db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
4564562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
4664562Sgshapiro# if DB_VERSION_MAJOR == 2
4764562Sgshapirostatic bool	db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
4864562Sgshapiro# endif /* DB_VERSION_MAJOR == 2 */
4964562Sgshapiro# if DB_VERSION_MAJOR > 2
5064562Sgshapirostatic bool	db_map_open __P((MAP *, int, char *, DBTYPE, void **));
5164562Sgshapiro# endif /* DB_VERSION_MAJOR > 2 */
5264562Sgshapiro#endif /* NEWDB */
5373188Sgshapirostatic bool	extract_canonname __P((char *, char *, char *, char[], int));
5490792Sgshapirostatic void	map_close __P((STAB *, int));
5590792Sgshapirostatic void	map_init __P((STAB *, int));
5664562Sgshapiro#ifdef LDAPMAP
5790792Sgshapirostatic STAB *	ldapmap_findconn __P((SM_LDAP_STRUCT *));
5864562Sgshapiro#endif /* LDAPMAP */
5990792Sgshapiro#if NISPLUS
6064562Sgshapirostatic bool	nisplus_getcanonname __P((char *, int, int *));
6164562Sgshapiro#endif /* NISPLUS */
6290792Sgshapiro#if NIS
6364562Sgshapirostatic bool	nis_getcanonname __P((char *, int, int *));
6464562Sgshapiro#endif /* NIS */
6564562Sgshapiro#if NETINFO
6664562Sgshapirostatic bool	ni_getcanonname __P((char *, int, int *));
6764562Sgshapiro#endif /* NETINFO */
6864562Sgshapirostatic bool	text_getcanonname __P((char *, int, int *));
69132943Sgshapiro#if SOCKETMAP
70132943Sgshapirostatic STAB	*socket_map_findconn __P((const char*));
7164562Sgshapiro
72132943Sgshapiro/* XXX arbitrary limit for sanity */
73132943Sgshapiro# define SOCKETMAP_MAXL 1000000
74132943Sgshapiro#endif /* SOCKETMAP */
75132943Sgshapiro
7690792Sgshapiro/* default error message for trying to open a map in write mode */
7790792Sgshapiro#ifdef ENOSYS
7890792Sgshapiro# define SM_EMAPCANTWRITE	ENOSYS
7990792Sgshapiro#else /* ENOSYS */
8090792Sgshapiro# ifdef EFTYPE
8190792Sgshapiro#  define SM_EMAPCANTWRITE	EFTYPE
8290792Sgshapiro# else /* EFTYPE */
8390792Sgshapiro#  define SM_EMAPCANTWRITE	ENXIO
8490792Sgshapiro# endif /* EFTYPE */
8590792Sgshapiro#endif /* ENOSYS */
8690792Sgshapiro
8738032Speter/*
8838032Speter**  MAP.C -- implementations for various map classes.
8938032Speter**
9038032Speter**	Each map class implements a series of functions:
9138032Speter**
9238032Speter**	bool map_parse(MAP *map, char *args)
9390792Sgshapiro**		Parse the arguments from the config file.  Return true
9490792Sgshapiro**		if they were ok, false otherwise.  Fill in map with the
9538032Speter**		values.
9638032Speter**
9738032Speter**	char *map_lookup(MAP *map, char *key, char **args, int *pstat)
9838032Speter**		Look up the key in the given map.  If found, do any
9938032Speter**		rewriting the map wants (including "args" if desired)
10038032Speter**		and return the value.  Set *pstat to the appropriate status
10138032Speter**		on error and return NULL.  Args will be NULL if called
10238032Speter**		from the alias routines, although this should probably
10338032Speter**		not be relied upon.  It is suggested you call map_rewrite
10438032Speter**		to return the results -- it takes care of null termination
10538032Speter**		and uses a dynamically expanded buffer as needed.
10638032Speter**
10738032Speter**	void map_store(MAP *map, char *key, char *value)
10838032Speter**		Store the key:value pair in the map.
10938032Speter**
11038032Speter**	bool map_open(MAP *map, int mode)
11138032Speter**		Open the map for the indicated mode.  Mode should
11290792Sgshapiro**		be either O_RDONLY or O_RDWR.  Return true if it
11390792Sgshapiro**		was opened successfully, false otherwise.  If the open
11490792Sgshapiro**		failed and the MF_OPTIONAL flag is not set, it should
11538032Speter**		also print an error.  If the MF_ALIAS bit is set
11638032Speter**		and this map class understands the @:@ convention, it
11738032Speter**		should call aliaswait() before returning.
11838032Speter**
11938032Speter**	void map_close(MAP *map)
12038032Speter**		Close the map.
12138032Speter**
12238032Speter**	This file also includes the implementation for getcanonname.
12338032Speter**	It is currently implemented in a pretty ad-hoc manner; it ought
12438032Speter**	to be more properly integrated into the map structure.
12538032Speter*/
12638032Speter
12738032Speter#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
12838032Speter# define LOCK_ON_OPEN	1	/* we can open/create a locked file */
12964562Sgshapiro#else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
13038032Speter# define LOCK_ON_OPEN	0	/* no such luck -- bend over backwards */
13164562Sgshapiro#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
13238032Speter
13390792Sgshapiro/*
13438032Speter**  MAP_PARSEARGS -- parse config line arguments for database lookup
13538032Speter**
13638032Speter**	This is a generic version of the map_parse method.
13738032Speter**
13838032Speter**	Parameters:
13938032Speter**		map -- the map being initialized.
14038032Speter**		ap -- a pointer to the args on the config line.
14138032Speter**
14238032Speter**	Returns:
14390792Sgshapiro**		true -- if everything parsed OK.
14490792Sgshapiro**		false -- otherwise.
14538032Speter**
14638032Speter**	Side Effects:
14738032Speter**		null terminates the filename; stores it in map
14838032Speter*/
14938032Speter
15038032Speterbool
15138032Spetermap_parseargs(map, ap)
15238032Speter	MAP *map;
15338032Speter	char *ap;
15438032Speter{
15538032Speter	register char *p = ap;
15638032Speter
15764562Sgshapiro	/*
15890792Sgshapiro	**  There is no check whether there is really an argument,
15990792Sgshapiro	**  but that's not important enough to warrant extra code.
16064562Sgshapiro	*/
16190792Sgshapiro
16290792Sgshapiro	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
16364562Sgshapiro	map->map_spacesub = SpaceSub;	/* default value */
16438032Speter	for (;;)
16538032Speter	{
16638032Speter		while (isascii(*p) && isspace(*p))
16738032Speter			p++;
16838032Speter		if (*p != '-')
16938032Speter			break;
17038032Speter		switch (*++p)
17138032Speter		{
17238032Speter		  case 'N':
17338032Speter			map->map_mflags |= MF_INCLNULL;
17438032Speter			map->map_mflags &= ~MF_TRY0NULL;
17538032Speter			break;
17638032Speter
17738032Speter		  case 'O':
17838032Speter			map->map_mflags &= ~MF_TRY1NULL;
17938032Speter			break;
18038032Speter
18138032Speter		  case 'o':
18238032Speter			map->map_mflags |= MF_OPTIONAL;
18338032Speter			break;
18438032Speter
18538032Speter		  case 'f':
18638032Speter			map->map_mflags |= MF_NOFOLDCASE;
18738032Speter			break;
18838032Speter
18938032Speter		  case 'm':
19038032Speter			map->map_mflags |= MF_MATCHONLY;
19138032Speter			break;
19238032Speter
19338032Speter		  case 'A':
19438032Speter			map->map_mflags |= MF_APPEND;
19538032Speter			break;
19638032Speter
19738032Speter		  case 'q':
19838032Speter			map->map_mflags |= MF_KEEPQUOTES;
19938032Speter			break;
20038032Speter
20138032Speter		  case 'a':
20238032Speter			map->map_app = ++p;
20338032Speter			break;
20438032Speter
20538032Speter		  case 'T':
20638032Speter			map->map_tapp = ++p;
20738032Speter			break;
20838032Speter
20938032Speter		  case 'k':
21038032Speter			while (isascii(*++p) && isspace(*p))
21138032Speter				continue;
21238032Speter			map->map_keycolnm = p;
21338032Speter			break;
21438032Speter
21538032Speter		  case 'v':
21638032Speter			while (isascii(*++p) && isspace(*p))
21738032Speter				continue;
21838032Speter			map->map_valcolnm = p;
21938032Speter			break;
22038032Speter
22138032Speter		  case 'z':
22238032Speter			if (*++p != '\\')
22338032Speter				map->map_coldelim = *p;
22438032Speter			else
22538032Speter			{
22638032Speter				switch (*++p)
22738032Speter				{
22838032Speter				  case 'n':
22938032Speter					map->map_coldelim = '\n';
23038032Speter					break;
23138032Speter
23238032Speter				  case 't':
23338032Speter					map->map_coldelim = '\t';
23438032Speter					break;
23538032Speter
23638032Speter				  default:
23738032Speter					map->map_coldelim = '\\';
23838032Speter				}
23938032Speter			}
24038032Speter			break;
24138032Speter
24238032Speter		  case 't':
24338032Speter			map->map_mflags |= MF_NODEFER;
24438032Speter			break;
24538032Speter
24664562Sgshapiro
24764562Sgshapiro		  case 'S':
24864562Sgshapiro			map->map_spacesub = *++p;
24938032Speter			break;
25038032Speter
25164562Sgshapiro		  case 'D':
25264562Sgshapiro			map->map_mflags |= MF_DEFER;
25338032Speter			break;
25464562Sgshapiro
25564562Sgshapiro		  default:
25664562Sgshapiro			syserr("Illegal option %c map %s", *p, map->map_mname);
25764562Sgshapiro			break;
25838032Speter		}
25938032Speter		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
26038032Speter			p++;
26138032Speter		if (*p != '\0')
26238032Speter			*p++ = '\0';
26338032Speter	}
26438032Speter	if (map->map_app != NULL)
26538032Speter		map->map_app = newstr(map->map_app);
26638032Speter	if (map->map_tapp != NULL)
26738032Speter		map->map_tapp = newstr(map->map_tapp);
26838032Speter	if (map->map_keycolnm != NULL)
26938032Speter		map->map_keycolnm = newstr(map->map_keycolnm);
27038032Speter	if (map->map_valcolnm != NULL)
27138032Speter		map->map_valcolnm = newstr(map->map_valcolnm);
27238032Speter
27338032Speter	if (*p != '\0')
27438032Speter	{
27538032Speter		map->map_file = p;
27638032Speter		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
27738032Speter			p++;
27838032Speter		if (*p != '\0')
27938032Speter			*p++ = '\0';
28038032Speter		map->map_file = newstr(map->map_file);
28138032Speter	}
28238032Speter
28338032Speter	while (*p != '\0' && isascii(*p) && isspace(*p))
28438032Speter		p++;
28538032Speter	if (*p != '\0')
28638032Speter		map->map_rebuild = newstr(p);
28738032Speter
28838032Speter	if (map->map_file == NULL &&
28938032Speter	    !bitset(MCF_OPTFILE, map->map_class->map_cflags))
29038032Speter	{
29138032Speter		syserr("No file name for %s map %s",
29238032Speter			map->map_class->map_cname, map->map_mname);
29390792Sgshapiro		return false;
29438032Speter	}
29590792Sgshapiro	return true;
29638032Speter}
29790792Sgshapiro/*
29838032Speter**  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
29938032Speter**
30038032Speter**	It also adds the map_app string.  It can be used as a utility
30138032Speter**	in the map_lookup method.
30238032Speter**
30338032Speter**	Parameters:
30438032Speter**		map -- the map that causes this.
30538032Speter**		s -- the string to rewrite, NOT necessarily null terminated.
30638032Speter**		slen -- the length of s.
30738032Speter**		av -- arguments to interpolate into buf.
30838032Speter**
30938032Speter**	Returns:
31038032Speter**		Pointer to rewritten result.  This is static data that
31138032Speter**		should be copied if it is to be saved!
31238032Speter*/
31338032Speter
31438032Speterchar *
31538032Spetermap_rewrite(map, s, slen, av)
31638032Speter	register MAP *map;
31738032Speter	register const char *s;
31838032Speter	size_t slen;
31938032Speter	char **av;
32038032Speter{
32138032Speter	register char *bp;
32238032Speter	register char c;
32338032Speter	char **avp;
32438032Speter	register char *ap;
32538032Speter	size_t l;
32638032Speter	size_t len;
32738032Speter	static size_t buflen = 0;
32838032Speter	static char *buf = NULL;
32938032Speter
33038032Speter	if (tTd(39, 1))
33138032Speter	{
33290792Sgshapiro		sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
33338032Speter		if (av == NULL)
33490792Sgshapiro			sm_dprintf(" (nullv)");
33538032Speter		else
33638032Speter		{
33738032Speter			for (avp = av; *avp != NULL; avp++)
33890792Sgshapiro				sm_dprintf("\n\t%s", *avp);
33938032Speter		}
34090792Sgshapiro		sm_dprintf("\n");
34138032Speter	}
34238032Speter
34338032Speter	/* count expected size of output (can safely overestimate) */
34438032Speter	l = len = slen;
34538032Speter	if (av != NULL)
34638032Speter	{
34738032Speter		const char *sp = s;
34838032Speter
34938032Speter		while (l-- > 0 && (c = *sp++) != '\0')
35038032Speter		{
35138032Speter			if (c != '%')
35238032Speter				continue;
35338032Speter			if (l-- <= 0)
35438032Speter				break;
35538032Speter			c = *sp++;
35638032Speter			if (!(isascii(c) && isdigit(c)))
35738032Speter				continue;
35838032Speter			for (avp = av; --c >= '0' && *avp != NULL; avp++)
35938032Speter				continue;
36038032Speter			if (*avp == NULL)
36138032Speter				continue;
36238032Speter			len += strlen(*avp);
36338032Speter		}
36438032Speter	}
36538032Speter	if (map->map_app != NULL)
36638032Speter		len += strlen(map->map_app);
36738032Speter	if (buflen < ++len)
36838032Speter	{
36938032Speter		/* need to malloc additional space */
37038032Speter		buflen = len;
37138032Speter		if (buf != NULL)
37277349Sgshapiro			sm_free(buf);
37390792Sgshapiro		buf = sm_pmalloc_x(buflen);
37438032Speter	}
37538032Speter
37638032Speter	bp = buf;
37738032Speter	if (av == NULL)
37838032Speter	{
37964562Sgshapiro		memmove(bp, s, slen);
38038032Speter		bp += slen;
38164562Sgshapiro
38264562Sgshapiro		/* assert(len > slen); */
38364562Sgshapiro		len -= slen;
38438032Speter	}
38538032Speter	else
38638032Speter	{
38738032Speter		while (slen-- > 0 && (c = *s++) != '\0')
38838032Speter		{
38938032Speter			if (c != '%')
39038032Speter			{
39138032Speter  pushc:
392120256Sgshapiro				if (len-- <= 1)
39390792Sgshapiro				     break;
39438032Speter				*bp++ = c;
39538032Speter				continue;
39638032Speter			}
39738032Speter			if (slen-- <= 0 || (c = *s++) == '\0')
39838032Speter				c = '%';
39938032Speter			if (c == '%')
40038032Speter				goto pushc;
40138032Speter			if (!(isascii(c) && isdigit(c)))
40238032Speter			{
403120256Sgshapiro				if (len-- <= 1)
404120256Sgshapiro				     break;
40538032Speter				*bp++ = '%';
40638032Speter				goto pushc;
40738032Speter			}
40838032Speter			for (avp = av; --c >= '0' && *avp != NULL; avp++)
40938032Speter				continue;
41038032Speter			if (*avp == NULL)
41138032Speter				continue;
41238032Speter
41338032Speter			/* transliterate argument into output string */
41464562Sgshapiro			for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
41538032Speter				*bp++ = c;
41638032Speter		}
41738032Speter	}
41864562Sgshapiro	if (map->map_app != NULL && len > 0)
41990792Sgshapiro		(void) sm_strlcpy(bp, map->map_app, len);
42038032Speter	else
42138032Speter		*bp = '\0';
42238032Speter	if (tTd(39, 1))
42390792Sgshapiro		sm_dprintf("map_rewrite => %s\n", buf);
42438032Speter	return buf;
42538032Speter}
42690792Sgshapiro/*
42764562Sgshapiro**  INITMAPS -- rebuild alias maps
42838032Speter**
42938032Speter**	Parameters:
43064562Sgshapiro**		none.
43138032Speter**
43238032Speter**	Returns:
43338032Speter**		none.
43438032Speter*/
43538032Speter
43638032Spetervoid
43764562Sgshapiroinitmaps()
43838032Speter{
43938032Speter#if XDEBUG
44038032Speter	checkfd012("entering initmaps");
44164562Sgshapiro#endif /* XDEBUG */
44238032Speter	stabapply(map_init, 0);
44338032Speter#if XDEBUG
44438032Speter	checkfd012("exiting initmaps");
44564562Sgshapiro#endif /* XDEBUG */
44638032Speter}
44790792Sgshapiro/*
44864562Sgshapiro**  MAP_INIT -- rebuild a map
44964562Sgshapiro**
45064562Sgshapiro**	Parameters:
45164562Sgshapiro**		s -- STAB entry: if map: try to rebuild
45264562Sgshapiro**		unused -- unused variable
45364562Sgshapiro**
45464562Sgshapiro**	Returns:
45564562Sgshapiro**		none.
45664562Sgshapiro**
45764562Sgshapiro**	Side Effects:
45864562Sgshapiro**		will close already open rebuildable map.
45964562Sgshapiro*/
46038032Speter
46164562Sgshapiro/* ARGSUSED1 */
46264562Sgshapirostatic void
46364562Sgshapiromap_init(s, unused)
46438032Speter	register STAB *s;
46564562Sgshapiro	int unused;
46638032Speter{
46738032Speter	register MAP *map;
46838032Speter
46938032Speter	/* has to be a map */
47090792Sgshapiro	if (s->s_symtype != ST_MAP)
47138032Speter		return;
47238032Speter
47338032Speter	map = &s->s_map;
47438032Speter	if (!bitset(MF_VALID, map->map_mflags))
47538032Speter		return;
47638032Speter
47738032Speter	if (tTd(38, 2))
47890792Sgshapiro		sm_dprintf("map_init(%s:%s, %s)\n",
47938032Speter			map->map_class->map_cname == NULL ? "NULL" :
48038032Speter				map->map_class->map_cname,
48138032Speter			map->map_mname == NULL ? "NULL" : map->map_mname,
48264562Sgshapiro			map->map_file == NULL ? "NULL" : map->map_file);
48338032Speter
48464562Sgshapiro	if (!bitset(MF_ALIAS, map->map_mflags) ||
48564562Sgshapiro	    !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
48638032Speter	{
48738032Speter		if (tTd(38, 3))
48890792Sgshapiro			sm_dprintf("\tnot rebuildable\n");
48938032Speter		return;
49038032Speter	}
49138032Speter
49238032Speter	/* if already open, close it (for nested open) */
49338032Speter	if (bitset(MF_OPEN, map->map_mflags))
49438032Speter	{
49577349Sgshapiro		map->map_mflags |= MF_CLOSING;
49638032Speter		map->map_class->map_close(map);
49777349Sgshapiro		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
49838032Speter	}
49938032Speter
50090792Sgshapiro	(void) rebuildaliases(map, false);
50164562Sgshapiro	return;
50264562Sgshapiro}
50390792Sgshapiro/*
50464562Sgshapiro**  OPENMAP -- open a map
50564562Sgshapiro**
50664562Sgshapiro**	Parameters:
50764562Sgshapiro**		map -- map to open (it must not be open).
50864562Sgshapiro**
50964562Sgshapiro**	Returns:
51064562Sgshapiro**		whether open succeeded.
51164562Sgshapiro*/
51264562Sgshapiro
51364562Sgshapirobool
51464562Sgshapiroopenmap(map)
51564562Sgshapiro	MAP *map;
51664562Sgshapiro{
51790792Sgshapiro	bool restore = false;
51864562Sgshapiro	bool savehold = HoldErrs;
51964562Sgshapiro	bool savequick = QuickAbort;
52064562Sgshapiro	int saveerrors = Errors;
52164562Sgshapiro
52264562Sgshapiro	if (!bitset(MF_VALID, map->map_mflags))
52390792Sgshapiro		return false;
52464562Sgshapiro
52564562Sgshapiro	/* better safe than sorry... */
52664562Sgshapiro	if (bitset(MF_OPEN, map->map_mflags))
52790792Sgshapiro		return true;
52864562Sgshapiro
52964562Sgshapiro	/* Don't send a map open error out via SMTP */
53064562Sgshapiro	if ((OnlyOneError || QuickAbort) &&
53164562Sgshapiro	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
53238032Speter	{
53390792Sgshapiro		restore = true;
53490792Sgshapiro		HoldErrs = true;
53590792Sgshapiro		QuickAbort = false;
53638032Speter	}
53738032Speter
53864562Sgshapiro	errno = 0;
53938032Speter	if (map->map_class->map_open(map, O_RDONLY))
54038032Speter	{
54138032Speter		if (tTd(38, 4))
54290792Sgshapiro			sm_dprintf("openmap()\t%s:%s %s: valid\n",
54338032Speter				map->map_class->map_cname == NULL ? "NULL" :
54438032Speter					map->map_class->map_cname,
54538032Speter				map->map_mname == NULL ? "NULL" :
54638032Speter					map->map_mname,
54738032Speter				map->map_file == NULL ? "NULL" :
54838032Speter					map->map_file);
54938032Speter		map->map_mflags |= MF_OPEN;
55090792Sgshapiro		map->map_pid = CurrentPid;
55138032Speter	}
55238032Speter	else
55338032Speter	{
55438032Speter		if (tTd(38, 4))
55590792Sgshapiro			sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
55638032Speter				map->map_class->map_cname == NULL ? "NULL" :
55738032Speter					map->map_class->map_cname,
55838032Speter				map->map_mname == NULL ? "NULL" :
55938032Speter					map->map_mname,
56038032Speter				map->map_file == NULL ? "NULL" :
56138032Speter					map->map_file,
56264562Sgshapiro				errno == 0 ? "" : ": ",
56390792Sgshapiro				errno == 0 ? "" : sm_errstring(errno));
56438032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
56538032Speter		{
56638032Speter			extern MAPCLASS BogusMapClass;
56738032Speter
56890792Sgshapiro			map->map_orgclass = map->map_class;
56938032Speter			map->map_class = &BogusMapClass;
57090792Sgshapiro			map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
57190792Sgshapiro			map->map_pid = CurrentPid;
57238032Speter		}
57364562Sgshapiro		else
57464562Sgshapiro		{
57564562Sgshapiro			/* don't try again */
57664562Sgshapiro			map->map_mflags &= ~MF_VALID;
57764562Sgshapiro		}
57838032Speter	}
57964562Sgshapiro
58064562Sgshapiro	if (restore)
58164562Sgshapiro	{
58264562Sgshapiro		Errors = saveerrors;
58364562Sgshapiro		HoldErrs = savehold;
58464562Sgshapiro		QuickAbort = savequick;
58564562Sgshapiro	}
58664562Sgshapiro
58764562Sgshapiro	return bitset(MF_OPEN, map->map_mflags);
58838032Speter}
58990792Sgshapiro/*
59042575Speter**  CLOSEMAPS -- close all open maps opened by the current pid.
59142575Speter**
59242575Speter**	Parameters:
59390792Sgshapiro**		bogus -- only close bogus maps.
59442575Speter**
59542575Speter**	Returns:
59642575Speter**		none.
59742575Speter*/
59842575Speter
59942575Spetervoid
60090792Sgshapiroclosemaps(bogus)
60190792Sgshapiro	bool bogus;
60242575Speter{
60390792Sgshapiro	stabapply(map_close, bogus);
60442575Speter}
60590792Sgshapiro/*
60664562Sgshapiro**  MAP_CLOSE -- close a map opened by the current pid.
60764562Sgshapiro**
60864562Sgshapiro**	Parameters:
60990792Sgshapiro**		s -- STAB entry: if map: try to close
61090792Sgshapiro**		bogus -- only close bogus maps or MCF_NOTPERSIST maps.
61164562Sgshapiro**
61264562Sgshapiro**	Returns:
61364562Sgshapiro**		none.
61464562Sgshapiro*/
61542575Speter
61642575Speter/* ARGSUSED1 */
61764562Sgshapirostatic void
61890792Sgshapiromap_close(s, bogus)
61942575Speter	register STAB *s;
62090792Sgshapiro	int bogus;	/* int because of stabapply(), used as bool */
62142575Speter{
62242575Speter	MAP *map;
62390792Sgshapiro	extern MAPCLASS BogusMapClass;
62442575Speter
62590792Sgshapiro	if (s->s_symtype != ST_MAP)
62642575Speter		return;
62764562Sgshapiro
62842575Speter	map = &s->s_map;
62942575Speter
63090792Sgshapiro	/*
63190792Sgshapiro	**  close the map iff:
63290792Sgshapiro	**  it is valid and open and opened by this process
63390792Sgshapiro	**  and (!bogus or it's a bogus map or it is not persistent)
63490792Sgshapiro	**  negate this: return iff
63590792Sgshapiro	**  it is not valid or it is not open or not opened by this process
63690792Sgshapiro	**  or (bogus and it's not a bogus map and it's not not-persistent)
63790792Sgshapiro	*/
63890792Sgshapiro
63942575Speter	if (!bitset(MF_VALID, map->map_mflags) ||
64042575Speter	    !bitset(MF_OPEN, map->map_mflags) ||
64177349Sgshapiro	    bitset(MF_CLOSING, map->map_mflags) ||
64290792Sgshapiro	    map->map_pid != CurrentPid ||
64390792Sgshapiro	    (bogus && map->map_class != &BogusMapClass &&
64490792Sgshapiro	     !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
64542575Speter		return;
64664562Sgshapiro
64790792Sgshapiro	if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
64890792Sgshapiro	    map->map_orgclass != &BogusMapClass)
64990792Sgshapiro		map->map_class = map->map_orgclass;
65042575Speter	if (tTd(38, 5))
65190792Sgshapiro		sm_dprintf("closemaps: closing %s (%s)\n",
65264562Sgshapiro			map->map_mname == NULL ? "NULL" : map->map_mname,
65364562Sgshapiro			map->map_file == NULL ? "NULL" : map->map_file);
65464562Sgshapiro
65590792Sgshapiro	if (!bitset(MF_OPENBOGUS, map->map_mflags))
65690792Sgshapiro	{
65790792Sgshapiro		map->map_mflags |= MF_CLOSING;
65890792Sgshapiro		map->map_class->map_close(map);
65990792Sgshapiro	}
66090792Sgshapiro	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
66142575Speter}
66290792Sgshapiro/*
66338032Speter**  GETCANONNAME -- look up name using service switch
66438032Speter**
66538032Speter**	Parameters:
66638032Speter**		host -- the host name to look up.
66738032Speter**		hbsize -- the size of the host buffer.
66838032Speter**		trymx -- if set, try MX records.
66990792Sgshapiro**		pttl -- pointer to return TTL (can be NULL).
67038032Speter**
67138032Speter**	Returns:
67290792Sgshapiro**		true -- if the host was found.
67390792Sgshapiro**		false -- otherwise.
67438032Speter*/
67538032Speter
67638032Speterbool
67790792Sgshapirogetcanonname(host, hbsize, trymx, pttl)
67838032Speter	char *host;
67938032Speter	int hbsize;
68038032Speter	bool trymx;
68190792Sgshapiro	int *pttl;
68238032Speter{
68338032Speter	int nmaps;
68438032Speter	int mapno;
68590792Sgshapiro	bool found = false;
68690792Sgshapiro	bool got_tempfail = false;
68764562Sgshapiro	auto int status;
68838032Speter	char *maptype[MAXMAPSTACK];
68938032Speter	short mapreturn[MAXMAPACTIONS];
69038032Speter
69138032Speter	nmaps = switch_map_find("hosts", maptype, mapreturn);
69290792Sgshapiro	if (pttl != 0)
69390792Sgshapiro		*pttl = SM_DEFAULT_TTL;
69438032Speter	for (mapno = 0; mapno < nmaps; mapno++)
69538032Speter	{
69638032Speter		int i;
69738032Speter
69838032Speter		if (tTd(38, 20))
69990792Sgshapiro			sm_dprintf("getcanonname(%s), trying %s\n",
70038032Speter				host, maptype[mapno]);
70138032Speter		if (strcmp("files", maptype[mapno]) == 0)
70238032Speter		{
70364562Sgshapiro			found = text_getcanonname(host, hbsize, &status);
70438032Speter		}
70590792Sgshapiro#if NIS
70638032Speter		else if (strcmp("nis", maptype[mapno]) == 0)
70738032Speter		{
70864562Sgshapiro			found = nis_getcanonname(host, hbsize, &status);
70938032Speter		}
71064562Sgshapiro#endif /* NIS */
71190792Sgshapiro#if NISPLUS
71238032Speter		else if (strcmp("nisplus", maptype[mapno]) == 0)
71338032Speter		{
71464562Sgshapiro			found = nisplus_getcanonname(host, hbsize, &status);
71538032Speter		}
71664562Sgshapiro#endif /* NISPLUS */
71738032Speter#if NAMED_BIND
71838032Speter		else if (strcmp("dns", maptype[mapno]) == 0)
71938032Speter		{
72090792Sgshapiro			found = dns_getcanonname(host, hbsize, trymx, &status,							 pttl);
72138032Speter		}
72264562Sgshapiro#endif /* NAMED_BIND */
72338032Speter#if NETINFO
72438032Speter		else if (strcmp("netinfo", maptype[mapno]) == 0)
72538032Speter		{
72664562Sgshapiro			found = ni_getcanonname(host, hbsize, &status);
72738032Speter		}
72864562Sgshapiro#endif /* NETINFO */
72938032Speter		else
73038032Speter		{
73190792Sgshapiro			found = false;
73264562Sgshapiro			status = EX_UNAVAILABLE;
73338032Speter		}
73438032Speter
73538032Speter		/*
73638032Speter		**  Heuristic: if $m is not set, we are running during system
73738032Speter		**  startup.  In this case, when a name is apparently found
73838032Speter		**  but has no dot, treat is as not found.  This avoids
73938032Speter		**  problems if /etc/hosts has no FQDN but is listed first
74038032Speter		**  in the service switch.
74138032Speter		*/
74238032Speter
74338032Speter		if (found &&
74438032Speter		    (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
74538032Speter			break;
74638032Speter
74738032Speter		/* see if we should continue */
74864562Sgshapiro		if (status == EX_TEMPFAIL)
74938032Speter		{
75038032Speter			i = MA_TRYAGAIN;
75190792Sgshapiro			got_tempfail = true;
75238032Speter		}
75364562Sgshapiro		else if (status == EX_NOTFOUND)
75438032Speter			i = MA_NOTFOUND;
75538032Speter		else
75638032Speter			i = MA_UNAVAIL;
75738032Speter		if (bitset(1 << mapno, mapreturn[i]))
75838032Speter			break;
75938032Speter	}
76038032Speter
76138032Speter	if (found)
76238032Speter	{
76338032Speter		char *d;
76438032Speter
76538032Speter		if (tTd(38, 20))
76690792Sgshapiro			sm_dprintf("getcanonname(%s), found\n", host);
76738032Speter
76838032Speter		/*
76938032Speter		**  If returned name is still single token, compensate
77038032Speter		**  by tagging on $m.  This is because some sites set
77138032Speter		**  up their DNS or NIS databases wrong.
77238032Speter		*/
77338032Speter
77438032Speter		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
77538032Speter		{
77638032Speter			d = macvalue('m', CurEnv);
77738032Speter			if (d != NULL &&
77838032Speter			    hbsize > (int) (strlen(host) + strlen(d) + 1))
77938032Speter			{
78038032Speter				if (host[strlen(host) - 1] != '.')
78190792Sgshapiro					(void) sm_strlcat2(host, ".", d,
78290792Sgshapiro							   hbsize);
78390792Sgshapiro				else
78490792Sgshapiro					(void) sm_strlcat(host, d, hbsize);
78538032Speter			}
78638032Speter			else
78790792Sgshapiro				return false;
78838032Speter		}
78990792Sgshapiro		return true;
79038032Speter	}
79138032Speter
79238032Speter	if (tTd(38, 20))
79390792Sgshapiro		sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
79490792Sgshapiro			status);
79538032Speter
79638032Speter	if (got_tempfail)
79773188Sgshapiro		SM_SET_H_ERRNO(TRY_AGAIN);
79838032Speter	else
79973188Sgshapiro		SM_SET_H_ERRNO(HOST_NOT_FOUND);
80090792Sgshapiro
80190792Sgshapiro	return false;
80238032Speter}
80390792Sgshapiro/*
80438032Speter**  EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
80538032Speter**
80638032Speter**	Parameters:
80738032Speter**		name -- the name against which to match.
80873188Sgshapiro**		dot -- where to reinsert '.' to get FQDN
80938032Speter**		line -- the /etc/hosts line.
81038032Speter**		cbuf -- the location to store the result.
81138032Speter**		cbuflen -- the size of cbuf.
81238032Speter**
81338032Speter**	Returns:
81490792Sgshapiro**		true -- if the line matched the desired name.
81590792Sgshapiro**		false -- otherwise.
81638032Speter*/
81738032Speter
81864562Sgshapirostatic bool
81973188Sgshapiroextract_canonname(name, dot, line, cbuf, cbuflen)
82038032Speter	char *name;
82173188Sgshapiro	char *dot;
82238032Speter	char *line;
82338032Speter	char cbuf[];
82438032Speter	int cbuflen;
82538032Speter{
82638032Speter	int i;
82738032Speter	char *p;
82890792Sgshapiro	bool found = false;
82938032Speter
83038032Speter	cbuf[0] = '\0';
83138032Speter	if (line[0] == '#')
83290792Sgshapiro		return false;
83338032Speter
83438032Speter	for (i = 1; ; i++)
83538032Speter	{
83638032Speter		char nbuf[MAXNAME + 1];
83738032Speter
83838032Speter		p = get_column(line, i, '\0', nbuf, sizeof nbuf);
83938032Speter		if (p == NULL)
84038032Speter			break;
84138032Speter		if (*p == '\0')
84238032Speter			continue;
84338032Speter		if (cbuf[0] == '\0' ||
84438032Speter		    (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
84538032Speter		{
84690792Sgshapiro			(void) sm_strlcpy(cbuf, p, cbuflen);
84738032Speter		}
84890792Sgshapiro		if (sm_strcasecmp(name, p) == 0)
84990792Sgshapiro			found = true;
85073188Sgshapiro		else if (dot != NULL)
85173188Sgshapiro		{
85273188Sgshapiro			/* try looking for the FQDN as well */
85373188Sgshapiro			*dot = '.';
85490792Sgshapiro			if (sm_strcasecmp(name, p) == 0)
85590792Sgshapiro				found = true;
85673188Sgshapiro			*dot = '\0';
85773188Sgshapiro		}
85838032Speter	}
85938032Speter	if (found && strchr(cbuf, '.') == NULL)
86038032Speter	{
86138032Speter		/* try to add a domain on the end of the name */
86238032Speter		char *domain = macvalue('m', CurEnv);
86338032Speter
86438032Speter		if (domain != NULL &&
86564562Sgshapiro		    strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
86638032Speter		{
86764562Sgshapiro			p = &cbuf[i];
86838032Speter			*p++ = '.';
86990792Sgshapiro			(void) sm_strlcpy(p, domain, cbuflen - i - 1);
87038032Speter		}
87138032Speter	}
87238032Speter	return found;
87338032Speter}
87490792Sgshapiro
87590792Sgshapiro/*
87690792Sgshapiro**  DNS modules
87790792Sgshapiro*/
87890792Sgshapiro
87990792Sgshapiro#if NAMED_BIND
88090792Sgshapiro# if DNSMAP
88190792Sgshapiro
88290792Sgshapiro#  include "sm_resolve.h"
88390792Sgshapiro#  if NETINET || NETINET6
88490792Sgshapiro#   include <arpa/inet.h>
88590792Sgshapiro#  endif /* NETINET || NETINET6 */
88690792Sgshapiro
88790792Sgshapiro/*
88890792Sgshapiro**  DNS_MAP_OPEN -- stub to check proper value for dns map type
88990792Sgshapiro*/
89090792Sgshapiro
89190792Sgshapirobool
89290792Sgshapirodns_map_open(map, mode)
89390792Sgshapiro	MAP *map;
89490792Sgshapiro	int mode;
89590792Sgshapiro{
89690792Sgshapiro	if (tTd(38,2))
89790792Sgshapiro		sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
89890792Sgshapiro
89990792Sgshapiro	mode &= O_ACCMODE;
90090792Sgshapiro	if (mode != O_RDONLY)
90190792Sgshapiro	{
90290792Sgshapiro		/* issue a pseudo-error message */
90390792Sgshapiro		errno = SM_EMAPCANTWRITE;
90490792Sgshapiro		return false;
90590792Sgshapiro	}
90690792Sgshapiro	return true;
90790792Sgshapiro}
90890792Sgshapiro
90990792Sgshapiro/*
91090792Sgshapiro**  DNS_MAP_PARSEARGS -- parse dns map definition args.
91190792Sgshapiro**
91290792Sgshapiro**	Parameters:
91390792Sgshapiro**		map -- pointer to MAP
91490792Sgshapiro**		args -- pointer to the args on the config line.
91590792Sgshapiro**
91690792Sgshapiro**	Returns:
91790792Sgshapiro**		true -- if everything parsed OK.
91890792Sgshapiro**		false -- otherwise.
91990792Sgshapiro*/
92090792Sgshapiro
92190792Sgshapiro#  if _FFR_DNSMAP_MULTILIMIT
92290792Sgshapiro#   if !_FFR_DNSMAP_MULTI
92390792Sgshapiro  ERROR README:	You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT
92490792Sgshapiro#   endif /* ! _FFR_DNSMAP_MULTI */
92590792Sgshapiro#  endif /* _FFR_DNSMAP_MULTILIMIT */
92690792Sgshapiro
92790792Sgshapiro#  if _FFR_DNSMAP_MULTI
92890792Sgshapiro#   if _FFR_DNSMAP_MULTILIMIT
92990792Sgshapiro#    define map_sizelimit	map_lockfd	/* overload field */
93090792Sgshapiro#   endif /* _FFR_DNSMAP_MULTILIMIT */
93190792Sgshapiro#  endif /* _FFR_DNSMAP_MULTI */
93290792Sgshapiro
93390792Sgshapirostruct dns_map
93490792Sgshapiro{
93590792Sgshapiro	int dns_m_type;
93690792Sgshapiro};
93790792Sgshapiro
93890792Sgshapirobool
93990792Sgshapirodns_map_parseargs(map,args)
94090792Sgshapiro	MAP *map;
94190792Sgshapiro	char *args;
94290792Sgshapiro{
94390792Sgshapiro	register char *p = args;
94490792Sgshapiro	struct dns_map *map_p;
94590792Sgshapiro
94690792Sgshapiro	map_p = (struct dns_map *) xalloc(sizeof *map_p);
94790792Sgshapiro	map_p->dns_m_type = -1;
94890792Sgshapiro	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
94990792Sgshapiro
95090792Sgshapiro	for (;;)
95190792Sgshapiro	{
95290792Sgshapiro		while (isascii(*p) && isspace(*p))
95390792Sgshapiro			p++;
95490792Sgshapiro		if (*p != '-')
95590792Sgshapiro			break;
95690792Sgshapiro		switch (*++p)
95790792Sgshapiro		{
95890792Sgshapiro		  case 'N':
95990792Sgshapiro			map->map_mflags |= MF_INCLNULL;
96090792Sgshapiro			map->map_mflags &= ~MF_TRY0NULL;
96190792Sgshapiro			break;
96290792Sgshapiro
96390792Sgshapiro		  case 'O':
96490792Sgshapiro			map->map_mflags &= ~MF_TRY1NULL;
96590792Sgshapiro			break;
96690792Sgshapiro
96790792Sgshapiro		  case 'o':
96890792Sgshapiro			map->map_mflags |= MF_OPTIONAL;
96990792Sgshapiro			break;
97090792Sgshapiro
97190792Sgshapiro		  case 'f':
97290792Sgshapiro			map->map_mflags |= MF_NOFOLDCASE;
97390792Sgshapiro			break;
97490792Sgshapiro
97590792Sgshapiro		  case 'm':
97690792Sgshapiro			map->map_mflags |= MF_MATCHONLY;
97790792Sgshapiro			break;
97890792Sgshapiro
97990792Sgshapiro		  case 'A':
98090792Sgshapiro			map->map_mflags |= MF_APPEND;
98190792Sgshapiro			break;
98290792Sgshapiro
98390792Sgshapiro		  case 'q':
98490792Sgshapiro			map->map_mflags |= MF_KEEPQUOTES;
98590792Sgshapiro			break;
98690792Sgshapiro
98790792Sgshapiro		  case 't':
98890792Sgshapiro			map->map_mflags |= MF_NODEFER;
98990792Sgshapiro			break;
99090792Sgshapiro
99190792Sgshapiro		  case 'a':
99290792Sgshapiro			map->map_app = ++p;
99390792Sgshapiro			break;
99490792Sgshapiro
99590792Sgshapiro		  case 'T':
99690792Sgshapiro			map->map_tapp = ++p;
99790792Sgshapiro			break;
99890792Sgshapiro
99990792Sgshapiro		  case 'd':
100090792Sgshapiro			{
100190792Sgshapiro				char *h;
100290792Sgshapiro
100390792Sgshapiro				++p;
100490792Sgshapiro				h = strchr(p, ' ');
100590792Sgshapiro				if (h != NULL)
100690792Sgshapiro					*h = '\0';
100790792Sgshapiro				map->map_timeout = convtime(p, 's');
100890792Sgshapiro				if (h != NULL)
100990792Sgshapiro					*h = ' ';
101090792Sgshapiro			}
101190792Sgshapiro			break;
101290792Sgshapiro
101390792Sgshapiro		  case 'r':
101490792Sgshapiro			while (isascii(*++p) && isspace(*p))
101590792Sgshapiro				continue;
101690792Sgshapiro			map->map_retry = atoi(p);
101790792Sgshapiro			break;
101890792Sgshapiro
101990792Sgshapiro#  if _FFR_DNSMAP_MULTI
102090792Sgshapiro		  case 'z':
102190792Sgshapiro			if (*++p != '\\')
102290792Sgshapiro				map->map_coldelim = *p;
102390792Sgshapiro			else
102490792Sgshapiro			{
102590792Sgshapiro				switch (*++p)
102690792Sgshapiro				{
102790792Sgshapiro				  case 'n':
102890792Sgshapiro					map->map_coldelim = '\n';
102990792Sgshapiro					break;
103090792Sgshapiro
103190792Sgshapiro				  case 't':
103290792Sgshapiro					map->map_coldelim = '\t';
103390792Sgshapiro					break;
103490792Sgshapiro
103590792Sgshapiro				  default:
103690792Sgshapiro					map->map_coldelim = '\\';
103790792Sgshapiro				}
103890792Sgshapiro			}
103990792Sgshapiro			break;
104090792Sgshapiro
104190792Sgshapiro#   if _FFR_DNSMAP_MULTILIMIT
104290792Sgshapiro		  case 'Z':
104390792Sgshapiro			while (isascii(*++p) && isspace(*p))
104490792Sgshapiro				continue;
104590792Sgshapiro			map->map_sizelimit = atoi(p);
104690792Sgshapiro			break;
104790792Sgshapiro#   endif /* _FFR_DNSMAP_MULTILIMIT */
104890792Sgshapiro#  endif /* _FFR_DNSMAP_MULTI */
104990792Sgshapiro
105090792Sgshapiro			/* Start of dns_map specific args */
105190792Sgshapiro		  case 'R':		/* search field */
105290792Sgshapiro			{
105390792Sgshapiro				char *h;
105490792Sgshapiro
105590792Sgshapiro				while (isascii(*++p) && isspace(*p))
105690792Sgshapiro					continue;
105790792Sgshapiro				h = strchr(p, ' ');
105890792Sgshapiro				if (h != NULL)
105990792Sgshapiro					*h = '\0';
106090792Sgshapiro				map_p->dns_m_type = dns_string_to_type(p);
106190792Sgshapiro				if (h != NULL)
106290792Sgshapiro					*h = ' ';
106390792Sgshapiro				if (map_p->dns_m_type < 0)
106490792Sgshapiro					syserr("dns map %s: wrong type %s",
106590792Sgshapiro						map->map_mname, p);
106690792Sgshapiro			}
106790792Sgshapiro			break;
106890792Sgshapiro
106990792Sgshapiro#  if _FFR_DNSMAP_BASE
107090792Sgshapiro		  case 'B':		/* base domain */
107190792Sgshapiro			{
107290792Sgshapiro				char *h;
107390792Sgshapiro
107490792Sgshapiro				while (isascii(*++p) && isspace(*p))
107590792Sgshapiro					continue;
107690792Sgshapiro				h = strchr(p, ' ');
107790792Sgshapiro				if (h != NULL)
107890792Sgshapiro					*h = '\0';
107990792Sgshapiro
108090792Sgshapiro				/*
108190792Sgshapiro				**  slight abuse of map->map_file; it isn't
108290792Sgshapiro				**	used otherwise in this map type.
108390792Sgshapiro				*/
108490792Sgshapiro
108590792Sgshapiro				map->map_file = newstr(p);
108690792Sgshapiro				if (h != NULL)
108790792Sgshapiro					*h = ' ';
108890792Sgshapiro			}
108990792Sgshapiro			break;
109090792Sgshapiro#  endif /* _FFR_DNSMAP_BASE */
109190792Sgshapiro
109290792Sgshapiro		}
109390792Sgshapiro		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
109490792Sgshapiro			p++;
109590792Sgshapiro		if (*p != '\0')
109690792Sgshapiro			*p++ = '\0';
109790792Sgshapiro	}
109890792Sgshapiro	if (map_p->dns_m_type < 0)
109990792Sgshapiro		syserr("dns map %s: missing -R type", map->map_mname);
110090792Sgshapiro	if (map->map_app != NULL)
110190792Sgshapiro		map->map_app = newstr(map->map_app);
110290792Sgshapiro	if (map->map_tapp != NULL)
110390792Sgshapiro		map->map_tapp = newstr(map->map_tapp);
110490792Sgshapiro
110590792Sgshapiro	/*
110690792Sgshapiro	**  Assumption: assert(sizeof int <= sizeof(ARBPTR_T));
110790792Sgshapiro	**  Even if this assumption is wrong, we use only one byte,
110890792Sgshapiro	**  so it doesn't really matter.
110990792Sgshapiro	*/
111090792Sgshapiro
111190792Sgshapiro	map->map_db1 = (ARBPTR_T) map_p;
111290792Sgshapiro	return true;
111390792Sgshapiro}
111490792Sgshapiro
111590792Sgshapiro/*
111690792Sgshapiro**  DNS_MAP_LOOKUP -- perform dns map lookup.
111790792Sgshapiro**
111890792Sgshapiro**	Parameters:
111990792Sgshapiro**		map -- pointer to MAP
112090792Sgshapiro**		name -- name to lookup
112190792Sgshapiro**		av -- arguments to interpolate into buf.
112290792Sgshapiro**		statp -- pointer to status (EX_)
112390792Sgshapiro**
112490792Sgshapiro**	Returns:
112590792Sgshapiro**		result of lookup if succeeded.
112690792Sgshapiro**		NULL -- otherwise.
112790792Sgshapiro*/
112890792Sgshapiro
112990792Sgshapirochar *
113090792Sgshapirodns_map_lookup(map, name, av, statp)
113190792Sgshapiro	MAP *map;
113290792Sgshapiro	char *name;
113390792Sgshapiro	char **av;
113490792Sgshapiro	int *statp;
113590792Sgshapiro{
113690792Sgshapiro#  if _FFR_DNSMAP_MULTI
113790792Sgshapiro#   if _FFR_DNSMAP_MULTILIMIT
113890792Sgshapiro	int resnum = 0;
113990792Sgshapiro#   endif /* _FFR_DNSMAP_MULTILIMIT */
114090792Sgshapiro#  endif /* _FFR_DNSMAP_MULTI */
114190792Sgshapiro	char *vp = NULL, *result = NULL;
114290792Sgshapiro	size_t vsize;
114390792Sgshapiro	struct dns_map *map_p;
114490792Sgshapiro	RESOURCE_RECORD_T *rr = NULL;
114590792Sgshapiro	DNS_REPLY_T *r = NULL;
114690792Sgshapiro#  if NETINET6
114790792Sgshapiro	static char buf6[INET6_ADDRSTRLEN];
114890792Sgshapiro#  endif /* NETINET6 */
114990792Sgshapiro
115090792Sgshapiro	if (tTd(38, 20))
115190792Sgshapiro		sm_dprintf("dns_map_lookup(%s, %s)\n",
115290792Sgshapiro			   map->map_mname, name);
115390792Sgshapiro
115490792Sgshapiro	map_p = (struct dns_map *)(map->map_db1);
115590792Sgshapiro#  if _FFR_DNSMAP_BASE
115690792Sgshapiro	if (map->map_file != NULL && *map->map_file != '\0')
115790792Sgshapiro	{
115890792Sgshapiro		size_t len;
115990792Sgshapiro		char *appdomain;
116090792Sgshapiro
116190792Sgshapiro		len = strlen(map->map_file) + strlen(name) + 2;
116290792Sgshapiro		appdomain = (char *) sm_malloc(len);
116390792Sgshapiro		if (appdomain == NULL)
116490792Sgshapiro		{
116590792Sgshapiro			*statp = EX_UNAVAILABLE;
116690792Sgshapiro			return NULL;
116790792Sgshapiro		}
116890792Sgshapiro		(void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
116990792Sgshapiro		r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
117090792Sgshapiro				   map->map_timeout, map->map_retry);
117190792Sgshapiro		sm_free(appdomain);
117290792Sgshapiro	}
117390792Sgshapiro	else
117490792Sgshapiro#  endif /* _FFR_DNSMAP_BASE */
117590792Sgshapiro	{
117690792Sgshapiro		r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
117790792Sgshapiro				   map->map_timeout, map->map_retry);
117890792Sgshapiro	}
117990792Sgshapiro
118090792Sgshapiro	if (r == NULL)
118190792Sgshapiro	{
118290792Sgshapiro		result = NULL;
1183120256Sgshapiro		if (h_errno == TRY_AGAIN || transienterror(errno))
118490792Sgshapiro			*statp = EX_TEMPFAIL;
118590792Sgshapiro		else
118690792Sgshapiro			*statp = EX_NOTFOUND;
118790792Sgshapiro		goto cleanup;
118890792Sgshapiro	}
118990792Sgshapiro	*statp = EX_OK;
119090792Sgshapiro	for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
119190792Sgshapiro	{
119290792Sgshapiro		char *type = NULL;
119390792Sgshapiro		char *value = NULL;
119490792Sgshapiro
119590792Sgshapiro		switch (rr->rr_type)
119690792Sgshapiro		{
119790792Sgshapiro		  case T_NS:
119890792Sgshapiro			type = "T_NS";
119990792Sgshapiro			value = rr->rr_u.rr_txt;
120090792Sgshapiro			break;
120190792Sgshapiro		  case T_CNAME:
120290792Sgshapiro			type = "T_CNAME";
120390792Sgshapiro			value = rr->rr_u.rr_txt;
120490792Sgshapiro			break;
120590792Sgshapiro		  case T_AFSDB:
120690792Sgshapiro			type = "T_AFSDB";
120790792Sgshapiro			value = rr->rr_u.rr_mx->mx_r_domain;
120890792Sgshapiro			break;
120990792Sgshapiro		  case T_SRV:
121090792Sgshapiro			type = "T_SRV";
121190792Sgshapiro			value = rr->rr_u.rr_srv->srv_r_target;
121290792Sgshapiro			break;
121390792Sgshapiro		  case T_PTR:
121490792Sgshapiro			type = "T_PTR";
121590792Sgshapiro			value = rr->rr_u.rr_txt;
121690792Sgshapiro			break;
121790792Sgshapiro		  case T_TXT:
121890792Sgshapiro			type = "T_TXT";
121990792Sgshapiro			value = rr->rr_u.rr_txt;
122090792Sgshapiro			break;
122190792Sgshapiro		  case T_MX:
122290792Sgshapiro			type = "T_MX";
122390792Sgshapiro			value = rr->rr_u.rr_mx->mx_r_domain;
122490792Sgshapiro			break;
122590792Sgshapiro#  if NETINET
122690792Sgshapiro		  case T_A:
122790792Sgshapiro			type = "T_A";
122890792Sgshapiro			value = inet_ntoa(*(rr->rr_u.rr_a));
122990792Sgshapiro			break;
123090792Sgshapiro#  endif /* NETINET */
123190792Sgshapiro#  if NETINET6
123290792Sgshapiro		  case T_AAAA:
123390792Sgshapiro			type = "T_AAAA";
123490792Sgshapiro			value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
123590792Sgshapiro					    sizeof buf6);
123690792Sgshapiro			break;
123790792Sgshapiro#  endif /* NETINET6 */
123890792Sgshapiro		}
123990792Sgshapiro
124098841Sgshapiro		(void) strreplnonprt(value, 'X');
124190792Sgshapiro		if (map_p->dns_m_type != rr->rr_type)
124290792Sgshapiro		{
124390792Sgshapiro			if (tTd(38, 40))
124490792Sgshapiro				sm_dprintf("\tskipping type %s (%d) value %s\n",
124590792Sgshapiro					   type != NULL ? type : "<UNKNOWN>",
124690792Sgshapiro					   rr->rr_type,
124790792Sgshapiro					   value != NULL ? value : "<NO VALUE>");
124890792Sgshapiro			continue;
124990792Sgshapiro		}
125090792Sgshapiro
125190792Sgshapiro#  if NETINET6
125290792Sgshapiro		if (rr->rr_type == T_AAAA && value == NULL)
125390792Sgshapiro		{
125490792Sgshapiro			result = NULL;
125590792Sgshapiro			*statp = EX_DATAERR;
125690792Sgshapiro			if (tTd(38, 40))
125790792Sgshapiro				sm_dprintf("\tbad T_AAAA conversion\n");
125890792Sgshapiro			goto cleanup;
125990792Sgshapiro		}
126090792Sgshapiro#  endif /* NETINET6 */
126190792Sgshapiro		if (tTd(38, 40))
126290792Sgshapiro			sm_dprintf("\tfound type %s (%d) value %s\n",
126390792Sgshapiro				   type != NULL ? type : "<UNKNOWN>",
126490792Sgshapiro				   rr->rr_type,
126590792Sgshapiro				   value != NULL ? value : "<NO VALUE>");
126690792Sgshapiro#  if _FFR_DNSMAP_MULTI
126790792Sgshapiro		if (value != NULL &&
126890792Sgshapiro		    (map->map_coldelim == '\0' ||
126990792Sgshapiro#   if _FFR_DNSMAP_MULTILIMIT
127090792Sgshapiro		     map->map_sizelimit == 1 ||
127190792Sgshapiro#   endif /* _FFR_DNSMAP_MULTILIMIT */
127290792Sgshapiro		     bitset(MF_MATCHONLY, map->map_mflags)))
127390792Sgshapiro		{
127490792Sgshapiro			/* Only care about the first match */
127590792Sgshapiro			vp = newstr(value);
127690792Sgshapiro			break;
127790792Sgshapiro		}
127890792Sgshapiro		else if (vp == NULL)
127990792Sgshapiro		{
128090792Sgshapiro			/* First result */
128190792Sgshapiro			vp = newstr(value);
128290792Sgshapiro		}
128390792Sgshapiro		else
128490792Sgshapiro		{
128590792Sgshapiro			/* concatenate the results */
128690792Sgshapiro			int sz;
128790792Sgshapiro			char *new;
128890792Sgshapiro
128990792Sgshapiro			sz = strlen(vp) + strlen(value) + 2;
129090792Sgshapiro			new = xalloc(sz);
129190792Sgshapiro			(void) sm_snprintf(new, sz, "%s%c%s",
129290792Sgshapiro					   vp, map->map_coldelim, value);
129390792Sgshapiro			sm_free(vp);
129490792Sgshapiro			vp = new;
129590792Sgshapiro#   if _FFR_DNSMAP_MULTILIMIT
129690792Sgshapiro			if (map->map_sizelimit > 0 &&
129790792Sgshapiro			    ++resnum >= map->map_sizelimit)
129890792Sgshapiro				break;
129990792Sgshapiro#   endif /* _FFR_DNSMAP_MULTILIMIT */
130090792Sgshapiro		}
130190792Sgshapiro#  else /* _FFR_DNSMAP_MULTI */
130290792Sgshapiro		vp = value;
130390792Sgshapiro		break;
130490792Sgshapiro#  endif /* _FFR_DNSMAP_MULTI */
130590792Sgshapiro	}
130690792Sgshapiro	if (vp == NULL)
130790792Sgshapiro	{
130890792Sgshapiro		result = NULL;
130990792Sgshapiro		*statp = EX_NOTFOUND;
131090792Sgshapiro		if (tTd(38, 40))
131190792Sgshapiro			sm_dprintf("\tno match found\n");
131290792Sgshapiro		goto cleanup;
131390792Sgshapiro	}
131490792Sgshapiro
131590792Sgshapiro#  if _FFR_DNSMAP_MULTI
131690792Sgshapiro	/* Cleanly truncate for rulesets */
131790792Sgshapiro	truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
131890792Sgshapiro#  endif /* _FFR_DNSMAP_MULTI */
131990792Sgshapiro
132090792Sgshapiro	vsize = strlen(vp);
132190792Sgshapiro
132290792Sgshapiro	if (LogLevel > 9)
132390792Sgshapiro		sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
132490792Sgshapiro			  name, vp);
132590792Sgshapiro	if (bitset(MF_MATCHONLY, map->map_mflags))
132690792Sgshapiro		result = map_rewrite(map, name, strlen(name), NULL);
132790792Sgshapiro	else
132890792Sgshapiro		result = map_rewrite(map, vp, vsize, av);
132990792Sgshapiro
133090792Sgshapiro  cleanup:
133190792Sgshapiro#  if _FFR_DNSMAP_MULTI
133290792Sgshapiro	if (vp != NULL)
133390792Sgshapiro		sm_free(vp);
133490792Sgshapiro#  endif /* _FFR_DNSMAP_MULTI */
133590792Sgshapiro	if (r != NULL)
133690792Sgshapiro		dns_free_data(r);
133790792Sgshapiro	return result;
133890792Sgshapiro}
133990792Sgshapiro# endif /* DNSMAP */
134090792Sgshapiro#endif /* NAMED_BIND */
134190792Sgshapiro
134290792Sgshapiro/*
134338032Speter**  NDBM modules
134438032Speter*/
134538032Speter
134690792Sgshapiro#if NDBM
134738032Speter
134838032Speter/*
134938032Speter**  NDBM_MAP_OPEN -- DBM-style map open
135038032Speter*/
135138032Speter
135238032Speterbool
135338032Speterndbm_map_open(map, mode)
135438032Speter	MAP *map;
135538032Speter	int mode;
135638032Speter{
135738032Speter	register DBM *dbm;
135864562Sgshapiro	int save_errno;
135938032Speter	int dfd;
136038032Speter	int pfd;
136164562Sgshapiro	long sff;
136238032Speter	int ret;
136338032Speter	int smode = S_IREAD;
136498121Sgshapiro	char dirfile[MAXPATHLEN];
136598121Sgshapiro	char pagfile[MAXPATHLEN];
136664562Sgshapiro	struct stat st;
136738032Speter	struct stat std, stp;
136838032Speter
136938032Speter	if (tTd(38, 2))
137090792Sgshapiro		sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
137138032Speter			map->map_mname, map->map_file, mode);
137238032Speter	map->map_lockfd = -1;
137338032Speter	mode &= O_ACCMODE;
137438032Speter
137538032Speter	/* do initial file and directory checks */
137698121Sgshapiro	if (sm_strlcpyn(dirfile, sizeof dirfile, 2,
137798121Sgshapiro			map->map_file, ".dir") >= sizeof dirfile ||
137898121Sgshapiro	    sm_strlcpyn(pagfile, sizeof pagfile, 2,
137998121Sgshapiro			map->map_file, ".pag") >= sizeof pagfile)
138098121Sgshapiro	{
138198121Sgshapiro		errno = 0;
138298121Sgshapiro		if (!bitset(MF_OPTIONAL, map->map_mflags))
138398121Sgshapiro			syserr("dbm map \"%s\": map file %s name too long",
138498121Sgshapiro				map->map_mname, map->map_file);
138598121Sgshapiro		return false;
138698121Sgshapiro	}
138738032Speter	sff = SFF_ROOTOK|SFF_REGONLY;
138838032Speter	if (mode == O_RDWR)
138938032Speter	{
139038032Speter		sff |= SFF_CREAT;
139164562Sgshapiro		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
139238032Speter			sff |= SFF_NOSLINK;
139364562Sgshapiro		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
139438032Speter			sff |= SFF_NOHLINK;
139538032Speter		smode = S_IWRITE;
139638032Speter	}
139738032Speter	else
139838032Speter	{
139964562Sgshapiro		if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
140038032Speter			sff |= SFF_NOWLINK;
140138032Speter	}
140264562Sgshapiro	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
140338032Speter		sff |= SFF_SAFEDIRPATH;
140438032Speter	ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
140590792Sgshapiro		       sff, smode, &std);
140638032Speter	if (ret == 0)
140738032Speter		ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
140838032Speter			       sff, smode, &stp);
140964562Sgshapiro
141038032Speter	if (ret != 0)
141138032Speter	{
141238032Speter		char *prob = "unsafe";
141338032Speter
141438032Speter		/* cannot open this map */
141538032Speter		if (ret == ENOENT)
141638032Speter			prob = "missing";
141738032Speter		if (tTd(38, 2))
141890792Sgshapiro			sm_dprintf("\t%s map file: %d\n", prob, ret);
141938032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
142038032Speter			syserr("dbm map \"%s\": %s map file %s",
142138032Speter				map->map_mname, prob, map->map_file);
142290792Sgshapiro		return false;
142338032Speter	}
142438032Speter	if (std.st_mode == ST_MODE_NOFILE)
142538032Speter		mode |= O_CREAT|O_EXCL;
142638032Speter
142764562Sgshapiro# if LOCK_ON_OPEN
142838032Speter	if (mode == O_RDONLY)
142938032Speter		mode |= O_SHLOCK;
143038032Speter	else
143138032Speter		mode |= O_TRUNC|O_EXLOCK;
143264562Sgshapiro# else /* LOCK_ON_OPEN */
143338032Speter	if ((mode & O_ACCMODE) == O_RDWR)
143438032Speter	{
143564562Sgshapiro#  if NOFTRUNCATE
143638032Speter		/*
143738032Speter		**  Warning: race condition.  Try to lock the file as
143838032Speter		**  quickly as possible after opening it.
143938032Speter		**	This may also have security problems on some systems,
144038032Speter		**	but there isn't anything we can do about it.
144138032Speter		*/
144238032Speter
144338032Speter		mode |= O_TRUNC;
144464562Sgshapiro#  else /* NOFTRUNCATE */
144538032Speter		/*
144638032Speter		**  This ugly code opens the map without truncating it,
144738032Speter		**  locks the file, then truncates it.  Necessary to
144838032Speter		**  avoid race conditions.
144938032Speter		*/
145038032Speter
145138032Speter		int dirfd;
145238032Speter		int pagfd;
145364562Sgshapiro		long sff = SFF_CREAT|SFF_OPENASROOT;
145438032Speter
145564562Sgshapiro		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
145638032Speter			sff |= SFF_NOSLINK;
145764562Sgshapiro		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
145838032Speter			sff |= SFF_NOHLINK;
145938032Speter
146038032Speter		dirfd = safeopen(dirfile, mode, DBMMODE, sff);
146138032Speter		pagfd = safeopen(pagfile, mode, DBMMODE, sff);
146238032Speter
146338032Speter		if (dirfd < 0 || pagfd < 0)
146438032Speter		{
146564562Sgshapiro			save_errno = errno;
146638032Speter			if (dirfd >= 0)
146738032Speter				(void) close(dirfd);
146838032Speter			if (pagfd >= 0)
146938032Speter				(void) close(pagfd);
147038032Speter			errno = save_errno;
147138032Speter			syserr("ndbm_map_open: cannot create database %s",
147238032Speter				map->map_file);
147390792Sgshapiro			return false;
147438032Speter		}
147538032Speter		if (ftruncate(dirfd, (off_t) 0) < 0 ||
147638032Speter		    ftruncate(pagfd, (off_t) 0) < 0)
147738032Speter		{
147864562Sgshapiro			save_errno = errno;
147938032Speter			(void) close(dirfd);
148038032Speter			(void) close(pagfd);
148138032Speter			errno = save_errno;
148238032Speter			syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
148338032Speter				map->map_file);
148490792Sgshapiro			return false;
148538032Speter		}
148638032Speter
148738032Speter		/* if new file, get "before" bits for later filechanged check */
148838032Speter		if (std.st_mode == ST_MODE_NOFILE &&
148938032Speter		    (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
149038032Speter		{
149164562Sgshapiro			save_errno = errno;
149238032Speter			(void) close(dirfd);
149338032Speter			(void) close(pagfd);
149438032Speter			errno = save_errno;
149538032Speter			syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
149638032Speter				map->map_file);
149790792Sgshapiro			return false;
149838032Speter		}
149938032Speter
150038032Speter		/* have to save the lock for the duration (bletch) */
150138032Speter		map->map_lockfd = dirfd;
150264562Sgshapiro		(void) close(pagfd);
150338032Speter
150438032Speter		/* twiddle bits for dbm_open */
150538032Speter		mode &= ~(O_CREAT|O_EXCL);
150664562Sgshapiro#  endif /* NOFTRUNCATE */
150738032Speter	}
150864562Sgshapiro# endif /* LOCK_ON_OPEN */
150938032Speter
151038032Speter	/* open the database */
151138032Speter	dbm = dbm_open(map->map_file, mode, DBMMODE);
151238032Speter	if (dbm == NULL)
151338032Speter	{
151464562Sgshapiro		save_errno = errno;
151538032Speter		if (bitset(MF_ALIAS, map->map_mflags) &&
151690792Sgshapiro		    aliaswait(map, ".pag", false))
151790792Sgshapiro			return true;
151864562Sgshapiro# if !LOCK_ON_OPEN && !NOFTRUNCATE
151938032Speter		if (map->map_lockfd >= 0)
152064562Sgshapiro			(void) close(map->map_lockfd);
152164562Sgshapiro# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
152238032Speter		errno = save_errno;
152338032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
152438032Speter			syserr("Cannot open DBM database %s", map->map_file);
152590792Sgshapiro		return false;
152638032Speter	}
152738032Speter	dfd = dbm_dirfno(dbm);
152838032Speter	pfd = dbm_pagfno(dbm);
152938032Speter	if (dfd == pfd)
153038032Speter	{
153138032Speter		/* heuristic: if files are linked, this is actually gdbm */
153238032Speter		dbm_close(dbm);
153364562Sgshapiro# if !LOCK_ON_OPEN && !NOFTRUNCATE
153438032Speter		if (map->map_lockfd >= 0)
153564562Sgshapiro			(void) close(map->map_lockfd);
153664562Sgshapiro# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
153738032Speter		errno = 0;
153838032Speter		syserr("dbm map \"%s\": cannot support GDBM",
153938032Speter			map->map_mname);
154090792Sgshapiro		return false;
154138032Speter	}
154238032Speter
154338032Speter	if (filechanged(dirfile, dfd, &std) ||
154438032Speter	    filechanged(pagfile, pfd, &stp))
154538032Speter	{
154664562Sgshapiro		save_errno = errno;
154738032Speter		dbm_close(dbm);
154864562Sgshapiro# if !LOCK_ON_OPEN && !NOFTRUNCATE
154938032Speter		if (map->map_lockfd >= 0)
155064562Sgshapiro			(void) close(map->map_lockfd);
155164562Sgshapiro# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
155238032Speter		errno = save_errno;
155338032Speter		syserr("ndbm_map_open(%s): file changed after open",
155438032Speter			map->map_file);
155590792Sgshapiro		return false;
155638032Speter	}
155738032Speter
155838032Speter	map->map_db1 = (ARBPTR_T) dbm;
155964562Sgshapiro
156064562Sgshapiro	/*
156164562Sgshapiro	**  Need to set map_mtime before the call to aliaswait()
156264562Sgshapiro	**  as aliaswait() will call map_lookup() which requires
156364562Sgshapiro	**  map_mtime to be set
156464562Sgshapiro	*/
156564562Sgshapiro
156677349Sgshapiro	if (fstat(pfd, &st) >= 0)
156764562Sgshapiro		map->map_mtime = st.st_mtime;
156864562Sgshapiro
156938032Speter	if (mode == O_RDONLY)
157038032Speter	{
157164562Sgshapiro# if LOCK_ON_OPEN
157238032Speter		if (dfd >= 0)
157338032Speter			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
157438032Speter		if (pfd >= 0)
157538032Speter			(void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
157664562Sgshapiro# endif /* LOCK_ON_OPEN */
157738032Speter		if (bitset(MF_ALIAS, map->map_mflags) &&
157890792Sgshapiro		    !aliaswait(map, ".pag", true))
157990792Sgshapiro			return false;
158038032Speter	}
158138032Speter	else
158238032Speter	{
158338032Speter		map->map_mflags |= MF_LOCKED;
158442575Speter		if (geteuid() == 0 && TrustedUid != 0)
158538032Speter		{
158664562Sgshapiro#  if HASFCHOWN
158742575Speter			if (fchown(dfd, TrustedUid, -1) < 0 ||
158842575Speter			    fchown(pfd, TrustedUid, -1) < 0)
158938032Speter			{
159038032Speter				int err = errno;
159138032Speter
159238032Speter				sm_syslog(LOG_ALERT, NOQID,
159338032Speter					  "ownership change on %s failed: %s",
159490792Sgshapiro					  map->map_file, sm_errstring(err));
159538032Speter				message("050 ownership change on %s failed: %s",
159690792Sgshapiro					map->map_file, sm_errstring(err));
159738032Speter			}
159890792Sgshapiro#  else /* HASFCHOWN */
159990792Sgshapiro			sm_syslog(LOG_ALERT, NOQID,
160090792Sgshapiro				  "no fchown(): cannot change ownership on %s",
160190792Sgshapiro				  map->map_file);
160290792Sgshapiro			message("050 no fchown(): cannot change ownership on %s",
160390792Sgshapiro				map->map_file);
160464562Sgshapiro#  endif /* HASFCHOWN */
160538032Speter		}
160638032Speter	}
160790792Sgshapiro	return true;
160838032Speter}
160938032Speter
161038032Speter
161138032Speter/*
161238032Speter**  NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
161338032Speter*/
161438032Speter
161538032Speterchar *
161638032Speterndbm_map_lookup(map, name, av, statp)
161738032Speter	MAP *map;
161838032Speter	char *name;
161938032Speter	char **av;
162038032Speter	int *statp;
162138032Speter{
162238032Speter	datum key, val;
162377349Sgshapiro	int dfd, pfd;
162438032Speter	char keybuf[MAXNAME + 1];
162538032Speter	struct stat stbuf;
162638032Speter
162738032Speter	if (tTd(38, 20))
162890792Sgshapiro		sm_dprintf("ndbm_map_lookup(%s, %s)\n",
162938032Speter			map->map_mname, name);
163038032Speter
163138032Speter	key.dptr = name;
163238032Speter	key.dsize = strlen(name);
163338032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
163438032Speter	{
163538032Speter		if (key.dsize > sizeof keybuf - 1)
163638032Speter			key.dsize = sizeof keybuf - 1;
163764562Sgshapiro		memmove(keybuf, key.dptr, key.dsize);
163838032Speter		keybuf[key.dsize] = '\0';
163938032Speter		makelower(keybuf);
164038032Speter		key.dptr = keybuf;
164138032Speter	}
164238032Speterlockdbm:
164377349Sgshapiro	dfd = dbm_dirfno((DBM *) map->map_db1);
164477349Sgshapiro	if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
164577349Sgshapiro		(void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
164677349Sgshapiro	pfd = dbm_pagfno((DBM *) map->map_db1);
164777349Sgshapiro	if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
164877349Sgshapiro	    stbuf.st_mtime > map->map_mtime)
164938032Speter	{
165038032Speter		/* Reopen the database to sync the cache */
165138032Speter		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
165238032Speter								 : O_RDONLY;
165338032Speter
165477349Sgshapiro		if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
165577349Sgshapiro			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
165677349Sgshapiro		map->map_mflags |= MF_CLOSING;
165738032Speter		map->map_class->map_close(map);
165877349Sgshapiro		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
165938032Speter		if (map->map_class->map_open(map, omode))
166038032Speter		{
166138032Speter			map->map_mflags |= MF_OPEN;
166290792Sgshapiro			map->map_pid = CurrentPid;
166338032Speter			if ((omode && O_ACCMODE) == O_RDWR)
166438032Speter				map->map_mflags |= MF_WRITABLE;
166538032Speter			goto lockdbm;
166638032Speter		}
166738032Speter		else
166838032Speter		{
166938032Speter			if (!bitset(MF_OPTIONAL, map->map_mflags))
167038032Speter			{
167138032Speter				extern MAPCLASS BogusMapClass;
167238032Speter
167338032Speter				*statp = EX_TEMPFAIL;
167490792Sgshapiro				map->map_orgclass = map->map_class;
167538032Speter				map->map_class = &BogusMapClass;
167638032Speter				map->map_mflags |= MF_OPEN;
167790792Sgshapiro				map->map_pid = CurrentPid;
167838032Speter				syserr("Cannot reopen NDBM database %s",
167938032Speter					map->map_file);
168038032Speter			}
168138032Speter			return NULL;
168238032Speter		}
168338032Speter	}
168438032Speter	val.dptr = NULL;
168538032Speter	if (bitset(MF_TRY0NULL, map->map_mflags))
168638032Speter	{
168738032Speter		val = dbm_fetch((DBM *) map->map_db1, key);
168838032Speter		if (val.dptr != NULL)
168938032Speter			map->map_mflags &= ~MF_TRY1NULL;
169038032Speter	}
169138032Speter	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
169238032Speter	{
169338032Speter		key.dsize++;
169438032Speter		val = dbm_fetch((DBM *) map->map_db1, key);
169538032Speter		if (val.dptr != NULL)
169638032Speter			map->map_mflags &= ~MF_TRY0NULL;
169738032Speter	}
169877349Sgshapiro	if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
169977349Sgshapiro		(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
170038032Speter	if (val.dptr == NULL)
170138032Speter		return NULL;
170238032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
170338032Speter		return map_rewrite(map, name, strlen(name), NULL);
170438032Speter	else
170538032Speter		return map_rewrite(map, val.dptr, val.dsize, av);
170638032Speter}
170738032Speter
170838032Speter
170938032Speter/*
171038032Speter**  NDBM_MAP_STORE -- store a datum in the database
171138032Speter*/
171238032Speter
171338032Spetervoid
171438032Speterndbm_map_store(map, lhs, rhs)
171538032Speter	register MAP *map;
171638032Speter	char *lhs;
171738032Speter	char *rhs;
171838032Speter{
171938032Speter	datum key;
172038032Speter	datum data;
172164562Sgshapiro	int status;
172238032Speter	char keybuf[MAXNAME + 1];
172338032Speter
172438032Speter	if (tTd(38, 12))
172590792Sgshapiro		sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
172638032Speter			map->map_mname, lhs, rhs);
172738032Speter
172838032Speter	key.dsize = strlen(lhs);
172938032Speter	key.dptr = lhs;
173038032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
173138032Speter	{
173238032Speter		if (key.dsize > sizeof keybuf - 1)
173338032Speter			key.dsize = sizeof keybuf - 1;
173464562Sgshapiro		memmove(keybuf, key.dptr, key.dsize);
173538032Speter		keybuf[key.dsize] = '\0';
173638032Speter		makelower(keybuf);
173738032Speter		key.dptr = keybuf;
173838032Speter	}
173938032Speter
174038032Speter	data.dsize = strlen(rhs);
174138032Speter	data.dptr = rhs;
174238032Speter
174338032Speter	if (bitset(MF_INCLNULL, map->map_mflags))
174438032Speter	{
174538032Speter		key.dsize++;
174638032Speter		data.dsize++;
174738032Speter	}
174838032Speter
174964562Sgshapiro	status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
175064562Sgshapiro	if (status > 0)
175138032Speter	{
175238032Speter		if (!bitset(MF_APPEND, map->map_mflags))
175338032Speter			message("050 Warning: duplicate alias name %s", lhs);
175438032Speter		else
175538032Speter		{
175638032Speter			static char *buf = NULL;
175738032Speter			static int bufsiz = 0;
175838032Speter			auto int xstat;
175938032Speter			datum old;
176038032Speter
176138032Speter			old.dptr = ndbm_map_lookup(map, key.dptr,
176290792Sgshapiro						   (char **) NULL, &xstat);
176338032Speter			if (old.dptr != NULL && *(char *) old.dptr != '\0')
176438032Speter			{
176538032Speter				old.dsize = strlen(old.dptr);
176638032Speter				if (data.dsize + old.dsize + 2 > bufsiz)
176738032Speter				{
176838032Speter					if (buf != NULL)
176990792Sgshapiro						(void) sm_free(buf);
177038032Speter					bufsiz = data.dsize + old.dsize + 2;
177190792Sgshapiro					buf = sm_pmalloc_x(bufsiz);
177238032Speter				}
177390792Sgshapiro				(void) sm_strlcpyn(buf, bufsiz, 3,
177490792Sgshapiro					data.dptr, ",", old.dptr);
177538032Speter				data.dsize = data.dsize + old.dsize + 1;
177638032Speter				data.dptr = buf;
177738032Speter				if (tTd(38, 9))
177890792Sgshapiro					sm_dprintf("ndbm_map_store append=%s\n",
177964562Sgshapiro						data.dptr);
178038032Speter			}
178138032Speter		}
178264562Sgshapiro		status = dbm_store((DBM *) map->map_db1,
178364562Sgshapiro				   key, data, DBM_REPLACE);
178438032Speter	}
178564562Sgshapiro	if (status != 0)
178664562Sgshapiro		syserr("readaliases: dbm put (%s): %d", lhs, status);
178738032Speter}
178838032Speter
178938032Speter
179038032Speter/*
179138032Speter**  NDBM_MAP_CLOSE -- close the database
179238032Speter*/
179338032Speter
179438032Spetervoid
179538032Speterndbm_map_close(map)
179638032Speter	register MAP  *map;
179738032Speter{
179838032Speter	if (tTd(38, 9))
179990792Sgshapiro		sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
180038032Speter			map->map_mname, map->map_file, map->map_mflags);
180138032Speter
180238032Speter	if (bitset(MF_WRITABLE, map->map_mflags))
180338032Speter	{
180464562Sgshapiro# ifdef NDBM_YP_COMPAT
180538032Speter		bool inclnull;
180642575Speter		char buf[MAXHOSTNAMELEN];
180738032Speter
180838032Speter		inclnull = bitset(MF_INCLNULL, map->map_mflags);
180938032Speter		map->map_mflags &= ~MF_INCLNULL;
181038032Speter
181138032Speter		if (strstr(map->map_file, "/yp/") != NULL)
181238032Speter		{
181338032Speter			long save_mflags = map->map_mflags;
181438032Speter
181538032Speter			map->map_mflags |= MF_NOFOLDCASE;
181638032Speter
181790792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%010ld", curtime());
181838032Speter			ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
181938032Speter
182038032Speter			(void) gethostname(buf, sizeof buf);
182138032Speter			ndbm_map_store(map, "YP_MASTER_NAME", buf);
182238032Speter
182338032Speter			map->map_mflags = save_mflags;
182438032Speter		}
182538032Speter
182638032Speter		if (inclnull)
182738032Speter			map->map_mflags |= MF_INCLNULL;
182864562Sgshapiro# endif /* NDBM_YP_COMPAT */
182938032Speter
183038032Speter		/* write out the distinguished alias */
183138032Speter		ndbm_map_store(map, "@", "@");
183238032Speter	}
183338032Speter	dbm_close((DBM *) map->map_db1);
183438032Speter
183538032Speter	/* release lock (if needed) */
183664562Sgshapiro# if !LOCK_ON_OPEN
183738032Speter	if (map->map_lockfd >= 0)
183838032Speter		(void) close(map->map_lockfd);
183964562Sgshapiro# endif /* !LOCK_ON_OPEN */
184038032Speter}
184138032Speter
184264562Sgshapiro#endif /* NDBM */
184390792Sgshapiro/*
184438032Speter**  NEWDB (Hash and BTree) Modules
184538032Speter*/
184638032Speter
184790792Sgshapiro#if NEWDB
184838032Speter
184938032Speter/*
185038032Speter**  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
185138032Speter**
185238032Speter**	These do rather bizarre locking.  If you can lock on open,
185338032Speter**	do that to avoid the condition of opening a database that
185438032Speter**	is being rebuilt.  If you don't, we'll try to fake it, but
185538032Speter**	there will be a race condition.  If opening for read-only,
185638032Speter**	we immediately release the lock to avoid freezing things up.
185738032Speter**	We really ought to hold the lock, but guarantee that we won't
185838032Speter**	be pokey about it.  That's hard to do.
185938032Speter*/
186038032Speter
186138032Speter/* these should be K line arguments */
186264562Sgshapiro# if DB_VERSION_MAJOR < 2
186364562Sgshapiro#  define db_cachesize	cachesize
186464562Sgshapiro#  define h_nelem	nelem
186564562Sgshapiro#  ifndef DB_CACHE_SIZE
186664562Sgshapiro#   define DB_CACHE_SIZE	(1024 * 1024)	/* database memory cache size */
186764562Sgshapiro#  endif /* ! DB_CACHE_SIZE */
186864562Sgshapiro#  ifndef DB_HASH_NELEM
186964562Sgshapiro#   define DB_HASH_NELEM	4096		/* (starting) size of hash table */
187064562Sgshapiro#  endif /* ! DB_HASH_NELEM */
187164562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
187238032Speter
187338032Speterbool
187438032Speterbt_map_open(map, mode)
187538032Speter	MAP *map;
187638032Speter	int mode;
187738032Speter{
187864562Sgshapiro# if DB_VERSION_MAJOR < 2
187938032Speter	BTREEINFO btinfo;
188064562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
188164562Sgshapiro# if DB_VERSION_MAJOR == 2
188238032Speter	DB_INFO btinfo;
188364562Sgshapiro# endif /* DB_VERSION_MAJOR == 2 */
188464562Sgshapiro# if DB_VERSION_MAJOR > 2
188564562Sgshapiro	void *btinfo = NULL;
188664562Sgshapiro# endif /* DB_VERSION_MAJOR > 2 */
188738032Speter
188838032Speter	if (tTd(38, 2))
188990792Sgshapiro		sm_dprintf("bt_map_open(%s, %s, %d)\n",
189038032Speter			map->map_mname, map->map_file, mode);
189138032Speter
189264562Sgshapiro# if DB_VERSION_MAJOR < 3
189364562Sgshapiro	memset(&btinfo, '\0', sizeof btinfo);
189464562Sgshapiro#  ifdef DB_CACHE_SIZE
189538032Speter	btinfo.db_cachesize = DB_CACHE_SIZE;
189664562Sgshapiro#  endif /* DB_CACHE_SIZE */
189764562Sgshapiro# endif /* DB_VERSION_MAJOR < 3 */
189864562Sgshapiro
189938032Speter	return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
190038032Speter}
190138032Speter
190238032Speterbool
190338032Speterhash_map_open(map, mode)
190438032Speter	MAP *map;
190538032Speter	int mode;
190638032Speter{
190764562Sgshapiro# if DB_VERSION_MAJOR < 2
190838032Speter	HASHINFO hinfo;
190964562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
191064562Sgshapiro# if DB_VERSION_MAJOR == 2
191138032Speter	DB_INFO hinfo;
191264562Sgshapiro# endif /* DB_VERSION_MAJOR == 2 */
191364562Sgshapiro# if DB_VERSION_MAJOR > 2
191464562Sgshapiro	void *hinfo = NULL;
191564562Sgshapiro# endif /* DB_VERSION_MAJOR > 2 */
191638032Speter
191738032Speter	if (tTd(38, 2))
191890792Sgshapiro		sm_dprintf("hash_map_open(%s, %s, %d)\n",
191938032Speter			map->map_mname, map->map_file, mode);
192038032Speter
192164562Sgshapiro# if DB_VERSION_MAJOR < 3
192264562Sgshapiro	memset(&hinfo, '\0', sizeof hinfo);
192364562Sgshapiro#  ifdef DB_HASH_NELEM
192438032Speter	hinfo.h_nelem = DB_HASH_NELEM;
192564562Sgshapiro#  endif /* DB_HASH_NELEM */
192664562Sgshapiro#  ifdef DB_CACHE_SIZE
192738032Speter	hinfo.db_cachesize = DB_CACHE_SIZE;
192864562Sgshapiro#  endif /* DB_CACHE_SIZE */
192964562Sgshapiro# endif /* DB_VERSION_MAJOR < 3 */
193064562Sgshapiro
193138032Speter	return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
193238032Speter}
193338032Speter
193464562Sgshapirostatic bool
193538032Speterdb_map_open(map, mode, mapclassname, dbtype, openinfo)
193638032Speter	MAP *map;
193738032Speter	int mode;
193838032Speter	char *mapclassname;
193938032Speter	DBTYPE dbtype;
194064562Sgshapiro# if DB_VERSION_MAJOR < 2
194138032Speter	const void *openinfo;
194264562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
194364562Sgshapiro# if DB_VERSION_MAJOR == 2
194438032Speter	DB_INFO *openinfo;
194564562Sgshapiro# endif /* DB_VERSION_MAJOR == 2 */
194664562Sgshapiro# if DB_VERSION_MAJOR > 2
194764562Sgshapiro	void **openinfo;
194864562Sgshapiro# endif /* DB_VERSION_MAJOR > 2 */
194938032Speter{
195038032Speter	DB *db = NULL;
195138032Speter	int i;
195238032Speter	int omode;
195338032Speter	int smode = S_IREAD;
195438032Speter	int fd;
195564562Sgshapiro	long sff;
195664562Sgshapiro	int save_errno;
195738032Speter	struct stat st;
195898121Sgshapiro	char buf[MAXPATHLEN];
195938032Speter
196038032Speter	/* do initial file and directory checks */
196198121Sgshapiro	if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
196298121Sgshapiro	{
196398121Sgshapiro		errno = 0;
196498121Sgshapiro		if (!bitset(MF_OPTIONAL, map->map_mflags))
196598121Sgshapiro			syserr("map \"%s\": map file %s name too long",
196698121Sgshapiro				map->map_mname, map->map_file);
196798121Sgshapiro		return false;
196898121Sgshapiro	}
196938032Speter	i = strlen(buf);
197038032Speter	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
197198121Sgshapiro	{
197298121Sgshapiro		if (sm_strlcat(buf, ".db", sizeof buf) >= sizeof buf)
197398121Sgshapiro		{
197498121Sgshapiro			errno = 0;
197598121Sgshapiro			if (!bitset(MF_OPTIONAL, map->map_mflags))
197698121Sgshapiro				syserr("map \"%s\": map file %s name too long",
197798121Sgshapiro					map->map_mname, map->map_file);
197898121Sgshapiro			return false;
197998121Sgshapiro		}
198098121Sgshapiro	}
198138032Speter
198238032Speter	mode &= O_ACCMODE;
198338032Speter	omode = mode;
198438032Speter
198538032Speter	sff = SFF_ROOTOK|SFF_REGONLY;
198638032Speter	if (mode == O_RDWR)
198738032Speter	{
198838032Speter		sff |= SFF_CREAT;
198964562Sgshapiro		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
199038032Speter			sff |= SFF_NOSLINK;
199164562Sgshapiro		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
199238032Speter			sff |= SFF_NOHLINK;
199338032Speter		smode = S_IWRITE;
199438032Speter	}
199538032Speter	else
199638032Speter	{
199764562Sgshapiro		if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
199838032Speter			sff |= SFF_NOWLINK;
199938032Speter	}
200064562Sgshapiro	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
200138032Speter		sff |= SFF_SAFEDIRPATH;
200238032Speter	i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
200364562Sgshapiro
200438032Speter	if (i != 0)
200538032Speter	{
200638032Speter		char *prob = "unsafe";
200738032Speter
200838032Speter		/* cannot open this map */
200938032Speter		if (i == ENOENT)
201038032Speter			prob = "missing";
201138032Speter		if (tTd(38, 2))
201290792Sgshapiro			sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
201338032Speter		errno = i;
201438032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
201538032Speter			syserr("%s map \"%s\": %s map file %s",
201638032Speter				mapclassname, map->map_mname, prob, buf);
201790792Sgshapiro		return false;
201838032Speter	}
201938032Speter	if (st.st_mode == ST_MODE_NOFILE)
202038032Speter		omode |= O_CREAT|O_EXCL;
202138032Speter
202238032Speter	map->map_lockfd = -1;
202338032Speter
202464562Sgshapiro# if LOCK_ON_OPEN
202538032Speter	if (mode == O_RDWR)
202638032Speter		omode |= O_TRUNC|O_EXLOCK;
202738032Speter	else
202838032Speter		omode |= O_SHLOCK;
202964562Sgshapiro# else /* LOCK_ON_OPEN */
203038032Speter	/*
203138032Speter	**  Pre-lock the file to avoid race conditions.  In particular,
203238032Speter	**  since dbopen returns NULL if the file is zero length, we
203338032Speter	**  must have a locked instance around the dbopen.
203438032Speter	*/
203538032Speter
203638032Speter	fd = open(buf, omode, DBMMODE);
203738032Speter	if (fd < 0)
203838032Speter	{
203938032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
204038032Speter			syserr("db_map_open: cannot pre-open database %s", buf);
204190792Sgshapiro		return false;
204238032Speter	}
204338032Speter
204438032Speter	/* make sure no baddies slipped in just before the open... */
204538032Speter	if (filechanged(buf, fd, &st))
204638032Speter	{
204764562Sgshapiro		save_errno = errno;
204838032Speter		(void) close(fd);
204938032Speter		errno = save_errno;
205038032Speter		syserr("db_map_open(%s): file changed after pre-open", buf);
205190792Sgshapiro		return false;
205238032Speter	}
205338032Speter
205438032Speter	/* if new file, get the "before" bits for later filechanged check */
205538032Speter	if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
205638032Speter	{
205764562Sgshapiro		save_errno = errno;
205838032Speter		(void) close(fd);
205938032Speter		errno = save_errno;
206038032Speter		syserr("db_map_open(%s): cannot fstat pre-opened file",
206138032Speter			buf);
206290792Sgshapiro		return false;
206338032Speter	}
206438032Speter
206538032Speter	/* actually lock the pre-opened file */
206638032Speter	if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
206738032Speter		syserr("db_map_open: cannot lock %s", buf);
206838032Speter
206938032Speter	/* set up mode bits for dbopen */
207038032Speter	if (mode == O_RDWR)
207138032Speter		omode |= O_TRUNC;
207238032Speter	omode &= ~(O_EXCL|O_CREAT);
207364562Sgshapiro# endif /* LOCK_ON_OPEN */
207438032Speter
207564562Sgshapiro# if DB_VERSION_MAJOR < 2
207638032Speter	db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
207764562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
207838032Speter	{
207938032Speter		int flags = 0;
208064562Sgshapiro#  if DB_VERSION_MAJOR > 2
208164562Sgshapiro		int ret;
208264562Sgshapiro#  endif /* DB_VERSION_MAJOR > 2 */
208338032Speter
208438032Speter		if (mode == O_RDONLY)
208538032Speter			flags |= DB_RDONLY;
208638032Speter		if (bitset(O_CREAT, omode))
208738032Speter			flags |= DB_CREATE;
208838032Speter		if (bitset(O_TRUNC, omode))
208938032Speter			flags |= DB_TRUNCATE;
2090110560Sgshapiro		SM_DB_FLAG_ADD(flags);
209138032Speter
209264562Sgshapiro#  if DB_VERSION_MAJOR > 2
209364562Sgshapiro		ret = db_create(&db, NULL, 0);
209464562Sgshapiro#  ifdef DB_CACHE_SIZE
209564562Sgshapiro		if (ret == 0 && db != NULL)
209664562Sgshapiro		{
209764562Sgshapiro			ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
209864562Sgshapiro			if (ret != 0)
209964562Sgshapiro			{
210064562Sgshapiro				(void) db->close(db, 0);
210164562Sgshapiro				db = NULL;
210264562Sgshapiro			}
210364562Sgshapiro		}
210464562Sgshapiro#  endif /* DB_CACHE_SIZE */
210564562Sgshapiro#  ifdef DB_HASH_NELEM
210664562Sgshapiro		if (dbtype == DB_HASH && ret == 0 && db != NULL)
210764562Sgshapiro		{
210864562Sgshapiro			ret = db->set_h_nelem(db, DB_HASH_NELEM);
210964562Sgshapiro			if (ret != 0)
211064562Sgshapiro			{
211164562Sgshapiro				(void) db->close(db, 0);
211264562Sgshapiro				db = NULL;
211364562Sgshapiro			}
211464562Sgshapiro		}
211564562Sgshapiro#  endif /* DB_HASH_NELEM */
211664562Sgshapiro		if (ret == 0 && db != NULL)
211764562Sgshapiro		{
2118110560Sgshapiro			ret = db->open(db,
2119110560Sgshapiro					DBTXN	/* transaction for DB 4.1 */
2120110560Sgshapiro					buf, NULL, dbtype, flags, DBMMODE);
212164562Sgshapiro			if (ret != 0)
212264562Sgshapiro			{
212373188Sgshapiro#ifdef DB_OLD_VERSION
212473188Sgshapiro				if (ret == DB_OLD_VERSION)
212573188Sgshapiro					ret = EINVAL;
212673188Sgshapiro#endif /* DB_OLD_VERSION */
212764562Sgshapiro				(void) db->close(db, 0);
212864562Sgshapiro				db = NULL;
212964562Sgshapiro			}
213064562Sgshapiro		}
213164562Sgshapiro		errno = ret;
213264562Sgshapiro#  else /* DB_VERSION_MAJOR > 2 */
213338032Speter		errno = db_open(buf, dbtype, flags, DBMMODE,
213438032Speter				NULL, openinfo, &db);
213564562Sgshapiro#  endif /* DB_VERSION_MAJOR > 2 */
213638032Speter	}
213764562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
213864562Sgshapiro	save_errno = errno;
213938032Speter
214064562Sgshapiro# if !LOCK_ON_OPEN
214138032Speter	if (mode == O_RDWR)
214238032Speter		map->map_lockfd = fd;
214338032Speter	else
214438032Speter		(void) close(fd);
214564562Sgshapiro# endif /* !LOCK_ON_OPEN */
214638032Speter
214738032Speter	if (db == NULL)
214838032Speter	{
214938032Speter		if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
215090792Sgshapiro		    aliaswait(map, ".db", false))
215190792Sgshapiro			return true;
215264562Sgshapiro# if !LOCK_ON_OPEN
215338032Speter		if (map->map_lockfd >= 0)
215438032Speter			(void) close(map->map_lockfd);
215564562Sgshapiro# endif /* !LOCK_ON_OPEN */
215664562Sgshapiro		errno = save_errno;
215738032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
215838032Speter			syserr("Cannot open %s database %s",
215938032Speter				mapclassname, buf);
216090792Sgshapiro		return false;
216138032Speter	}
216238032Speter
216364562Sgshapiro# if DB_VERSION_MAJOR < 2
216438032Speter	fd = db->fd(db);
216564562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
216638032Speter	fd = -1;
216738032Speter	errno = db->fd(db, &fd);
216864562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
216938032Speter	if (filechanged(buf, fd, &st))
217038032Speter	{
217164562Sgshapiro		save_errno = errno;
217264562Sgshapiro# if DB_VERSION_MAJOR < 2
217364562Sgshapiro		(void) db->close(db);
217464562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
217538032Speter		errno = db->close(db, 0);
217664562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
217764562Sgshapiro# if !LOCK_ON_OPEN
217838032Speter		if (map->map_lockfd >= 0)
217964562Sgshapiro			(void) close(map->map_lockfd);
218064562Sgshapiro# endif /* !LOCK_ON_OPEN */
218138032Speter		errno = save_errno;
218238032Speter		syserr("db_map_open(%s): file changed after open", buf);
218390792Sgshapiro		return false;
218438032Speter	}
218538032Speter
218638032Speter	if (mode == O_RDWR)
218738032Speter		map->map_mflags |= MF_LOCKED;
218864562Sgshapiro# if LOCK_ON_OPEN
218938032Speter	if (fd >= 0 && mode == O_RDONLY)
219038032Speter	{
219138032Speter		(void) lockfile(fd, buf, NULL, LOCK_UN);
219238032Speter	}
219364562Sgshapiro# endif /* LOCK_ON_OPEN */
219438032Speter
219538032Speter	/* try to make sure that at least the database header is on disk */
219638032Speter	if (mode == O_RDWR)
219738032Speter	{
219838032Speter		(void) db->sync(db, 0);
219942575Speter		if (geteuid() == 0 && TrustedUid != 0)
220038032Speter		{
220164562Sgshapiro#  if HASFCHOWN
220242575Speter			if (fchown(fd, TrustedUid, -1) < 0)
220338032Speter			{
220438032Speter				int err = errno;
220538032Speter
220638032Speter				sm_syslog(LOG_ALERT, NOQID,
220738032Speter					  "ownership change on %s failed: %s",
220890792Sgshapiro					  buf, sm_errstring(err));
220938032Speter				message("050 ownership change on %s failed: %s",
221090792Sgshapiro					buf, sm_errstring(err));
221138032Speter			}
221290792Sgshapiro#  else /* HASFCHOWN */
221390792Sgshapiro			sm_syslog(LOG_ALERT, NOQID,
221490792Sgshapiro				  "no fchown(): cannot change ownership on %s",
221590792Sgshapiro				  map->map_file);
221690792Sgshapiro			message("050 no fchown(): cannot change ownership on %s",
221790792Sgshapiro				map->map_file);
221864562Sgshapiro#  endif /* HASFCHOWN */
221938032Speter		}
222038032Speter	}
222138032Speter
222264562Sgshapiro	map->map_db2 = (ARBPTR_T) db;
222364562Sgshapiro
222464562Sgshapiro	/*
222564562Sgshapiro	**  Need to set map_mtime before the call to aliaswait()
222664562Sgshapiro	**  as aliaswait() will call map_lookup() which requires
222764562Sgshapiro	**  map_mtime to be set
222864562Sgshapiro	*/
222964562Sgshapiro
223038032Speter	if (fd >= 0 && fstat(fd, &st) >= 0)
223138032Speter		map->map_mtime = st.st_mtime;
223238032Speter
223338032Speter	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
223490792Sgshapiro	    !aliaswait(map, ".db", true))
223590792Sgshapiro		return false;
223690792Sgshapiro	return true;
223738032Speter}
223838032Speter
223938032Speter
224038032Speter/*
224138032Speter**  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
224238032Speter*/
224338032Speter
224438032Speterchar *
224538032Speterdb_map_lookup(map, name, av, statp)
224638032Speter	MAP *map;
224738032Speter	char *name;
224838032Speter	char **av;
224938032Speter	int *statp;
225038032Speter{
225138032Speter	DBT key, val;
225238032Speter	register DB *db = (DB *) map->map_db2;
225338032Speter	int i;
225438032Speter	int st;
225564562Sgshapiro	int save_errno;
225638032Speter	int fd;
225738032Speter	struct stat stbuf;
225838032Speter	char keybuf[MAXNAME + 1];
225998121Sgshapiro	char buf[MAXPATHLEN];
226038032Speter
226164562Sgshapiro	memset(&key, '\0', sizeof key);
226264562Sgshapiro	memset(&val, '\0', sizeof val);
226338032Speter
226438032Speter	if (tTd(38, 20))
226590792Sgshapiro		sm_dprintf("db_map_lookup(%s, %s)\n",
226638032Speter			map->map_mname, name);
226738032Speter
226898121Sgshapiro	if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
226998121Sgshapiro	{
227098121Sgshapiro		errno = 0;
227198121Sgshapiro		if (!bitset(MF_OPTIONAL, map->map_mflags))
227298121Sgshapiro			syserr("map \"%s\": map file %s name too long",
227398121Sgshapiro				map->map_mname, map->map_file);
227498121Sgshapiro		return NULL;
227598121Sgshapiro	}
227698121Sgshapiro	i = strlen(buf);
227738032Speter	if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
227838032Speter		buf[i - 3] = '\0';
227938032Speter
228038032Speter	key.size = strlen(name);
228138032Speter	if (key.size > sizeof keybuf - 1)
228238032Speter		key.size = sizeof keybuf - 1;
228338032Speter	key.data = keybuf;
228464562Sgshapiro	memmove(keybuf, name, key.size);
228538032Speter	keybuf[key.size] = '\0';
228638032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
228738032Speter		makelower(keybuf);
228838032Speter  lockdb:
228964562Sgshapiro# if DB_VERSION_MAJOR < 2
229038032Speter	fd = db->fd(db);
229164562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
229238032Speter	fd = -1;
229338032Speter	errno = db->fd(db, &fd);
229464562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
229538032Speter	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
229638032Speter		(void) lockfile(fd, buf, ".db", LOCK_SH);
229738032Speter	if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
229838032Speter	{
229938032Speter		/* Reopen the database to sync the cache */
230038032Speter		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
230138032Speter								 : O_RDONLY;
230238032Speter
230364562Sgshapiro		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
230464562Sgshapiro			(void) lockfile(fd, buf, ".db", LOCK_UN);
230577349Sgshapiro		map->map_mflags |= MF_CLOSING;
230638032Speter		map->map_class->map_close(map);
230777349Sgshapiro		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
230838032Speter		if (map->map_class->map_open(map, omode))
230938032Speter		{
231038032Speter			map->map_mflags |= MF_OPEN;
231190792Sgshapiro			map->map_pid = CurrentPid;
231238032Speter			if ((omode && O_ACCMODE) == O_RDWR)
231338032Speter				map->map_mflags |= MF_WRITABLE;
231438032Speter			db = (DB *) map->map_db2;
231538032Speter			goto lockdb;
231638032Speter		}
231738032Speter		else
231838032Speter		{
231938032Speter			if (!bitset(MF_OPTIONAL, map->map_mflags))
232038032Speter			{
232138032Speter				extern MAPCLASS BogusMapClass;
232238032Speter
232338032Speter				*statp = EX_TEMPFAIL;
232490792Sgshapiro				map->map_orgclass = map->map_class;
232538032Speter				map->map_class = &BogusMapClass;
232638032Speter				map->map_mflags |= MF_OPEN;
232790792Sgshapiro				map->map_pid = CurrentPid;
232838032Speter				syserr("Cannot reopen DB database %s",
232938032Speter					map->map_file);
233038032Speter			}
233138032Speter			return NULL;
233238032Speter		}
233338032Speter	}
233438032Speter
233538032Speter	st = 1;
233638032Speter	if (bitset(MF_TRY0NULL, map->map_mflags))
233738032Speter	{
233864562Sgshapiro# if DB_VERSION_MAJOR < 2
233938032Speter		st = db->get(db, &key, &val, 0);
234064562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
234138032Speter		errno = db->get(db, NULL, &key, &val, 0);
234238032Speter		switch (errno)
234338032Speter		{
234438032Speter		  case DB_NOTFOUND:
234538032Speter		  case DB_KEYEMPTY:
234638032Speter			st = 1;
234738032Speter			break;
234838032Speter
234938032Speter		  case 0:
235038032Speter			st = 0;
235138032Speter			break;
235238032Speter
235338032Speter		  default:
235438032Speter			st = -1;
235538032Speter			break;
235638032Speter		}
235764562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
235838032Speter		if (st == 0)
235938032Speter			map->map_mflags &= ~MF_TRY1NULL;
236038032Speter	}
236138032Speter	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
236238032Speter	{
236338032Speter		key.size++;
236464562Sgshapiro# if DB_VERSION_MAJOR < 2
236538032Speter		st = db->get(db, &key, &val, 0);
236664562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
236738032Speter		errno = db->get(db, NULL, &key, &val, 0);
236838032Speter		switch (errno)
236938032Speter		{
237038032Speter		  case DB_NOTFOUND:
237138032Speter		  case DB_KEYEMPTY:
237238032Speter			st = 1;
237338032Speter			break;
237438032Speter
237538032Speter		  case 0:
237638032Speter			st = 0;
237738032Speter			break;
237838032Speter
237938032Speter		  default:
238038032Speter			st = -1;
238138032Speter			break;
238238032Speter		}
238364562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
238438032Speter		if (st == 0)
238538032Speter			map->map_mflags &= ~MF_TRY0NULL;
238638032Speter	}
238764562Sgshapiro	save_errno = errno;
238838032Speter	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
238938032Speter		(void) lockfile(fd, buf, ".db", LOCK_UN);
239038032Speter	if (st != 0)
239138032Speter	{
239264562Sgshapiro		errno = save_errno;
239338032Speter		if (st < 0)
239438032Speter			syserr("db_map_lookup: get (%s)", name);
239538032Speter		return NULL;
239638032Speter	}
239738032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
239838032Speter		return map_rewrite(map, name, strlen(name), NULL);
239938032Speter	else
240038032Speter		return map_rewrite(map, val.data, val.size, av);
240138032Speter}
240238032Speter
240338032Speter
240438032Speter/*
240538032Speter**  DB_MAP_STORE -- store a datum in the NEWDB database
240638032Speter*/
240738032Speter
240838032Spetervoid
240938032Speterdb_map_store(map, lhs, rhs)
241038032Speter	register MAP *map;
241138032Speter	char *lhs;
241238032Speter	char *rhs;
241338032Speter{
241464562Sgshapiro	int status;
241538032Speter	DBT key;
241638032Speter	DBT data;
241738032Speter	register DB *db = map->map_db2;
241838032Speter	char keybuf[MAXNAME + 1];
241938032Speter
242064562Sgshapiro	memset(&key, '\0', sizeof key);
242164562Sgshapiro	memset(&data, '\0', sizeof data);
242238032Speter
242338032Speter	if (tTd(38, 12))
242490792Sgshapiro		sm_dprintf("db_map_store(%s, %s, %s)\n",
242538032Speter			map->map_mname, lhs, rhs);
242638032Speter
242738032Speter	key.size = strlen(lhs);
242838032Speter	key.data = lhs;
242938032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
243038032Speter	{
243138032Speter		if (key.size > sizeof keybuf - 1)
243238032Speter			key.size = sizeof keybuf - 1;
243364562Sgshapiro		memmove(keybuf, key.data, key.size);
243438032Speter		keybuf[key.size] = '\0';
243538032Speter		makelower(keybuf);
243638032Speter		key.data = keybuf;
243738032Speter	}
243838032Speter
243938032Speter	data.size = strlen(rhs);
244038032Speter	data.data = rhs;
244138032Speter
244238032Speter	if (bitset(MF_INCLNULL, map->map_mflags))
244338032Speter	{
244438032Speter		key.size++;
244538032Speter		data.size++;
244638032Speter	}
244738032Speter
244864562Sgshapiro# if DB_VERSION_MAJOR < 2
244964562Sgshapiro	status = db->put(db, &key, &data, R_NOOVERWRITE);
245064562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
245138032Speter	errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
245238032Speter	switch (errno)
245338032Speter	{
245438032Speter	  case DB_KEYEXIST:
245564562Sgshapiro		status = 1;
245638032Speter		break;
245738032Speter
245838032Speter	  case 0:
245964562Sgshapiro		status = 0;
246038032Speter		break;
246138032Speter
246238032Speter	  default:
246364562Sgshapiro		status = -1;
246438032Speter		break;
246538032Speter	}
246664562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
246764562Sgshapiro	if (status > 0)
246838032Speter	{
246938032Speter		if (!bitset(MF_APPEND, map->map_mflags))
247038032Speter			message("050 Warning: duplicate alias name %s", lhs);
247138032Speter		else
247238032Speter		{
247338032Speter			static char *buf = NULL;
247438032Speter			static int bufsiz = 0;
247538032Speter			DBT old;
247638032Speter
247764562Sgshapiro			memset(&old, '\0', sizeof old);
247838032Speter
247964562Sgshapiro			old.data = db_map_lookup(map, key.data,
248090792Sgshapiro						 (char **) NULL, &status);
248138032Speter			if (old.data != NULL)
248238032Speter			{
248338032Speter				old.size = strlen(old.data);
248490792Sgshapiro				if (data.size + old.size + 2 > (size_t) bufsiz)
248538032Speter				{
248638032Speter					if (buf != NULL)
248777349Sgshapiro						sm_free(buf);
248838032Speter					bufsiz = data.size + old.size + 2;
248990792Sgshapiro					buf = sm_pmalloc_x(bufsiz);
249038032Speter				}
249190792Sgshapiro				(void) sm_strlcpyn(buf, bufsiz, 3,
249290792Sgshapiro					(char *) data.data, ",",
249390792Sgshapiro					(char *) old.data);
249438032Speter				data.size = data.size + old.size + 1;
249538032Speter				data.data = buf;
249638032Speter				if (tTd(38, 9))
249790792Sgshapiro					sm_dprintf("db_map_store append=%s\n",
249864562Sgshapiro						(char *) data.data);
249938032Speter			}
250038032Speter		}
250164562Sgshapiro# if DB_VERSION_MAJOR < 2
250264562Sgshapiro		status = db->put(db, &key, &data, 0);
250364562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
250464562Sgshapiro		status = errno = db->put(db, NULL, &key, &data, 0);
250564562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
250638032Speter	}
250764562Sgshapiro	if (status != 0)
250838032Speter		syserr("readaliases: db put (%s)", lhs);
250938032Speter}
251038032Speter
251138032Speter
251238032Speter/*
251338032Speter**  DB_MAP_CLOSE -- add distinguished entries and close the database
251438032Speter*/
251538032Speter
251638032Spetervoid
251738032Speterdb_map_close(map)
251838032Speter	MAP *map;
251938032Speter{
252038032Speter	register DB *db = map->map_db2;
252138032Speter
252238032Speter	if (tTd(38, 9))
252390792Sgshapiro		sm_dprintf("db_map_close(%s, %s, %lx)\n",
252438032Speter			map->map_mname, map->map_file, map->map_mflags);
252538032Speter
252638032Speter	if (bitset(MF_WRITABLE, map->map_mflags))
252738032Speter	{
252838032Speter		/* write out the distinguished alias */
252938032Speter		db_map_store(map, "@", "@");
253038032Speter	}
253138032Speter
253238032Speter	(void) db->sync(db, 0);
253338032Speter
253464562Sgshapiro# if !LOCK_ON_OPEN
253538032Speter	if (map->map_lockfd >= 0)
253638032Speter		(void) close(map->map_lockfd);
253764562Sgshapiro# endif /* !LOCK_ON_OPEN */
253838032Speter
253964562Sgshapiro# if DB_VERSION_MAJOR < 2
254038032Speter	if (db->close(db) != 0)
254164562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
254242575Speter	/*
254342575Speter	**  Berkeley DB can use internal shared memory
254442575Speter	**  locking for its memory pool.  Closing a map
254542575Speter	**  opened by another process will interfere
254642575Speter	**  with the shared memory and locks of the parent
254742575Speter	**  process leaving things in a bad state.
254843730Speter	*/
254943730Speter
255043730Speter	/*
255142575Speter	**  If this map was not opened by the current
255243730Speter	**  process, do not close the map but recover
255342575Speter	**  the file descriptor.
255442575Speter	*/
255590792Sgshapiro
255690792Sgshapiro	if (map->map_pid != CurrentPid)
255742575Speter	{
255842575Speter		int fd = -1;
255942575Speter
256042575Speter		errno = db->fd(db, &fd);
256142575Speter		if (fd >= 0)
256242575Speter			(void) close(fd);
256342575Speter		return;
256442575Speter	}
256542575Speter
256638032Speter	if ((errno = db->close(db, 0)) != 0)
256764562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
256842575Speter		syserr("db_map_close(%s, %s, %lx): db close failure",
256942575Speter			map->map_mname, map->map_file, map->map_mflags);
257038032Speter}
257164562Sgshapiro#endif /* NEWDB */
257290792Sgshapiro/*
257338032Speter**  NIS Modules
257438032Speter*/
257538032Speter
257690792Sgshapiro#if NIS
257738032Speter
257838032Speter# ifndef YPERR_BUSY
257938032Speter#  define YPERR_BUSY	16
258064562Sgshapiro# endif /* ! YPERR_BUSY */
258138032Speter
258238032Speter/*
258338032Speter**  NIS_MAP_OPEN -- open DBM map
258438032Speter*/
258538032Speter
258638032Speterbool
258738032Speternis_map_open(map, mode)
258838032Speter	MAP *map;
258938032Speter	int mode;
259038032Speter{
259138032Speter	int yperr;
259238032Speter	register char *p;
259338032Speter	auto char *vp;
259438032Speter	auto int vsize;
259538032Speter
259638032Speter	if (tTd(38, 2))
259790792Sgshapiro		sm_dprintf("nis_map_open(%s, %s, %d)\n",
259838032Speter			map->map_mname, map->map_file, mode);
259938032Speter
260038032Speter	mode &= O_ACCMODE;
260138032Speter	if (mode != O_RDONLY)
260238032Speter	{
260338032Speter		/* issue a pseudo-error message */
260490792Sgshapiro		errno = SM_EMAPCANTWRITE;
260590792Sgshapiro		return false;
260638032Speter	}
260738032Speter
260838032Speter	p = strchr(map->map_file, '@');
260938032Speter	if (p != NULL)
261038032Speter	{
261138032Speter		*p++ = '\0';
261238032Speter		if (*p != '\0')
261338032Speter			map->map_domain = p;
261438032Speter	}
261538032Speter
261638032Speter	if (*map->map_file == '\0')
261738032Speter		map->map_file = "mail.aliases";
261838032Speter
261938032Speter	if (map->map_domain == NULL)
262038032Speter	{
262138032Speter		yperr = yp_get_default_domain(&map->map_domain);
262238032Speter		if (yperr != 0)
262338032Speter		{
262438032Speter			if (!bitset(MF_OPTIONAL, map->map_mflags))
262594334Sgshapiro				syserr("451 4.3.5 NIS map %s specified, but NIS not running",
262664562Sgshapiro				       map->map_file);
262790792Sgshapiro			return false;
262838032Speter		}
262938032Speter	}
263038032Speter
263138032Speter	/* check to see if this map actually exists */
263264562Sgshapiro	vp = NULL;
263338032Speter	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
263438032Speter			&vp, &vsize);
263538032Speter	if (tTd(38, 10))
263690792Sgshapiro		sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
263738032Speter			map->map_domain, map->map_file, yperr_string(yperr));
263864562Sgshapiro	if (vp != NULL)
263977349Sgshapiro		sm_free(vp);
264064562Sgshapiro
264138032Speter	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
264238032Speter	{
264338032Speter		/*
264438032Speter		**  We ought to be calling aliaswait() here if this is an
264538032Speter		**  alias file, but powerful HP-UX NIS servers  apparently
264638032Speter		**  don't insert the @:@ token into the alias map when it
264738032Speter		**  is rebuilt, so aliaswait() just hangs.  I hate HP-UX.
264838032Speter		*/
264938032Speter
265064562Sgshapiro# if 0
265138032Speter		if (!bitset(MF_ALIAS, map->map_mflags) ||
265290792Sgshapiro		    aliaswait(map, NULL, true))
265364562Sgshapiro# endif /* 0 */
265490792Sgshapiro			return true;
265538032Speter	}
265638032Speter
265738032Speter	if (!bitset(MF_OPTIONAL, map->map_mflags))
265838032Speter	{
265994334Sgshapiro		syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
266038032Speter			map->map_file, map->map_domain, yperr_string(yperr));
266138032Speter	}
266238032Speter
266390792Sgshapiro	return false;
266438032Speter}
266538032Speter
266638032Speter
266738032Speter/*
266838032Speter**  NIS_MAP_LOOKUP -- look up a datum in a NIS map
266938032Speter*/
267038032Speter
267138032Speter/* ARGSUSED3 */
267238032Speterchar *
267338032Speternis_map_lookup(map, name, av, statp)
267438032Speter	MAP *map;
267538032Speter	char *name;
267638032Speter	char **av;
267738032Speter	int *statp;
267838032Speter{
267938032Speter	char *vp;
268038032Speter	auto int vsize;
268138032Speter	int buflen;
268238032Speter	int yperr;
268338032Speter	char keybuf[MAXNAME + 1];
268490792Sgshapiro	char *SM_NONVOLATILE result = NULL;
268538032Speter
268638032Speter	if (tTd(38, 20))
268790792Sgshapiro		sm_dprintf("nis_map_lookup(%s, %s)\n",
268838032Speter			map->map_mname, name);
268938032Speter
269038032Speter	buflen = strlen(name);
269138032Speter	if (buflen > sizeof keybuf - 1)
269238032Speter		buflen = sizeof keybuf - 1;
269364562Sgshapiro	memmove(keybuf, name, buflen);
269438032Speter	keybuf[buflen] = '\0';
269538032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
269638032Speter		makelower(keybuf);
269738032Speter	yperr = YPERR_KEY;
269864562Sgshapiro	vp = NULL;
269938032Speter	if (bitset(MF_TRY0NULL, map->map_mflags))
270038032Speter	{
270138032Speter		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
270238032Speter			     &vp, &vsize);
270338032Speter		if (yperr == 0)
270438032Speter			map->map_mflags &= ~MF_TRY1NULL;
270538032Speter	}
270638032Speter	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
270738032Speter	{
270890792Sgshapiro		SM_FREE_CLR(vp);
270938032Speter		buflen++;
271038032Speter		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
271138032Speter			     &vp, &vsize);
271238032Speter		if (yperr == 0)
271338032Speter			map->map_mflags &= ~MF_TRY0NULL;
271438032Speter	}
271538032Speter	if (yperr != 0)
271638032Speter	{
271738032Speter		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
271838032Speter			map->map_mflags &= ~(MF_VALID|MF_OPEN);
271964562Sgshapiro		if (vp != NULL)
272077349Sgshapiro			sm_free(vp);
272138032Speter		return NULL;
272238032Speter	}
272390792Sgshapiro	SM_TRY
272490792Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
272590792Sgshapiro			result = map_rewrite(map, name, strlen(name), NULL);
272690792Sgshapiro		else
272790792Sgshapiro			result = map_rewrite(map, vp, vsize, av);
272890792Sgshapiro	SM_FINALLY
272964562Sgshapiro		if (vp != NULL)
273077349Sgshapiro			sm_free(vp);
273190792Sgshapiro	SM_END_TRY
273290792Sgshapiro	return result;
273338032Speter}
273438032Speter
273538032Speter
273638032Speter/*
273738032Speter**  NIS_GETCANONNAME -- look up canonical name in NIS
273838032Speter*/
273938032Speter
274064562Sgshapirostatic bool
274138032Speternis_getcanonname(name, hbsize, statp)
274238032Speter	char *name;
274338032Speter	int hbsize;
274438032Speter	int *statp;
274538032Speter{
274638032Speter	char *vp;
274738032Speter	auto int vsize;
274838032Speter	int keylen;
274938032Speter	int yperr;
275090792Sgshapiro	static bool try0null = true;
275190792Sgshapiro	static bool try1null = true;
275238032Speter	static char *yp_domain = NULL;
275338032Speter	char host_record[MAXLINE];
275438032Speter	char cbuf[MAXNAME];
275538032Speter	char nbuf[MAXNAME + 1];
275638032Speter
275738032Speter	if (tTd(38, 20))
275890792Sgshapiro		sm_dprintf("nis_getcanonname(%s)\n", name);
275938032Speter
276090792Sgshapiro	if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
276138032Speter	{
276238032Speter		*statp = EX_UNAVAILABLE;
276390792Sgshapiro		return false;
276438032Speter	}
276573188Sgshapiro	(void) shorten_hostname(nbuf);
276638032Speter	keylen = strlen(nbuf);
276738032Speter
276838032Speter	if (yp_domain == NULL)
276964562Sgshapiro		(void) yp_get_default_domain(&yp_domain);
277038032Speter	makelower(nbuf);
277138032Speter	yperr = YPERR_KEY;
277264562Sgshapiro	vp = NULL;
277338032Speter	if (try0null)
277438032Speter	{
277538032Speter		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
277638032Speter			     &vp, &vsize);
277738032Speter		if (yperr == 0)
277890792Sgshapiro			try1null = false;
277938032Speter	}
278038032Speter	if (yperr == YPERR_KEY && try1null)
278138032Speter	{
278290792Sgshapiro		SM_FREE_CLR(vp);
278338032Speter		keylen++;
278438032Speter		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
278538032Speter			     &vp, &vsize);
278638032Speter		if (yperr == 0)
278790792Sgshapiro			try0null = false;
278838032Speter	}
278938032Speter	if (yperr != 0)
279038032Speter	{
279138032Speter		if (yperr == YPERR_KEY)
279238032Speter			*statp = EX_NOHOST;
279338032Speter		else if (yperr == YPERR_BUSY)
279438032Speter			*statp = EX_TEMPFAIL;
279538032Speter		else
279638032Speter			*statp = EX_UNAVAILABLE;
279764562Sgshapiro		if (vp != NULL)
279877349Sgshapiro			sm_free(vp);
279990792Sgshapiro		return false;
280038032Speter	}
280190792Sgshapiro	(void) sm_strlcpy(host_record, vp, sizeof host_record);
280277349Sgshapiro	sm_free(vp);
280338032Speter	if (tTd(38, 44))
280490792Sgshapiro		sm_dprintf("got record `%s'\n", host_record);
280590792Sgshapiro	vp = strpbrk(host_record, "#\n");
280690792Sgshapiro	if (vp != NULL)
280790792Sgshapiro		*vp = '\0';
280873188Sgshapiro	if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof cbuf))
280938032Speter	{
281038032Speter		/* this should not happen, but.... */
281138032Speter		*statp = EX_NOHOST;
281290792Sgshapiro		return false;
281338032Speter	}
281490792Sgshapiro	if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
281538032Speter	{
281638032Speter		*statp = EX_UNAVAILABLE;
281790792Sgshapiro		return false;
281838032Speter	}
281938032Speter	*statp = EX_OK;
282090792Sgshapiro	return true;
282138032Speter}
282238032Speter
282364562Sgshapiro#endif /* NIS */
282490792Sgshapiro/*
282538032Speter**  NISPLUS Modules
282638032Speter**
282738032Speter**	This code donated by Sun Microsystems.
282838032Speter*/
282938032Speter
283090792Sgshapiro#if NISPLUS
283138032Speter
283264562Sgshapiro# undef NIS		/* symbol conflict in nis.h */
283364562Sgshapiro# undef T_UNSPEC	/* symbol conflict in nis.h -> ... -> sys/tiuser.h */
283464562Sgshapiro# include <rpcsvc/nis.h>
283564562Sgshapiro# include <rpcsvc/nislib.h>
283638032Speter
283764562Sgshapiro# define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
283864562Sgshapiro# define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
283964562Sgshapiro# define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
284064562Sgshapiro# define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
284138032Speter
284238032Speter/*
284338032Speter**  NISPLUS_MAP_OPEN -- open nisplus table
284438032Speter*/
284538032Speter
284638032Speterbool
284738032Speternisplus_map_open(map, mode)
284838032Speter	MAP *map;
284938032Speter	int mode;
285038032Speter{
285138032Speter	nis_result *res = NULL;
285238032Speter	int retry_cnt, max_col, i;
285338032Speter	char qbuf[MAXLINE + NIS_MAXNAMELEN];
285438032Speter
285538032Speter	if (tTd(38, 2))
285690792Sgshapiro		sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
285738032Speter			map->map_mname, map->map_file, mode);
285838032Speter
285938032Speter	mode &= O_ACCMODE;
286038032Speter	if (mode != O_RDONLY)
286138032Speter	{
286238032Speter		errno = EPERM;
286390792Sgshapiro		return false;
286438032Speter	}
286538032Speter
286638032Speter	if (*map->map_file == '\0')
286738032Speter		map->map_file = "mail_aliases.org_dir";
286838032Speter
286938032Speter	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
287038032Speter	{
287138032Speter		/* set default NISPLUS Domain to $m */
287238032Speter		map->map_domain = newstr(nisplus_default_domain());
287338032Speter		if (tTd(38, 2))
287490792Sgshapiro			sm_dprintf("nisplus_map_open(%s): using domain %s\n",
287564562Sgshapiro				map->map_file, map->map_domain);
287638032Speter	}
287738032Speter	if (!PARTIAL_NAME(map->map_file))
287838032Speter	{
287938032Speter		map->map_domain = newstr("");
288090792Sgshapiro		(void) sm_strlcpy(qbuf, map->map_file, sizeof qbuf);
288138032Speter	}
288238032Speter	else
288338032Speter	{
288438032Speter		/* check to see if this map actually exists */
288590792Sgshapiro		(void) sm_strlcpyn(qbuf, sizeof qbuf, 3,
288690792Sgshapiro				   map->map_file, ".", map->map_domain);
288738032Speter	}
288838032Speter
288938032Speter	retry_cnt = 0;
289038032Speter	while (res == NULL || res->status != NIS_SUCCESS)
289138032Speter	{
289238032Speter		res = nis_lookup(qbuf, FOLLOW_LINKS);
289338032Speter		switch (res->status)
289438032Speter		{
289538032Speter		  case NIS_SUCCESS:
289638032Speter			break;
289738032Speter
289838032Speter		  case NIS_TRYAGAIN:
289938032Speter		  case NIS_RPCERROR:
290038032Speter		  case NIS_NAMEUNREACHABLE:
290138032Speter			if (retry_cnt++ > 4)
290238032Speter			{
290338032Speter				errno = EAGAIN;
290490792Sgshapiro				return false;
290538032Speter			}
290638032Speter			/* try not to overwhelm hosed server */
290738032Speter			sleep(2);
290838032Speter			break;
290938032Speter
291038032Speter		  default:		/* all other nisplus errors */
291164562Sgshapiro# if 0
291238032Speter			if (!bitset(MF_OPTIONAL, map->map_mflags))
291394334Sgshapiro				syserr("451 4.3.5 Cannot find table %s.%s: %s",
291438032Speter					map->map_file, map->map_domain,
291538032Speter					nis_sperrno(res->status));
291664562Sgshapiro# endif /* 0 */
291738032Speter			errno = EAGAIN;
291890792Sgshapiro			return false;
291938032Speter		}
292038032Speter	}
292138032Speter
292238032Speter	if (NIS_RES_NUMOBJ(res) != 1 ||
292338032Speter	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
292438032Speter	{
292538032Speter		if (tTd(38, 10))
292690792Sgshapiro			sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
292764562Sgshapiro# if 0
292838032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
292994334Sgshapiro			syserr("451 4.3.5 %s.%s: %s is not a table",
293038032Speter				map->map_file, map->map_domain,
293138032Speter				nis_sperrno(res->status));
293264562Sgshapiro# endif /* 0 */
293338032Speter		errno = EBADF;
293490792Sgshapiro		return false;
293538032Speter	}
293638032Speter	/* default key column is column 0 */
293738032Speter	if (map->map_keycolnm == NULL)
293838032Speter		map->map_keycolnm = newstr(COL_NAME(res,0));
293938032Speter
294038032Speter	max_col = COL_MAX(res);
294138032Speter
294238032Speter	/* verify the key column exist */
294390792Sgshapiro	for (i = 0; i < max_col; i++)
294438032Speter	{
294564562Sgshapiro		if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
294638032Speter			break;
294738032Speter	}
294838032Speter	if (i == max_col)
294938032Speter	{
295038032Speter		if (tTd(38, 2))
295190792Sgshapiro			sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
295238032Speter				map->map_file, map->map_keycolnm);
295338032Speter		errno = ENOENT;
295490792Sgshapiro		return false;
295538032Speter	}
295638032Speter
295738032Speter	/* default value column is the last column */
295838032Speter	if (map->map_valcolnm == NULL)
295938032Speter	{
296038032Speter		map->map_valcolno = max_col - 1;
296190792Sgshapiro		return true;
296238032Speter	}
296338032Speter
296464562Sgshapiro	for (i = 0; i< max_col; i++)
296538032Speter	{
296638032Speter		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
296738032Speter		{
296838032Speter			map->map_valcolno = i;
296990792Sgshapiro			return true;
297038032Speter		}
297138032Speter	}
297238032Speter
297338032Speter	if (tTd(38, 2))
297490792Sgshapiro		sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
297564562Sgshapiro			map->map_file, map->map_keycolnm);
297638032Speter	errno = ENOENT;
297790792Sgshapiro	return false;
297838032Speter}
297938032Speter
298038032Speter
298138032Speter/*
298238032Speter**  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
298338032Speter*/
298438032Speter
298538032Speterchar *
298638032Speternisplus_map_lookup(map, name, av, statp)
298738032Speter	MAP *map;
298838032Speter	char *name;
298938032Speter	char **av;
299038032Speter	int *statp;
299138032Speter{
299238032Speter	char *p;
299338032Speter	auto int vsize;
299438032Speter	char *skp;
299538032Speter	int skleft;
299638032Speter	char search_key[MAXNAME + 4];
299738032Speter	char qbuf[MAXLINE + NIS_MAXNAMELEN];
299838032Speter	nis_result *result;
299938032Speter
300038032Speter	if (tTd(38, 20))
300190792Sgshapiro		sm_dprintf("nisplus_map_lookup(%s, %s)\n",
300238032Speter			map->map_mname, name);
300338032Speter
300438032Speter	if (!bitset(MF_OPEN, map->map_mflags))
300538032Speter	{
300638032Speter		if (nisplus_map_open(map, O_RDONLY))
300742575Speter		{
300838032Speter			map->map_mflags |= MF_OPEN;
300990792Sgshapiro			map->map_pid = CurrentPid;
301042575Speter		}
301138032Speter		else
301238032Speter		{
301338032Speter			*statp = EX_UNAVAILABLE;
301438032Speter			return NULL;
301538032Speter		}
301638032Speter	}
301738032Speter
301838032Speter	/*
301938032Speter	**  Copy the name to the key buffer, escaping double quote characters
302038032Speter	**  by doubling them and quoting "]" and "," to avoid having the
302138032Speter	**  NIS+ parser choke on them.
302238032Speter	*/
302338032Speter
302438032Speter	skleft = sizeof search_key - 4;
302538032Speter	skp = search_key;
302638032Speter	for (p = name; *p != '\0' && skleft > 0; p++)
302738032Speter	{
302838032Speter		switch (*p)
302938032Speter		{
303038032Speter		  case ']':
303138032Speter		  case ',':
303238032Speter			/* quote the character */
303338032Speter			*skp++ = '"';
303438032Speter			*skp++ = *p;
303538032Speter			*skp++ = '"';
303638032Speter			skleft -= 3;
303738032Speter			break;
303838032Speter
303938032Speter		  case '"':
304038032Speter			/* double the quote */
304138032Speter			*skp++ = '"';
304238032Speter			skleft--;
304364562Sgshapiro			/* FALLTHROUGH */
304438032Speter
304538032Speter		  default:
304638032Speter			*skp++ = *p;
304738032Speter			skleft--;
304838032Speter			break;
304938032Speter		}
305038032Speter	}
305138032Speter	*skp = '\0';
305238032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
305338032Speter		makelower(search_key);
305438032Speter
305538032Speter	/* construct the query */
305638032Speter	if (PARTIAL_NAME(map->map_file))
305790792Sgshapiro		(void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
305838032Speter			map->map_keycolnm, search_key, map->map_file,
305938032Speter			map->map_domain);
306038032Speter	else
306190792Sgshapiro		(void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
306238032Speter			map->map_keycolnm, search_key, map->map_file);
306338032Speter
306438032Speter	if (tTd(38, 20))
306590792Sgshapiro		sm_dprintf("qbuf=%s\n", qbuf);
306638032Speter	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
306738032Speter	if (result->status == NIS_SUCCESS)
306838032Speter	{
306938032Speter		int count;
307038032Speter		char *str;
307138032Speter
307238032Speter		if ((count = NIS_RES_NUMOBJ(result)) != 1)
307338032Speter		{
307438032Speter			if (LogLevel > 10)
307538032Speter				sm_syslog(LOG_WARNING, CurEnv->e_id,
307664562Sgshapiro					  "%s: lookup error, expected 1 entry, got %d",
307764562Sgshapiro					  map->map_file, count);
307838032Speter
307938032Speter			/* ignore second entry */
308038032Speter			if (tTd(38, 20))
308190792Sgshapiro				sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
308238032Speter					name, count);
308338032Speter		}
308438032Speter
308538032Speter		p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
308638032Speter		/* set the length of the result */
308738032Speter		if (p == NULL)
308838032Speter			p = "";
308938032Speter		vsize = strlen(p);
309038032Speter		if (tTd(38, 20))
309190792Sgshapiro			sm_dprintf("nisplus_map_lookup(%s), found %s\n",
309238032Speter				name, p);
309338032Speter		if (bitset(MF_MATCHONLY, map->map_mflags))
309438032Speter			str = map_rewrite(map, name, strlen(name), NULL);
309538032Speter		else
309638032Speter			str = map_rewrite(map, p, vsize, av);
309738032Speter		nis_freeresult(result);
309838032Speter		*statp = EX_OK;
309938032Speter		return str;
310038032Speter	}
310138032Speter	else
310238032Speter	{
310338032Speter		if (result->status == NIS_NOTFOUND)
310438032Speter			*statp = EX_NOTFOUND;
310538032Speter		else if (result->status == NIS_TRYAGAIN)
310638032Speter			*statp = EX_TEMPFAIL;
310738032Speter		else
310838032Speter		{
310938032Speter			*statp = EX_UNAVAILABLE;
311038032Speter			map->map_mflags &= ~(MF_VALID|MF_OPEN);
311138032Speter		}
311238032Speter	}
311338032Speter	if (tTd(38, 20))
311490792Sgshapiro		sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
311538032Speter	nis_freeresult(result);
311638032Speter	return NULL;
311738032Speter}
311838032Speter
311938032Speter
312038032Speter
312138032Speter/*
312238032Speter**  NISPLUS_GETCANONNAME -- look up canonical name in NIS+
312338032Speter*/
312438032Speter
312564562Sgshapirostatic bool
312638032Speternisplus_getcanonname(name, hbsize, statp)
312738032Speter	char *name;
312838032Speter	int hbsize;
312938032Speter	int *statp;
313038032Speter{
313138032Speter	char *vp;
313238032Speter	auto int vsize;
313338032Speter	nis_result *result;
313438032Speter	char *p;
313538032Speter	char nbuf[MAXNAME + 1];
313638032Speter	char qbuf[MAXLINE + NIS_MAXNAMELEN];
313738032Speter
313890792Sgshapiro	if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
313938032Speter	{
314038032Speter		*statp = EX_UNAVAILABLE;
314190792Sgshapiro		return false;
314238032Speter	}
314373188Sgshapiro	(void) shorten_hostname(nbuf);
314438032Speter
314538032Speter	p = strchr(nbuf, '.');
314638032Speter	if (p == NULL)
314738032Speter	{
314838032Speter		/* single token */
314990792Sgshapiro		(void) sm_snprintf(qbuf, sizeof qbuf,
315090792Sgshapiro			"[name=%s],hosts.org_dir", nbuf);
315138032Speter	}
315238032Speter	else if (p[1] != '\0')
315338032Speter	{
315438032Speter		/* multi token -- take only first token in nbuf */
315538032Speter		*p = '\0';
315690792Sgshapiro		(void) sm_snprintf(qbuf, sizeof qbuf,
315790792Sgshapiro				   "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
315838032Speter	}
315938032Speter	else
316038032Speter	{
316138032Speter		*statp = EX_NOHOST;
316290792Sgshapiro		return false;
316338032Speter	}
316438032Speter
316538032Speter	if (tTd(38, 20))
316694334Sgshapiro		sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
316790792Sgshapiro			   name, qbuf);
316838032Speter
316938032Speter	result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
317090792Sgshapiro			  NULL, NULL);
317138032Speter
317238032Speter	if (result->status == NIS_SUCCESS)
317338032Speter	{
317438032Speter		int count;
317538032Speter		char *domain;
317638032Speter
317738032Speter		if ((count = NIS_RES_NUMOBJ(result)) != 1)
317838032Speter		{
317938032Speter			if (LogLevel > 10)
318038032Speter				sm_syslog(LOG_WARNING, CurEnv->e_id,
318164562Sgshapiro					  "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
318264562Sgshapiro					  count);
318338032Speter
318438032Speter			/* ignore second entry */
318538032Speter			if (tTd(38, 20))
318694334Sgshapiro				sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
318790792Sgshapiro					   name, count);
318838032Speter		}
318938032Speter
319038032Speter		if (tTd(38, 20))
319194334Sgshapiro			sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
319290792Sgshapiro				   name, (NIS_RES_OBJECT(result))->zo_domain);
319338032Speter
319438032Speter
319538032Speter		vp = ((NIS_RES_OBJECT(result))->EN_col(0));
319638032Speter		vsize = strlen(vp);
319738032Speter		if (tTd(38, 20))
319890792Sgshapiro			sm_dprintf("nisplus_getcanonname(%s), found %s\n",
319990792Sgshapiro				   name, vp);
320038032Speter		if (strchr(vp, '.') != NULL)
320138032Speter		{
320238032Speter			domain = "";
320338032Speter		}
320438032Speter		else
320538032Speter		{
320638032Speter			domain = macvalue('m', CurEnv);
320738032Speter			if (domain == NULL)
320838032Speter				domain = "";
320938032Speter		}
321038032Speter		if (hbsize > vsize + (int) strlen(domain) + 1)
321138032Speter		{
321238032Speter			if (domain[0] == '\0')
321390792Sgshapiro				(void) sm_strlcpy(name, vp, hbsize);
321438032Speter			else
321590792Sgshapiro				(void) sm_snprintf(name, hbsize,
321690792Sgshapiro						   "%s.%s", vp, domain);
321738032Speter			*statp = EX_OK;
321838032Speter		}
321938032Speter		else
322038032Speter			*statp = EX_NOHOST;
322138032Speter		nis_freeresult(result);
322290792Sgshapiro		return true;
322338032Speter	}
322438032Speter	else
322538032Speter	{
322638032Speter		if (result->status == NIS_NOTFOUND)
322738032Speter			*statp = EX_NOHOST;
322838032Speter		else if (result->status == NIS_TRYAGAIN)
322938032Speter			*statp = EX_TEMPFAIL;
323038032Speter		else
323138032Speter			*statp = EX_UNAVAILABLE;
323238032Speter	}
323338032Speter	if (tTd(38, 20))
323490792Sgshapiro		sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
323590792Sgshapiro			   name, result->status, *statp);
323638032Speter	nis_freeresult(result);
323790792Sgshapiro	return false;
323838032Speter}
323938032Speter
324038032Speterchar *
324138032Speternisplus_default_domain()
324238032Speter{
324338032Speter	static char default_domain[MAXNAME + 1] = "";
324438032Speter	char *p;
324538032Speter
324638032Speter	if (default_domain[0] != '\0')
324764562Sgshapiro		return default_domain;
324838032Speter
324938032Speter	p = nis_local_directory();
325090792Sgshapiro	(void) sm_strlcpy(default_domain, p, sizeof default_domain);
325138032Speter	return default_domain;
325238032Speter}
325338032Speter
325438032Speter#endif /* NISPLUS */
325590792Sgshapiro/*
325638032Speter**  LDAP Modules
325738032Speter*/
325838032Speter
325964562Sgshapiro/*
326064562Sgshapiro**  LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
326164562Sgshapiro*/
326264562Sgshapiro
326364562Sgshapiro#if defined(LDAPMAP) || defined(PH_MAP)
326464562Sgshapiro
326590792Sgshapiro# if PH_MAP
326664562Sgshapiro#  define ph_map_dequote ldapmap_dequote
326764562Sgshapiro# endif /* PH_MAP */
326864562Sgshapiro
326990792Sgshapirostatic char *ldapmap_dequote __P((char *));
327090792Sgshapiro
327190792Sgshapirostatic char *
327264562Sgshapiroldapmap_dequote(str)
327364562Sgshapiro	char *str;
327464562Sgshapiro{
327564562Sgshapiro	char *p;
327664562Sgshapiro	char *start;
327764562Sgshapiro
327864562Sgshapiro	if (str == NULL)
327964562Sgshapiro		return NULL;
328064562Sgshapiro
328164562Sgshapiro	p = str;
328264562Sgshapiro	if (*p == '"')
328364562Sgshapiro	{
328464562Sgshapiro		/* Should probably swallow initial whitespace here */
328564562Sgshapiro		start = ++p;
328664562Sgshapiro	}
328764562Sgshapiro	else
328864562Sgshapiro		return str;
328964562Sgshapiro	while (*p != '"' && *p != '\0')
329064562Sgshapiro		p++;
329164562Sgshapiro	if (*p != '\0')
329264562Sgshapiro		*p = '\0';
329364562Sgshapiro	return start;
329464562Sgshapiro}
329564562Sgshapiro#endif /* defined(LDAPMAP) || defined(PH_MAP) */
329664562Sgshapiro
329790792Sgshapiro#if LDAPMAP
329838032Speter
329990792Sgshapirostatic SM_LDAP_STRUCT *LDAPDefaults = NULL;
330038032Speter
330138032Speter/*
330264562Sgshapiro**  LDAPMAP_OPEN -- open LDAP map
330338032Speter**
330464562Sgshapiro**	Connect to the LDAP server.  Re-use existing connections since a
330564562Sgshapiro**	single server connection to a host (with the same host, port,
330664562Sgshapiro**	bind DN, and secret) can answer queries for multiple maps.
330738032Speter*/
330838032Speter
330938032Speterbool
331064562Sgshapiroldapmap_open(map, mode)
331138032Speter	MAP *map;
331238032Speter	int mode;
331338032Speter{
331490792Sgshapiro	SM_LDAP_STRUCT *lmap;
331564562Sgshapiro	STAB *s;
3316132943Sgshapiro	char *id;
331764562Sgshapiro
331838032Speter	if (tTd(38, 2))
331990792Sgshapiro		sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
332038032Speter
332138032Speter	mode &= O_ACCMODE;
332264562Sgshapiro
332364562Sgshapiro	/* sendmail doesn't have the ability to write to LDAP (yet) */
332438032Speter	if (mode != O_RDONLY)
332538032Speter	{
332638032Speter		/* issue a pseudo-error message */
332790792Sgshapiro		errno = SM_EMAPCANTWRITE;
332890792Sgshapiro		return false;
332938032Speter	}
333064562Sgshapiro
333190792Sgshapiro	lmap = (SM_LDAP_STRUCT *) map->map_db1;
333264562Sgshapiro
333364562Sgshapiro	s = ldapmap_findconn(lmap);
333477349Sgshapiro	if (s->s_lmap != NULL)
333564562Sgshapiro	{
333664562Sgshapiro		/* Already have a connection open to this LDAP server */
333790792Sgshapiro		lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
333890792Sgshapiro		lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
333977349Sgshapiro
334077349Sgshapiro		/* Add this map as head of linked list */
334177349Sgshapiro		lmap->ldap_next = s->s_lmap;
334277349Sgshapiro		s->s_lmap = map;
334377349Sgshapiro
334466494Sgshapiro		if (tTd(38, 2))
334590792Sgshapiro			sm_dprintf("using cached connection\n");
334690792Sgshapiro		return true;
334764562Sgshapiro	}
334864562Sgshapiro
334966494Sgshapiro	if (tTd(38, 2))
335090792Sgshapiro		sm_dprintf("opening new connection\n");
335166494Sgshapiro
3352132943Sgshapiro	if (lmap->ldap_host != NULL)
3353132943Sgshapiro		id = lmap->ldap_host;
3354132943Sgshapiro	else if (lmap->ldap_uri != NULL)
3355132943Sgshapiro		id = lmap->ldap_uri;
3356132943Sgshapiro	else
3357132943Sgshapiro		id = "localhost";
3358132943Sgshapiro
335964562Sgshapiro	/* No connection yet, connect */
336090792Sgshapiro	if (!sm_ldap_start(map->map_mname, lmap))
336138032Speter	{
336290792Sgshapiro		if (errno == ETIMEDOUT)
336338032Speter		{
336438032Speter			if (LogLevel > 1)
336538032Speter				sm_syslog(LOG_NOTICE, CurEnv->e_id,
336664562Sgshapiro					  "timeout conning to LDAP server %.100s",
3367132943Sgshapiro					  id);
336838032Speter		}
336938032Speter
337038032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
337138032Speter		{
337264562Sgshapiro			if (bitset(MF_NODEFER, map->map_mflags))
3373132943Sgshapiro			{
337464562Sgshapiro				syserr("%s failed to %s in map %s",
337564562Sgshapiro# if USE_LDAP_INIT
337690792Sgshapiro				       "ldap_init/ldap_bind",
337764562Sgshapiro# else /* USE_LDAP_INIT */
337864562Sgshapiro				       "ldap_open",
337964562Sgshapiro# endif /* USE_LDAP_INIT */
3380132943Sgshapiro				       id, map->map_mname);
3381132943Sgshapiro			}
338264562Sgshapiro			else
3383132943Sgshapiro			{
338494334Sgshapiro				syserr("451 4.3.5 %s failed to %s in map %s",
338564562Sgshapiro# if USE_LDAP_INIT
338690792Sgshapiro				       "ldap_init/ldap_bind",
338764562Sgshapiro# else /* USE_LDAP_INIT */
338864562Sgshapiro				       "ldap_open",
338964562Sgshapiro# endif /* USE_LDAP_INIT */
3390132943Sgshapiro				       id, map->map_mname);
3391132943Sgshapiro			}
339238032Speter		}
339390792Sgshapiro		return false;
339438032Speter	}
339538032Speter
339690792Sgshapiro	/* Save connection for reuse */
339790792Sgshapiro	s->s_lmap = map;
339890792Sgshapiro	return true;
339938032Speter}
340038032Speter
340138032Speter/*
340264562Sgshapiro**  LDAPMAP_CLOSE -- close ldap map
340338032Speter*/
340438032Speter
340538032Spetervoid
340664562Sgshapiroldapmap_close(map)
340738032Speter	MAP *map;
340838032Speter{
340990792Sgshapiro	SM_LDAP_STRUCT *lmap;
341064562Sgshapiro	STAB *s;
341143730Speter
341264562Sgshapiro	if (tTd(38, 2))
341390792Sgshapiro		sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
341464562Sgshapiro
341590792Sgshapiro	lmap = (SM_LDAP_STRUCT *) map->map_db1;
341664562Sgshapiro
341764562Sgshapiro	/* Check if already closed */
341864562Sgshapiro	if (lmap->ldap_ld == NULL)
341964562Sgshapiro		return;
342064562Sgshapiro
342177349Sgshapiro	/* Close the LDAP connection */
342290792Sgshapiro	sm_ldap_close(lmap);
342377349Sgshapiro
342477349Sgshapiro	/* Mark all the maps that share the connection as closed */
342564562Sgshapiro	s = ldapmap_findconn(lmap);
342664562Sgshapiro
342777349Sgshapiro	while (s->s_lmap != NULL)
342877349Sgshapiro	{
342977349Sgshapiro		MAP *smap = s->s_lmap;
343064562Sgshapiro
343177349Sgshapiro		if (tTd(38, 2) && smap != map)
343290792Sgshapiro			sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
343390792Sgshapiro				   map->map_mname, smap->map_mname);
343477349Sgshapiro		smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
343590792Sgshapiro		lmap = (SM_LDAP_STRUCT *) smap->map_db1;
343664562Sgshapiro		lmap->ldap_ld = NULL;
343777349Sgshapiro		s->s_lmap = lmap->ldap_next;
343877349Sgshapiro		lmap->ldap_next = NULL;
343943730Speter	}
344038032Speter}
344138032Speter
344264562Sgshapiro# ifdef SUNET_ID
344343730Speter/*
344490792Sgshapiro**  SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
344542575Speter**  This only makes sense at Stanford University.
344638032Speter*/
344738032Speter
344890792Sgshapirostatic char *
344938032Spetersunet_id_hash(str)
345038032Speter	char *str;
345138032Speter{
345238032Speter	char *p, *p_last;
345338032Speter
345438032Speter	p = str;
345538032Speter	p_last = p;
345638032Speter	while (*p != '\0')
345738032Speter	{
345838032Speter		if (islower(*p) || isdigit(*p))
345938032Speter		{
346038032Speter			*p_last = *p;
346138032Speter			p_last++;
346238032Speter		}
346338032Speter		else if (isupper(*p))
346438032Speter		{
346538032Speter			*p_last = tolower(*p);
346638032Speter			p_last++;
346738032Speter		}
346838032Speter		++p;
346938032Speter	}
347038032Speter	if (*p_last != '\0')
347138032Speter		*p_last = '\0';
347264562Sgshapiro	return str;
347338032Speter}
347464562Sgshapiro# endif /* SUNET_ID */
347538032Speter
347638032Speter/*
347764562Sgshapiro**  LDAPMAP_LOOKUP -- look up a datum in a LDAP map
347838032Speter*/
347938032Speter
348038032Speterchar *
348164562Sgshapiroldapmap_lookup(map, name, av, statp)
348238032Speter	MAP *map;
348338032Speter	char *name;
348438032Speter	char **av;
348538032Speter	int *statp;
348638032Speter{
3487132943Sgshapiro	int flags;
348894334Sgshapiro	int plen = 0;
348994334Sgshapiro	int psize = 0;
349064562Sgshapiro	int msgid;
349190792Sgshapiro	int save_errno;
349290792Sgshapiro	char *vp, *p;
349364562Sgshapiro	char *result = NULL;
3494132943Sgshapiro	SM_RPOOL_T *rpool;
349590792Sgshapiro	SM_LDAP_STRUCT *lmap = NULL;
349638032Speter	char keybuf[MAXNAME + 1];
349738032Speter
349838032Speter	if (tTd(38, 20))
349990792Sgshapiro		sm_dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name);
350038032Speter
350138032Speter	/* Get ldap struct pointer from map */
350290792Sgshapiro	lmap = (SM_LDAP_STRUCT *) map->map_db1;
350390792Sgshapiro	sm_ldap_setopts(lmap->ldap_ld, lmap);
350438032Speter
350590792Sgshapiro	(void) sm_strlcpy(keybuf, name, sizeof keybuf);
350638032Speter
350738032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
350864562Sgshapiro	{
350964562Sgshapiro# ifdef SUNET_ID
351038032Speter		sunet_id_hash(keybuf);
351164562Sgshapiro# else /* SUNET_ID */
351238032Speter		makelower(keybuf);
351364562Sgshapiro# endif /* SUNET_ID */
351464562Sgshapiro	}
351538032Speter
351690792Sgshapiro	msgid = sm_ldap_search(lmap, keybuf);
351764562Sgshapiro	if (msgid == -1)
351838032Speter	{
351990792Sgshapiro		errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
352077349Sgshapiro		save_errno = errno;
352164562Sgshapiro		if (!bitset(MF_OPTIONAL, map->map_mflags))
352238032Speter		{
352364562Sgshapiro			if (bitset(MF_NODEFER, map->map_mflags))
352466494Sgshapiro				syserr("Error in ldap_search using %s in map %s",
352590792Sgshapiro				       keybuf, map->map_mname);
352664562Sgshapiro			else
352794334Sgshapiro				syserr("451 4.3.5 Error in ldap_search using %s in map %s",
352890792Sgshapiro				       keybuf, map->map_mname);
352938032Speter		}
353064562Sgshapiro		*statp = EX_TEMPFAIL;
353190792Sgshapiro		switch (save_errno - E_LDAPBASE)
353290792Sgshapiro		{
353394334Sgshapiro# ifdef LDAP_SERVER_DOWN
353490792Sgshapiro		  case LDAP_SERVER_DOWN:
353594334Sgshapiro# endif /* LDAP_SERVER_DOWN */
353690792Sgshapiro		  case LDAP_TIMEOUT:
353790792Sgshapiro		  case LDAP_UNAVAILABLE:
353866494Sgshapiro			/* server disappeared, try reopen on next search */
353977349Sgshapiro			ldapmap_close(map);
354090792Sgshapiro			break;
354166494Sgshapiro		}
354277349Sgshapiro		errno = save_errno;
354364562Sgshapiro		return NULL;
354464562Sgshapiro	}
354564562Sgshapiro
354664562Sgshapiro	*statp = EX_NOTFOUND;
354764562Sgshapiro	vp = NULL;
354864562Sgshapiro
3549132943Sgshapiro	flags = 0;
3550132943Sgshapiro	if (bitset(MF_SINGLEMATCH, map->map_mflags))
3551132943Sgshapiro		flags |= SM_LDAP_SINGLEMATCH;
3552132943Sgshapiro	if (bitset(MF_MATCHONLY, map->map_mflags))
3553132943Sgshapiro		flags |= SM_LDAP_MATCHONLY;
355490792Sgshapiro
3555132943Sgshapiro	/* Create an rpool for search related memory usage */
3556132943Sgshapiro	rpool = sm_rpool_new_x(NULL);
355790792Sgshapiro
3558132943Sgshapiro	p = NULL;
3559132943Sgshapiro	*statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
3560132943Sgshapiro				 rpool, &p, &plen, &psize, NULL);
3561132943Sgshapiro	save_errno = errno;
356290792Sgshapiro
3563132943Sgshapiro	/* Copy result so rpool can be freed */
3564132943Sgshapiro	if (*statp == EX_OK && p != NULL)
3565132943Sgshapiro		vp = newstr(p);
3566132943Sgshapiro	sm_rpool_free(rpool);
356790792Sgshapiro
3568132943Sgshapiro	/* need to restart LDAP connection? */
3569132943Sgshapiro	if (*statp == EX_RESTART)
357064562Sgshapiro	{
3571132943Sgshapiro		*statp = EX_TEMPFAIL;
3572132943Sgshapiro		ldapmap_close(map);
357338032Speter	}
357438032Speter
3575132943Sgshapiro	errno = save_errno;
3576132943Sgshapiro	if (*statp != EX_OK && *statp != EX_NOTFOUND)
357738032Speter	{
357864562Sgshapiro		if (!bitset(MF_OPTIONAL, map->map_mflags))
357964562Sgshapiro		{
358064562Sgshapiro			if (bitset(MF_NODEFER, map->map_mflags))
358164562Sgshapiro				syserr("Error getting LDAP results in map %s",
358264562Sgshapiro				       map->map_mname);
358364562Sgshapiro			else
358494334Sgshapiro				syserr("451 4.3.5 Error getting LDAP results in map %s",
358564562Sgshapiro				       map->map_mname);
358664562Sgshapiro		}
358777349Sgshapiro		errno = save_errno;
358864562Sgshapiro		return NULL;
358938032Speter	}
359090792Sgshapiro
359164562Sgshapiro	/* Did we match anything? */
359271345Sgshapiro	if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
359364562Sgshapiro		return NULL;
359438032Speter
359564562Sgshapiro	if (*statp == EX_OK)
359664562Sgshapiro	{
359764562Sgshapiro		if (LogLevel > 9)
359864562Sgshapiro			sm_syslog(LOG_INFO, CurEnv->e_id,
359971345Sgshapiro				  "ldap %.100s => %s", name,
360071345Sgshapiro				  vp == NULL ? "<NULL>" : vp);
360164562Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
360264562Sgshapiro			result = map_rewrite(map, name, strlen(name), NULL);
360364562Sgshapiro		else
360471345Sgshapiro		{
360571345Sgshapiro			/* vp != NULL according to test above */
360664562Sgshapiro			result = map_rewrite(map, vp, strlen(vp), av);
360771345Sgshapiro		}
360871345Sgshapiro		if (vp != NULL)
360990792Sgshapiro			sm_free(vp); /* XXX */
361064562Sgshapiro	}
361164562Sgshapiro	return result;
361238032Speter}
361338032Speter
361438032Speter/*
361564562Sgshapiro**  LDAPMAP_FINDCONN -- find an LDAP connection to the server
361664562Sgshapiro**
361764562Sgshapiro**	Cache LDAP connections based on the host, port, bind DN,
361866494Sgshapiro**	secret, and PID so we don't have multiple connections open to
361966494Sgshapiro**	the same server for different maps.  Need a separate connection
362066494Sgshapiro**	per PID since a parent process may close the map before the
362166494Sgshapiro**	child is done with it.
362264562Sgshapiro**
362364562Sgshapiro**	Parameters:
362464562Sgshapiro**		lmap -- LDAP map information
362564562Sgshapiro**
362664562Sgshapiro**	Returns:
362764562Sgshapiro**		Symbol table entry for the LDAP connection.
362838032Speter*/
362938032Speter
363064562Sgshapirostatic STAB *
363164562Sgshapiroldapmap_findconn(lmap)
363290792Sgshapiro	SM_LDAP_STRUCT *lmap;
363338032Speter{
363494334Sgshapiro	char *format;
363564562Sgshapiro	char *nbuf;
3636132943Sgshapiro	char *id;
363790792Sgshapiro	STAB *SM_NONVOLATILE s = NULL;
363838032Speter
3639132943Sgshapiro	if (lmap->ldap_host != NULL)
3640132943Sgshapiro		id = lmap->ldap_host;
3641132943Sgshapiro	else if (lmap->ldap_uri != NULL)
3642132943Sgshapiro		id = lmap->ldap_uri;
3643132943Sgshapiro	else
3644132943Sgshapiro		id = "localhost";
3645132943Sgshapiro
364694334Sgshapiro	format = "%s%c%d%c%d%c%s%c%s%d";
364794334Sgshapiro	nbuf = sm_stringf_x(format,
3648132943Sgshapiro			    id,
364990792Sgshapiro			    CONDELSE,
365090792Sgshapiro			    lmap->ldap_port,
365190792Sgshapiro			    CONDELSE,
365294334Sgshapiro			    lmap->ldap_version,
365394334Sgshapiro			    CONDELSE,
365490792Sgshapiro			    (lmap->ldap_binddn == NULL ? ""
365590792Sgshapiro						       : lmap->ldap_binddn),
365690792Sgshapiro			    CONDELSE,
365790792Sgshapiro			    (lmap->ldap_secret == NULL ? ""
365890792Sgshapiro						       : lmap->ldap_secret),
365990792Sgshapiro			    (int) CurrentPid);
366090792Sgshapiro	SM_TRY
366190792Sgshapiro		s = stab(nbuf, ST_LMAP, ST_ENTER);
366290792Sgshapiro	SM_FINALLY
366390792Sgshapiro		sm_free(nbuf);
366490792Sgshapiro	SM_END_TRY
366564562Sgshapiro	return s;
366664562Sgshapiro}
366738032Speter/*
366864562Sgshapiro**  LDAPMAP_PARSEARGS -- parse ldap map definition args.
366964562Sgshapiro*/
367038032Speter
367190792Sgshapirostatic struct lamvalues LDAPAuthMethods[] =
367264562Sgshapiro{
367364562Sgshapiro	{	"none",		LDAP_AUTH_NONE		},
367464562Sgshapiro	{	"simple",	LDAP_AUTH_SIMPLE	},
367564562Sgshapiro# ifdef LDAP_AUTH_KRBV4
367664562Sgshapiro	{	"krbv4",	LDAP_AUTH_KRBV4		},
367764562Sgshapiro# endif /* LDAP_AUTH_KRBV4 */
367864562Sgshapiro	{	NULL,		0			}
367964562Sgshapiro};
368038032Speter
368190792Sgshapirostatic struct ladvalues LDAPAliasDereference[] =
368264562Sgshapiro{
368364562Sgshapiro	{	"never",	LDAP_DEREF_NEVER	},
368464562Sgshapiro	{	"always",	LDAP_DEREF_ALWAYS	},
368564562Sgshapiro	{	"search",	LDAP_DEREF_SEARCHING	},
368664562Sgshapiro	{	"find",		LDAP_DEREF_FINDING	},
368764562Sgshapiro	{	NULL,		0			}
368864562Sgshapiro};
368938032Speter
369090792Sgshapirostatic struct lssvalues LDAPSearchScope[] =
369164562Sgshapiro{
369264562Sgshapiro	{	"base",		LDAP_SCOPE_BASE		},
369364562Sgshapiro	{	"one",		LDAP_SCOPE_ONELEVEL	},
369464562Sgshapiro	{	"sub",		LDAP_SCOPE_SUBTREE	},
369564562Sgshapiro	{	NULL,		0			}
369664562Sgshapiro};
369738032Speter
369864562Sgshapirobool
369964562Sgshapiroldapmap_parseargs(map, args)
370064562Sgshapiro	MAP *map;
370164562Sgshapiro	char *args;
370264562Sgshapiro{
370390792Sgshapiro	bool secretread = true;
3704132943Sgshapiro	bool attrssetup = false;
370564562Sgshapiro	int i;
370664562Sgshapiro	register char *p = args;
370790792Sgshapiro	SM_LDAP_STRUCT *lmap;
370864562Sgshapiro	struct lamvalues *lam;
370964562Sgshapiro	struct ladvalues *lad;
371064562Sgshapiro	struct lssvalues *lss;
371190792Sgshapiro	char ldapfilt[MAXLINE];
371264562Sgshapiro	char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
371364562Sgshapiro
371464562Sgshapiro	/* Get ldap struct pointer from map */
371590792Sgshapiro	lmap = (SM_LDAP_STRUCT *) map->map_db1;
371664562Sgshapiro
371764562Sgshapiro	/* Check if setting the initial LDAP defaults */
371864562Sgshapiro	if (lmap == NULL || lmap != LDAPDefaults)
371964562Sgshapiro	{
372090792Sgshapiro		/* We need to alloc an SM_LDAP_STRUCT struct */
372190792Sgshapiro		lmap = (SM_LDAP_STRUCT *) xalloc(sizeof *lmap);
372264562Sgshapiro		if (LDAPDefaults == NULL)
372390792Sgshapiro			sm_ldap_clear(lmap);
372464562Sgshapiro		else
372564562Sgshapiro			STRUCTCOPY(*LDAPDefaults, *lmap);
372664562Sgshapiro	}
372764562Sgshapiro
372864562Sgshapiro	/* there is no check whether there is really an argument */
372964562Sgshapiro	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
373064562Sgshapiro	map->map_spacesub = SpaceSub;	/* default value */
373190792Sgshapiro
373290792Sgshapiro	/* Check if setting up an alias or file class LDAP map */
373390792Sgshapiro	if (bitset(MF_ALIAS, map->map_mflags))
373490792Sgshapiro	{
373590792Sgshapiro		/* Comma separate if used as an alias file */
373690792Sgshapiro		map->map_coldelim = ',';
373790792Sgshapiro		if (*args == '\0')
373890792Sgshapiro		{
373990792Sgshapiro			int n;
374090792Sgshapiro			char *lc;
374190792Sgshapiro			char jbuf[MAXHOSTNAMELEN];
374290792Sgshapiro			char lcbuf[MAXLINE];
374390792Sgshapiro
374490792Sgshapiro			/* Get $j */
374590792Sgshapiro			expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
374690792Sgshapiro			if (jbuf[0] == '\0')
374790792Sgshapiro			{
374890792Sgshapiro				(void) sm_strlcpy(jbuf, "localhost",
374990792Sgshapiro						  sizeof jbuf);
375090792Sgshapiro			}
375190792Sgshapiro
375290792Sgshapiro			lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
375390792Sgshapiro			if (lc == NULL)
375490792Sgshapiro				lc = "";
375590792Sgshapiro			else
375690792Sgshapiro			{
375790792Sgshapiro				expand(lc, lcbuf, sizeof lcbuf, CurEnv);
375890792Sgshapiro				lc = lcbuf;
375990792Sgshapiro			}
376090792Sgshapiro
376190792Sgshapiro			n = sm_snprintf(ldapfilt, sizeof ldapfilt,
376290792Sgshapiro					"(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
376390792Sgshapiro					lc, jbuf);
376490792Sgshapiro			if (n >= sizeof ldapfilt)
376590792Sgshapiro			{
376690792Sgshapiro				syserr("%s: Default LDAP string too long",
376790792Sgshapiro				       map->map_mname);
376890792Sgshapiro				return false;
376990792Sgshapiro			}
377090792Sgshapiro
377190792Sgshapiro			/* default args for an alias LDAP entry */
377290792Sgshapiro			lmap->ldap_filter = ldapfilt;
3773132943Sgshapiro			lmap->ldap_attr[0] = "objectClass";
3774132943Sgshapiro			lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
3775132943Sgshapiro			lmap->ldap_attr_needobjclass[0] = NULL;
3776132943Sgshapiro			lmap->ldap_attr[1] = "sendmailMTAAliasValue";
3777132943Sgshapiro			lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
3778132943Sgshapiro			lmap->ldap_attr_needobjclass[1] = NULL;
3779132943Sgshapiro			lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
3780132943Sgshapiro			lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
3781132943Sgshapiro			lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
3782132943Sgshapiro			lmap->ldap_attr[3] = "sendmailMTAAliasURL";
3783132943Sgshapiro			lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
3784132943Sgshapiro			lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
3785132943Sgshapiro			lmap->ldap_attr[4] = NULL;
3786132943Sgshapiro			lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
3787132943Sgshapiro			lmap->ldap_attr_needobjclass[4] = NULL;
3788132943Sgshapiro			attrssetup = true;
378990792Sgshapiro		}
379090792Sgshapiro	}
379190792Sgshapiro	else if (bitset(MF_FILECLASS, map->map_mflags))
379290792Sgshapiro	{
379390792Sgshapiro		/* Space separate if used as a file class file */
379490792Sgshapiro		map->map_coldelim = ' ';
379590792Sgshapiro	}
379690792Sgshapiro
379738032Speter	for (;;)
379838032Speter	{
379938032Speter		while (isascii(*p) && isspace(*p))
380038032Speter			p++;
380138032Speter		if (*p != '-')
380238032Speter			break;
380338032Speter		switch (*++p)
380438032Speter		{
380538032Speter		  case 'N':
380638032Speter			map->map_mflags |= MF_INCLNULL;
380738032Speter			map->map_mflags &= ~MF_TRY0NULL;
380838032Speter			break;
380938032Speter
381038032Speter		  case 'O':
381138032Speter			map->map_mflags &= ~MF_TRY1NULL;
381238032Speter			break;
381338032Speter
381438032Speter		  case 'o':
381538032Speter			map->map_mflags |= MF_OPTIONAL;
381638032Speter			break;
381738032Speter
381838032Speter		  case 'f':
381938032Speter			map->map_mflags |= MF_NOFOLDCASE;
382038032Speter			break;
382138032Speter
382238032Speter		  case 'm':
382338032Speter			map->map_mflags |= MF_MATCHONLY;
382438032Speter			break;
382538032Speter
382638032Speter		  case 'A':
382738032Speter			map->map_mflags |= MF_APPEND;
382838032Speter			break;
382938032Speter
383038032Speter		  case 'q':
383138032Speter			map->map_mflags |= MF_KEEPQUOTES;
383238032Speter			break;
383338032Speter
383438032Speter		  case 'a':
383538032Speter			map->map_app = ++p;
383638032Speter			break;
383738032Speter
383838032Speter		  case 'T':
383938032Speter			map->map_tapp = ++p;
384038032Speter			break;
384138032Speter
384264562Sgshapiro		  case 't':
384364562Sgshapiro			map->map_mflags |= MF_NODEFER;
384464562Sgshapiro			break;
384564562Sgshapiro
384664562Sgshapiro		  case 'S':
384764562Sgshapiro			map->map_spacesub = *++p;
384864562Sgshapiro			break;
384964562Sgshapiro
385064562Sgshapiro		  case 'D':
385164562Sgshapiro			map->map_mflags |= MF_DEFER;
385264562Sgshapiro			break;
385364562Sgshapiro
385464562Sgshapiro		  case 'z':
385564562Sgshapiro			if (*++p != '\\')
385664562Sgshapiro				map->map_coldelim = *p;
385764562Sgshapiro			else
385864562Sgshapiro			{
385964562Sgshapiro				switch (*++p)
386064562Sgshapiro				{
386164562Sgshapiro				  case 'n':
386264562Sgshapiro					map->map_coldelim = '\n';
386364562Sgshapiro					break;
386464562Sgshapiro
386564562Sgshapiro				  case 't':
386664562Sgshapiro					map->map_coldelim = '\t';
386764562Sgshapiro					break;
386864562Sgshapiro
386964562Sgshapiro				  default:
387064562Sgshapiro					map->map_coldelim = '\\';
387164562Sgshapiro				}
387264562Sgshapiro			}
387364562Sgshapiro			break;
387464562Sgshapiro
387564562Sgshapiro			/* Start of ldapmap specific args */
387690792Sgshapiro		  case 'V':
387790792Sgshapiro			if (*++p != '\\')
387890792Sgshapiro				lmap->ldap_attrsep = *p;
387990792Sgshapiro			else
388090792Sgshapiro			{
388190792Sgshapiro				switch (*++p)
388290792Sgshapiro				{
388390792Sgshapiro				  case 'n':
388490792Sgshapiro					lmap->ldap_attrsep = '\n';
388590792Sgshapiro					break;
388690792Sgshapiro
388790792Sgshapiro				  case 't':
388890792Sgshapiro					lmap->ldap_attrsep = '\t';
388990792Sgshapiro					break;
389090792Sgshapiro
389190792Sgshapiro				  default:
389290792Sgshapiro					lmap->ldap_attrsep = '\\';
389390792Sgshapiro				}
389490792Sgshapiro			}
389590792Sgshapiro			break;
389690792Sgshapiro
389738032Speter		  case 'k':		/* search field */
389838032Speter			while (isascii(*++p) && isspace(*p))
389938032Speter				continue;
390064562Sgshapiro			lmap->ldap_filter = p;
390138032Speter			break;
390238032Speter
390338032Speter		  case 'v':		/* attr to return */
390438032Speter			while (isascii(*++p) && isspace(*p))
390538032Speter				continue;
390664562Sgshapiro			lmap->ldap_attr[0] = p;
390764562Sgshapiro			lmap->ldap_attr[1] = NULL;
390838032Speter			break;
390938032Speter
391064562Sgshapiro		  case '1':
391164562Sgshapiro			map->map_mflags |= MF_SINGLEMATCH;
391264562Sgshapiro			break;
391364562Sgshapiro
391438032Speter			/* args stolen from ldapsearch.c */
391538032Speter		  case 'R':		/* don't auto chase referrals */
391664562Sgshapiro# ifdef LDAP_REFERRALS
391738032Speter			lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
391864562Sgshapiro# else /* LDAP_REFERRALS */
391990792Sgshapiro			syserr("compile with -DLDAP_REFERRALS for referral support");
392064562Sgshapiro# endif /* LDAP_REFERRALS */
392138032Speter			break;
392238032Speter
392364562Sgshapiro		  case 'n':		/* retrieve attribute names only */
392464562Sgshapiro			lmap->ldap_attrsonly = LDAPMAP_TRUE;
392538032Speter			break;
392638032Speter
392764562Sgshapiro		  case 'r':		/* alias dereferencing */
392864562Sgshapiro			while (isascii(*++p) && isspace(*p))
392964562Sgshapiro				continue;
393064562Sgshapiro
393190792Sgshapiro			if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
393264562Sgshapiro				p += 11;
393364562Sgshapiro
393464562Sgshapiro			for (lad = LDAPAliasDereference;
393564562Sgshapiro			     lad != NULL && lad->lad_name != NULL; lad++)
393638032Speter			{
393790792Sgshapiro				if (sm_strncasecmp(p, lad->lad_name,
393890792Sgshapiro						   strlen(lad->lad_name)) == 0)
393964562Sgshapiro					break;
394038032Speter			}
394164562Sgshapiro			if (lad->lad_name != NULL)
394264562Sgshapiro				lmap->ldap_deref = lad->lad_code;
394364562Sgshapiro			else
394438032Speter			{
394564562Sgshapiro				/* bad config line */
394664562Sgshapiro				if (!bitset(MCF_OPTFILE,
394764562Sgshapiro					    map->map_class->map_cflags))
394864562Sgshapiro				{
394964562Sgshapiro					char *ptr;
395064562Sgshapiro
395164562Sgshapiro					if ((ptr = strchr(p, ' ')) != NULL)
395264562Sgshapiro						*ptr = '\0';
395373188Sgshapiro					syserr("Deref must be [never|always|search|find] (not %s) in map %s",
395464562Sgshapiro						p, map->map_mname);
395564562Sgshapiro					if (ptr != NULL)
395664562Sgshapiro						*ptr = ' ';
395790792Sgshapiro					return false;
395864562Sgshapiro				}
395938032Speter			}
396064562Sgshapiro			break;
396164562Sgshapiro
396264562Sgshapiro		  case 's':		/* search scope */
396364562Sgshapiro			while (isascii(*++p) && isspace(*p))
396464562Sgshapiro				continue;
396564562Sgshapiro
396690792Sgshapiro			if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
396764562Sgshapiro				p += 11;
396864562Sgshapiro
396964562Sgshapiro			for (lss = LDAPSearchScope;
397064562Sgshapiro			     lss != NULL && lss->lss_name != NULL; lss++)
397138032Speter			{
397290792Sgshapiro				if (sm_strncasecmp(p, lss->lss_name,
397390792Sgshapiro						   strlen(lss->lss_name)) == 0)
397464562Sgshapiro					break;
397538032Speter			}
397664562Sgshapiro			if (lss->lss_name != NULL)
397764562Sgshapiro				lmap->ldap_scope = lss->lss_code;
397838032Speter			else
397964562Sgshapiro			{
398064562Sgshapiro				/* bad config line */
398164562Sgshapiro				if (!bitset(MCF_OPTFILE,
398264562Sgshapiro					    map->map_class->map_cflags))
398338032Speter				{
398438032Speter					char *ptr;
398538032Speter
398638032Speter					if ((ptr = strchr(p, ' ')) != NULL)
398738032Speter						*ptr = '\0';
398873188Sgshapiro					syserr("Scope must be [base|one|sub] (not %s) in map %s",
398938032Speter						p, map->map_mname);
399038032Speter					if (ptr != NULL)
399138032Speter						*ptr = ' ';
399290792Sgshapiro					return false;
399338032Speter				}
399438032Speter			}
399538032Speter			break;
399638032Speter
399738032Speter		  case 'h':		/* ldap host */
399838032Speter			while (isascii(*++p) && isspace(*p))
399938032Speter				continue;
4000132943Sgshapiro			if (lmap->ldap_uri != NULL)
400194334Sgshapiro			{
400294334Sgshapiro				syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
400394334Sgshapiro				       map->map_mname);
400494334Sgshapiro				return false;
400594334Sgshapiro			}
4006132943Sgshapiro			lmap->ldap_host = p;
400738032Speter			break;
400838032Speter
400938032Speter		  case 'b':		/* search base */
401038032Speter			while (isascii(*++p) && isspace(*p))
401138032Speter				continue;
401264562Sgshapiro			lmap->ldap_base = p;
401338032Speter			break;
401438032Speter
401538032Speter		  case 'p':		/* ldap port */
401638032Speter			while (isascii(*++p) && isspace(*p))
401738032Speter				continue;
401864562Sgshapiro			lmap->ldap_port = atoi(p);
401938032Speter			break;
402038032Speter
402138032Speter		  case 'l':		/* time limit */
402238032Speter			while (isascii(*++p) && isspace(*p))
402338032Speter				continue;
402464562Sgshapiro			lmap->ldap_timelimit = atoi(p);
402564562Sgshapiro			lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
402638032Speter			break;
402738032Speter
402864562Sgshapiro		  case 'Z':
402964562Sgshapiro			while (isascii(*++p) && isspace(*p))
403064562Sgshapiro				continue;
403164562Sgshapiro			lmap->ldap_sizelimit = atoi(p);
403264562Sgshapiro			break;
403364562Sgshapiro
403464562Sgshapiro		  case 'd':		/* Dn to bind to server as */
403564562Sgshapiro			while (isascii(*++p) && isspace(*p))
403664562Sgshapiro				continue;
403764562Sgshapiro			lmap->ldap_binddn = p;
403864562Sgshapiro			break;
403964562Sgshapiro
404064562Sgshapiro		  case 'M':		/* Method for binding */
404164562Sgshapiro			while (isascii(*++p) && isspace(*p))
404264562Sgshapiro				continue;
404364562Sgshapiro
404490792Sgshapiro			if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
404564562Sgshapiro				p += 10;
404664562Sgshapiro
404764562Sgshapiro			for (lam = LDAPAuthMethods;
404864562Sgshapiro			     lam != NULL && lam->lam_name != NULL; lam++)
404964562Sgshapiro			{
405090792Sgshapiro				if (sm_strncasecmp(p, lam->lam_name,
405190792Sgshapiro						   strlen(lam->lam_name)) == 0)
405264562Sgshapiro					break;
405364562Sgshapiro			}
405464562Sgshapiro			if (lam->lam_name != NULL)
405564562Sgshapiro				lmap->ldap_method = lam->lam_code;
405664562Sgshapiro			else
405764562Sgshapiro			{
405864562Sgshapiro				/* bad config line */
405964562Sgshapiro				if (!bitset(MCF_OPTFILE,
406064562Sgshapiro					    map->map_class->map_cflags))
406164562Sgshapiro				{
406264562Sgshapiro					char *ptr;
406364562Sgshapiro
406464562Sgshapiro					if ((ptr = strchr(p, ' ')) != NULL)
406564562Sgshapiro						*ptr = '\0';
406673188Sgshapiro					syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
406764562Sgshapiro						p, map->map_mname);
406864562Sgshapiro					if (ptr != NULL)
406964562Sgshapiro						*ptr = ' ';
407090792Sgshapiro					return false;
407164562Sgshapiro				}
407264562Sgshapiro			}
407364562Sgshapiro
407464562Sgshapiro			break;
407564562Sgshapiro
407664562Sgshapiro			/*
407764562Sgshapiro			**  This is a string that is dependent on the
407864562Sgshapiro			**  method used defined above.
407964562Sgshapiro			*/
408064562Sgshapiro
408164562Sgshapiro		  case 'P':		/* Secret password for binding */
408264562Sgshapiro			 while (isascii(*++p) && isspace(*p))
408364562Sgshapiro				continue;
408464562Sgshapiro			lmap->ldap_secret = p;
408590792Sgshapiro			secretread = false;
408664562Sgshapiro			break;
408764562Sgshapiro
408894334Sgshapiro		  case 'H':		/* Use LDAP URI */
408994334Sgshapiro#  if !USE_LDAP_INIT
409094334Sgshapiro			syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
409194334Sgshapiro			       map->map_mname);
409294334Sgshapiro			return false;
4093132943Sgshapiro#   else /* !USE_LDAP_INIT */
4094132943Sgshapiro			if (lmap->ldap_host != NULL)
409594334Sgshapiro			{
409694334Sgshapiro				syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
409794334Sgshapiro				       map->map_mname);
409894334Sgshapiro				return false;
409994334Sgshapiro			}
410094334Sgshapiro			while (isascii(*++p) && isspace(*p))
410194334Sgshapiro				continue;
4102132943Sgshapiro			lmap->ldap_uri = p;
410394334Sgshapiro			break;
410494334Sgshapiro#  endif /* !USE_LDAP_INIT */
410594334Sgshapiro
410694334Sgshapiro		  case 'w':
410794334Sgshapiro			/* -w should be for passwd, -P should be for version */
410894334Sgshapiro			while (isascii(*++p) && isspace(*p))
410994334Sgshapiro				continue;
411094334Sgshapiro			lmap->ldap_version = atoi(p);
4111132943Sgshapiro# ifdef LDAP_VERSION_MAX
411294334Sgshapiro			if (lmap->ldap_version > LDAP_VERSION_MAX)
411394334Sgshapiro			{
411494334Sgshapiro				syserr("LDAP version %d exceeds max of %d in map %s",
411594334Sgshapiro				       lmap->ldap_version, LDAP_VERSION_MAX,
411694334Sgshapiro				       map->map_mname);
411794334Sgshapiro				return false;
411894334Sgshapiro			}
4119132943Sgshapiro# endif /* LDAP_VERSION_MAX */
4120132943Sgshapiro# ifdef LDAP_VERSION_MIN
412194334Sgshapiro			if (lmap->ldap_version < LDAP_VERSION_MIN)
412294334Sgshapiro			{
412394334Sgshapiro				syserr("LDAP version %d is lower than min of %d in map %s",
412494334Sgshapiro				       lmap->ldap_version, LDAP_VERSION_MIN,
412594334Sgshapiro				       map->map_mname);
412694334Sgshapiro				return false;
412794334Sgshapiro			}
4128132943Sgshapiro# endif /* LDAP_VERSION_MIN */
412994334Sgshapiro			break;
413094334Sgshapiro
413164562Sgshapiro		  default:
413264562Sgshapiro			syserr("Illegal option %c map %s", *p, map->map_mname);
413364562Sgshapiro			break;
413438032Speter		}
413538032Speter
413664562Sgshapiro		/* need to account for quoted strings here */
413764562Sgshapiro		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
413838032Speter		{
413938032Speter			if (*p == '"')
414038032Speter			{
414138032Speter				while (*++p != '"' && *p != '\0')
414238032Speter					continue;
414338032Speter				if (*p != '\0')
414438032Speter					p++;
414538032Speter			}
414638032Speter			else
414738032Speter				p++;
414838032Speter		}
414938032Speter
415038032Speter		if (*p != '\0')
415138032Speter			*p++ = '\0';
415238032Speter	}
415338032Speter
415438032Speter	if (map->map_app != NULL)
415564562Sgshapiro		map->map_app = newstr(ldapmap_dequote(map->map_app));
415638032Speter	if (map->map_tapp != NULL)
415764562Sgshapiro		map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
415838032Speter
415938032Speter	/*
416042575Speter	**  We need to swallow up all the stuff into a struct
416142575Speter	**  and dump it into map->map_dbptr1
416238032Speter	*/
416338032Speter
4164132943Sgshapiro	if (lmap->ldap_host != NULL &&
416564562Sgshapiro	    (LDAPDefaults == NULL ||
416664562Sgshapiro	     LDAPDefaults == lmap ||
4167132943Sgshapiro	     LDAPDefaults->ldap_host != lmap->ldap_host))
4168132943Sgshapiro		lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
4169132943Sgshapiro	map->map_domain = lmap->ldap_host;
417064562Sgshapiro
4171132943Sgshapiro	if (lmap->ldap_uri != NULL &&
4172132943Sgshapiro	    (LDAPDefaults == NULL ||
4173132943Sgshapiro	     LDAPDefaults == lmap ||
4174132943Sgshapiro	     LDAPDefaults->ldap_uri != lmap->ldap_uri))
4175132943Sgshapiro		lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
4176132943Sgshapiro	map->map_domain = lmap->ldap_uri;
4177132943Sgshapiro
417864562Sgshapiro	if (lmap->ldap_binddn != NULL &&
417964562Sgshapiro	    (LDAPDefaults == NULL ||
418064562Sgshapiro	     LDAPDefaults == lmap ||
418164562Sgshapiro	     LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
418264562Sgshapiro		lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
418364562Sgshapiro
418464562Sgshapiro	if (lmap->ldap_secret != NULL &&
418564562Sgshapiro	    (LDAPDefaults == NULL ||
418664562Sgshapiro	     LDAPDefaults == lmap ||
418764562Sgshapiro	     LDAPDefaults->ldap_secret != lmap->ldap_secret))
418838032Speter	{
418990792Sgshapiro		SM_FILE_T *sfd;
419064562Sgshapiro		long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
419138032Speter
419264562Sgshapiro		if (DontLockReadFiles)
419364562Sgshapiro			sff |= SFF_NOLOCK;
419438032Speter
419564562Sgshapiro		/* need to use method to map secret to passwd string */
419664562Sgshapiro		switch (lmap->ldap_method)
419764562Sgshapiro		{
419864562Sgshapiro		  case LDAP_AUTH_NONE:
419964562Sgshapiro			/* Do nothing */
420064562Sgshapiro			break;
420138032Speter
420264562Sgshapiro		  case LDAP_AUTH_SIMPLE:
420338032Speter
420464562Sgshapiro			/*
420564562Sgshapiro			**  Secret is the name of a file with
420664562Sgshapiro			**  the first line as the password.
420764562Sgshapiro			*/
420864562Sgshapiro
420964562Sgshapiro			/* Already read in the secret? */
421064562Sgshapiro			if (secretread)
421164562Sgshapiro				break;
421264562Sgshapiro
421364562Sgshapiro			sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
421464562Sgshapiro					O_RDONLY, 0, sff);
421564562Sgshapiro			if (sfd == NULL)
421664562Sgshapiro			{
421764562Sgshapiro				syserr("LDAP map: cannot open secret %s",
421864562Sgshapiro				       ldapmap_dequote(lmap->ldap_secret));
421990792Sgshapiro				return false;
422064562Sgshapiro			}
422198121Sgshapiro			lmap->ldap_secret = sfgets(m_tmp, sizeof m_tmp,
422266494Sgshapiro						   sfd, TimeOuts.to_fileopen,
422366494Sgshapiro						   "ldapmap_parseargs");
422490792Sgshapiro			(void) sm_io_close(sfd, SM_TIME_DEFAULT);
422598121Sgshapiro			if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
422698121Sgshapiro			{
422798121Sgshapiro				syserr("LDAP map: secret in %s too long",
422898121Sgshapiro				       ldapmap_dequote(lmap->ldap_secret));
422998121Sgshapiro				return false;
423098121Sgshapiro			}
423164562Sgshapiro			if (lmap->ldap_secret != NULL &&
423264562Sgshapiro			    strlen(m_tmp) > 0)
423364562Sgshapiro			{
423464562Sgshapiro				/* chomp newline */
423564562Sgshapiro				if (m_tmp[strlen(m_tmp) - 1] == '\n')
423664562Sgshapiro					m_tmp[strlen(m_tmp) - 1] = '\0';
423764562Sgshapiro
423864562Sgshapiro				lmap->ldap_secret = m_tmp;
423964562Sgshapiro			}
424064562Sgshapiro			break;
424164562Sgshapiro
424264562Sgshapiro# ifdef LDAP_AUTH_KRBV4
424364562Sgshapiro		  case LDAP_AUTH_KRBV4:
424464562Sgshapiro
424564562Sgshapiro			/*
424664562Sgshapiro			**  Secret is where the ticket file is
424764562Sgshapiro			**  stashed
424864562Sgshapiro			*/
424964562Sgshapiro
425098121Sgshapiro			(void) sm_snprintf(m_tmp, sizeof m_tmp,
425190792Sgshapiro				"KRBTKFILE=%s",
425290792Sgshapiro				ldapmap_dequote(lmap->ldap_secret));
425364562Sgshapiro			lmap->ldap_secret = m_tmp;
425464562Sgshapiro			break;
425564562Sgshapiro# endif /* LDAP_AUTH_KRBV4 */
425664562Sgshapiro
425764562Sgshapiro		  default:	       /* Should NEVER get here */
425864562Sgshapiro			syserr("LDAP map: Illegal value in lmap method");
425990792Sgshapiro			return false;
426090792Sgshapiro			/* NOTREACHED */
426164562Sgshapiro			break;
426264562Sgshapiro		}
426338032Speter	}
426438032Speter
426564562Sgshapiro	if (lmap->ldap_secret != NULL &&
426664562Sgshapiro	    (LDAPDefaults == NULL ||
426764562Sgshapiro	     LDAPDefaults == lmap ||
426864562Sgshapiro	     LDAPDefaults->ldap_secret != lmap->ldap_secret))
426964562Sgshapiro		lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
427038032Speter
427164562Sgshapiro	if (lmap->ldap_base != NULL &&
427264562Sgshapiro	    (LDAPDefaults == NULL ||
427364562Sgshapiro	     LDAPDefaults == lmap ||
427464562Sgshapiro	     LDAPDefaults->ldap_base != lmap->ldap_base))
427564562Sgshapiro		lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
427664562Sgshapiro
427764562Sgshapiro	/*
427864562Sgshapiro	**  Save the server from extra work.  If request is for a single
427964562Sgshapiro	**  match, tell the server to only return enough records to
428064562Sgshapiro	**  determine if there is a single match or not.  This can not
428164562Sgshapiro	**  be one since the server would only return one and we wouldn't
428264562Sgshapiro	**  know if there were others available.
428364562Sgshapiro	*/
428464562Sgshapiro
428564562Sgshapiro	if (bitset(MF_SINGLEMATCH, map->map_mflags))
428664562Sgshapiro		lmap->ldap_sizelimit = 2;
428764562Sgshapiro
428864562Sgshapiro	/* If setting defaults, don't process ldap_filter and ldap_attr */
428964562Sgshapiro	if (lmap == LDAPDefaults)
429090792Sgshapiro		return true;
429164562Sgshapiro
429264562Sgshapiro	if (lmap->ldap_filter != NULL)
429364562Sgshapiro		lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
429438032Speter	else
429538032Speter	{
429638032Speter		if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
429738032Speter		{
429838032Speter			syserr("No filter given in map %s", map->map_mname);
429990792Sgshapiro			return false;
430038032Speter		}
430138032Speter	}
430264562Sgshapiro
4303132943Sgshapiro	if (!attrssetup && lmap->ldap_attr[0] != NULL)
430438032Speter	{
430590792Sgshapiro		bool recurse = false;
430694334Sgshapiro		bool normalseen = false;
430790792Sgshapiro
430864562Sgshapiro		i = 0;
430964562Sgshapiro		p = ldapmap_dequote(lmap->ldap_attr[0]);
431064562Sgshapiro		lmap->ldap_attr[0] = NULL;
431164562Sgshapiro
431294334Sgshapiro		/* Prime the attr list with the objectClass attribute */
431394334Sgshapiro		lmap->ldap_attr[i] = "objectClass";
431494334Sgshapiro		lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
431594334Sgshapiro		lmap->ldap_attr_needobjclass[i] = NULL;
431694334Sgshapiro		i++;
431794334Sgshapiro
431864562Sgshapiro		while (p != NULL)
431938032Speter		{
432064562Sgshapiro			char *v;
432164562Sgshapiro
432264562Sgshapiro			while (isascii(*p) && isspace(*p))
432364562Sgshapiro				p++;
432464562Sgshapiro			if (*p == '\0')
432564562Sgshapiro				break;
432664562Sgshapiro			v = p;
432764562Sgshapiro			p = strchr(v, ',');
432864562Sgshapiro			if (p != NULL)
432964562Sgshapiro				*p++ = '\0';
433064562Sgshapiro
433171345Sgshapiro			if (i >= LDAPMAP_MAX_ATTR)
433264562Sgshapiro			{
433364562Sgshapiro				syserr("Too many return attributes in %s (max %d)",
433464562Sgshapiro				       map->map_mname, LDAPMAP_MAX_ATTR);
433590792Sgshapiro				return false;
433664562Sgshapiro			}
433764562Sgshapiro			if (*v != '\0')
433890792Sgshapiro			{
433994334Sgshapiro				int j;
434094334Sgshapiro				int use;
434190792Sgshapiro				char *type;
434294334Sgshapiro				char *needobjclass;
434390792Sgshapiro
434490792Sgshapiro				type = strchr(v, ':');
434590792Sgshapiro				if (type != NULL)
434694334Sgshapiro				{
434790792Sgshapiro					*type++ = '\0';
434894334Sgshapiro					needobjclass = strchr(type, ':');
434994334Sgshapiro					if (needobjclass != NULL)
435094334Sgshapiro						*needobjclass++ = '\0';
435194334Sgshapiro				}
435294334Sgshapiro				else
435394334Sgshapiro				{
435494334Sgshapiro					needobjclass = NULL;
435594334Sgshapiro				}
435690792Sgshapiro
435794334Sgshapiro				use = i;
435890792Sgshapiro
435994334Sgshapiro				/* allow override on "objectClass" type */
436094334Sgshapiro				if (sm_strcasecmp(v, "objectClass") == 0 &&
436194334Sgshapiro				    lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
436290792Sgshapiro				{
436394334Sgshapiro					use = 0;
436494334Sgshapiro				}
436594334Sgshapiro				else
436694334Sgshapiro				{
436794334Sgshapiro					/*
436894334Sgshapiro					**  Don't add something to attribute
436994334Sgshapiro					**  list twice.
437094334Sgshapiro					*/
437194334Sgshapiro
437294334Sgshapiro					for (j = 1; j < i; j++)
437390792Sgshapiro					{
437494334Sgshapiro						if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
437594334Sgshapiro						{
437694334Sgshapiro							syserr("Duplicate attribute (%s) in %s",
437794334Sgshapiro							       v, map->map_mname);
437894334Sgshapiro							return false;
437994334Sgshapiro						}
438090792Sgshapiro					}
438194334Sgshapiro
438294334Sgshapiro					lmap->ldap_attr[use] = newstr(v);
438394334Sgshapiro					if (needobjclass != NULL &&
438494334Sgshapiro					    *needobjclass != '\0' &&
438594334Sgshapiro					    *needobjclass != '*')
438690792Sgshapiro					{
438794334Sgshapiro						lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
438894334Sgshapiro					}
438994334Sgshapiro					else
439094334Sgshapiro					{
439194334Sgshapiro						lmap->ldap_attr_needobjclass[use] = NULL;
439294334Sgshapiro					}
439394334Sgshapiro
439494334Sgshapiro				}
439594334Sgshapiro
439694334Sgshapiro				if (type != NULL && *type != '\0')
439794334Sgshapiro				{
439894334Sgshapiro					if (sm_strcasecmp(type, "dn") == 0)
439994334Sgshapiro					{
440090792Sgshapiro						recurse = true;
440194334Sgshapiro						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
440290792Sgshapiro					}
440390792Sgshapiro					else if (sm_strcasecmp(type, "filter") == 0)
440490792Sgshapiro					{
440590792Sgshapiro						recurse = true;
440694334Sgshapiro						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
440790792Sgshapiro					}
440890792Sgshapiro					else if (sm_strcasecmp(type, "url") == 0)
440990792Sgshapiro					{
441090792Sgshapiro						recurse = true;
441194334Sgshapiro						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
441290792Sgshapiro					}
441394334Sgshapiro					else if (sm_strcasecmp(type, "normal") == 0)
441490792Sgshapiro					{
441594334Sgshapiro						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
441694334Sgshapiro						normalseen = true;
441790792Sgshapiro					}
441890792Sgshapiro					else
441990792Sgshapiro					{
442090792Sgshapiro						syserr("Unknown attribute type (%s) in %s",
442190792Sgshapiro						       type, map->map_mname);
442290792Sgshapiro						return false;
442390792Sgshapiro					}
442490792Sgshapiro				}
442590792Sgshapiro				else
442694334Sgshapiro				{
442794334Sgshapiro					lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
442894334Sgshapiro					normalseen = true;
442994334Sgshapiro				}
443090792Sgshapiro				i++;
443190792Sgshapiro			}
443238032Speter		}
443364562Sgshapiro		lmap->ldap_attr[i] = NULL;
4434132943Sgshapiro		attrssetup = true;
443594334Sgshapiro		if (recurse && !normalseen)
443690792Sgshapiro		{
443794334Sgshapiro			syserr("LDAP recursion requested in %s but no returnable attribute given",
443890792Sgshapiro			       map->map_mname);
443990792Sgshapiro			return false;
444090792Sgshapiro		}
444190792Sgshapiro		if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
444290792Sgshapiro		{
444390792Sgshapiro			syserr("LDAP recursion requested in %s can not be used with -n",
444490792Sgshapiro			       map->map_mname);
444590792Sgshapiro			return false;
444690792Sgshapiro		}
444738032Speter	}
444838032Speter	map->map_db1 = (ARBPTR_T) lmap;
444990792Sgshapiro	return true;
445038032Speter}
445138032Speter
445264562Sgshapiro/*
445364562Sgshapiro**  LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
445464562Sgshapiro**
445564562Sgshapiro**	Parameters:
445664562Sgshapiro**		spec -- map argument string from LDAPDefaults option
445764562Sgshapiro**
445864562Sgshapiro**	Returns:
445964562Sgshapiro**		None.
446064562Sgshapiro*/
446164562Sgshapiro
446264562Sgshapirovoid
446364562Sgshapiroldapmap_set_defaults(spec)
446464562Sgshapiro	char *spec;
446564562Sgshapiro{
446673188Sgshapiro	STAB *class;
446764562Sgshapiro	MAP map;
446864562Sgshapiro
446964562Sgshapiro	/* Allocate and set the default values */
447064562Sgshapiro	if (LDAPDefaults == NULL)
447190792Sgshapiro		LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
447290792Sgshapiro	sm_ldap_clear(LDAPDefaults);
447364562Sgshapiro
447464562Sgshapiro	memset(&map, '\0', sizeof map);
447573188Sgshapiro
447673188Sgshapiro	/* look up the class */
447773188Sgshapiro	class = stab("ldap", ST_MAPCLASS, ST_FIND);
447873188Sgshapiro	if (class == NULL)
447973188Sgshapiro	{
448073188Sgshapiro		syserr("readcf: LDAPDefaultSpec: class ldap not available");
448173188Sgshapiro		return;
448273188Sgshapiro	}
448373188Sgshapiro	map.map_class = &class->s_mapclass;
448464562Sgshapiro	map.map_db1 = (ARBPTR_T) LDAPDefaults;
448573188Sgshapiro	map.map_mname = "O LDAPDefaultSpec";
448664562Sgshapiro
448764562Sgshapiro	(void) ldapmap_parseargs(&map, spec);
448864562Sgshapiro
448964562Sgshapiro	/* These should never be set in LDAPDefaults */
449064562Sgshapiro	if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
449164562Sgshapiro	    map.map_spacesub != SpaceSub ||
449264562Sgshapiro	    map.map_app != NULL ||
449364562Sgshapiro	    map.map_tapp != NULL)
449464562Sgshapiro	{
449564562Sgshapiro		syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
449690792Sgshapiro		SM_FREE_CLR(map.map_app);
449790792Sgshapiro		SM_FREE_CLR(map.map_tapp);
449864562Sgshapiro	}
449964562Sgshapiro
450064562Sgshapiro	if (LDAPDefaults->ldap_filter != NULL)
450164562Sgshapiro	{
450264562Sgshapiro		syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
450394334Sgshapiro
450464562Sgshapiro		/* don't free, it isn't malloc'ed in parseargs */
450564562Sgshapiro		LDAPDefaults->ldap_filter = NULL;
450664562Sgshapiro	}
450764562Sgshapiro
450864562Sgshapiro	if (LDAPDefaults->ldap_attr[0] != NULL)
450964562Sgshapiro	{
451064562Sgshapiro		syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
451164562Sgshapiro		/* don't free, they aren't malloc'ed in parseargs */
451264562Sgshapiro		LDAPDefaults->ldap_attr[0] = NULL;
451364562Sgshapiro	}
451464562Sgshapiro}
451564562Sgshapiro#endif /* LDAPMAP */
451690792Sgshapiro/*
451764562Sgshapiro**  PH map
451864562Sgshapiro*/
451964562Sgshapiro
452090792Sgshapiro#if PH_MAP
452164562Sgshapiro
452264562Sgshapiro/*
452364562Sgshapiro**  Support for the CCSO Nameserver (ph/qi).
452464562Sgshapiro**  This code is intended to replace the so-called "ph mailer".
452564562Sgshapiro**  Contributed by Mark D. Roth <roth@uiuc.edu>.  Contact him for support.
452664562Sgshapiro*/
452764562Sgshapiro
452890792Sgshapiro/* what version of the ph map code we're running */
4529110560Sgshapirostatic char phmap_id[128];
453064562Sgshapiro
453190792Sgshapiro/* sendmail version for phmap id string */
453290792Sgshapiroextern const char Version[];
453390792Sgshapiro
4534132943Sgshapiro/* assume we're using nph-1.2.x if not specified */
4535110560Sgshapiro# ifndef NPH_VERSION
4536132943Sgshapiro#  define NPH_VERSION		10200
4537110560Sgshapiro# endif
4538110560Sgshapiro
4539110560Sgshapiro/* compatibility for versions older than nph-1.2.0 */
4540110560Sgshapiro# if NPH_VERSION < 10200
4541110560Sgshapiro#  define PH_OPEN_ROUNDROBIN	PH_ROUNDROBIN
4542110560Sgshapiro#  define PH_OPEN_DONTID	PH_DONTID
4543110560Sgshapiro#  define PH_CLOSE_FAST		PH_FASTCLOSE
4544110560Sgshapiro#  define PH_ERR_DATAERR	PH_DATAERR
4545110560Sgshapiro#  define PH_ERR_NOMATCH	PH_NOMATCH
4546110560Sgshapiro# endif /* NPH_VERSION < 10200 */
4547110560Sgshapiro
454864562Sgshapiro/*
454964562Sgshapiro**  PH_MAP_PARSEARGS -- parse ph map definition args.
455064562Sgshapiro*/
455164562Sgshapiro
455264562Sgshapirobool
455364562Sgshapiroph_map_parseargs(map, args)
455464562Sgshapiro	MAP *map;
455564562Sgshapiro	char *args;
455664562Sgshapiro{
455790792Sgshapiro	register bool done;
455890792Sgshapiro	register char *p = args;
455964562Sgshapiro	PH_MAP_STRUCT *pmap = NULL;
456064562Sgshapiro
456190792Sgshapiro	/* initialize version string */
456290792Sgshapiro	(void) sm_snprintf(phmap_id, sizeof phmap_id,
456390792Sgshapiro			   "sendmail-%s phmap-20010529 libphclient-%s",
456490792Sgshapiro			   Version, libphclient_version);
456590792Sgshapiro
456664562Sgshapiro	pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap);
456764562Sgshapiro
456864562Sgshapiro	/* defaults */
456964562Sgshapiro	pmap->ph_servers = NULL;
457064562Sgshapiro	pmap->ph_field_list = NULL;
457190792Sgshapiro	pmap->ph = NULL;
457264562Sgshapiro	pmap->ph_timeout = 0;
457390792Sgshapiro	pmap->ph_fastclose = 0;
457464562Sgshapiro
457564562Sgshapiro	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
457664562Sgshapiro	for (;;)
457764562Sgshapiro	{
457864562Sgshapiro		while (isascii(*p) && isspace(*p))
457964562Sgshapiro			p++;
458064562Sgshapiro		if (*p != '-')
458164562Sgshapiro			break;
458264562Sgshapiro		switch (*++p)
458364562Sgshapiro		{
458464562Sgshapiro		  case 'N':
458564562Sgshapiro			map->map_mflags |= MF_INCLNULL;
458664562Sgshapiro			map->map_mflags &= ~MF_TRY0NULL;
458764562Sgshapiro			break;
458864562Sgshapiro
458964562Sgshapiro		  case 'O':
459064562Sgshapiro			map->map_mflags &= ~MF_TRY1NULL;
459164562Sgshapiro			break;
459264562Sgshapiro
459364562Sgshapiro		  case 'o':
459464562Sgshapiro			map->map_mflags |= MF_OPTIONAL;
459564562Sgshapiro			break;
459664562Sgshapiro
459764562Sgshapiro		  case 'f':
459864562Sgshapiro			map->map_mflags |= MF_NOFOLDCASE;
459964562Sgshapiro			break;
460064562Sgshapiro
460164562Sgshapiro		  case 'm':
460264562Sgshapiro			map->map_mflags |= MF_MATCHONLY;
460364562Sgshapiro			break;
460464562Sgshapiro
460564562Sgshapiro		  case 'A':
460664562Sgshapiro			map->map_mflags |= MF_APPEND;
460764562Sgshapiro			break;
460864562Sgshapiro
460964562Sgshapiro		  case 'q':
461064562Sgshapiro			map->map_mflags |= MF_KEEPQUOTES;
461164562Sgshapiro			break;
461264562Sgshapiro
461364562Sgshapiro		  case 't':
461464562Sgshapiro			map->map_mflags |= MF_NODEFER;
461564562Sgshapiro			break;
461664562Sgshapiro
461764562Sgshapiro		  case 'a':
461864562Sgshapiro			map->map_app = ++p;
461964562Sgshapiro			break;
462064562Sgshapiro
462164562Sgshapiro		  case 'T':
462264562Sgshapiro			map->map_tapp = ++p;
462364562Sgshapiro			break;
462464562Sgshapiro
462564562Sgshapiro		  case 'l':
462664562Sgshapiro			while (isascii(*++p) && isspace(*p))
462764562Sgshapiro				continue;
462864562Sgshapiro			pmap->ph_timeout = atoi(p);
462964562Sgshapiro			break;
463064562Sgshapiro
463164562Sgshapiro		  case 'S':
463264562Sgshapiro			map->map_spacesub = *++p;
463364562Sgshapiro			break;
463464562Sgshapiro
463564562Sgshapiro		  case 'D':
463664562Sgshapiro			map->map_mflags |= MF_DEFER;
463764562Sgshapiro			break;
463864562Sgshapiro
463964562Sgshapiro		  case 'h':		/* PH server list */
464064562Sgshapiro			while (isascii(*++p) && isspace(*p))
464164562Sgshapiro				continue;
464264562Sgshapiro			pmap->ph_servers = p;
464364562Sgshapiro			break;
464464562Sgshapiro
464590792Sgshapiro		  case 'k':		/* fields to search for */
464664562Sgshapiro			while (isascii(*++p) && isspace(*p))
464764562Sgshapiro				continue;
464864562Sgshapiro			pmap->ph_field_list = p;
464964562Sgshapiro			break;
465064562Sgshapiro
465164562Sgshapiro		  default:
465290792Sgshapiro			syserr("ph_map_parseargs: unknown option -%c", *p);
465364562Sgshapiro		}
465464562Sgshapiro
465564562Sgshapiro		/* try to account for quoted strings */
465664562Sgshapiro		done = isascii(*p) && isspace(*p);
465764562Sgshapiro		while (*p != '\0' && !done)
465864562Sgshapiro		{
465964562Sgshapiro			if (*p == '"')
466064562Sgshapiro			{
466164562Sgshapiro				while (*++p != '"' && *p != '\0')
466264562Sgshapiro					continue;
466364562Sgshapiro				if (*p != '\0')
466464562Sgshapiro					p++;
466564562Sgshapiro			}
466664562Sgshapiro			else
466764562Sgshapiro				p++;
466864562Sgshapiro			done = isascii(*p) && isspace(*p);
466964562Sgshapiro		}
467064562Sgshapiro
467164562Sgshapiro		if (*p != '\0')
467264562Sgshapiro			*p++ = '\0';
467364562Sgshapiro	}
467464562Sgshapiro
467564562Sgshapiro	if (map->map_app != NULL)
467664562Sgshapiro		map->map_app = newstr(ph_map_dequote(map->map_app));
467764562Sgshapiro	if (map->map_tapp != NULL)
467864562Sgshapiro		map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
467964562Sgshapiro
468064562Sgshapiro	if (pmap->ph_field_list != NULL)
468164562Sgshapiro		pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
468264562Sgshapiro
468364562Sgshapiro	if (pmap->ph_servers != NULL)
468464562Sgshapiro		pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
468564562Sgshapiro	else
468664562Sgshapiro	{
468764562Sgshapiro		syserr("ph_map_parseargs: -h flag is required");
468890792Sgshapiro		return false;
468964562Sgshapiro	}
469064562Sgshapiro
469164562Sgshapiro	map->map_db1 = (ARBPTR_T) pmap;
469290792Sgshapiro	return true;
469364562Sgshapiro}
469464562Sgshapiro
469564562Sgshapiro/*
469664562Sgshapiro**  PH_MAP_CLOSE -- close the connection to the ph server
469764562Sgshapiro*/
469864562Sgshapiro
469990792Sgshapirovoid
470090792Sgshapiroph_map_close(map)
470164562Sgshapiro	MAP *map;
470264562Sgshapiro{
470364562Sgshapiro	PH_MAP_STRUCT *pmap;
470464562Sgshapiro
470564562Sgshapiro	pmap = (PH_MAP_STRUCT *)map->map_db1;
470690792Sgshapiro	if (tTd(38, 9))
470794334Sgshapiro		sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
470890792Sgshapiro			   map->map_mname, pmap->ph_fastclose);
470964562Sgshapiro
471090792Sgshapiro
471190792Sgshapiro	if (pmap->ph != NULL)
471264562Sgshapiro	{
471390792Sgshapiro		ph_set_sendhook(pmap->ph, NULL);
471490792Sgshapiro		ph_set_recvhook(pmap->ph, NULL);
471590792Sgshapiro		ph_close(pmap->ph, pmap->ph_fastclose);
471664562Sgshapiro	}
471790792Sgshapiro
471864562Sgshapiro	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
471964562Sgshapiro}
472064562Sgshapiro
472164562Sgshapirostatic jmp_buf  PHTimeout;
472264562Sgshapiro
472364562Sgshapiro/* ARGSUSED */
472464562Sgshapirostatic void
472590792Sgshapiroph_timeout(unused)
472690792Sgshapiro	int unused;
472764562Sgshapiro{
472877349Sgshapiro	/*
472977349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
473077349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
473177349Sgshapiro	**	DOING.
473277349Sgshapiro	*/
473377349Sgshapiro
473477349Sgshapiro	errno = ETIMEDOUT;
473564562Sgshapiro	longjmp(PHTimeout, 1);
473664562Sgshapiro}
473764562Sgshapiro
473890792Sgshapirostatic void
4739110560Sgshapiro#if NPH_VERSION >= 10200
4740110560Sgshapiroph_map_send_debug(appdata, text)
4741110560Sgshapiro	void *appdata;
4742110560Sgshapiro#else
474390792Sgshapiroph_map_send_debug(text)
4744110560Sgshapiro#endif
474590792Sgshapiro	char *text;
474664562Sgshapiro{
474790792Sgshapiro	if (LogLevel > 9)
474890792Sgshapiro		sm_syslog(LOG_NOTICE, CurEnv->e_id,
474990792Sgshapiro			  "ph_map_send_debug: ==> %s", text);
475090792Sgshapiro	if (tTd(38, 20))
475190792Sgshapiro		sm_dprintf("ph_map_send_debug: ==> %s\n", text);
475290792Sgshapiro}
475364562Sgshapiro
475490792Sgshapirostatic void
4755110560Sgshapiro#if NPH_VERSION >= 10200
4756110560Sgshapiroph_map_recv_debug(appdata, text)
4757110560Sgshapiro	void *appdata;
4758110560Sgshapiro#else
475990792Sgshapiroph_map_recv_debug(text)
4760110560Sgshapiro#endif
476190792Sgshapiro	char *text;
476290792Sgshapiro{
476390792Sgshapiro	if (LogLevel > 10)
476490792Sgshapiro		sm_syslog(LOG_NOTICE, CurEnv->e_id,
476590792Sgshapiro			  "ph_map_recv_debug: <== %s", text);
476690792Sgshapiro	if (tTd(38, 21))
476790792Sgshapiro		sm_dprintf("ph_map_recv_debug: <== %s\n", text);
476864562Sgshapiro}
476964562Sgshapiro
477090792Sgshapiro/*
477164562Sgshapiro**  PH_MAP_OPEN -- sub for opening PH map
477264562Sgshapiro*/
477364562Sgshapirobool
477464562Sgshapiroph_map_open(map, mode)
477564562Sgshapiro	MAP *map;
477664562Sgshapiro	int mode;
477764562Sgshapiro{
477890792Sgshapiro	PH_MAP_STRUCT *pmap;
477990792Sgshapiro	register SM_EVENT *ev = NULL;
478064562Sgshapiro	int save_errno = 0;
478190792Sgshapiro	char *hostlist, *host;
478264562Sgshapiro
478364562Sgshapiro	if (tTd(38, 2))
478490792Sgshapiro		sm_dprintf("ph_map_open(%s)\n", map->map_mname);
478564562Sgshapiro
478664562Sgshapiro	mode &= O_ACCMODE;
478764562Sgshapiro	if (mode != O_RDONLY)
478864562Sgshapiro	{
478964562Sgshapiro		/* issue a pseudo-error message */
479090792Sgshapiro		errno = SM_EMAPCANTWRITE;
479190792Sgshapiro		return false;
479264562Sgshapiro	}
479364562Sgshapiro
479466494Sgshapiro	if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
479566494Sgshapiro	    bitset(MF_DEFER, map->map_mflags))
479666494Sgshapiro	{
479766494Sgshapiro		if (tTd(9, 1))
479890792Sgshapiro			sm_dprintf("ph_map_open(%s) => DEFERRED\n",
479990792Sgshapiro				   map->map_mname);
480066494Sgshapiro
480166494Sgshapiro		/*
480290792Sgshapiro		**  Unset MF_DEFER here so that map_lookup() returns
480390792Sgshapiro		**  a temporary failure using the bogus map and
480490792Sgshapiro		**  map->map_tapp instead of the default permanent error.
480566494Sgshapiro		*/
480666494Sgshapiro
480766494Sgshapiro		map->map_mflags &= ~MF_DEFER;
480890792Sgshapiro		return false;
480966494Sgshapiro	}
481066494Sgshapiro
481164562Sgshapiro	pmap = (PH_MAP_STRUCT *)map->map_db1;
481290792Sgshapiro	pmap->ph_fastclose = 0;		/* refresh field for reopen */
481364562Sgshapiro
481490792Sgshapiro	/* try each host in the list */
481564562Sgshapiro	hostlist = newstr(pmap->ph_servers);
481690792Sgshapiro	for (host = strtok(hostlist, " ");
481790792Sgshapiro	     host != NULL;
481890792Sgshapiro	     host = strtok(NULL, " "))
481964562Sgshapiro	{
482090792Sgshapiro		/* set timeout */
482164562Sgshapiro		if (pmap->ph_timeout != 0)
482264562Sgshapiro		{
482364562Sgshapiro			if (setjmp(PHTimeout) != 0)
482464562Sgshapiro			{
482564562Sgshapiro				ev = NULL;
482664562Sgshapiro				if (LogLevel > 1)
482764562Sgshapiro					sm_syslog(LOG_NOTICE, CurEnv->e_id,
482864562Sgshapiro						  "timeout connecting to PH server %.100s",
482990792Sgshapiro						  host);
483064562Sgshapiro				errno = ETIMEDOUT;
483164562Sgshapiro				goto ph_map_open_abort;
483264562Sgshapiro			}
483390792Sgshapiro			ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
483464562Sgshapiro		}
483590792Sgshapiro
483690792Sgshapiro		/* open connection to server */
4837110560Sgshapiro		if (ph_open(&(pmap->ph), host,
4838110560Sgshapiro			    PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
4839110560Sgshapiro			    ph_map_send_debug, ph_map_recv_debug
4840110560Sgshapiro#if NPH_VERSION >= 10200
4841110560Sgshapiro			    , NULL
4842110560Sgshapiro#endif
4843110560Sgshapiro			    ) == 0
4844110560Sgshapiro		    && ph_id(pmap->ph, phmap_id) == 0)
484564562Sgshapiro		{
484664562Sgshapiro			if (ev != NULL)
484790792Sgshapiro				sm_clrevent(ev);
484890792Sgshapiro			sm_free(hostlist); /* XXX */
484990792Sgshapiro			return true;
485064562Sgshapiro		}
485190792Sgshapiro
485264562Sgshapiro  ph_map_open_abort:
485390792Sgshapiro		save_errno = errno;
485464562Sgshapiro		if (ev != NULL)
485590792Sgshapiro			sm_clrevent(ev);
4856110560Sgshapiro		pmap->ph_fastclose = PH_CLOSE_FAST;
485790792Sgshapiro		ph_map_close(map);
485890792Sgshapiro		errno = save_errno;
485990792Sgshapiro	}
486064562Sgshapiro
486166494Sgshapiro	if (bitset(MF_NODEFER, map->map_mflags))
486264562Sgshapiro	{
486366494Sgshapiro		if (errno == 0)
486464562Sgshapiro			errno = EAGAIN;
486566494Sgshapiro		syserr("ph_map_open: %s: cannot connect to PH server",
486666494Sgshapiro		       map->map_mname);
486764562Sgshapiro	}
486866494Sgshapiro	else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
486964562Sgshapiro		sm_syslog(LOG_NOTICE, CurEnv->e_id,
487066494Sgshapiro			  "ph_map_open: %s: cannot connect to PH server",
487166494Sgshapiro			  map->map_mname);
487290792Sgshapiro	sm_free(hostlist); /* XXX */
487390792Sgshapiro	return false;
487464562Sgshapiro}
487564562Sgshapiro
487664562Sgshapiro/*
487764562Sgshapiro**  PH_MAP_LOOKUP -- look up key from ph server
487864562Sgshapiro*/
487964562Sgshapiro
488064562Sgshapirochar *
488164562Sgshapiroph_map_lookup(map, key, args, pstat)
488264562Sgshapiro	MAP *map;
488364562Sgshapiro	char *key;
488464562Sgshapiro	char **args;
488564562Sgshapiro	int *pstat;
488664562Sgshapiro{
488790792Sgshapiro	int i, save_errno = 0;
488890792Sgshapiro	register SM_EVENT *ev = NULL;
488964562Sgshapiro	PH_MAP_STRUCT *pmap;
489090792Sgshapiro	char *value = NULL;
489164562Sgshapiro
489264562Sgshapiro	pmap = (PH_MAP_STRUCT *)map->map_db1;
489364562Sgshapiro
489464562Sgshapiro	*pstat = EX_OK;
489564562Sgshapiro
489690792Sgshapiro	/* set timeout */
489764562Sgshapiro	if (pmap->ph_timeout != 0)
489864562Sgshapiro	{
489964562Sgshapiro		if (setjmp(PHTimeout) != 0)
490064562Sgshapiro		{
490164562Sgshapiro			ev = NULL;
490264562Sgshapiro			if (LogLevel > 1)
490364562Sgshapiro				sm_syslog(LOG_NOTICE, CurEnv->e_id,
490464562Sgshapiro					  "timeout during PH lookup of %.100s",
490564562Sgshapiro					  key);
490664562Sgshapiro			errno = ETIMEDOUT;
490764562Sgshapiro			*pstat = EX_TEMPFAIL;
490864562Sgshapiro			goto ph_map_lookup_abort;
490964562Sgshapiro		}
491090792Sgshapiro		ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
491164562Sgshapiro	}
491264562Sgshapiro
491390792Sgshapiro	/* perform lookup */
491490792Sgshapiro	i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
491590792Sgshapiro	if (i == -1)
491690792Sgshapiro		*pstat = EX_TEMPFAIL;
4917110560Sgshapiro	else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
491890792Sgshapiro		*pstat = EX_UNAVAILABLE;
491964562Sgshapiro
492064562Sgshapiro  ph_map_lookup_abort:
492164562Sgshapiro	if (ev != NULL)
492290792Sgshapiro		sm_clrevent(ev);
492364562Sgshapiro
492464562Sgshapiro	/*
492590792Sgshapiro	**  Close the connection if the timer popped
492664562Sgshapiro	**  or we got a temporary PH error
492764562Sgshapiro	*/
492864562Sgshapiro
492964562Sgshapiro	if (*pstat == EX_TEMPFAIL)
493090792Sgshapiro	{
493190792Sgshapiro		save_errno = errno;
4932110560Sgshapiro		pmap->ph_fastclose = PH_CLOSE_FAST;
493390792Sgshapiro		ph_map_close(map);
493490792Sgshapiro		errno = save_errno;
493590792Sgshapiro	}
493664562Sgshapiro
493764562Sgshapiro	if (*pstat == EX_OK)
493864562Sgshapiro	{
493964562Sgshapiro		if (tTd(38,20))
494090792Sgshapiro			sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
494164562Sgshapiro
494264562Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
494390792Sgshapiro			return map_rewrite(map, key, strlen(key), NULL);
494464562Sgshapiro		else
494590792Sgshapiro			return map_rewrite(map, value, strlen(value), args);
494664562Sgshapiro	}
494764562Sgshapiro
494864562Sgshapiro	return NULL;
494964562Sgshapiro}
495064562Sgshapiro#endif /* PH_MAP */
495190792Sgshapiro/*
495242575Speter**  syslog map
495338032Speter*/
495438032Speter
495538032Speter#define map_prio	map_lockfd	/* overload field */
495638032Speter
495738032Speter/*
495842575Speter**  SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
495938032Speter*/
496038032Speter
496138032Speterbool
496238032Spetersyslog_map_parseargs(map, args)
496338032Speter	MAP *map;
496438032Speter	char *args;
496538032Speter{
496638032Speter	char *p = args;
496738032Speter	char *priority = NULL;
496838032Speter
496964562Sgshapiro	/* there is no check whether there is really an argument */
497064562Sgshapiro	while (*p != '\0')
497138032Speter	{
497238032Speter		while (isascii(*p) && isspace(*p))
497338032Speter			p++;
497438032Speter		if (*p != '-')
497538032Speter			break;
497664562Sgshapiro		++p;
497764562Sgshapiro		if (*p == 'D')
497864562Sgshapiro		{
497964562Sgshapiro			map->map_mflags |= MF_DEFER;
498064562Sgshapiro			++p;
498164562Sgshapiro		}
498264562Sgshapiro		else if (*p == 'S')
498364562Sgshapiro		{
498464562Sgshapiro			map->map_spacesub = *++p;
498564562Sgshapiro			if (*p != '\0')
498664562Sgshapiro				p++;
498764562Sgshapiro		}
498864562Sgshapiro		else if (*p == 'L')
498964562Sgshapiro		{
499064562Sgshapiro			while (*++p != '\0' && isascii(*p) && isspace(*p))
499164562Sgshapiro				continue;
499264562Sgshapiro			if (*p == '\0')
499364562Sgshapiro				break;
499464562Sgshapiro			priority = p;
499564562Sgshapiro			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
499664562Sgshapiro				p++;
499764562Sgshapiro			if (*p != '\0')
499864562Sgshapiro				*p++ = '\0';
499964562Sgshapiro		}
500064562Sgshapiro		else
500164562Sgshapiro		{
500264562Sgshapiro			syserr("Illegal option %c map syslog", *p);
500364562Sgshapiro			++p;
500464562Sgshapiro		}
500538032Speter	}
500638032Speter
500738032Speter	if (priority == NULL)
500838032Speter		map->map_prio = LOG_INFO;
500938032Speter	else
501038032Speter	{
501190792Sgshapiro		if (sm_strncasecmp("LOG_", priority, 4) == 0)
501238032Speter			priority += 4;
501338032Speter
501438032Speter#ifdef LOG_EMERG
501590792Sgshapiro		if (sm_strcasecmp("EMERG", priority) == 0)
501638032Speter			map->map_prio = LOG_EMERG;
501738032Speter		else
501864562Sgshapiro#endif /* LOG_EMERG */
501938032Speter#ifdef LOG_ALERT
502090792Sgshapiro		if (sm_strcasecmp("ALERT", priority) == 0)
502138032Speter			map->map_prio = LOG_ALERT;
502238032Speter		else
502364562Sgshapiro#endif /* LOG_ALERT */
502438032Speter#ifdef LOG_CRIT
502590792Sgshapiro		if (sm_strcasecmp("CRIT", priority) == 0)
502638032Speter			map->map_prio = LOG_CRIT;
502738032Speter		else
502864562Sgshapiro#endif /* LOG_CRIT */
502938032Speter#ifdef LOG_ERR
503090792Sgshapiro		if (sm_strcasecmp("ERR", priority) == 0)
503138032Speter			map->map_prio = LOG_ERR;
503238032Speter		else
503364562Sgshapiro#endif /* LOG_ERR */
503438032Speter#ifdef LOG_WARNING
503590792Sgshapiro		if (sm_strcasecmp("WARNING", priority) == 0)
503638032Speter			map->map_prio = LOG_WARNING;
503738032Speter		else
503864562Sgshapiro#endif /* LOG_WARNING */
503938032Speter#ifdef LOG_NOTICE
504090792Sgshapiro		if (sm_strcasecmp("NOTICE", priority) == 0)
504138032Speter			map->map_prio = LOG_NOTICE;
504238032Speter		else
504364562Sgshapiro#endif /* LOG_NOTICE */
504438032Speter#ifdef LOG_INFO
504590792Sgshapiro		if (sm_strcasecmp("INFO", priority) == 0)
504638032Speter			map->map_prio = LOG_INFO;
504738032Speter		else
504864562Sgshapiro#endif /* LOG_INFO */
504938032Speter#ifdef LOG_DEBUG
505090792Sgshapiro		if (sm_strcasecmp("DEBUG", priority) == 0)
505138032Speter			map->map_prio = LOG_DEBUG;
505238032Speter		else
505364562Sgshapiro#endif /* LOG_DEBUG */
505438032Speter		{
505590792Sgshapiro			syserr("syslog_map_parseargs: Unknown priority %s",
505638032Speter			       priority);
505790792Sgshapiro			return false;
505838032Speter		}
505938032Speter	}
506090792Sgshapiro	return true;
506138032Speter}
506238032Speter
506338032Speter/*
506442575Speter**  SYSLOG_MAP_LOOKUP -- rewrite and syslog message.  Always return empty string
506538032Speter*/
506638032Speter
506738032Speterchar *
506838032Spetersyslog_map_lookup(map, string, args, statp)
506938032Speter	MAP *map;
507038032Speter	char *string;
507138032Speter	char **args;
507238032Speter	int *statp;
507338032Speter{
507438032Speter	char *ptr = map_rewrite(map, string, strlen(string), args);
507538032Speter
507638032Speter	if (ptr != NULL)
507738032Speter	{
507838032Speter		if (tTd(38, 20))
507990792Sgshapiro			sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
508064562Sgshapiro				map->map_mname, map->map_prio, ptr);
508138032Speter
508238032Speter		sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
508338032Speter	}
508438032Speter
508538032Speter	*statp = EX_OK;
508638032Speter	return "";
508738032Speter}
508838032Speter
508990792Sgshapiro/*
509038032Speter**  HESIOD Modules
509138032Speter*/
509238032Speter
509390792Sgshapiro#if HESIOD
509438032Speter
509538032Speterbool
509638032Speterhes_map_open(map, mode)
509738032Speter	MAP *map;
509838032Speter	int mode;
509938032Speter{
510038032Speter	if (tTd(38, 2))
510190792Sgshapiro		sm_dprintf("hes_map_open(%s, %s, %d)\n",
510238032Speter			map->map_mname, map->map_file, mode);
510338032Speter
510438032Speter	if (mode != O_RDONLY)
510538032Speter	{
510638032Speter		/* issue a pseudo-error message */
510790792Sgshapiro		errno = SM_EMAPCANTWRITE;
510890792Sgshapiro		return false;
510938032Speter	}
511038032Speter
511164562Sgshapiro# ifdef HESIOD_INIT
511238032Speter	if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
511390792Sgshapiro		return true;
511438032Speter
511538032Speter	if (!bitset(MF_OPTIONAL, map->map_mflags))
511694334Sgshapiro		syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
511790792Sgshapiro			sm_errstring(errno));
511890792Sgshapiro	return false;
511964562Sgshapiro# else /* HESIOD_INIT */
512038032Speter	if (hes_error() == HES_ER_UNINIT)
512138032Speter		hes_init();
512238032Speter	switch (hes_error())
512338032Speter	{
512438032Speter	  case HES_ER_OK:
512538032Speter	  case HES_ER_NOTFOUND:
512690792Sgshapiro		return true;
512738032Speter	}
512838032Speter
512938032Speter	if (!bitset(MF_OPTIONAL, map->map_mflags))
513094334Sgshapiro		syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
513138032Speter
513290792Sgshapiro	return false;
513364562Sgshapiro# endif /* HESIOD_INIT */
513438032Speter}
513538032Speter
513638032Speterchar *
513738032Speterhes_map_lookup(map, name, av, statp)
513838032Speter	MAP *map;
513938032Speter	char *name;
514038032Speter	char **av;
514138032Speter	int *statp;
514238032Speter{
514338032Speter	char **hp;
514438032Speter
514538032Speter	if (tTd(38, 20))
514690792Sgshapiro		sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
514738032Speter
514838032Speter	if (name[0] == '\\')
514938032Speter	{
515038032Speter		char *np;
515138032Speter		int nl;
515277349Sgshapiro		int save_errno;
515338032Speter		char nbuf[MAXNAME];
515438032Speter
515538032Speter		nl = strlen(name);
515638032Speter		if (nl < sizeof nbuf - 1)
515738032Speter			np = nbuf;
515838032Speter		else
515938032Speter			np = xalloc(strlen(name) + 2);
516038032Speter		np[0] = '\\';
516190792Sgshapiro		(void) sm_strlcpy(&np[1], name, (sizeof nbuf) - 1);
516264562Sgshapiro# ifdef HESIOD_INIT
516338032Speter		hp = hesiod_resolve(HesiodContext, np, map->map_file);
516464562Sgshapiro# else /* HESIOD_INIT */
516538032Speter		hp = hes_resolve(np, map->map_file);
516664562Sgshapiro# endif /* HESIOD_INIT */
516777349Sgshapiro		save_errno = errno;
516838032Speter		if (np != nbuf)
516990792Sgshapiro			sm_free(np); /* XXX */
517077349Sgshapiro		errno = save_errno;
517138032Speter	}
517238032Speter	else
517338032Speter	{
517464562Sgshapiro# ifdef HESIOD_INIT
517538032Speter		hp = hesiod_resolve(HesiodContext, name, map->map_file);
517664562Sgshapiro# else /* HESIOD_INIT */
517738032Speter		hp = hes_resolve(name, map->map_file);
517864562Sgshapiro# endif /* HESIOD_INIT */
517938032Speter	}
518064562Sgshapiro# ifdef HESIOD_INIT
518177349Sgshapiro	if (hp == NULL || *hp == NULL)
518238032Speter	{
518338032Speter		switch (errno)
518438032Speter		{
518538032Speter		  case ENOENT:
518638032Speter			  *statp = EX_NOTFOUND;
518738032Speter			  break;
518838032Speter		  case ECONNREFUSED:
518938032Speter			  *statp = EX_TEMPFAIL;
519038032Speter			  break;
519190792Sgshapiro		  case EMSGSIZE:
519238032Speter		  case ENOMEM:
519338032Speter		  default:
519438032Speter			  *statp = EX_UNAVAILABLE;
519538032Speter			  break;
519638032Speter		}
519782017Sgshapiro		if (hp != NULL)
519882017Sgshapiro			hesiod_free_list(HesiodContext, hp);
519938032Speter		return NULL;
520038032Speter	}
520164562Sgshapiro# else /* HESIOD_INIT */
520238032Speter	if (hp == NULL || hp[0] == NULL)
520338032Speter	{
520438032Speter		switch (hes_error())
520538032Speter		{
520638032Speter		  case HES_ER_OK:
520738032Speter			*statp = EX_OK;
520838032Speter			break;
520938032Speter
521038032Speter		  case HES_ER_NOTFOUND:
521138032Speter			*statp = EX_NOTFOUND;
521238032Speter			break;
521338032Speter
521438032Speter		  case HES_ER_CONFIG:
521538032Speter			*statp = EX_UNAVAILABLE;
521638032Speter			break;
521738032Speter
521838032Speter		  case HES_ER_NET:
521938032Speter			*statp = EX_TEMPFAIL;
522038032Speter			break;
522138032Speter		}
522238032Speter		return NULL;
522338032Speter	}
522464562Sgshapiro# endif /* HESIOD_INIT */
522538032Speter
522638032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
522738032Speter		return map_rewrite(map, name, strlen(name), NULL);
522838032Speter	else
522938032Speter		return map_rewrite(map, hp[0], strlen(hp[0]), av);
523038032Speter}
523138032Speter
523290792Sgshapiro/*
523390792Sgshapiro**  HES_MAP_CLOSE -- free the Hesiod context
523490792Sgshapiro*/
523590792Sgshapiro
523690792Sgshapirovoid
523790792Sgshapirohes_map_close(map)
523890792Sgshapiro	MAP *map;
523990792Sgshapiro{
524090792Sgshapiro	if (tTd(38, 20))
524190792Sgshapiro		sm_dprintf("hes_map_close(%s)\n", map->map_file);
524290792Sgshapiro
524390792Sgshapiro# ifdef HESIOD_INIT
524490792Sgshapiro	/* Free the hesiod context */
524590792Sgshapiro	if (HesiodContext != NULL)
524690792Sgshapiro	{
524790792Sgshapiro		hesiod_end(HesiodContext);
524890792Sgshapiro		HesiodContext = NULL;
524990792Sgshapiro	}
525090792Sgshapiro# endif /* HESIOD_INIT */
525190792Sgshapiro}
525290792Sgshapiro
525364562Sgshapiro#endif /* HESIOD */
525490792Sgshapiro/*
525538032Speter**  NeXT NETINFO Modules
525638032Speter*/
525738032Speter
525838032Speter#if NETINFO
525938032Speter
526038032Speter# define NETINFO_DEFAULT_DIR		"/aliases"
526138032Speter# define NETINFO_DEFAULT_PROPERTY	"members"
526238032Speter
526338032Speter/*
526438032Speter**  NI_MAP_OPEN -- open NetInfo Aliases
526538032Speter*/
526638032Speter
526738032Speterbool
526838032Speterni_map_open(map, mode)
526938032Speter	MAP *map;
527038032Speter	int mode;
527138032Speter{
527238032Speter	if (tTd(38, 2))
527390792Sgshapiro		sm_dprintf("ni_map_open(%s, %s, %d)\n",
527438032Speter			map->map_mname, map->map_file, mode);
527538032Speter	mode &= O_ACCMODE;
527638032Speter
527738032Speter	if (*map->map_file == '\0')
527838032Speter		map->map_file = NETINFO_DEFAULT_DIR;
527938032Speter
528038032Speter	if (map->map_valcolnm == NULL)
528138032Speter		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
528238032Speter
528390792Sgshapiro	if (map->map_coldelim == '\0')
528490792Sgshapiro	{
528590792Sgshapiro		if (bitset(MF_ALIAS, map->map_mflags))
528690792Sgshapiro			map->map_coldelim = ',';
528790792Sgshapiro		else if (bitset(MF_FILECLASS, map->map_mflags))
528890792Sgshapiro			map->map_coldelim = ' ';
528990792Sgshapiro	}
529090792Sgshapiro	return true;
529138032Speter}
529238032Speter
529338032Speter
529438032Speter/*
529538032Speter**  NI_MAP_LOOKUP -- look up a datum in NetInfo
529638032Speter*/
529738032Speter
529838032Speterchar *
529938032Speterni_map_lookup(map, name, av, statp)
530038032Speter	MAP *map;
530138032Speter	char *name;
530238032Speter	char **av;
530338032Speter	int *statp;
530438032Speter{
530538032Speter	char *res;
530638032Speter	char *propval;
530738032Speter
530838032Speter	if (tTd(38, 20))
530990792Sgshapiro		sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
531038032Speter
531138032Speter	propval = ni_propval(map->map_file, map->map_keycolnm, name,
531238032Speter			     map->map_valcolnm, map->map_coldelim);
531338032Speter
531438032Speter	if (propval == NULL)
531538032Speter		return NULL;
531638032Speter
531790792Sgshapiro	SM_TRY
531890792Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
531990792Sgshapiro			res = map_rewrite(map, name, strlen(name), NULL);
532090792Sgshapiro		else
532190792Sgshapiro			res = map_rewrite(map, propval, strlen(propval), av);
532290792Sgshapiro	SM_FINALLY
532390792Sgshapiro		sm_free(propval);
532490792Sgshapiro	SM_END_TRY
532538032Speter	return res;
532638032Speter}
532738032Speter
532838032Speter
532964562Sgshapirostatic bool
533038032Speterni_getcanonname(name, hbsize, statp)
533138032Speter	char *name;
533238032Speter	int hbsize;
533338032Speter	int *statp;
533438032Speter{
533538032Speter	char *vptr;
533638032Speter	char *ptr;
533738032Speter	char nbuf[MAXNAME + 1];
533838032Speter
533938032Speter	if (tTd(38, 20))
534090792Sgshapiro		sm_dprintf("ni_getcanonname(%s)\n", name);
534138032Speter
534290792Sgshapiro	if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
534338032Speter	{
534438032Speter		*statp = EX_UNAVAILABLE;
534590792Sgshapiro		return false;
534638032Speter	}
534773188Sgshapiro	(void) shorten_hostname(nbuf);
534838032Speter
534938032Speter	/* we only accept single token search key */
535038032Speter	if (strchr(nbuf, '.'))
535138032Speter	{
535238032Speter		*statp = EX_NOHOST;
535390792Sgshapiro		return false;
535438032Speter	}
535538032Speter
535638032Speter	/* Do the search */
535738032Speter	vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
535838032Speter
535938032Speter	if (vptr == NULL)
536038032Speter	{
536138032Speter		*statp = EX_NOHOST;
536290792Sgshapiro		return false;
536338032Speter	}
536438032Speter
536538032Speter	/* Only want the first machine name */
536638032Speter	if ((ptr = strchr(vptr, '\n')) != NULL)
536738032Speter		*ptr = '\0';
536838032Speter
536990792Sgshapiro	if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
537038032Speter	{
537177349Sgshapiro		sm_free(vptr);
537290792Sgshapiro		*statp = EX_UNAVAILABLE;
537390792Sgshapiro		return true;
537438032Speter	}
537577349Sgshapiro	sm_free(vptr);
537690792Sgshapiro	*statp = EX_OK;
537790792Sgshapiro	return false;
537838032Speter}
537990792Sgshapiro#endif /* NETINFO */
538038032Speter/*
538138032Speter**  TEXT (unindexed text file) Modules
538238032Speter**
538338032Speter**	This code donated by Sun Microsystems.
538438032Speter*/
538538032Speter
538638032Speter#define map_sff		map_lockfd	/* overload field */
538738032Speter
538838032Speter
538938032Speter/*
539038032Speter**  TEXT_MAP_OPEN -- open text table
539138032Speter*/
539238032Speter
539338032Speterbool
539438032Spetertext_map_open(map, mode)
539538032Speter	MAP *map;
539638032Speter	int mode;
539738032Speter{
539864562Sgshapiro	long sff;
539938032Speter	int i;
540038032Speter
540138032Speter	if (tTd(38, 2))
540290792Sgshapiro		sm_dprintf("text_map_open(%s, %s, %d)\n",
540338032Speter			map->map_mname, map->map_file, mode);
540438032Speter
540538032Speter	mode &= O_ACCMODE;
540638032Speter	if (mode != O_RDONLY)
540738032Speter	{
540838032Speter		errno = EPERM;
540990792Sgshapiro		return false;
541038032Speter	}
541138032Speter
541238032Speter	if (*map->map_file == '\0')
541338032Speter	{
541438032Speter		syserr("text map \"%s\": file name required",
541538032Speter			map->map_mname);
541690792Sgshapiro		return false;
541738032Speter	}
541838032Speter
541938032Speter	if (map->map_file[0] != '/')
542038032Speter	{
542138032Speter		syserr("text map \"%s\": file name must be fully qualified",
542238032Speter			map->map_mname);
542390792Sgshapiro		return false;
542438032Speter	}
542538032Speter
542638032Speter	sff = SFF_ROOTOK|SFF_REGONLY;
542764562Sgshapiro	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
542838032Speter		sff |= SFF_NOWLINK;
542964562Sgshapiro	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
543038032Speter		sff |= SFF_SAFEDIRPATH;
543138032Speter	if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
543238032Speter			  sff, S_IRUSR, NULL)) != 0)
543338032Speter	{
543464562Sgshapiro		int save_errno = errno;
543564562Sgshapiro
543638032Speter		/* cannot open this map */
543738032Speter		if (tTd(38, 2))
543890792Sgshapiro			sm_dprintf("\tunsafe map file: %d\n", i);
543964562Sgshapiro		errno = save_errno;
544038032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
544138032Speter			syserr("text map \"%s\": unsafe map file %s",
544238032Speter				map->map_mname, map->map_file);
544390792Sgshapiro		return false;
544438032Speter	}
544538032Speter
544638032Speter	if (map->map_keycolnm == NULL)
544738032Speter		map->map_keycolno = 0;
544838032Speter	else
544938032Speter	{
545038032Speter		if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
545138032Speter		{
545238032Speter			syserr("text map \"%s\", file %s: -k should specify a number, not %s",
545338032Speter				map->map_mname, map->map_file,
545438032Speter				map->map_keycolnm);
545590792Sgshapiro			return false;
545638032Speter		}
545738032Speter		map->map_keycolno = atoi(map->map_keycolnm);
545838032Speter	}
545938032Speter
546038032Speter	if (map->map_valcolnm == NULL)
546138032Speter		map->map_valcolno = 0;
546238032Speter	else
546338032Speter	{
546438032Speter		if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
546538032Speter		{
546638032Speter			syserr("text map \"%s\", file %s: -v should specify a number, not %s",
546738032Speter					map->map_mname, map->map_file,
546838032Speter					map->map_valcolnm);
546990792Sgshapiro			return false;
547038032Speter		}
547138032Speter		map->map_valcolno = atoi(map->map_valcolnm);
547238032Speter	}
547338032Speter
547438032Speter	if (tTd(38, 2))
547538032Speter	{
547690792Sgshapiro		sm_dprintf("text_map_open(%s, %s): delimiter = ",
547738032Speter			map->map_mname, map->map_file);
547838032Speter		if (map->map_coldelim == '\0')
547990792Sgshapiro			sm_dprintf("(white space)\n");
548038032Speter		else
548190792Sgshapiro			sm_dprintf("%c\n", map->map_coldelim);
548238032Speter	}
548338032Speter
548438032Speter	map->map_sff = sff;
548590792Sgshapiro	return true;
548638032Speter}
548738032Speter
548838032Speter
548938032Speter/*
549038032Speter**  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
549138032Speter*/
549238032Speter
549338032Speterchar *
549438032Spetertext_map_lookup(map, name, av, statp)
549538032Speter	MAP *map;
549638032Speter	char *name;
549738032Speter	char **av;
549838032Speter	int *statp;
549938032Speter{
550038032Speter	char *vp;
550138032Speter	auto int vsize;
550238032Speter	int buflen;
550390792Sgshapiro	SM_FILE_T *f;
550438032Speter	char delim;
550538032Speter	int key_idx;
550638032Speter	bool found_it;
550764562Sgshapiro	long sff = map->map_sff;
550838032Speter	char search_key[MAXNAME + 1];
550938032Speter	char linebuf[MAXLINE];
551038032Speter	char buf[MAXNAME + 1];
551138032Speter
551290792Sgshapiro	found_it = false;
551338032Speter	if (tTd(38, 20))
551490792Sgshapiro		sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname,  name);
551538032Speter
551638032Speter	buflen = strlen(name);
551738032Speter	if (buflen > sizeof search_key - 1)
551890792Sgshapiro		buflen = sizeof search_key - 1;	/* XXX just cut if off? */
551964562Sgshapiro	memmove(search_key, name, buflen);
552038032Speter	search_key[buflen] = '\0';
552138032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
552238032Speter		makelower(search_key);
552338032Speter
552438032Speter	f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
552538032Speter	if (f == NULL)
552638032Speter	{
552738032Speter		map->map_mflags &= ~(MF_VALID|MF_OPEN);
552838032Speter		*statp = EX_UNAVAILABLE;
552938032Speter		return NULL;
553038032Speter	}
553138032Speter	key_idx = map->map_keycolno;
553238032Speter	delim = map->map_coldelim;
553398121Sgshapiro	while (sm_io_fgets(f, SM_TIME_DEFAULT,
553498121Sgshapiro			   linebuf, sizeof linebuf) != NULL)
553538032Speter	{
553638032Speter		char *p;
553738032Speter
553838032Speter		/* skip comment line */
553938032Speter		if (linebuf[0] == '#')
554038032Speter			continue;
554138032Speter		p = strchr(linebuf, '\n');
554238032Speter		if (p != NULL)
554338032Speter			*p = '\0';
554438032Speter		p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
554590792Sgshapiro		if (p != NULL && sm_strcasecmp(search_key, p) == 0)
554638032Speter		{
554790792Sgshapiro			found_it = true;
554838032Speter			break;
554938032Speter		}
555038032Speter	}
555190792Sgshapiro	(void) sm_io_close(f, SM_TIME_DEFAULT);
555238032Speter	if (!found_it)
555338032Speter	{
555438032Speter		*statp = EX_NOTFOUND;
555538032Speter		return NULL;
555638032Speter	}
555738032Speter	vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
555842575Speter	if (vp == NULL)
555942575Speter	{
556042575Speter		*statp = EX_NOTFOUND;
556142575Speter		return NULL;
556242575Speter	}
556338032Speter	vsize = strlen(vp);
556438032Speter	*statp = EX_OK;
556538032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
556638032Speter		return map_rewrite(map, name, strlen(name), NULL);
556738032Speter	else
556838032Speter		return map_rewrite(map, vp, vsize, av);
556938032Speter}
557038032Speter
557138032Speter/*
557238032Speter**  TEXT_GETCANONNAME -- look up canonical name in hosts file
557338032Speter*/
557438032Speter
557564562Sgshapirostatic bool
557638032Spetertext_getcanonname(name, hbsize, statp)
557738032Speter	char *name;
557838032Speter	int hbsize;
557938032Speter	int *statp;
558038032Speter{
558138032Speter	bool found;
558273188Sgshapiro	char *dot;
558390792Sgshapiro	SM_FILE_T *f;
558438032Speter	char linebuf[MAXLINE];
558538032Speter	char cbuf[MAXNAME + 1];
558638032Speter	char nbuf[MAXNAME + 1];
558738032Speter
558838032Speter	if (tTd(38, 20))
558990792Sgshapiro		sm_dprintf("text_getcanonname(%s)\n", name);
559038032Speter
559190792Sgshapiro	if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
559238032Speter	{
559338032Speter		*statp = EX_UNAVAILABLE;
559490792Sgshapiro		return false;
559538032Speter	}
559673188Sgshapiro	dot = shorten_hostname(nbuf);
559738032Speter
559890792Sgshapiro	f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
559990792Sgshapiro		       NULL);
560038032Speter	if (f == NULL)
560138032Speter	{
560238032Speter		*statp = EX_UNAVAILABLE;
560390792Sgshapiro		return false;
560438032Speter	}
560590792Sgshapiro	found = false;
560690792Sgshapiro	while (!found &&
560798121Sgshapiro		sm_io_fgets(f, SM_TIME_DEFAULT,
560898121Sgshapiro			    linebuf, sizeof linebuf) != NULL)
560938032Speter	{
561038032Speter		char *p = strpbrk(linebuf, "#\n");
561138032Speter
561238032Speter		if (p != NULL)
561338032Speter			*p = '\0';
561438032Speter		if (linebuf[0] != '\0')
561573188Sgshapiro			found = extract_canonname(nbuf, dot, linebuf,
561673188Sgshapiro						  cbuf, sizeof cbuf);
561738032Speter	}
561890792Sgshapiro	(void) sm_io_close(f, SM_TIME_DEFAULT);
561938032Speter	if (!found)
562038032Speter	{
562138032Speter		*statp = EX_NOHOST;
562290792Sgshapiro		return false;
562338032Speter	}
562438032Speter
562590792Sgshapiro	if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
562638032Speter	{
562790792Sgshapiro		*statp = EX_UNAVAILABLE;
562890792Sgshapiro		return false;
562938032Speter	}
563090792Sgshapiro	*statp = EX_OK;
563190792Sgshapiro	return true;
563238032Speter}
563390792Sgshapiro/*
563438032Speter**  STAB (Symbol Table) Modules
563538032Speter*/
563638032Speter
563738032Speter
563838032Speter/*
563938032Speter**  STAB_MAP_LOOKUP -- look up alias in symbol table
564038032Speter*/
564138032Speter
564238032Speter/* ARGSUSED2 */
564338032Speterchar *
564438032Speterstab_map_lookup(map, name, av, pstat)
564538032Speter	register MAP *map;
564638032Speter	char *name;
564738032Speter	char **av;
564838032Speter	int *pstat;
564938032Speter{
565038032Speter	register STAB *s;
565138032Speter
565238032Speter	if (tTd(38, 20))
565390792Sgshapiro		sm_dprintf("stab_lookup(%s, %s)\n",
565438032Speter			map->map_mname, name);
565538032Speter
565638032Speter	s = stab(name, ST_ALIAS, ST_FIND);
565738032Speter	if (s != NULL)
565864562Sgshapiro		return s->s_alias;
565964562Sgshapiro	return NULL;
566038032Speter}
566138032Speter
566238032Speter
566338032Speter/*
566438032Speter**  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
566538032Speter*/
566638032Speter
566738032Spetervoid
566838032Speterstab_map_store(map, lhs, rhs)
566938032Speter	register MAP *map;
567038032Speter	char *lhs;
567138032Speter	char *rhs;
567238032Speter{
567338032Speter	register STAB *s;
567438032Speter
567538032Speter	s = stab(lhs, ST_ALIAS, ST_ENTER);
567638032Speter	s->s_alias = newstr(rhs);
567738032Speter}
567838032Speter
567938032Speter
568038032Speter/*
568138032Speter**  STAB_MAP_OPEN -- initialize (reads data file)
568238032Speter**
568338032Speter**	This is a wierd case -- it is only intended as a fallback for
568438032Speter**	aliases.  For this reason, opens for write (only during a
568538032Speter**	"newaliases") always fails, and opens for read open the
568638032Speter**	actual underlying text file instead of the database.
568738032Speter*/
568838032Speter
568938032Speterbool
569038032Speterstab_map_open(map, mode)
569138032Speter	register MAP *map;
569238032Speter	int mode;
569338032Speter{
569490792Sgshapiro	SM_FILE_T *af;
569564562Sgshapiro	long sff;
569638032Speter	struct stat st;
569738032Speter
569838032Speter	if (tTd(38, 2))
569990792Sgshapiro		sm_dprintf("stab_map_open(%s, %s, %d)\n",
570038032Speter			map->map_mname, map->map_file, mode);
570138032Speter
570238032Speter	mode &= O_ACCMODE;
570338032Speter	if (mode != O_RDONLY)
570438032Speter	{
570538032Speter		errno = EPERM;
570690792Sgshapiro		return false;
570738032Speter	}
570838032Speter
570938032Speter	sff = SFF_ROOTOK|SFF_REGONLY;
571064562Sgshapiro	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
571138032Speter		sff |= SFF_NOWLINK;
571264562Sgshapiro	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
571338032Speter		sff |= SFF_SAFEDIRPATH;
571438032Speter	af = safefopen(map->map_file, O_RDONLY, 0444, sff);
571538032Speter	if (af == NULL)
571690792Sgshapiro		return false;
571790792Sgshapiro	readaliases(map, af, false, false);
571838032Speter
571990792Sgshapiro	if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
572038032Speter		map->map_mtime = st.st_mtime;
572190792Sgshapiro	(void) sm_io_close(af, SM_TIME_DEFAULT);
572238032Speter
572390792Sgshapiro	return true;
572438032Speter}
572590792Sgshapiro/*
572638032Speter**  Implicit Modules
572738032Speter**
572838032Speter**	Tries several types.  For back compatibility of aliases.
572938032Speter*/
573038032Speter
573138032Speter
573238032Speter/*
573338032Speter**  IMPL_MAP_LOOKUP -- lookup in best open database
573438032Speter*/
573538032Speter
573638032Speterchar *
573738032Speterimpl_map_lookup(map, name, av, pstat)
573838032Speter	MAP *map;
573938032Speter	char *name;
574038032Speter	char **av;
574138032Speter	int *pstat;
574238032Speter{
574338032Speter	if (tTd(38, 20))
574490792Sgshapiro		sm_dprintf("impl_map_lookup(%s, %s)\n",
574538032Speter			map->map_mname, name);
574638032Speter
574790792Sgshapiro#if NEWDB
574838032Speter	if (bitset(MF_IMPL_HASH, map->map_mflags))
574938032Speter		return db_map_lookup(map, name, av, pstat);
575064562Sgshapiro#endif /* NEWDB */
575190792Sgshapiro#if NDBM
575238032Speter	if (bitset(MF_IMPL_NDBM, map->map_mflags))
575338032Speter		return ndbm_map_lookup(map, name, av, pstat);
575464562Sgshapiro#endif /* NDBM */
575538032Speter	return stab_map_lookup(map, name, av, pstat);
575638032Speter}
575738032Speter
575838032Speter/*
575938032Speter**  IMPL_MAP_STORE -- store in open databases
576038032Speter*/
576138032Speter
576238032Spetervoid
576338032Speterimpl_map_store(map, lhs, rhs)
576438032Speter	MAP *map;
576538032Speter	char *lhs;
576638032Speter	char *rhs;
576738032Speter{
576838032Speter	if (tTd(38, 12))
576990792Sgshapiro		sm_dprintf("impl_map_store(%s, %s, %s)\n",
577038032Speter			map->map_mname, lhs, rhs);
577190792Sgshapiro#if NEWDB
577238032Speter	if (bitset(MF_IMPL_HASH, map->map_mflags))
577338032Speter		db_map_store(map, lhs, rhs);
577464562Sgshapiro#endif /* NEWDB */
577590792Sgshapiro#if NDBM
577638032Speter	if (bitset(MF_IMPL_NDBM, map->map_mflags))
577738032Speter		ndbm_map_store(map, lhs, rhs);
577864562Sgshapiro#endif /* NDBM */
577938032Speter	stab_map_store(map, lhs, rhs);
578038032Speter}
578138032Speter
578238032Speter/*
578338032Speter**  IMPL_MAP_OPEN -- implicit database open
578438032Speter*/
578538032Speter
578638032Speterbool
578738032Speterimpl_map_open(map, mode)
578838032Speter	MAP *map;
578938032Speter	int mode;
579038032Speter{
579138032Speter	if (tTd(38, 2))
579290792Sgshapiro		sm_dprintf("impl_map_open(%s, %s, %d)\n",
579338032Speter			map->map_mname, map->map_file, mode);
579438032Speter
579538032Speter	mode &= O_ACCMODE;
579690792Sgshapiro#if NEWDB
579738032Speter	map->map_mflags |= MF_IMPL_HASH;
579838032Speter	if (hash_map_open(map, mode))
579938032Speter	{
580038032Speter# ifdef NDBM_YP_COMPAT
580138032Speter		if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
580264562Sgshapiro# endif /* NDBM_YP_COMPAT */
580390792Sgshapiro			return true;
580438032Speter	}
580538032Speter	else
580638032Speter		map->map_mflags &= ~MF_IMPL_HASH;
580764562Sgshapiro#endif /* NEWDB */
580890792Sgshapiro#if NDBM
580938032Speter	map->map_mflags |= MF_IMPL_NDBM;
581038032Speter	if (ndbm_map_open(map, mode))
581138032Speter	{
581290792Sgshapiro		return true;
581338032Speter	}
581438032Speter	else
581538032Speter		map->map_mflags &= ~MF_IMPL_NDBM;
581664562Sgshapiro#endif /* NDBM */
581738032Speter
581838032Speter#if defined(NEWDB) || defined(NDBM)
581938032Speter	if (Verbose)
582038032Speter		message("WARNING: cannot open alias database %s%s",
582138032Speter			map->map_file,
582238032Speter			mode == O_RDONLY ? "; reading text version" : "");
582364562Sgshapiro#else /* defined(NEWDB) || defined(NDBM) */
582438032Speter	if (mode != O_RDONLY)
582538032Speter		usrerr("Cannot rebuild aliases: no database format defined");
582664562Sgshapiro#endif /* defined(NEWDB) || defined(NDBM) */
582738032Speter
582838032Speter	if (mode == O_RDONLY)
582938032Speter		return stab_map_open(map, mode);
583038032Speter	else
583190792Sgshapiro		return false;
583238032Speter}
583338032Speter
583438032Speter
583538032Speter/*
583638032Speter**  IMPL_MAP_CLOSE -- close any open database(s)
583738032Speter*/
583838032Speter
583938032Spetervoid
584038032Speterimpl_map_close(map)
584138032Speter	MAP *map;
584238032Speter{
584338032Speter	if (tTd(38, 9))
584490792Sgshapiro		sm_dprintf("impl_map_close(%s, %s, %lx)\n",
584538032Speter			map->map_mname, map->map_file, map->map_mflags);
584690792Sgshapiro#if NEWDB
584738032Speter	if (bitset(MF_IMPL_HASH, map->map_mflags))
584838032Speter	{
584938032Speter		db_map_close(map);
585038032Speter		map->map_mflags &= ~MF_IMPL_HASH;
585138032Speter	}
585264562Sgshapiro#endif /* NEWDB */
585338032Speter
585490792Sgshapiro#if NDBM
585538032Speter	if (bitset(MF_IMPL_NDBM, map->map_mflags))
585638032Speter	{
585738032Speter		ndbm_map_close(map);
585838032Speter		map->map_mflags &= ~MF_IMPL_NDBM;
585938032Speter	}
586064562Sgshapiro#endif /* NDBM */
586138032Speter}
586290792Sgshapiro/*
586338032Speter**  User map class.
586438032Speter**
586538032Speter**	Provides access to the system password file.
586638032Speter*/
586738032Speter
586838032Speter/*
586938032Speter**  USER_MAP_OPEN -- open user map
587038032Speter**
587138032Speter**	Really just binds field names to field numbers.
587238032Speter*/
587338032Speter
587438032Speterbool
587538032Speteruser_map_open(map, mode)
587638032Speter	MAP *map;
587738032Speter	int mode;
587838032Speter{
587938032Speter	if (tTd(38, 2))
588090792Sgshapiro		sm_dprintf("user_map_open(%s, %d)\n",
588138032Speter			map->map_mname, mode);
588238032Speter
588338032Speter	mode &= O_ACCMODE;
588438032Speter	if (mode != O_RDONLY)
588538032Speter	{
588638032Speter		/* issue a pseudo-error message */
588790792Sgshapiro		errno = SM_EMAPCANTWRITE;
588890792Sgshapiro		return false;
588938032Speter	}
589038032Speter	if (map->map_valcolnm == NULL)
589164562Sgshapiro		/* EMPTY */
589238032Speter		/* nothing */ ;
589390792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
589438032Speter		map->map_valcolno = 1;
589590792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
589638032Speter		map->map_valcolno = 2;
589790792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
589838032Speter		map->map_valcolno = 3;
589990792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
590038032Speter		map->map_valcolno = 4;
590190792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
590238032Speter		map->map_valcolno = 5;
590390792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
590438032Speter		map->map_valcolno = 6;
590590792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
590638032Speter		map->map_valcolno = 7;
590738032Speter	else
590838032Speter	{
590938032Speter		syserr("User map %s: unknown column name %s",
591038032Speter			map->map_mname, map->map_valcolnm);
591190792Sgshapiro		return false;
591238032Speter	}
591390792Sgshapiro	return true;
591438032Speter}
591538032Speter
591638032Speter
591738032Speter/*
591838032Speter**  USER_MAP_LOOKUP -- look up a user in the passwd file.
591938032Speter*/
592038032Speter
592138032Speter/* ARGSUSED3 */
592238032Speterchar *
592338032Speteruser_map_lookup(map, key, av, statp)
592438032Speter	MAP *map;
592538032Speter	char *key;
592638032Speter	char **av;
592738032Speter	int *statp;
592838032Speter{
592938032Speter	auto bool fuzzy;
593090792Sgshapiro	SM_MBDB_T user;
593138032Speter
593238032Speter	if (tTd(38, 20))
593390792Sgshapiro		sm_dprintf("user_map_lookup(%s, %s)\n",
593438032Speter			map->map_mname, key);
593538032Speter
593690792Sgshapiro	*statp = finduser(key, &fuzzy, &user);
593790792Sgshapiro	if (*statp != EX_OK)
593838032Speter		return NULL;
593938032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
594038032Speter		return map_rewrite(map, key, strlen(key), NULL);
594138032Speter	else
594238032Speter	{
594338032Speter		char *rwval = NULL;
594438032Speter		char buf[30];
594538032Speter
594638032Speter		switch (map->map_valcolno)
594738032Speter		{
594838032Speter		  case 0:
594938032Speter		  case 1:
595090792Sgshapiro			rwval = user.mbdb_name;
595138032Speter			break;
595238032Speter
595338032Speter		  case 2:
595490792Sgshapiro			rwval = "x";	/* passwd no longer supported */
595538032Speter			break;
595638032Speter
595738032Speter		  case 3:
595890792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%d",
595990792Sgshapiro					   (int) user.mbdb_uid);
596038032Speter			rwval = buf;
596138032Speter			break;
596238032Speter
596338032Speter		  case 4:
596490792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%d",
596590792Sgshapiro					   (int) user.mbdb_gid);
596638032Speter			rwval = buf;
596738032Speter			break;
596838032Speter
596938032Speter		  case 5:
597090792Sgshapiro			rwval = user.mbdb_fullname;
597138032Speter			break;
597238032Speter
597338032Speter		  case 6:
597490792Sgshapiro			rwval = user.mbdb_homedir;
597538032Speter			break;
597638032Speter
597738032Speter		  case 7:
597890792Sgshapiro			rwval = user.mbdb_shell;
597938032Speter			break;
598038032Speter		}
598138032Speter		return map_rewrite(map, rwval, strlen(rwval), av);
598238032Speter	}
598338032Speter}
598490792Sgshapiro/*
598538032Speter**  Program map type.
598638032Speter**
598738032Speter**	This provides access to arbitrary programs.  It should be used
598838032Speter**	only very sparingly, since there is no way to bound the cost
598938032Speter**	of invoking an arbitrary program.
599038032Speter*/
599138032Speter
599238032Speterchar *
599338032Speterprog_map_lookup(map, name, av, statp)
599438032Speter	MAP *map;
599538032Speter	char *name;
599638032Speter	char **av;
599738032Speter	int *statp;
599838032Speter{
599938032Speter	int i;
600064562Sgshapiro	int save_errno;
600138032Speter	int fd;
600264562Sgshapiro	int status;
600338032Speter	auto pid_t pid;
600464562Sgshapiro	register char *p;
600538032Speter	char *rval;
600638032Speter	char *argv[MAXPV + 1];
600738032Speter	char buf[MAXLINE];
600838032Speter
600938032Speter	if (tTd(38, 20))
601090792Sgshapiro		sm_dprintf("prog_map_lookup(%s, %s) %s\n",
601138032Speter			map->map_mname, name, map->map_file);
601238032Speter
601338032Speter	i = 0;
601438032Speter	argv[i++] = map->map_file;
601538032Speter	if (map->map_rebuild != NULL)
601638032Speter	{
601790792Sgshapiro		(void) sm_strlcpy(buf, map->map_rebuild, sizeof buf);
601838032Speter		for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
601938032Speter		{
602038032Speter			if (i >= MAXPV - 1)
602138032Speter				break;
602238032Speter			argv[i++] = p;
602338032Speter		}
602438032Speter	}
602538032Speter	argv[i++] = name;
602638032Speter	argv[i] = NULL;
602738032Speter	if (tTd(38, 21))
602838032Speter	{
602990792Sgshapiro		sm_dprintf("prog_open:");
603038032Speter		for (i = 0; argv[i] != NULL; i++)
603190792Sgshapiro			sm_dprintf(" %s", argv[i]);
603290792Sgshapiro		sm_dprintf("\n");
603338032Speter	}
603490792Sgshapiro	(void) sm_blocksignal(SIGCHLD);
603538032Speter	pid = prog_open(argv, &fd, CurEnv);
603638032Speter	if (pid < 0)
603738032Speter	{
603838032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
603938032Speter			syserr("prog_map_lookup(%s) failed (%s) -- closing",
604090792Sgshapiro			       map->map_mname, sm_errstring(errno));
604138032Speter		else if (tTd(38, 9))
604290792Sgshapiro			sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
604390792Sgshapiro				   map->map_mname, sm_errstring(errno));
604438032Speter		map->map_mflags &= ~(MF_VALID|MF_OPEN);
604538032Speter		*statp = EX_OSFILE;
604638032Speter		return NULL;
604738032Speter	}
604838032Speter	i = read(fd, buf, sizeof buf - 1);
604938032Speter	if (i < 0)
605038032Speter	{
605190792Sgshapiro		syserr("prog_map_lookup(%s): read error %s",
605290792Sgshapiro		       map->map_mname, sm_errstring(errno));
605338032Speter		rval = NULL;
605438032Speter	}
605538032Speter	else if (i == 0)
605638032Speter	{
605738032Speter		if (tTd(38, 20))
605890792Sgshapiro			sm_dprintf("prog_map_lookup(%s): empty answer\n",
605990792Sgshapiro				   map->map_mname);
606038032Speter		rval = NULL;
606138032Speter	}
606238032Speter	else
606338032Speter	{
606438032Speter		buf[i] = '\0';
606538032Speter		p = strchr(buf, '\n');
606638032Speter		if (p != NULL)
606738032Speter			*p = '\0';
606838032Speter
606938032Speter		/* collect the return value */
607038032Speter		if (bitset(MF_MATCHONLY, map->map_mflags))
607138032Speter			rval = map_rewrite(map, name, strlen(name), NULL);
607238032Speter		else
607377349Sgshapiro			rval = map_rewrite(map, buf, strlen(buf), av);
607438032Speter
607538032Speter		/* now flush any additional output */
607638032Speter		while ((i = read(fd, buf, sizeof buf)) > 0)
607738032Speter			continue;
607838032Speter	}
607938032Speter
608038032Speter	/* wait for the process to terminate */
608164562Sgshapiro	(void) close(fd);
608264562Sgshapiro	status = waitfor(pid);
608364562Sgshapiro	save_errno = errno;
608490792Sgshapiro	(void) sm_releasesignal(SIGCHLD);
608564562Sgshapiro	errno = save_errno;
608638032Speter
608764562Sgshapiro	if (status == -1)
608838032Speter	{
608990792Sgshapiro		syserr("prog_map_lookup(%s): wait error %s",
609090792Sgshapiro		       map->map_mname, sm_errstring(errno));
609138032Speter		*statp = EX_SOFTWARE;
609238032Speter		rval = NULL;
609338032Speter	}
609464562Sgshapiro	else if (WIFEXITED(status))
609538032Speter	{
609664562Sgshapiro		if ((*statp = WEXITSTATUS(status)) != EX_OK)
609738032Speter			rval = NULL;
609838032Speter	}
609938032Speter	else
610038032Speter	{
610138032Speter		syserr("prog_map_lookup(%s): child died on signal %d",
610290792Sgshapiro		       map->map_mname, status);
610338032Speter		*statp = EX_UNAVAILABLE;
610438032Speter		rval = NULL;
610538032Speter	}
610638032Speter	return rval;
610738032Speter}
610890792Sgshapiro/*
610938032Speter**  Sequenced map type.
611038032Speter**
611138032Speter**	Tries each map in order until something matches, much like
611238032Speter**	implicit.  Stores go to the first map in the list that can
611338032Speter**	support storing.
611438032Speter**
611538032Speter**	This is slightly unusual in that there are two interfaces.
611638032Speter**	The "sequence" interface lets you stack maps arbitrarily.
611738032Speter**	The "switch" interface builds a sequence map by looking
611838032Speter**	at a system-dependent configuration file such as
611938032Speter**	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
612038032Speter**
612138032Speter**	We don't need an explicit open, since all maps are
612290792Sgshapiro**	opened on demand.
612338032Speter*/
612438032Speter
612538032Speter/*
612638032Speter**  SEQ_MAP_PARSE -- Sequenced map parsing
612738032Speter*/
612838032Speter
612938032Speterbool
613038032Speterseq_map_parse(map, ap)
613138032Speter	MAP *map;
613238032Speter	char *ap;
613338032Speter{
613438032Speter	int maxmap;
613538032Speter
613638032Speter	if (tTd(38, 2))
613790792Sgshapiro		sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
613838032Speter	maxmap = 0;
613938032Speter	while (*ap != '\0')
614038032Speter	{
614138032Speter		register char *p;
614238032Speter		STAB *s;
614338032Speter
614438032Speter		/* find beginning of map name */
614538032Speter		while (isascii(*ap) && isspace(*ap))
614638032Speter			ap++;
614764562Sgshapiro		for (p = ap;
614864562Sgshapiro		     (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
614964562Sgshapiro		     p++)
615038032Speter			continue;
615138032Speter		if (*p != '\0')
615238032Speter			*p++ = '\0';
615338032Speter		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
615438032Speter			p++;
615538032Speter		if (*ap == '\0')
615638032Speter		{
615738032Speter			ap = p;
615838032Speter			continue;
615938032Speter		}
616038032Speter		s = stab(ap, ST_MAP, ST_FIND);
616138032Speter		if (s == NULL)
616238032Speter		{
616338032Speter			syserr("Sequence map %s: unknown member map %s",
616438032Speter				map->map_mname, ap);
616538032Speter		}
616690792Sgshapiro		else if (maxmap >= MAXMAPSTACK)
616738032Speter		{
616838032Speter			syserr("Sequence map %s: too many member maps (%d max)",
616938032Speter				map->map_mname, MAXMAPSTACK);
617038032Speter			maxmap++;
617138032Speter		}
617238032Speter		else if (maxmap < MAXMAPSTACK)
617338032Speter		{
617438032Speter			map->map_stack[maxmap++] = &s->s_map;
617538032Speter		}
617638032Speter		ap = p;
617738032Speter	}
617890792Sgshapiro	return true;
617938032Speter}
618038032Speter
618138032Speter/*
618238032Speter**  SWITCH_MAP_OPEN -- open a switched map
618338032Speter**
618438032Speter**	This looks at the system-dependent configuration and builds
618538032Speter**	a sequence map that does the same thing.
618638032Speter**
618738032Speter**	Every system must define a switch_map_find routine in conf.c
618838032Speter**	that will return the list of service types associated with a
618938032Speter**	given service class.
619038032Speter*/
619138032Speter
619238032Speterbool
619338032Speterswitch_map_open(map, mode)
619438032Speter	MAP *map;
619538032Speter	int mode;
619638032Speter{
619738032Speter	int mapno;
619838032Speter	int nmaps;
619938032Speter	char *maptype[MAXMAPSTACK];
620038032Speter
620138032Speter	if (tTd(38, 2))
620290792Sgshapiro		sm_dprintf("switch_map_open(%s, %s, %d)\n",
620338032Speter			map->map_mname, map->map_file, mode);
620438032Speter
620538032Speter	mode &= O_ACCMODE;
620638032Speter	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
620738032Speter	if (tTd(38, 19))
620838032Speter	{
620990792Sgshapiro		sm_dprintf("\tswitch_map_find => %d\n", nmaps);
621038032Speter		for (mapno = 0; mapno < nmaps; mapno++)
621190792Sgshapiro			sm_dprintf("\t\t%s\n", maptype[mapno]);
621238032Speter	}
621338032Speter	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
621490792Sgshapiro		return false;
621538032Speter
621638032Speter	for (mapno = 0; mapno < nmaps; mapno++)
621738032Speter	{
621838032Speter		register STAB *s;
621938032Speter		char nbuf[MAXNAME + 1];
622038032Speter
622138032Speter		if (maptype[mapno] == NULL)
622238032Speter			continue;
622390792Sgshapiro		(void) sm_strlcpyn(nbuf, sizeof nbuf, 3,
622490792Sgshapiro				   map->map_mname, ".", maptype[mapno]);
622538032Speter		s = stab(nbuf, ST_MAP, ST_FIND);
622638032Speter		if (s == NULL)
622738032Speter		{
622838032Speter			syserr("Switch map %s: unknown member map %s",
622938032Speter				map->map_mname, nbuf);
623038032Speter		}
623138032Speter		else
623238032Speter		{
623338032Speter			map->map_stack[mapno] = &s->s_map;
623438032Speter			if (tTd(38, 4))
623590792Sgshapiro				sm_dprintf("\tmap_stack[%d] = %s:%s\n",
623690792Sgshapiro					   mapno,
623790792Sgshapiro					   s->s_map.map_class->map_cname,
623890792Sgshapiro					   nbuf);
623938032Speter		}
624038032Speter	}
624190792Sgshapiro	return true;
624238032Speter}
624338032Speter
624490792Sgshapiro#if 0
624538032Speter/*
624638032Speter**  SEQ_MAP_CLOSE -- close all underlying maps
624738032Speter*/
624838032Speter
624938032Spetervoid
625038032Speterseq_map_close(map)
625138032Speter	MAP *map;
625238032Speter{
625338032Speter	int mapno;
625438032Speter
625538032Speter	if (tTd(38, 9))
625690792Sgshapiro		sm_dprintf("seq_map_close(%s)\n", map->map_mname);
625738032Speter
625838032Speter	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
625938032Speter	{
626038032Speter		MAP *mm = map->map_stack[mapno];
626138032Speter
626238032Speter		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
626338032Speter			continue;
626477349Sgshapiro		mm->map_mflags |= MF_CLOSING;
626538032Speter		mm->map_class->map_close(mm);
626677349Sgshapiro		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
626738032Speter	}
626838032Speter}
626990792Sgshapiro#endif /* 0 */
627038032Speter
627138032Speter/*
627238032Speter**  SEQ_MAP_LOOKUP -- sequenced map lookup
627338032Speter*/
627438032Speter
627538032Speterchar *
627638032Speterseq_map_lookup(map, key, args, pstat)
627738032Speter	MAP *map;
627838032Speter	char *key;
627938032Speter	char **args;
628038032Speter	int *pstat;
628138032Speter{
628238032Speter	int mapno;
628338032Speter	int mapbit = 0x01;
628490792Sgshapiro	bool tempfail = false;
628538032Speter
628638032Speter	if (tTd(38, 20))
628790792Sgshapiro		sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
628838032Speter
628938032Speter	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
629038032Speter	{
629138032Speter		MAP *mm = map->map_stack[mapno];
629238032Speter		char *rv;
629338032Speter
629438032Speter		if (mm == NULL)
629538032Speter			continue;
629664562Sgshapiro		if (!bitset(MF_OPEN, mm->map_mflags) &&
629764562Sgshapiro		    !openmap(mm))
629838032Speter		{
629938032Speter			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
630038032Speter			{
630138032Speter				*pstat = EX_UNAVAILABLE;
630238032Speter				return NULL;
630338032Speter			}
630438032Speter			continue;
630538032Speter		}
630638032Speter		*pstat = EX_OK;
630738032Speter		rv = mm->map_class->map_lookup(mm, key, args, pstat);
630838032Speter		if (rv != NULL)
630938032Speter			return rv;
631038032Speter		if (*pstat == EX_TEMPFAIL)
631138032Speter		{
631238032Speter			if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
631338032Speter				return NULL;
631490792Sgshapiro			tempfail = true;
631538032Speter		}
631638032Speter		else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
631738032Speter			break;
631838032Speter	}
631938032Speter	if (tempfail)
632038032Speter		*pstat = EX_TEMPFAIL;
632138032Speter	else if (*pstat == EX_OK)
632238032Speter		*pstat = EX_NOTFOUND;
632338032Speter	return NULL;
632438032Speter}
632538032Speter
632638032Speter/*
632738032Speter**  SEQ_MAP_STORE -- sequenced map store
632838032Speter*/
632938032Speter
633038032Spetervoid
633138032Speterseq_map_store(map, key, val)
633238032Speter	MAP *map;
633338032Speter	char *key;
633438032Speter	char *val;
633538032Speter{
633638032Speter	int mapno;
633738032Speter
633838032Speter	if (tTd(38, 12))
633990792Sgshapiro		sm_dprintf("seq_map_store(%s, %s, %s)\n",
634038032Speter			map->map_mname, key, val);
634138032Speter
634238032Speter	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
634338032Speter	{
634438032Speter		MAP *mm = map->map_stack[mapno];
634538032Speter
634638032Speter		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
634738032Speter			continue;
634838032Speter
634938032Speter		mm->map_class->map_store(mm, key, val);
635038032Speter		return;
635138032Speter	}
635238032Speter	syserr("seq_map_store(%s, %s, %s): no writable map",
635338032Speter		map->map_mname, key, val);
635438032Speter}
635590792Sgshapiro/*
635638032Speter**  NULL stubs
635738032Speter*/
635838032Speter
635938032Speter/* ARGSUSED */
636038032Speterbool
636138032Speternull_map_open(map, mode)
636238032Speter	MAP *map;
636338032Speter	int mode;
636438032Speter{
636590792Sgshapiro	return true;
636638032Speter}
636738032Speter
636838032Speter/* ARGSUSED */
636938032Spetervoid
637038032Speternull_map_close(map)
637138032Speter	MAP *map;
637238032Speter{
637338032Speter	return;
637438032Speter}
637538032Speter
637638032Speterchar *
637738032Speternull_map_lookup(map, key, args, pstat)
637838032Speter	MAP *map;
637938032Speter	char *key;
638038032Speter	char **args;
638138032Speter	int *pstat;
638238032Speter{
638338032Speter	*pstat = EX_NOTFOUND;
638438032Speter	return NULL;
638538032Speter}
638638032Speter
638738032Speter/* ARGSUSED */
638838032Spetervoid
638938032Speternull_map_store(map, key, val)
639038032Speter	MAP *map;
639138032Speter	char *key;
639238032Speter	char *val;
639338032Speter{
639438032Speter	return;
639538032Speter}
639638032Speter
639738032Speter/*
639838032Speter**  BOGUS stubs
639938032Speter*/
640038032Speter
640138032Speterchar *
640238032Speterbogus_map_lookup(map, key, args, pstat)
640338032Speter	MAP *map;
640438032Speter	char *key;
640538032Speter	char **args;
640638032Speter	int *pstat;
640738032Speter{
640838032Speter	*pstat = EX_TEMPFAIL;
640938032Speter	return NULL;
641038032Speter}
641138032Speter
641238032SpeterMAPCLASS	BogusMapClass =
641338032Speter{
641490792Sgshapiro	"bogus-map",		NULL,			0,
641590792Sgshapiro	NULL,			bogus_map_lookup,	null_map_store,
641690792Sgshapiro	null_map_open,		null_map_close,
641738032Speter};
641890792Sgshapiro/*
641964562Sgshapiro**  MACRO modules
642064562Sgshapiro*/
642164562Sgshapiro
642264562Sgshapirochar *
642364562Sgshapiromacro_map_lookup(map, name, av, statp)
642464562Sgshapiro	MAP *map;
642564562Sgshapiro	char *name;
642664562Sgshapiro	char **av;
642764562Sgshapiro	int *statp;
642864562Sgshapiro{
642964562Sgshapiro	int mid;
643064562Sgshapiro
643164562Sgshapiro	if (tTd(38, 20))
643290792Sgshapiro		sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
643364562Sgshapiro			name == NULL ? "NULL" : name);
643464562Sgshapiro
643564562Sgshapiro	if (name == NULL ||
643664562Sgshapiro	    *name == '\0' ||
643790792Sgshapiro	    (mid = macid(name)) == 0)
643864562Sgshapiro	{
643964562Sgshapiro		*statp = EX_CONFIG;
644064562Sgshapiro		return NULL;
644164562Sgshapiro	}
644264562Sgshapiro
644364562Sgshapiro	if (av[1] == NULL)
644490792Sgshapiro		macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
644564562Sgshapiro	else
644690792Sgshapiro		macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
644764562Sgshapiro
644864562Sgshapiro	*statp = EX_OK;
644964562Sgshapiro	return "";
645064562Sgshapiro}
645190792Sgshapiro/*
645238032Speter**  REGEX modules
645338032Speter*/
645438032Speter
645590792Sgshapiro#if MAP_REGEX
645638032Speter
645738032Speter# include <regex.h>
645838032Speter
645938032Speter# define DEFAULT_DELIM	CONDELSE
646038032Speter# define END_OF_FIELDS	-1
646138032Speter# define ERRBUF_SIZE	80
646238032Speter# define MAX_MATCH	32
646338032Speter
646464562Sgshapiro# define xnalloc(s)	memset(xalloc(s), '\0', s);
646538032Speter
646638032Speterstruct regex_map
646738032Speter{
646871345Sgshapiro	regex_t	*regex_pattern_buf;	/* xalloc it */
646938032Speter	int	*regex_subfields;	/* move to type MAP */
647064562Sgshapiro	char	*regex_delim;		/* move to type MAP */
647138032Speter};
647238032Speter
647338032Speterstatic int
647438032Speterparse_fields(s, ibuf, blen, nr_substrings)
647538032Speter	char *s;
647638032Speter	int *ibuf;		/* array */
647738032Speter	int blen;		/* number of elements in ibuf */
647838032Speter	int nr_substrings;	/* number of substrings in the pattern */
647938032Speter{
648038032Speter	register char *cp;
648138032Speter	int i = 0;
648290792Sgshapiro	bool lastone = false;
648338032Speter
648438032Speter	blen--;		/* for terminating END_OF_FIELDS */
648538032Speter	cp = s;
648638032Speter	do
648738032Speter	{
648838032Speter		for (;; cp++)
648938032Speter		{
649038032Speter			if (*cp == ',')
649138032Speter			{
649238032Speter				*cp = '\0';
649338032Speter				break;
649438032Speter			}
649538032Speter			if (*cp == '\0')
649638032Speter			{
649790792Sgshapiro				lastone = true;
649838032Speter				break;
649938032Speter			}
650038032Speter		}
650138032Speter		if (i < blen)
650238032Speter		{
650338032Speter			int val = atoi(s);
650438032Speter
650538032Speter			if (val < 0 || val >= nr_substrings)
650638032Speter			{
650738032Speter				syserr("field (%d) out of range, only %d substrings in pattern",
650838032Speter				       val, nr_substrings);
650938032Speter				return -1;
651038032Speter			}
651138032Speter			ibuf[i++] = val;
651238032Speter		}
651338032Speter		else
651438032Speter		{
651590792Sgshapiro			syserr("too many fields, %d max", blen);
651638032Speter			return -1;
651738032Speter		}
651838032Speter		s = ++cp;
651938032Speter	} while (!lastone);
652038032Speter	ibuf[i] = END_OF_FIELDS;
652138032Speter	return i;
652238032Speter}
652338032Speter
652438032Speterbool
652538032Speterregex_map_init(map, ap)
652638032Speter	MAP *map;
652738032Speter	char *ap;
652838032Speter{
652938032Speter	int regerr;
653038032Speter	struct regex_map *map_p;
653138032Speter	register char *p;
653238032Speter	char *sub_param = NULL;
653338032Speter	int pflags;
653490792Sgshapiro	static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
653538032Speter
653638032Speter	if (tTd(38, 2))
653790792Sgshapiro		sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
653864562Sgshapiro			map->map_mname, ap);
653938032Speter
654038032Speter	pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
654138032Speter	p = ap;
654264562Sgshapiro	map_p = (struct regex_map *) xnalloc(sizeof *map_p);
654371345Sgshapiro	map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
654438032Speter
654538032Speter	for (;;)
654664562Sgshapiro	{
654738032Speter		while (isascii(*p) && isspace(*p))
654838032Speter			p++;
654938032Speter		if (*p != '-')
655038032Speter			break;
655138032Speter		switch (*++p)
655238032Speter		{
655338032Speter		  case 'n':	/* not */
655438032Speter			map->map_mflags |= MF_REGEX_NOT;
655538032Speter			break;
655638032Speter
655738032Speter		  case 'f':	/* case sensitive */
655838032Speter			map->map_mflags |= MF_NOFOLDCASE;
655938032Speter			pflags &= ~REG_ICASE;
656038032Speter			break;
656138032Speter
656238032Speter		  case 'b':	/* basic regular expressions */
656338032Speter			pflags &= ~REG_EXTENDED;
656438032Speter			break;
656538032Speter
656638032Speter		  case 's':	/* substring match () syntax */
656738032Speter			sub_param = ++p;
656838032Speter			pflags &= ~REG_NOSUB;
656938032Speter			break;
657038032Speter
657138032Speter		  case 'd':	/* delimiter */
657264562Sgshapiro			map_p->regex_delim = ++p;
657338032Speter			break;
657438032Speter
657538032Speter		  case 'a':	/* map append */
657638032Speter			map->map_app = ++p;
657738032Speter			break;
657838032Speter
657938032Speter		  case 'm':	/* matchonly */
658038032Speter			map->map_mflags |= MF_MATCHONLY;
658138032Speter			break;
658238032Speter
6583120256Sgshapiro		  case 'q':
6584120256Sgshapiro			map->map_mflags |= MF_KEEPQUOTES;
6585120256Sgshapiro			break;
6586120256Sgshapiro
658764562Sgshapiro		  case 'S':
658864562Sgshapiro			map->map_spacesub = *++p;
658964562Sgshapiro			break;
659064562Sgshapiro
659164562Sgshapiro		  case 'D':
659264562Sgshapiro			map->map_mflags |= MF_DEFER;
659364562Sgshapiro			break;
659464562Sgshapiro
659538032Speter		}
659664562Sgshapiro		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
659764562Sgshapiro			p++;
659864562Sgshapiro		if (*p != '\0')
659964562Sgshapiro			*p++ = '\0';
660038032Speter	}
660138032Speter	if (tTd(38, 3))
660290792Sgshapiro		sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
660338032Speter
660471345Sgshapiro	if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
660538032Speter	{
660638032Speter		/* Errorhandling */
660738032Speter		char errbuf[ERRBUF_SIZE];
660838032Speter
660971345Sgshapiro		(void) regerror(regerr, map_p->regex_pattern_buf,
661090792Sgshapiro			 errbuf, sizeof errbuf);
661190792Sgshapiro		syserr("pattern-compile-error: %s", errbuf);
661290792Sgshapiro		sm_free(map_p->regex_pattern_buf); /* XXX */
661390792Sgshapiro		sm_free(map_p); /* XXX */
661490792Sgshapiro		return false;
661538032Speter	}
661638032Speter
661738032Speter	if (map->map_app != NULL)
661838032Speter		map->map_app = newstr(map->map_app);
661964562Sgshapiro	if (map_p->regex_delim != NULL)
662064562Sgshapiro		map_p->regex_delim = newstr(map_p->regex_delim);
662138032Speter	else
662264562Sgshapiro		map_p->regex_delim = defdstr;
662338032Speter
662438032Speter	if (!bitset(REG_NOSUB, pflags))
662538032Speter	{
662638032Speter		/* substring matching */
662738032Speter		int substrings;
662864562Sgshapiro		int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
662938032Speter
663071345Sgshapiro		substrings = map_p->regex_pattern_buf->re_nsub + 1;
663138032Speter
663238032Speter		if (tTd(38, 3))
663390792Sgshapiro			sm_dprintf("regex_map_init: nr of substrings %d\n",
663464562Sgshapiro				substrings);
663538032Speter
663638032Speter		if (substrings >= MAX_MATCH)
663738032Speter		{
663890792Sgshapiro			syserr("too many substrings, %d max", MAX_MATCH);
663990792Sgshapiro			sm_free(map_p->regex_pattern_buf); /* XXX */
664090792Sgshapiro			sm_free(map_p); /* XXX */
664190792Sgshapiro			return false;
664238032Speter		}
664338032Speter		if (sub_param != NULL && sub_param[0] != '\0')
664438032Speter		{
664538032Speter			/* optional parameter -sfields */
664638032Speter			if (parse_fields(sub_param, fields,
664738032Speter					 MAX_MATCH + 1, substrings) == -1)
664890792Sgshapiro				return false;
664938032Speter		}
665038032Speter		else
665138032Speter		{
665238032Speter			int i;
665338032Speter
665490792Sgshapiro			/* set default fields */
665538032Speter			for (i = 0; i < substrings; i++)
665638032Speter				fields[i] = i;
665738032Speter			fields[i] = END_OF_FIELDS;
665838032Speter		}
665938032Speter		map_p->regex_subfields = fields;
666038032Speter		if (tTd(38, 3))
666138032Speter		{
666238032Speter			int *ip;
666338032Speter
666490792Sgshapiro			sm_dprintf("regex_map_init: subfields");
666538032Speter			for (ip = fields; *ip != END_OF_FIELDS; ip++)
666690792Sgshapiro				sm_dprintf(" %d", *ip);
666790792Sgshapiro			sm_dprintf("\n");
666838032Speter		}
666938032Speter	}
667090792Sgshapiro	map->map_db1 = (ARBPTR_T) map_p;	/* dirty hack */
667190792Sgshapiro	return true;
667238032Speter}
667338032Speter
667438032Speterstatic char *
667538032Speterregex_map_rewrite(map, s, slen, av)
667638032Speter	MAP *map;
667738032Speter	const char *s;
667838032Speter	size_t slen;
667938032Speter	char **av;
668038032Speter{
668138032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
668238032Speter		return map_rewrite(map, av[0], strlen(av[0]), NULL);
668338032Speter	else
668477349Sgshapiro		return map_rewrite(map, s, slen, av);
668538032Speter}
668638032Speter
668738032Speterchar *
668838032Speterregex_map_lookup(map, name, av, statp)
668938032Speter	MAP *map;
669038032Speter	char *name;
669138032Speter	char **av;
669238032Speter	int *statp;
669338032Speter{
669438032Speter	int reg_res;
669538032Speter	struct regex_map *map_p;
669638032Speter	regmatch_t pmatch[MAX_MATCH];
669738032Speter
669838032Speter	if (tTd(38, 20))
669938032Speter	{
670038032Speter		char **cpp;
670138032Speter
670290792Sgshapiro		sm_dprintf("regex_map_lookup: key '%s'\n", name);
670364562Sgshapiro		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
670490792Sgshapiro			sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
670538032Speter	}
670638032Speter
670738032Speter	map_p = (struct regex_map *)(map->map_db1);
670871345Sgshapiro	reg_res = regexec(map_p->regex_pattern_buf,
670964562Sgshapiro			  name, MAX_MATCH, pmatch, 0);
671038032Speter
671138032Speter	if (bitset(MF_REGEX_NOT, map->map_mflags))
671238032Speter	{
671338032Speter		/* option -n */
671438032Speter		if (reg_res == REG_NOMATCH)
671590792Sgshapiro			return regex_map_rewrite(map, "", (size_t) 0, av);
671638032Speter		else
671738032Speter			return NULL;
671838032Speter	}
671938032Speter	if (reg_res == REG_NOMATCH)
672038032Speter		return NULL;
672138032Speter
672238032Speter	if (map_p->regex_subfields != NULL)
672338032Speter	{
672438032Speter		/* option -s */
672538032Speter		static char retbuf[MAXNAME];
672638032Speter		int fields[MAX_MATCH + 1];
672790792Sgshapiro		bool first = true;
672838032Speter		int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
672990792Sgshapiro		bool quotemode = false, bslashmode = false;
673038032Speter		register char *dp, *sp;
673138032Speter		char *endp, *ldp;
673238032Speter		int *ip;
673338032Speter
673438032Speter		dp = retbuf;
673538032Speter		ldp = retbuf + sizeof(retbuf) - 1;
673638032Speter
673738032Speter		if (av[1] != NULL)
673838032Speter		{
673938032Speter			if (parse_fields(av[1], fields, MAX_MATCH + 1,
674071345Sgshapiro					 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
674138032Speter			{
674238032Speter				*statp = EX_CONFIG;
674338032Speter				return NULL;
674438032Speter			}
674538032Speter			ip = fields;
674638032Speter		}
674738032Speter		else
674838032Speter			ip = map_p->regex_subfields;
674938032Speter
675038032Speter		for ( ; *ip != END_OF_FIELDS; ip++)
675138032Speter		{
675238032Speter			if (!first)
675338032Speter			{
675464562Sgshapiro				for (sp = map_p->regex_delim; *sp; sp++)
675538032Speter				{
675638032Speter					if (dp < ldp)
675738032Speter						*dp++ = *sp;
675838032Speter				}
675938032Speter			}
676038032Speter			else
676190792Sgshapiro				first = false;
676238032Speter
676371345Sgshapiro			if (*ip >= MAX_MATCH ||
676471345Sgshapiro			    pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
676538032Speter				continue;
676638032Speter
676738032Speter			sp = name + pmatch[*ip].rm_so;
676838032Speter			endp = name + pmatch[*ip].rm_eo;
676938032Speter			for (; endp > sp; sp++)
677038032Speter			{
677138032Speter				if (dp < ldp)
677238032Speter				{
677364562Sgshapiro					if (bslashmode)
677464562Sgshapiro					{
677538032Speter						*dp++ = *sp;
677690792Sgshapiro						bslashmode = false;
677738032Speter					}
677864562Sgshapiro					else if (quotemode && *sp != '"' &&
677938032Speter						*sp != '\\')
678038032Speter					{
678138032Speter						*dp++ = *sp;
678238032Speter					}
678390792Sgshapiro					else switch (*dp++ = *sp)
678438032Speter					{
678590792Sgshapiro					  case '\\':
678690792Sgshapiro						bslashmode = true;
678738032Speter						break;
678838032Speter
678990792Sgshapiro					  case '(':
679038032Speter						cmntcnt++;
679138032Speter						break;
679238032Speter
679390792Sgshapiro					  case ')':
679438032Speter						cmntcnt--;
679538032Speter						break;
679638032Speter
679790792Sgshapiro					  case '<':
679838032Speter						anglecnt++;
679938032Speter						break;
680038032Speter
680190792Sgshapiro					  case '>':
680238032Speter						anglecnt--;
680338032Speter						break;
680438032Speter
680590792Sgshapiro					  case ' ':
680638032Speter						spacecnt++;
680738032Speter						break;
680838032Speter
680990792Sgshapiro					  case '"':
681038032Speter						quotemode = !quotemode;
681138032Speter						break;
681238032Speter					}
681338032Speter				}
681438032Speter			}
681538032Speter		}
681638032Speter		if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
681738032Speter		    bslashmode || spacecnt != 0)
681838032Speter		{
681964562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
682064562Sgshapiro				  "Warning: regex may cause prescan() failure map=%s lookup=%s",
682164562Sgshapiro				  map->map_mname, name);
682238032Speter			return NULL;
682338032Speter		}
682438032Speter
682538032Speter		*dp = '\0';
682638032Speter
682738032Speter		return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
682838032Speter	}
682938032Speter	return regex_map_rewrite(map, "", (size_t)0, av);
683038032Speter}
683138032Speter#endif /* MAP_REGEX */
683290792Sgshapiro/*
683364562Sgshapiro**  NSD modules
683464562Sgshapiro*/
683590792Sgshapiro#if MAP_NSD
683664562Sgshapiro
683764562Sgshapiro# include <ndbm.h>
683864562Sgshapiro# define _DATUM_DEFINED
683964562Sgshapiro# include <ns_api.h>
684064562Sgshapiro
684164562Sgshapirotypedef struct ns_map_list
684264562Sgshapiro{
684390792Sgshapiro	ns_map_t		*map;		/* XXX ns_ ? */
684490792Sgshapiro	char			*mapname;
684590792Sgshapiro	struct ns_map_list	*next;
684664562Sgshapiro} ns_map_list_t;
684764562Sgshapiro
684864562Sgshapirostatic ns_map_t *
684964562Sgshapirons_map_t_find(mapname)
685064562Sgshapiro	char *mapname;
685164562Sgshapiro{
685264562Sgshapiro	static ns_map_list_t *ns_maps = NULL;
685364562Sgshapiro	ns_map_list_t *ns_map;
685464562Sgshapiro
685564562Sgshapiro	/* walk the list of maps looking for the correctly named map */
685664562Sgshapiro	for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
685764562Sgshapiro	{
685864562Sgshapiro		if (strcmp(ns_map->mapname, mapname) == 0)
685964562Sgshapiro			break;
686064562Sgshapiro	}
686164562Sgshapiro
686264562Sgshapiro	/* if we are looking at a NULL ns_map_list_t, then create a new one */
686364562Sgshapiro	if (ns_map == NULL)
686464562Sgshapiro	{
686564562Sgshapiro		ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map);
686664562Sgshapiro		ns_map->mapname = newstr(mapname);
686764562Sgshapiro		ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map);
6868102528Sgshapiro		memset(ns_map->map, '\0', sizeof *ns_map->map);
686964562Sgshapiro		ns_map->next = ns_maps;
687064562Sgshapiro		ns_maps = ns_map;
687164562Sgshapiro	}
687264562Sgshapiro	return ns_map->map;
687364562Sgshapiro}
687464562Sgshapiro
687564562Sgshapirochar *
687664562Sgshapironsd_map_lookup(map, name, av, statp)
687764562Sgshapiro	MAP *map;
687864562Sgshapiro	char *name;
687964562Sgshapiro	char **av;
688064562Sgshapiro	int *statp;
688164562Sgshapiro{
688271345Sgshapiro	int buflen, r;
688364562Sgshapiro	char *p;
688464562Sgshapiro	ns_map_t *ns_map;
688564562Sgshapiro	char keybuf[MAXNAME + 1];
688664562Sgshapiro	char buf[MAXLINE];
688764562Sgshapiro
688864562Sgshapiro	if (tTd(38, 20))
688990792Sgshapiro		sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
689064562Sgshapiro
689164562Sgshapiro	buflen = strlen(name);
689264562Sgshapiro	if (buflen > sizeof keybuf - 1)
689390792Sgshapiro		buflen = sizeof keybuf - 1;	/* XXX simply cut off? */
689464562Sgshapiro	memmove(keybuf, name, buflen);
689564562Sgshapiro	keybuf[buflen] = '\0';
689664562Sgshapiro	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
689764562Sgshapiro		makelower(keybuf);
689864562Sgshapiro
689964562Sgshapiro	ns_map = ns_map_t_find(map->map_file);
690064562Sgshapiro	if (ns_map == NULL)
690164562Sgshapiro	{
690264562Sgshapiro		if (tTd(38, 20))
690390792Sgshapiro			sm_dprintf("nsd_map_t_find failed\n");
690471345Sgshapiro		*statp = EX_UNAVAILABLE;
690564562Sgshapiro		return NULL;
690664562Sgshapiro	}
690798121Sgshapiro	r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
690898121Sgshapiro		      buf, sizeof buf);
690971345Sgshapiro	if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
691071345Sgshapiro	{
691171345Sgshapiro		*statp = EX_TEMPFAIL;
691264562Sgshapiro		return NULL;
691371345Sgshapiro	}
691477349Sgshapiro	if (r == NS_BADREQ
691577349Sgshapiro# ifdef NS_NOPERM
691677349Sgshapiro	    || r == NS_NOPERM
691777349Sgshapiro# endif /* NS_NOPERM */
691877349Sgshapiro	    )
691971345Sgshapiro	{
692071345Sgshapiro		*statp = EX_CONFIG;
692171345Sgshapiro		return NULL;
692271345Sgshapiro	}
692371345Sgshapiro	if (r != NS_SUCCESS)
692471345Sgshapiro	{
692571345Sgshapiro		*statp = EX_NOTFOUND;
692671345Sgshapiro		return NULL;
692771345Sgshapiro	}
692864562Sgshapiro
692971345Sgshapiro	*statp = EX_OK;
693071345Sgshapiro
693164562Sgshapiro	/* Null out trailing \n */
693264562Sgshapiro	if ((p = strchr(buf, '\n')) != NULL)
693364562Sgshapiro		*p = '\0';
693464562Sgshapiro
693564562Sgshapiro	return map_rewrite(map, buf, strlen(buf), av);
693664562Sgshapiro}
693764562Sgshapiro#endif /* MAP_NSD */
693864562Sgshapiro
693964562Sgshapirochar *
694064562Sgshapiroarith_map_lookup(map, name, av, statp)
694164562Sgshapiro	MAP *map;
694264562Sgshapiro	char *name;
694364562Sgshapiro	char **av;
694464562Sgshapiro	int *statp;
694564562Sgshapiro{
694664562Sgshapiro	long r;
694764562Sgshapiro	long v[2];
694890792Sgshapiro	bool res = false;
694964562Sgshapiro	bool boolres;
695064562Sgshapiro	static char result[16];
695164562Sgshapiro	char **cpp;
695264562Sgshapiro
695364562Sgshapiro	if (tTd(38, 2))
695464562Sgshapiro	{
695590792Sgshapiro		sm_dprintf("arith_map_lookup: key '%s'\n", name);
695664562Sgshapiro		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
695790792Sgshapiro			sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
695864562Sgshapiro	}
695964562Sgshapiro	r = 0;
696090792Sgshapiro	boolres = false;
696164562Sgshapiro	cpp = av;
696264562Sgshapiro	*statp = EX_OK;
696364562Sgshapiro
696464562Sgshapiro	/*
696564562Sgshapiro	**  read arguments for arith map
696664562Sgshapiro	**  - no check is made whether they are really numbers
696764562Sgshapiro	**  - just ignores args after the second
696864562Sgshapiro	*/
696990792Sgshapiro
697064562Sgshapiro	for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
697164562Sgshapiro		v[r++] = strtol(*cpp, NULL, 0);
697264562Sgshapiro
697364562Sgshapiro	/* operator and (at least) two operands given? */
697464562Sgshapiro	if (name != NULL && r == 2)
697564562Sgshapiro	{
697690792Sgshapiro		switch (*name)
697764562Sgshapiro		{
697864562Sgshapiro		  case '|':
697964562Sgshapiro			r = v[0] | v[1];
698064562Sgshapiro			break;
698164562Sgshapiro
698264562Sgshapiro		  case '&':
698364562Sgshapiro			r = v[0] & v[1];
698464562Sgshapiro			break;
698564562Sgshapiro
698664562Sgshapiro		  case '%':
698764562Sgshapiro			if (v[1] == 0)
698864562Sgshapiro				return NULL;
698964562Sgshapiro			r = v[0] % v[1];
699064562Sgshapiro			break;
699164562Sgshapiro		  case '+':
699264562Sgshapiro			r = v[0] + v[1];
699364562Sgshapiro			break;
699464562Sgshapiro
699564562Sgshapiro		  case '-':
699664562Sgshapiro			r = v[0] - v[1];
699764562Sgshapiro			break;
699864562Sgshapiro
699964562Sgshapiro		  case '*':
700064562Sgshapiro			r = v[0] * v[1];
700164562Sgshapiro			break;
700264562Sgshapiro
700364562Sgshapiro		  case '/':
700464562Sgshapiro			if (v[1] == 0)
700564562Sgshapiro				return NULL;
700664562Sgshapiro			r = v[0] / v[1];
700764562Sgshapiro			break;
700864562Sgshapiro
700964562Sgshapiro		  case 'l':
701064562Sgshapiro			res = v[0] < v[1];
701190792Sgshapiro			boolres = true;
701264562Sgshapiro			break;
701364562Sgshapiro
701464562Sgshapiro		  case '=':
701564562Sgshapiro			res = v[0] == v[1];
701690792Sgshapiro			boolres = true;
701764562Sgshapiro			break;
701864562Sgshapiro
701964562Sgshapiro		  default:
702064562Sgshapiro			/* XXX */
702164562Sgshapiro			*statp = EX_CONFIG;
702264562Sgshapiro			if (LogLevel > 10)
702364562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
702464562Sgshapiro					  "arith_map: unknown operator %c",
702564562Sgshapiro					  isprint(*name) ? *name : '?');
702664562Sgshapiro			return NULL;
702764562Sgshapiro		}
702864562Sgshapiro		if (boolres)
702990792Sgshapiro			(void) sm_snprintf(result, sizeof result,
703090792Sgshapiro				res ? "TRUE" : "FALSE");
703164562Sgshapiro		else
703290792Sgshapiro			(void) sm_snprintf(result, sizeof result, "%ld", r);
703364562Sgshapiro		return result;
703464562Sgshapiro	}
703564562Sgshapiro	*statp = EX_CONFIG;
703664562Sgshapiro	return NULL;
703764562Sgshapiro}
7038132943Sgshapiro
7039132943Sgshapiro#if SOCKETMAP
7040132943Sgshapiro
7041132943Sgshapiro# if NETINET || NETINET6
7042132943Sgshapiro#  include <arpa/inet.h>
7043132943Sgshapiro# endif /* NETINET || NETINET6 */
7044132943Sgshapiro
7045132943Sgshapiro# define socket_map_next map_stack[0]
7046132943Sgshapiro
7047132943Sgshapiro/*
7048132943Sgshapiro**  SOCKET_MAP_OPEN -- open socket table
7049132943Sgshapiro*/
7050132943Sgshapiro
7051132943Sgshapirobool
7052132943Sgshapirosocket_map_open(map, mode)
7053132943Sgshapiro	MAP *map;
7054132943Sgshapiro	int mode;
7055132943Sgshapiro{
7056132943Sgshapiro	STAB *s;
7057132943Sgshapiro	int sock = 0;
7058132943Sgshapiro	SOCKADDR_LEN_T addrlen = 0;
7059132943Sgshapiro	int addrno = 0;
7060132943Sgshapiro	int save_errno;
7061132943Sgshapiro	char *p;
7062132943Sgshapiro	char *colon;
7063132943Sgshapiro	char *at;
7064132943Sgshapiro	struct hostent *hp = NULL;
7065132943Sgshapiro	SOCKADDR addr;
7066132943Sgshapiro
7067132943Sgshapiro	if (tTd(38, 2))
7068132943Sgshapiro		sm_dprintf("socket_map_open(%s, %s, %d)\n",
7069132943Sgshapiro			map->map_mname, map->map_file, mode);
7070132943Sgshapiro
7071132943Sgshapiro	mode &= O_ACCMODE;
7072132943Sgshapiro
7073132943Sgshapiro	/* sendmail doesn't have the ability to write to SOCKET (yet) */
7074132943Sgshapiro	if (mode != O_RDONLY)
7075132943Sgshapiro	{
7076132943Sgshapiro		/* issue a pseudo-error message */
7077132943Sgshapiro		errno = SM_EMAPCANTWRITE;
7078132943Sgshapiro		return false;
7079132943Sgshapiro	}
7080132943Sgshapiro
7081132943Sgshapiro	if (*map->map_file == '\0')
7082132943Sgshapiro	{
7083132943Sgshapiro		syserr("socket map \"%s\": empty or missing socket information",
7084132943Sgshapiro			map->map_mname);
7085132943Sgshapiro		return false;
7086132943Sgshapiro	}
7087132943Sgshapiro
7088132943Sgshapiro	s = socket_map_findconn(map->map_file);
7089132943Sgshapiro	if (s->s_socketmap != NULL)
7090132943Sgshapiro	{
7091132943Sgshapiro		/* Copy open connection */
7092132943Sgshapiro		map->map_db1 = s->s_socketmap->map_db1;
7093132943Sgshapiro
7094132943Sgshapiro		/* Add this map as head of linked list */
7095132943Sgshapiro		map->socket_map_next = s->s_socketmap;
7096132943Sgshapiro		s->s_socketmap = map;
7097132943Sgshapiro
7098132943Sgshapiro		if (tTd(38, 2))
7099132943Sgshapiro			sm_dprintf("using cached connection\n");
7100132943Sgshapiro		return true;
7101132943Sgshapiro	}
7102132943Sgshapiro
7103132943Sgshapiro	if (tTd(38, 2))
7104132943Sgshapiro		sm_dprintf("opening new connection\n");
7105132943Sgshapiro
7106132943Sgshapiro	/* following code is ripped from milter.c */
7107132943Sgshapiro	/* XXX It should be put in a library... */
7108132943Sgshapiro
7109132943Sgshapiro	/* protocol:filename or protocol:port@host */
7110132943Sgshapiro	memset(&addr, '\0', sizeof addr);
7111132943Sgshapiro	p = map->map_file;
7112132943Sgshapiro	colon = strchr(p, ':');
7113132943Sgshapiro	if (colon != NULL)
7114132943Sgshapiro	{
7115132943Sgshapiro		*colon = '\0';
7116132943Sgshapiro
7117132943Sgshapiro		if (*p == '\0')
7118132943Sgshapiro		{
7119132943Sgshapiro# if NETUNIX
7120132943Sgshapiro			/* default to AF_UNIX */
7121132943Sgshapiro			addr.sa.sa_family = AF_UNIX;
7122132943Sgshapiro# else /* NETUNIX */
7123132943Sgshapiro#  if NETINET
7124132943Sgshapiro			/* default to AF_INET */
7125132943Sgshapiro			addr.sa.sa_family = AF_INET;
7126132943Sgshapiro#  else /* NETINET */
7127132943Sgshapiro#   if NETINET6
7128132943Sgshapiro			/* default to AF_INET6 */
7129132943Sgshapiro			addr.sa.sa_family = AF_INET6;
7130132943Sgshapiro#   else /* NETINET6 */
7131132943Sgshapiro			/* no protocols available */
7132132943Sgshapiro			syserr("socket map \"%s\": no valid socket protocols available",
7133132943Sgshapiro			map->map_mname);
7134132943Sgshapiro			return false;
7135132943Sgshapiro#   endif /* NETINET6 */
7136132943Sgshapiro#  endif /* NETINET */
7137132943Sgshapiro# endif /* NETUNIX */
7138132943Sgshapiro		}
7139132943Sgshapiro# if NETUNIX
7140132943Sgshapiro		else if (sm_strcasecmp(p, "unix") == 0 ||
7141132943Sgshapiro			 sm_strcasecmp(p, "local") == 0)
7142132943Sgshapiro			addr.sa.sa_family = AF_UNIX;
7143132943Sgshapiro# endif /* NETUNIX */
7144132943Sgshapiro# if NETINET
7145132943Sgshapiro		else if (sm_strcasecmp(p, "inet") == 0)
7146132943Sgshapiro			addr.sa.sa_family = AF_INET;
7147132943Sgshapiro# endif /* NETINET */
7148132943Sgshapiro# if NETINET6
7149132943Sgshapiro		else if (sm_strcasecmp(p, "inet6") == 0)
7150132943Sgshapiro			addr.sa.sa_family = AF_INET6;
7151132943Sgshapiro# endif /* NETINET6 */
7152132943Sgshapiro		else
7153132943Sgshapiro		{
7154132943Sgshapiro# ifdef EPROTONOSUPPORT
7155132943Sgshapiro			errno = EPROTONOSUPPORT;
7156132943Sgshapiro# else /* EPROTONOSUPPORT */
7157132943Sgshapiro			errno = EINVAL;
7158132943Sgshapiro# endif /* EPROTONOSUPPORT */
7159132943Sgshapiro			syserr("socket map \"%s\": unknown socket type %s",
7160132943Sgshapiro			       map->map_mname, p);
7161132943Sgshapiro			return false;
7162132943Sgshapiro		}
7163132943Sgshapiro		*colon++ = ':';
7164132943Sgshapiro	}
7165132943Sgshapiro	else
7166132943Sgshapiro	{
7167132943Sgshapiro		colon = p;
7168132943Sgshapiro#if NETUNIX
7169132943Sgshapiro		/* default to AF_UNIX */
7170132943Sgshapiro		addr.sa.sa_family = AF_UNIX;
7171132943Sgshapiro#else /* NETUNIX */
7172132943Sgshapiro# if NETINET
7173132943Sgshapiro		/* default to AF_INET */
7174132943Sgshapiro		addr.sa.sa_family = AF_INET;
7175132943Sgshapiro# else /* NETINET */
7176132943Sgshapiro#  if NETINET6
7177132943Sgshapiro		/* default to AF_INET6 */
7178132943Sgshapiro		addr.sa.sa_family = AF_INET6;
7179132943Sgshapiro#  else /* NETINET6 */
7180132943Sgshapiro		syserr("socket map \"%s\": unknown socket type %s",
7181132943Sgshapiro		       map->map_mname, p);
7182132943Sgshapiro		return false;
7183132943Sgshapiro#  endif /* NETINET6 */
7184132943Sgshapiro# endif /* NETINET */
7185132943Sgshapiro#endif /* NETUNIX */
7186132943Sgshapiro	}
7187132943Sgshapiro
7188132943Sgshapiro# if NETUNIX
7189132943Sgshapiro	if (addr.sa.sa_family == AF_UNIX)
7190132943Sgshapiro	{
7191132943Sgshapiro		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7192132943Sgshapiro
7193132943Sgshapiro		at = colon;
7194132943Sgshapiro		if (strlen(colon) >= sizeof addr.sunix.sun_path)
7195132943Sgshapiro		{
7196132943Sgshapiro			syserr("socket map \"%s\": local socket name %s too long",
7197132943Sgshapiro			       map->map_mname, colon);
7198132943Sgshapiro			return false;
7199132943Sgshapiro		}
7200132943Sgshapiro		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7201132943Sgshapiro				 S_IRUSR|S_IWUSR, NULL);
7202132943Sgshapiro
7203132943Sgshapiro		if (errno != 0)
7204132943Sgshapiro		{
7205132943Sgshapiro			/* if not safe, don't create */
7206132943Sgshapiro				syserr("socket map \"%s\": local socket name %s unsafe",
7207132943Sgshapiro			       map->map_mname, colon);
7208132943Sgshapiro			return false;
7209132943Sgshapiro		}
7210132943Sgshapiro
7211132943Sgshapiro		(void) sm_strlcpy(addr.sunix.sun_path, colon,
7212132943Sgshapiro			       sizeof addr.sunix.sun_path);
7213132943Sgshapiro		addrlen = sizeof (struct sockaddr_un);
7214132943Sgshapiro	}
7215132943Sgshapiro	else
7216132943Sgshapiro# endif /* NETUNIX */
7217132943Sgshapiro# if NETINET || NETINET6
7218132943Sgshapiro	if (false
7219132943Sgshapiro#  if NETINET
7220132943Sgshapiro		 || addr.sa.sa_family == AF_INET
7221132943Sgshapiro#  endif /* NETINET */
7222132943Sgshapiro#  if NETINET6
7223132943Sgshapiro		 || addr.sa.sa_family == AF_INET6
7224132943Sgshapiro#  endif /* NETINET6 */
7225132943Sgshapiro		 )
7226132943Sgshapiro	{
7227132943Sgshapiro		unsigned short port;
7228132943Sgshapiro
7229132943Sgshapiro		/* Parse port@host */
7230132943Sgshapiro		at = strchr(colon, '@');
7231132943Sgshapiro		if (at == NULL)
7232132943Sgshapiro		{
7233132943Sgshapiro			syserr("socket map \"%s\": bad address %s (expected port@host)",
7234132943Sgshapiro				       map->map_mname, colon);
7235132943Sgshapiro			return false;
7236132943Sgshapiro		}
7237132943Sgshapiro		*at = '\0';
7238132943Sgshapiro		if (isascii(*colon) && isdigit(*colon))
7239132943Sgshapiro			port = htons((unsigned short) atoi(colon));
7240132943Sgshapiro		else
7241132943Sgshapiro		{
7242132943Sgshapiro#  ifdef NO_GETSERVBYNAME
7243132943Sgshapiro			syserr("socket map \"%s\": invalid port number %s",
7244132943Sgshapiro				       map->map_mname, colon);
7245132943Sgshapiro			return false;
7246132943Sgshapiro#  else /* NO_GETSERVBYNAME */
7247132943Sgshapiro			register struct servent *sp;
7248132943Sgshapiro
7249132943Sgshapiro			sp = getservbyname(colon, "tcp");
7250132943Sgshapiro			if (sp == NULL)
7251132943Sgshapiro			{
7252132943Sgshapiro				syserr("socket map \"%s\": unknown port name %s",
7253132943Sgshapiro					       map->map_mname, colon);
7254132943Sgshapiro				return false;
7255132943Sgshapiro			}
7256132943Sgshapiro			port = sp->s_port;
7257132943Sgshapiro#  endif /* NO_GETSERVBYNAME */
7258132943Sgshapiro		}
7259132943Sgshapiro		*at++ = '@';
7260132943Sgshapiro		if (*at == '[')
7261132943Sgshapiro		{
7262132943Sgshapiro			char *end;
7263132943Sgshapiro
7264132943Sgshapiro			end = strchr(at, ']');
7265132943Sgshapiro			if (end != NULL)
7266132943Sgshapiro			{
7267132943Sgshapiro				bool found = false;
7268132943Sgshapiro#  if NETINET
7269132943Sgshapiro				unsigned long hid = INADDR_NONE;
7270132943Sgshapiro#  endif /* NETINET */
7271132943Sgshapiro#  if NETINET6
7272132943Sgshapiro				struct sockaddr_in6 hid6;
7273132943Sgshapiro#  endif /* NETINET6 */
7274132943Sgshapiro
7275132943Sgshapiro				*end = '\0';
7276132943Sgshapiro#  if NETINET
7277132943Sgshapiro				if (addr.sa.sa_family == AF_INET &&
7278132943Sgshapiro				    (hid = inet_addr(&at[1])) != INADDR_NONE)
7279132943Sgshapiro				{
7280132943Sgshapiro					addr.sin.sin_addr.s_addr = hid;
7281132943Sgshapiro					addr.sin.sin_port = port;
7282132943Sgshapiro					found = true;
7283132943Sgshapiro				}
7284132943Sgshapiro#  endif /* NETINET */
7285132943Sgshapiro#  if NETINET6
7286132943Sgshapiro				(void) memset(&hid6, '\0', sizeof hid6);
7287132943Sgshapiro				if (addr.sa.sa_family == AF_INET6 &&
7288132943Sgshapiro				    anynet_pton(AF_INET6, &at[1],
7289132943Sgshapiro						&hid6.sin6_addr) == 1)
7290132943Sgshapiro				{
7291132943Sgshapiro					addr.sin6.sin6_addr = hid6.sin6_addr;
7292132943Sgshapiro					addr.sin6.sin6_port = port;
7293132943Sgshapiro					found = true;
7294132943Sgshapiro				}
7295132943Sgshapiro#  endif /* NETINET6 */
7296132943Sgshapiro				*end = ']';
7297132943Sgshapiro				if (!found)
7298132943Sgshapiro				{
7299132943Sgshapiro					syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7300132943Sgshapiro					       map->map_mname, at);
7301132943Sgshapiro					return false;
7302132943Sgshapiro				}
7303132943Sgshapiro			}
7304132943Sgshapiro			else
7305132943Sgshapiro			{
7306132943Sgshapiro				syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7307132943Sgshapiro				       map->map_mname, at);
7308132943Sgshapiro				return false;
7309132943Sgshapiro			}
7310132943Sgshapiro		}
7311132943Sgshapiro		else
7312132943Sgshapiro		{
7313132943Sgshapiro			hp = sm_gethostbyname(at, addr.sa.sa_family);
7314132943Sgshapiro			if (hp == NULL)
7315132943Sgshapiro			{
7316132943Sgshapiro				syserr("socket map \"%s\": Unknown host name %s",
7317132943Sgshapiro					map->map_mname, at);
7318132943Sgshapiro				return false;
7319132943Sgshapiro			}
7320132943Sgshapiro			addr.sa.sa_family = hp->h_addrtype;
7321132943Sgshapiro			switch (hp->h_addrtype)
7322132943Sgshapiro			{
7323132943Sgshapiro#  if NETINET
7324132943Sgshapiro			  case AF_INET:
7325132943Sgshapiro				memmove(&addr.sin.sin_addr,
7326132943Sgshapiro					hp->h_addr, INADDRSZ);
7327132943Sgshapiro				addr.sin.sin_port = port;
7328132943Sgshapiro				addrlen = sizeof (struct sockaddr_in);
7329132943Sgshapiro				addrno = 1;
7330132943Sgshapiro				break;
7331132943Sgshapiro#  endif /* NETINET */
7332132943Sgshapiro
7333132943Sgshapiro#  if NETINET6
7334132943Sgshapiro			  case AF_INET6:
7335132943Sgshapiro				memmove(&addr.sin6.sin6_addr,
7336132943Sgshapiro					hp->h_addr, IN6ADDRSZ);
7337132943Sgshapiro				addr.sin6.sin6_port = port;
7338132943Sgshapiro				addrlen = sizeof (struct sockaddr_in6);
7339132943Sgshapiro				addrno = 1;
7340132943Sgshapiro				break;
7341132943Sgshapiro#  endif /* NETINET6 */
7342132943Sgshapiro
7343132943Sgshapiro			  default:
7344132943Sgshapiro				syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7345132943Sgshapiro					map->map_mname, at, hp->h_addrtype);
7346132943Sgshapiro#  if NETINET6
7347132943Sgshapiro				freehostent(hp);
7348132943Sgshapiro#  endif /* NETINET6 */
7349132943Sgshapiro				return false;
7350132943Sgshapiro			}
7351132943Sgshapiro		}
7352132943Sgshapiro	}
7353132943Sgshapiro	else
7354132943Sgshapiro# endif /* NETINET || NETINET6 */
7355132943Sgshapiro	{
7356132943Sgshapiro		syserr("socket map \"%s\": unknown socket protocol",
7357132943Sgshapiro			map->map_mname);
7358132943Sgshapiro		return false;
7359132943Sgshapiro	}
7360132943Sgshapiro
7361132943Sgshapiro	/* nope, actually connecting */
7362132943Sgshapiro	for (;;)
7363132943Sgshapiro	{
7364132943Sgshapiro		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
7365132943Sgshapiro		if (sock < 0)
7366132943Sgshapiro		{
7367132943Sgshapiro			save_errno = errno;
7368132943Sgshapiro			if (tTd(38, 5))
7369132943Sgshapiro				sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7370132943Sgshapiro					   map->map_mname,
7371132943Sgshapiro					   sm_errstring(save_errno));
7372132943Sgshapiro# if NETINET6
7373132943Sgshapiro			if (hp != NULL)
7374132943Sgshapiro				freehostent(hp);
7375132943Sgshapiro# endif /* NETINET6 */
7376132943Sgshapiro			return false;
7377132943Sgshapiro		}
7378132943Sgshapiro
7379132943Sgshapiro		if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
7380132943Sgshapiro			break;
7381132943Sgshapiro
7382132943Sgshapiro		/* couldn't connect.... try next address */
7383132943Sgshapiro		save_errno = errno;
7384132943Sgshapiro		p = CurHostName;
7385132943Sgshapiro		CurHostName = at;
7386132943Sgshapiro		if (tTd(38, 5))
7387132943Sgshapiro			sm_dprintf("socket_open (%s): open %s failed: %s\n",
7388132943Sgshapiro				map->map_mname, at, sm_errstring(save_errno));
7389132943Sgshapiro		CurHostName = p;
7390132943Sgshapiro		(void) close(sock);
7391132943Sgshapiro
7392132943Sgshapiro		/* try next address */
7393132943Sgshapiro		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
7394132943Sgshapiro		{
7395132943Sgshapiro			switch (addr.sa.sa_family)
7396132943Sgshapiro			{
7397132943Sgshapiro# if NETINET
7398132943Sgshapiro			  case AF_INET:
7399132943Sgshapiro				memmove(&addr.sin.sin_addr,
7400132943Sgshapiro					hp->h_addr_list[addrno++],
7401132943Sgshapiro					INADDRSZ);
7402132943Sgshapiro				break;
7403132943Sgshapiro# endif /* NETINET */
7404132943Sgshapiro
7405132943Sgshapiro# if NETINET6
7406132943Sgshapiro			  case AF_INET6:
7407132943Sgshapiro				memmove(&addr.sin6.sin6_addr,
7408132943Sgshapiro					hp->h_addr_list[addrno++],
7409132943Sgshapiro					IN6ADDRSZ);
7410132943Sgshapiro				break;
7411132943Sgshapiro# endif /* NETINET6 */
7412132943Sgshapiro
7413132943Sgshapiro			  default:
7414132943Sgshapiro				if (tTd(38, 5))
7415132943Sgshapiro					sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7416132943Sgshapiro						   map->map_mname, at,
7417132943Sgshapiro						   hp->h_addrtype);
7418132943Sgshapiro# if NETINET6
7419132943Sgshapiro				freehostent(hp);
7420132943Sgshapiro# endif /* NETINET6 */
7421132943Sgshapiro				return false;
7422132943Sgshapiro			}
7423132943Sgshapiro			continue;
7424132943Sgshapiro		}
7425132943Sgshapiro		p = CurHostName;
7426132943Sgshapiro		CurHostName = at;
7427132943Sgshapiro		if (tTd(38, 5))
7428132943Sgshapiro			sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7429132943Sgshapiro				   map->map_mname, sm_errstring(save_errno));
7430132943Sgshapiro		CurHostName = p;
7431132943Sgshapiro# if NETINET6
7432132943Sgshapiro		if (hp != NULL)
7433132943Sgshapiro			freehostent(hp);
7434132943Sgshapiro# endif /* NETINET6 */
7435132943Sgshapiro		return false;
7436132943Sgshapiro	}
7437132943Sgshapiro# if NETINET6
7438132943Sgshapiro	if (hp != NULL)
7439132943Sgshapiro	{
7440132943Sgshapiro		freehostent(hp);
7441132943Sgshapiro		hp = NULL;
7442132943Sgshapiro	}
7443132943Sgshapiro# endif /* NETINET6 */
7444132943Sgshapiro	if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
7445132943Sgshapiro						  SM_TIME_DEFAULT,
7446132943Sgshapiro						  (void *) &sock,
7447132943Sgshapiro						  SM_IO_RDWR,
7448132943Sgshapiro						  NULL)) == NULL)
7449132943Sgshapiro	{
7450132943Sgshapiro		close(sock);
7451132943Sgshapiro		if (tTd(38, 2))
7452132943Sgshapiro		    sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7453132943Sgshapiro			       map->map_mname, sm_errstring(errno));
7454132943Sgshapiro		return false;
7455132943Sgshapiro	}
7456132943Sgshapiro
7457132943Sgshapiro	/* Save connection for reuse */
7458132943Sgshapiro	s->s_socketmap = map;
7459132943Sgshapiro	return true;
7460132943Sgshapiro}
7461132943Sgshapiro
7462132943Sgshapiro/*
7463132943Sgshapiro**  SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7464132943Sgshapiro**
7465132943Sgshapiro**	Cache SOCKET connections based on the connection specifier
7466132943Sgshapiro**	and PID so we don't have multiple connections open to
7467132943Sgshapiro**	the same server for different maps.  Need a separate connection
7468132943Sgshapiro**	per PID since a parent process may close the map before the
7469132943Sgshapiro**	child is done with it.
7470132943Sgshapiro**
7471132943Sgshapiro**	Parameters:
7472132943Sgshapiro**		conn -- SOCKET map connection specifier
7473132943Sgshapiro**
7474132943Sgshapiro**	Returns:
7475132943Sgshapiro**		Symbol table entry for the SOCKET connection.
7476132943Sgshapiro*/
7477132943Sgshapiro
7478132943Sgshapirostatic STAB *
7479132943Sgshapirosocket_map_findconn(conn)
7480132943Sgshapiro	const char *conn;
7481132943Sgshapiro{
7482132943Sgshapiro	char *nbuf;
7483132943Sgshapiro	STAB *SM_NONVOLATILE s = NULL;
7484132943Sgshapiro
7485132943Sgshapiro	nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
7486132943Sgshapiro	SM_TRY
7487132943Sgshapiro		s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
7488132943Sgshapiro	SM_FINALLY
7489132943Sgshapiro		sm_free(nbuf);
7490132943Sgshapiro	SM_END_TRY
7491132943Sgshapiro	return s;
7492132943Sgshapiro}
7493132943Sgshapiro
7494132943Sgshapiro/*
7495132943Sgshapiro**  SOCKET_MAP_CLOSE -- close the socket
7496132943Sgshapiro*/
7497132943Sgshapiro
7498132943Sgshapirovoid
7499132943Sgshapirosocket_map_close(map)
7500132943Sgshapiro	MAP *map;
7501132943Sgshapiro{
7502132943Sgshapiro	STAB *s;
7503132943Sgshapiro	MAP *smap;
7504132943Sgshapiro
7505132943Sgshapiro	if (tTd(38, 20))
7506132943Sgshapiro		sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
7507132943Sgshapiro			(long) CurrentPid);
7508132943Sgshapiro
7509132943Sgshapiro	/* Check if already closed */
7510132943Sgshapiro	if (map->map_db1 == NULL)
7511132943Sgshapiro	{
7512132943Sgshapiro		if (tTd(38, 20))
7513132943Sgshapiro			sm_dprintf("socket_map_close(%s) already closed\n",
7514132943Sgshapiro				map->map_file);
7515132943Sgshapiro		return;
7516132943Sgshapiro	}
7517132943Sgshapiro	sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
7518132943Sgshapiro
7519132943Sgshapiro	/* Mark all the maps that share the connection as closed */
7520132943Sgshapiro	s = socket_map_findconn(map->map_file);
7521132943Sgshapiro	smap = s->s_socketmap;
7522132943Sgshapiro	while (smap != NULL)
7523132943Sgshapiro	{
7524132943Sgshapiro		MAP *next;
7525132943Sgshapiro
7526132943Sgshapiro		if (tTd(38, 2) && smap != map)
7527132943Sgshapiro			sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7528132943Sgshapiro				map->map_mname, smap->map_mname);
7529132943Sgshapiro
7530132943Sgshapiro		smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
7531132943Sgshapiro		smap->map_db1 = NULL;
7532132943Sgshapiro		next = smap->socket_map_next;
7533132943Sgshapiro		smap->socket_map_next = NULL;
7534132943Sgshapiro		smap = next;
7535132943Sgshapiro	}
7536132943Sgshapiro	s->s_socketmap = NULL;
7537132943Sgshapiro}
7538132943Sgshapiro
7539132943Sgshapiro/*
7540132943Sgshapiro** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7541132943Sgshapiro*/
7542132943Sgshapiro
7543132943Sgshapirochar *
7544132943Sgshapirosocket_map_lookup(map, name, av, statp)
7545132943Sgshapiro	MAP *map;
7546132943Sgshapiro	char *name;
7547132943Sgshapiro	char **av;
7548132943Sgshapiro	int *statp;
7549132943Sgshapiro{
7550132943Sgshapiro	unsigned int nettolen, replylen, recvlen;
7551132943Sgshapiro	char *replybuf, *rval, *value, *status;
7552132943Sgshapiro	SM_FILE_T *f;
7553132943Sgshapiro
7554132943Sgshapiro	replybuf = NULL;
7555132943Sgshapiro	rval = NULL;
7556132943Sgshapiro	f = (SM_FILE_T *)map->map_db1;
7557132943Sgshapiro	if (tTd(38, 20))
7558132943Sgshapiro		sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7559132943Sgshapiro			map->map_mname, name, map->map_file);
7560132943Sgshapiro
7561132943Sgshapiro	nettolen = strlen(map->map_mname) + 1 + strlen(name);
7562132943Sgshapiro	SM_ASSERT(nettolen > strlen(map->map_mname));
7563132943Sgshapiro	SM_ASSERT(nettolen > strlen(name));
7564132943Sgshapiro	if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
7565132943Sgshapiro			   nettolen, map->map_mname, name) == SM_IO_EOF) ||
7566132943Sgshapiro	    (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
7567132943Sgshapiro	    (sm_io_error(f)))
7568132943Sgshapiro	{
7569132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
7570132943Sgshapiro			map->map_mname);
7571132943Sgshapiro		*statp = EX_TEMPFAIL;
7572132943Sgshapiro		goto errcl;
7573132943Sgshapiro	}
7574132943Sgshapiro
7575132943Sgshapiro	if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
7576132943Sgshapiro	{
7577132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
7578132943Sgshapiro			map->map_mname);
7579132943Sgshapiro		*statp = EX_TEMPFAIL;
7580132943Sgshapiro		goto errcl;
7581132943Sgshapiro	}
7582132943Sgshapiro	if (replylen > SOCKETMAP_MAXL)
7583132943Sgshapiro	{
7584132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
7585132943Sgshapiro			   map->map_mname, replylen);
7586132943Sgshapiro		*statp = EX_TEMPFAIL;
7587132943Sgshapiro		goto errcl;
7588132943Sgshapiro	}
7589132943Sgshapiro	if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
7590132943Sgshapiro	{
7591132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
7592132943Sgshapiro			map->map_mname);
7593132943Sgshapiro		*statp = EX_TEMPFAIL;
7594132943Sgshapiro		goto error;
7595132943Sgshapiro	}
7596132943Sgshapiro
7597132943Sgshapiro	replybuf = (char *) sm_malloc(replylen + 1);
7598132943Sgshapiro	if (replybuf == NULL)
7599132943Sgshapiro	{
7600132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
7601132943Sgshapiro			map->map_mname, replylen + 1);
7602132943Sgshapiro		*statp = EX_OSERR;
7603132943Sgshapiro		goto error;
7604132943Sgshapiro	}
7605132943Sgshapiro
7606132943Sgshapiro	recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
7607132943Sgshapiro	if (recvlen < replylen)
7608132943Sgshapiro	{
7609132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
7610132943Sgshapiro			   map->map_mname, recvlen, replylen);
7611132943Sgshapiro		*statp = EX_TEMPFAIL;
7612132943Sgshapiro		goto errcl;
7613132943Sgshapiro	}
7614132943Sgshapiro	if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
7615132943Sgshapiro	{
7616132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
7617132943Sgshapiro			map->map_mname);
7618132943Sgshapiro		*statp = EX_TEMPFAIL;
7619132943Sgshapiro		goto errcl;
7620132943Sgshapiro	}
7621132943Sgshapiro	status = replybuf;
7622132943Sgshapiro	replybuf[recvlen] = '\0';
7623132943Sgshapiro	value = strchr(replybuf, ' ');
7624132943Sgshapiro	if (value != NULL)
7625132943Sgshapiro	{
7626132943Sgshapiro		*value = '\0';
7627132943Sgshapiro		value++;
7628132943Sgshapiro	}
7629132943Sgshapiro	if (strcmp(status, "OK") == 0)
7630132943Sgshapiro	{
7631132943Sgshapiro		*statp = EX_OK;
7632132943Sgshapiro
7633132943Sgshapiro		/* collect the return value */
7634132943Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
7635132943Sgshapiro			rval = map_rewrite(map, name, strlen(name), NULL);
7636132943Sgshapiro		else
7637132943Sgshapiro			rval = map_rewrite(map, value, strlen(value), av);
7638132943Sgshapiro	}
7639132943Sgshapiro	else if (strcmp(status, "NOTFOUND") == 0)
7640132943Sgshapiro	{
7641132943Sgshapiro		*statp = EX_NOTFOUND;
7642132943Sgshapiro		if (tTd(38, 20))
7643132943Sgshapiro			sm_dprintf("socket_map_lookup(%s): %s not found\n",
7644132943Sgshapiro				map->map_mname, name);
7645132943Sgshapiro	}
7646132943Sgshapiro	else
7647132943Sgshapiro	{
7648132943Sgshapiro		if (tTd(38, 5))
7649132943Sgshapiro			sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
7650132943Sgshapiro				map->map_mname, name, status,
7651132943Sgshapiro				value ? value : "");
7652132943Sgshapiro		if ((strcmp(status, "TEMP") == 0) ||
7653132943Sgshapiro		    (strcmp(status, "TIMEOUT") == 0))
7654132943Sgshapiro			*statp = EX_TEMPFAIL;
7655132943Sgshapiro		else if(strcmp(status, "PERM") == 0)
7656132943Sgshapiro			*statp = EX_UNAVAILABLE;
7657132943Sgshapiro		else
7658132943Sgshapiro			*statp = EX_PROTOCOL;
7659132943Sgshapiro	}
7660132943Sgshapiro
7661132943Sgshapiro	if (replybuf != NULL)
7662132943Sgshapiro		sm_free(replybuf);
7663132943Sgshapiro	return rval;
7664132943Sgshapiro
7665132943Sgshapiro  errcl:
7666132943Sgshapiro	socket_map_close(map);
7667132943Sgshapiro  error:
7668132943Sgshapiro	if (replybuf != NULL)
7669132943Sgshapiro		sm_free(replybuf);
7670132943Sgshapiro	return rval;
7671132943Sgshapiro}
7672132943Sgshapiro#endif /* SOCKETMAP */
7673