map.c revision 141858
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
16141858SgshapiroSM_RCSID("@(#)$Id: map.c,v 8.666 2004/08/17 16:50:19 gshapiro 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;
4434141858Sgshapiro
4435141858Sgshapiro		/* Set in case needed in future code */
4436132943Sgshapiro		attrssetup = true;
4437141858Sgshapiro
443894334Sgshapiro		if (recurse && !normalseen)
443990792Sgshapiro		{
444094334Sgshapiro			syserr("LDAP recursion requested in %s but no returnable attribute given",
444190792Sgshapiro			       map->map_mname);
444290792Sgshapiro			return false;
444390792Sgshapiro		}
444490792Sgshapiro		if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
444590792Sgshapiro		{
444690792Sgshapiro			syserr("LDAP recursion requested in %s can not be used with -n",
444790792Sgshapiro			       map->map_mname);
444890792Sgshapiro			return false;
444990792Sgshapiro		}
445038032Speter	}
445138032Speter	map->map_db1 = (ARBPTR_T) lmap;
445290792Sgshapiro	return true;
445338032Speter}
445438032Speter
445564562Sgshapiro/*
445664562Sgshapiro**  LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
445764562Sgshapiro**
445864562Sgshapiro**	Parameters:
445964562Sgshapiro**		spec -- map argument string from LDAPDefaults option
446064562Sgshapiro**
446164562Sgshapiro**	Returns:
446264562Sgshapiro**		None.
446364562Sgshapiro*/
446464562Sgshapiro
446564562Sgshapirovoid
446664562Sgshapiroldapmap_set_defaults(spec)
446764562Sgshapiro	char *spec;
446864562Sgshapiro{
446973188Sgshapiro	STAB *class;
447064562Sgshapiro	MAP map;
447164562Sgshapiro
447264562Sgshapiro	/* Allocate and set the default values */
447364562Sgshapiro	if (LDAPDefaults == NULL)
447490792Sgshapiro		LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
447590792Sgshapiro	sm_ldap_clear(LDAPDefaults);
447664562Sgshapiro
447764562Sgshapiro	memset(&map, '\0', sizeof map);
447873188Sgshapiro
447973188Sgshapiro	/* look up the class */
448073188Sgshapiro	class = stab("ldap", ST_MAPCLASS, ST_FIND);
448173188Sgshapiro	if (class == NULL)
448273188Sgshapiro	{
448373188Sgshapiro		syserr("readcf: LDAPDefaultSpec: class ldap not available");
448473188Sgshapiro		return;
448573188Sgshapiro	}
448673188Sgshapiro	map.map_class = &class->s_mapclass;
448764562Sgshapiro	map.map_db1 = (ARBPTR_T) LDAPDefaults;
448873188Sgshapiro	map.map_mname = "O LDAPDefaultSpec";
448964562Sgshapiro
449064562Sgshapiro	(void) ldapmap_parseargs(&map, spec);
449164562Sgshapiro
449264562Sgshapiro	/* These should never be set in LDAPDefaults */
449364562Sgshapiro	if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
449464562Sgshapiro	    map.map_spacesub != SpaceSub ||
449564562Sgshapiro	    map.map_app != NULL ||
449664562Sgshapiro	    map.map_tapp != NULL)
449764562Sgshapiro	{
449864562Sgshapiro		syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
449990792Sgshapiro		SM_FREE_CLR(map.map_app);
450090792Sgshapiro		SM_FREE_CLR(map.map_tapp);
450164562Sgshapiro	}
450264562Sgshapiro
450364562Sgshapiro	if (LDAPDefaults->ldap_filter != NULL)
450464562Sgshapiro	{
450564562Sgshapiro		syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
450694334Sgshapiro
450764562Sgshapiro		/* don't free, it isn't malloc'ed in parseargs */
450864562Sgshapiro		LDAPDefaults->ldap_filter = NULL;
450964562Sgshapiro	}
451064562Sgshapiro
451164562Sgshapiro	if (LDAPDefaults->ldap_attr[0] != NULL)
451264562Sgshapiro	{
451364562Sgshapiro		syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
451464562Sgshapiro		/* don't free, they aren't malloc'ed in parseargs */
451564562Sgshapiro		LDAPDefaults->ldap_attr[0] = NULL;
451664562Sgshapiro	}
451764562Sgshapiro}
451864562Sgshapiro#endif /* LDAPMAP */
451990792Sgshapiro/*
452064562Sgshapiro**  PH map
452164562Sgshapiro*/
452264562Sgshapiro
452390792Sgshapiro#if PH_MAP
452464562Sgshapiro
452564562Sgshapiro/*
452664562Sgshapiro**  Support for the CCSO Nameserver (ph/qi).
452764562Sgshapiro**  This code is intended to replace the so-called "ph mailer".
452864562Sgshapiro**  Contributed by Mark D. Roth <roth@uiuc.edu>.  Contact him for support.
452964562Sgshapiro*/
453064562Sgshapiro
453190792Sgshapiro/* what version of the ph map code we're running */
4532110560Sgshapirostatic char phmap_id[128];
453364562Sgshapiro
453490792Sgshapiro/* sendmail version for phmap id string */
453590792Sgshapiroextern const char Version[];
453690792Sgshapiro
4537132943Sgshapiro/* assume we're using nph-1.2.x if not specified */
4538110560Sgshapiro# ifndef NPH_VERSION
4539132943Sgshapiro#  define NPH_VERSION		10200
4540110560Sgshapiro# endif
4541110560Sgshapiro
4542110560Sgshapiro/* compatibility for versions older than nph-1.2.0 */
4543110560Sgshapiro# if NPH_VERSION < 10200
4544110560Sgshapiro#  define PH_OPEN_ROUNDROBIN	PH_ROUNDROBIN
4545110560Sgshapiro#  define PH_OPEN_DONTID	PH_DONTID
4546110560Sgshapiro#  define PH_CLOSE_FAST		PH_FASTCLOSE
4547110560Sgshapiro#  define PH_ERR_DATAERR	PH_DATAERR
4548110560Sgshapiro#  define PH_ERR_NOMATCH	PH_NOMATCH
4549110560Sgshapiro# endif /* NPH_VERSION < 10200 */
4550110560Sgshapiro
455164562Sgshapiro/*
455264562Sgshapiro**  PH_MAP_PARSEARGS -- parse ph map definition args.
455364562Sgshapiro*/
455464562Sgshapiro
455564562Sgshapirobool
455664562Sgshapiroph_map_parseargs(map, args)
455764562Sgshapiro	MAP *map;
455864562Sgshapiro	char *args;
455964562Sgshapiro{
456090792Sgshapiro	register bool done;
456190792Sgshapiro	register char *p = args;
456264562Sgshapiro	PH_MAP_STRUCT *pmap = NULL;
456364562Sgshapiro
456490792Sgshapiro	/* initialize version string */
456590792Sgshapiro	(void) sm_snprintf(phmap_id, sizeof phmap_id,
456690792Sgshapiro			   "sendmail-%s phmap-20010529 libphclient-%s",
456790792Sgshapiro			   Version, libphclient_version);
456890792Sgshapiro
456964562Sgshapiro	pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap);
457064562Sgshapiro
457164562Sgshapiro	/* defaults */
457264562Sgshapiro	pmap->ph_servers = NULL;
457364562Sgshapiro	pmap->ph_field_list = NULL;
457490792Sgshapiro	pmap->ph = NULL;
457564562Sgshapiro	pmap->ph_timeout = 0;
457690792Sgshapiro	pmap->ph_fastclose = 0;
457764562Sgshapiro
457864562Sgshapiro	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
457964562Sgshapiro	for (;;)
458064562Sgshapiro	{
458164562Sgshapiro		while (isascii(*p) && isspace(*p))
458264562Sgshapiro			p++;
458364562Sgshapiro		if (*p != '-')
458464562Sgshapiro			break;
458564562Sgshapiro		switch (*++p)
458664562Sgshapiro		{
458764562Sgshapiro		  case 'N':
458864562Sgshapiro			map->map_mflags |= MF_INCLNULL;
458964562Sgshapiro			map->map_mflags &= ~MF_TRY0NULL;
459064562Sgshapiro			break;
459164562Sgshapiro
459264562Sgshapiro		  case 'O':
459364562Sgshapiro			map->map_mflags &= ~MF_TRY1NULL;
459464562Sgshapiro			break;
459564562Sgshapiro
459664562Sgshapiro		  case 'o':
459764562Sgshapiro			map->map_mflags |= MF_OPTIONAL;
459864562Sgshapiro			break;
459964562Sgshapiro
460064562Sgshapiro		  case 'f':
460164562Sgshapiro			map->map_mflags |= MF_NOFOLDCASE;
460264562Sgshapiro			break;
460364562Sgshapiro
460464562Sgshapiro		  case 'm':
460564562Sgshapiro			map->map_mflags |= MF_MATCHONLY;
460664562Sgshapiro			break;
460764562Sgshapiro
460864562Sgshapiro		  case 'A':
460964562Sgshapiro			map->map_mflags |= MF_APPEND;
461064562Sgshapiro			break;
461164562Sgshapiro
461264562Sgshapiro		  case 'q':
461364562Sgshapiro			map->map_mflags |= MF_KEEPQUOTES;
461464562Sgshapiro			break;
461564562Sgshapiro
461664562Sgshapiro		  case 't':
461764562Sgshapiro			map->map_mflags |= MF_NODEFER;
461864562Sgshapiro			break;
461964562Sgshapiro
462064562Sgshapiro		  case 'a':
462164562Sgshapiro			map->map_app = ++p;
462264562Sgshapiro			break;
462364562Sgshapiro
462464562Sgshapiro		  case 'T':
462564562Sgshapiro			map->map_tapp = ++p;
462664562Sgshapiro			break;
462764562Sgshapiro
462864562Sgshapiro		  case 'l':
462964562Sgshapiro			while (isascii(*++p) && isspace(*p))
463064562Sgshapiro				continue;
463164562Sgshapiro			pmap->ph_timeout = atoi(p);
463264562Sgshapiro			break;
463364562Sgshapiro
463464562Sgshapiro		  case 'S':
463564562Sgshapiro			map->map_spacesub = *++p;
463664562Sgshapiro			break;
463764562Sgshapiro
463864562Sgshapiro		  case 'D':
463964562Sgshapiro			map->map_mflags |= MF_DEFER;
464064562Sgshapiro			break;
464164562Sgshapiro
464264562Sgshapiro		  case 'h':		/* PH server list */
464364562Sgshapiro			while (isascii(*++p) && isspace(*p))
464464562Sgshapiro				continue;
464564562Sgshapiro			pmap->ph_servers = p;
464664562Sgshapiro			break;
464764562Sgshapiro
464890792Sgshapiro		  case 'k':		/* fields to search for */
464964562Sgshapiro			while (isascii(*++p) && isspace(*p))
465064562Sgshapiro				continue;
465164562Sgshapiro			pmap->ph_field_list = p;
465264562Sgshapiro			break;
465364562Sgshapiro
465464562Sgshapiro		  default:
465590792Sgshapiro			syserr("ph_map_parseargs: unknown option -%c", *p);
465664562Sgshapiro		}
465764562Sgshapiro
465864562Sgshapiro		/* try to account for quoted strings */
465964562Sgshapiro		done = isascii(*p) && isspace(*p);
466064562Sgshapiro		while (*p != '\0' && !done)
466164562Sgshapiro		{
466264562Sgshapiro			if (*p == '"')
466364562Sgshapiro			{
466464562Sgshapiro				while (*++p != '"' && *p != '\0')
466564562Sgshapiro					continue;
466664562Sgshapiro				if (*p != '\0')
466764562Sgshapiro					p++;
466864562Sgshapiro			}
466964562Sgshapiro			else
467064562Sgshapiro				p++;
467164562Sgshapiro			done = isascii(*p) && isspace(*p);
467264562Sgshapiro		}
467364562Sgshapiro
467464562Sgshapiro		if (*p != '\0')
467564562Sgshapiro			*p++ = '\0';
467664562Sgshapiro	}
467764562Sgshapiro
467864562Sgshapiro	if (map->map_app != NULL)
467964562Sgshapiro		map->map_app = newstr(ph_map_dequote(map->map_app));
468064562Sgshapiro	if (map->map_tapp != NULL)
468164562Sgshapiro		map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
468264562Sgshapiro
468364562Sgshapiro	if (pmap->ph_field_list != NULL)
468464562Sgshapiro		pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
468564562Sgshapiro
468664562Sgshapiro	if (pmap->ph_servers != NULL)
468764562Sgshapiro		pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
468864562Sgshapiro	else
468964562Sgshapiro	{
469064562Sgshapiro		syserr("ph_map_parseargs: -h flag is required");
469190792Sgshapiro		return false;
469264562Sgshapiro	}
469364562Sgshapiro
469464562Sgshapiro	map->map_db1 = (ARBPTR_T) pmap;
469590792Sgshapiro	return true;
469664562Sgshapiro}
469764562Sgshapiro
469864562Sgshapiro/*
469964562Sgshapiro**  PH_MAP_CLOSE -- close the connection to the ph server
470064562Sgshapiro*/
470164562Sgshapiro
470290792Sgshapirovoid
470390792Sgshapiroph_map_close(map)
470464562Sgshapiro	MAP *map;
470564562Sgshapiro{
470664562Sgshapiro	PH_MAP_STRUCT *pmap;
470764562Sgshapiro
470864562Sgshapiro	pmap = (PH_MAP_STRUCT *)map->map_db1;
470990792Sgshapiro	if (tTd(38, 9))
471094334Sgshapiro		sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
471190792Sgshapiro			   map->map_mname, pmap->ph_fastclose);
471264562Sgshapiro
471390792Sgshapiro
471490792Sgshapiro	if (pmap->ph != NULL)
471564562Sgshapiro	{
471690792Sgshapiro		ph_set_sendhook(pmap->ph, NULL);
471790792Sgshapiro		ph_set_recvhook(pmap->ph, NULL);
471890792Sgshapiro		ph_close(pmap->ph, pmap->ph_fastclose);
471964562Sgshapiro	}
472090792Sgshapiro
472164562Sgshapiro	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
472264562Sgshapiro}
472364562Sgshapiro
472464562Sgshapirostatic jmp_buf  PHTimeout;
472564562Sgshapiro
472664562Sgshapiro/* ARGSUSED */
472764562Sgshapirostatic void
472890792Sgshapiroph_timeout(unused)
472990792Sgshapiro	int unused;
473064562Sgshapiro{
473177349Sgshapiro	/*
473277349Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
473377349Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
473477349Sgshapiro	**	DOING.
473577349Sgshapiro	*/
473677349Sgshapiro
473777349Sgshapiro	errno = ETIMEDOUT;
473864562Sgshapiro	longjmp(PHTimeout, 1);
473964562Sgshapiro}
474064562Sgshapiro
474190792Sgshapirostatic void
4742110560Sgshapiro#if NPH_VERSION >= 10200
4743110560Sgshapiroph_map_send_debug(appdata, text)
4744110560Sgshapiro	void *appdata;
4745110560Sgshapiro#else
474690792Sgshapiroph_map_send_debug(text)
4747110560Sgshapiro#endif
474890792Sgshapiro	char *text;
474964562Sgshapiro{
475090792Sgshapiro	if (LogLevel > 9)
475190792Sgshapiro		sm_syslog(LOG_NOTICE, CurEnv->e_id,
475290792Sgshapiro			  "ph_map_send_debug: ==> %s", text);
475390792Sgshapiro	if (tTd(38, 20))
475490792Sgshapiro		sm_dprintf("ph_map_send_debug: ==> %s\n", text);
475590792Sgshapiro}
475664562Sgshapiro
475790792Sgshapirostatic void
4758110560Sgshapiro#if NPH_VERSION >= 10200
4759110560Sgshapiroph_map_recv_debug(appdata, text)
4760110560Sgshapiro	void *appdata;
4761110560Sgshapiro#else
476290792Sgshapiroph_map_recv_debug(text)
4763110560Sgshapiro#endif
476490792Sgshapiro	char *text;
476590792Sgshapiro{
476690792Sgshapiro	if (LogLevel > 10)
476790792Sgshapiro		sm_syslog(LOG_NOTICE, CurEnv->e_id,
476890792Sgshapiro			  "ph_map_recv_debug: <== %s", text);
476990792Sgshapiro	if (tTd(38, 21))
477090792Sgshapiro		sm_dprintf("ph_map_recv_debug: <== %s\n", text);
477164562Sgshapiro}
477264562Sgshapiro
477390792Sgshapiro/*
477464562Sgshapiro**  PH_MAP_OPEN -- sub for opening PH map
477564562Sgshapiro*/
477664562Sgshapirobool
477764562Sgshapiroph_map_open(map, mode)
477864562Sgshapiro	MAP *map;
477964562Sgshapiro	int mode;
478064562Sgshapiro{
478190792Sgshapiro	PH_MAP_STRUCT *pmap;
478290792Sgshapiro	register SM_EVENT *ev = NULL;
478364562Sgshapiro	int save_errno = 0;
478490792Sgshapiro	char *hostlist, *host;
478564562Sgshapiro
478664562Sgshapiro	if (tTd(38, 2))
478790792Sgshapiro		sm_dprintf("ph_map_open(%s)\n", map->map_mname);
478864562Sgshapiro
478964562Sgshapiro	mode &= O_ACCMODE;
479064562Sgshapiro	if (mode != O_RDONLY)
479164562Sgshapiro	{
479264562Sgshapiro		/* issue a pseudo-error message */
479390792Sgshapiro		errno = SM_EMAPCANTWRITE;
479490792Sgshapiro		return false;
479564562Sgshapiro	}
479664562Sgshapiro
479766494Sgshapiro	if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
479866494Sgshapiro	    bitset(MF_DEFER, map->map_mflags))
479966494Sgshapiro	{
480066494Sgshapiro		if (tTd(9, 1))
480190792Sgshapiro			sm_dprintf("ph_map_open(%s) => DEFERRED\n",
480290792Sgshapiro				   map->map_mname);
480366494Sgshapiro
480466494Sgshapiro		/*
480590792Sgshapiro		**  Unset MF_DEFER here so that map_lookup() returns
480690792Sgshapiro		**  a temporary failure using the bogus map and
480790792Sgshapiro		**  map->map_tapp instead of the default permanent error.
480866494Sgshapiro		*/
480966494Sgshapiro
481066494Sgshapiro		map->map_mflags &= ~MF_DEFER;
481190792Sgshapiro		return false;
481266494Sgshapiro	}
481366494Sgshapiro
481464562Sgshapiro	pmap = (PH_MAP_STRUCT *)map->map_db1;
481590792Sgshapiro	pmap->ph_fastclose = 0;		/* refresh field for reopen */
481664562Sgshapiro
481790792Sgshapiro	/* try each host in the list */
481864562Sgshapiro	hostlist = newstr(pmap->ph_servers);
481990792Sgshapiro	for (host = strtok(hostlist, " ");
482090792Sgshapiro	     host != NULL;
482190792Sgshapiro	     host = strtok(NULL, " "))
482264562Sgshapiro	{
482390792Sgshapiro		/* set timeout */
482464562Sgshapiro		if (pmap->ph_timeout != 0)
482564562Sgshapiro		{
482664562Sgshapiro			if (setjmp(PHTimeout) != 0)
482764562Sgshapiro			{
482864562Sgshapiro				ev = NULL;
482964562Sgshapiro				if (LogLevel > 1)
483064562Sgshapiro					sm_syslog(LOG_NOTICE, CurEnv->e_id,
483164562Sgshapiro						  "timeout connecting to PH server %.100s",
483290792Sgshapiro						  host);
483364562Sgshapiro				errno = ETIMEDOUT;
483464562Sgshapiro				goto ph_map_open_abort;
483564562Sgshapiro			}
483690792Sgshapiro			ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
483764562Sgshapiro		}
483890792Sgshapiro
483990792Sgshapiro		/* open connection to server */
4840110560Sgshapiro		if (ph_open(&(pmap->ph), host,
4841110560Sgshapiro			    PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
4842110560Sgshapiro			    ph_map_send_debug, ph_map_recv_debug
4843110560Sgshapiro#if NPH_VERSION >= 10200
4844110560Sgshapiro			    , NULL
4845110560Sgshapiro#endif
4846110560Sgshapiro			    ) == 0
4847110560Sgshapiro		    && ph_id(pmap->ph, phmap_id) == 0)
484864562Sgshapiro		{
484964562Sgshapiro			if (ev != NULL)
485090792Sgshapiro				sm_clrevent(ev);
485190792Sgshapiro			sm_free(hostlist); /* XXX */
485290792Sgshapiro			return true;
485364562Sgshapiro		}
485490792Sgshapiro
485564562Sgshapiro  ph_map_open_abort:
485690792Sgshapiro		save_errno = errno;
485764562Sgshapiro		if (ev != NULL)
485890792Sgshapiro			sm_clrevent(ev);
4859110560Sgshapiro		pmap->ph_fastclose = PH_CLOSE_FAST;
486090792Sgshapiro		ph_map_close(map);
486190792Sgshapiro		errno = save_errno;
486290792Sgshapiro	}
486364562Sgshapiro
486466494Sgshapiro	if (bitset(MF_NODEFER, map->map_mflags))
486564562Sgshapiro	{
486666494Sgshapiro		if (errno == 0)
486764562Sgshapiro			errno = EAGAIN;
486866494Sgshapiro		syserr("ph_map_open: %s: cannot connect to PH server",
486966494Sgshapiro		       map->map_mname);
487064562Sgshapiro	}
487166494Sgshapiro	else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
487264562Sgshapiro		sm_syslog(LOG_NOTICE, CurEnv->e_id,
487366494Sgshapiro			  "ph_map_open: %s: cannot connect to PH server",
487466494Sgshapiro			  map->map_mname);
487590792Sgshapiro	sm_free(hostlist); /* XXX */
487690792Sgshapiro	return false;
487764562Sgshapiro}
487864562Sgshapiro
487964562Sgshapiro/*
488064562Sgshapiro**  PH_MAP_LOOKUP -- look up key from ph server
488164562Sgshapiro*/
488264562Sgshapiro
488364562Sgshapirochar *
488464562Sgshapiroph_map_lookup(map, key, args, pstat)
488564562Sgshapiro	MAP *map;
488664562Sgshapiro	char *key;
488764562Sgshapiro	char **args;
488864562Sgshapiro	int *pstat;
488964562Sgshapiro{
489090792Sgshapiro	int i, save_errno = 0;
489190792Sgshapiro	register SM_EVENT *ev = NULL;
489264562Sgshapiro	PH_MAP_STRUCT *pmap;
489390792Sgshapiro	char *value = NULL;
489464562Sgshapiro
489564562Sgshapiro	pmap = (PH_MAP_STRUCT *)map->map_db1;
489664562Sgshapiro
489764562Sgshapiro	*pstat = EX_OK;
489864562Sgshapiro
489990792Sgshapiro	/* set timeout */
490064562Sgshapiro	if (pmap->ph_timeout != 0)
490164562Sgshapiro	{
490264562Sgshapiro		if (setjmp(PHTimeout) != 0)
490364562Sgshapiro		{
490464562Sgshapiro			ev = NULL;
490564562Sgshapiro			if (LogLevel > 1)
490664562Sgshapiro				sm_syslog(LOG_NOTICE, CurEnv->e_id,
490764562Sgshapiro					  "timeout during PH lookup of %.100s",
490864562Sgshapiro					  key);
490964562Sgshapiro			errno = ETIMEDOUT;
491064562Sgshapiro			*pstat = EX_TEMPFAIL;
491164562Sgshapiro			goto ph_map_lookup_abort;
491264562Sgshapiro		}
491390792Sgshapiro		ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
491464562Sgshapiro	}
491564562Sgshapiro
491690792Sgshapiro	/* perform lookup */
491790792Sgshapiro	i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
491890792Sgshapiro	if (i == -1)
491990792Sgshapiro		*pstat = EX_TEMPFAIL;
4920110560Sgshapiro	else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
492190792Sgshapiro		*pstat = EX_UNAVAILABLE;
492264562Sgshapiro
492364562Sgshapiro  ph_map_lookup_abort:
492464562Sgshapiro	if (ev != NULL)
492590792Sgshapiro		sm_clrevent(ev);
492664562Sgshapiro
492764562Sgshapiro	/*
492890792Sgshapiro	**  Close the connection if the timer popped
492964562Sgshapiro	**  or we got a temporary PH error
493064562Sgshapiro	*/
493164562Sgshapiro
493264562Sgshapiro	if (*pstat == EX_TEMPFAIL)
493390792Sgshapiro	{
493490792Sgshapiro		save_errno = errno;
4935110560Sgshapiro		pmap->ph_fastclose = PH_CLOSE_FAST;
493690792Sgshapiro		ph_map_close(map);
493790792Sgshapiro		errno = save_errno;
493890792Sgshapiro	}
493964562Sgshapiro
494064562Sgshapiro	if (*pstat == EX_OK)
494164562Sgshapiro	{
494264562Sgshapiro		if (tTd(38,20))
494390792Sgshapiro			sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
494464562Sgshapiro
494564562Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
494690792Sgshapiro			return map_rewrite(map, key, strlen(key), NULL);
494764562Sgshapiro		else
494890792Sgshapiro			return map_rewrite(map, value, strlen(value), args);
494964562Sgshapiro	}
495064562Sgshapiro
495164562Sgshapiro	return NULL;
495264562Sgshapiro}
495364562Sgshapiro#endif /* PH_MAP */
495490792Sgshapiro/*
495542575Speter**  syslog map
495638032Speter*/
495738032Speter
495838032Speter#define map_prio	map_lockfd	/* overload field */
495938032Speter
496038032Speter/*
496142575Speter**  SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
496238032Speter*/
496338032Speter
496438032Speterbool
496538032Spetersyslog_map_parseargs(map, args)
496638032Speter	MAP *map;
496738032Speter	char *args;
496838032Speter{
496938032Speter	char *p = args;
497038032Speter	char *priority = NULL;
497138032Speter
497264562Sgshapiro	/* there is no check whether there is really an argument */
497364562Sgshapiro	while (*p != '\0')
497438032Speter	{
497538032Speter		while (isascii(*p) && isspace(*p))
497638032Speter			p++;
497738032Speter		if (*p != '-')
497838032Speter			break;
497964562Sgshapiro		++p;
498064562Sgshapiro		if (*p == 'D')
498164562Sgshapiro		{
498264562Sgshapiro			map->map_mflags |= MF_DEFER;
498364562Sgshapiro			++p;
498464562Sgshapiro		}
498564562Sgshapiro		else if (*p == 'S')
498664562Sgshapiro		{
498764562Sgshapiro			map->map_spacesub = *++p;
498864562Sgshapiro			if (*p != '\0')
498964562Sgshapiro				p++;
499064562Sgshapiro		}
499164562Sgshapiro		else if (*p == 'L')
499264562Sgshapiro		{
499364562Sgshapiro			while (*++p != '\0' && isascii(*p) && isspace(*p))
499464562Sgshapiro				continue;
499564562Sgshapiro			if (*p == '\0')
499664562Sgshapiro				break;
499764562Sgshapiro			priority = p;
499864562Sgshapiro			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
499964562Sgshapiro				p++;
500064562Sgshapiro			if (*p != '\0')
500164562Sgshapiro				*p++ = '\0';
500264562Sgshapiro		}
500364562Sgshapiro		else
500464562Sgshapiro		{
500564562Sgshapiro			syserr("Illegal option %c map syslog", *p);
500664562Sgshapiro			++p;
500764562Sgshapiro		}
500838032Speter	}
500938032Speter
501038032Speter	if (priority == NULL)
501138032Speter		map->map_prio = LOG_INFO;
501238032Speter	else
501338032Speter	{
501490792Sgshapiro		if (sm_strncasecmp("LOG_", priority, 4) == 0)
501538032Speter			priority += 4;
501638032Speter
501738032Speter#ifdef LOG_EMERG
501890792Sgshapiro		if (sm_strcasecmp("EMERG", priority) == 0)
501938032Speter			map->map_prio = LOG_EMERG;
502038032Speter		else
502164562Sgshapiro#endif /* LOG_EMERG */
502238032Speter#ifdef LOG_ALERT
502390792Sgshapiro		if (sm_strcasecmp("ALERT", priority) == 0)
502438032Speter			map->map_prio = LOG_ALERT;
502538032Speter		else
502664562Sgshapiro#endif /* LOG_ALERT */
502738032Speter#ifdef LOG_CRIT
502890792Sgshapiro		if (sm_strcasecmp("CRIT", priority) == 0)
502938032Speter			map->map_prio = LOG_CRIT;
503038032Speter		else
503164562Sgshapiro#endif /* LOG_CRIT */
503238032Speter#ifdef LOG_ERR
503390792Sgshapiro		if (sm_strcasecmp("ERR", priority) == 0)
503438032Speter			map->map_prio = LOG_ERR;
503538032Speter		else
503664562Sgshapiro#endif /* LOG_ERR */
503738032Speter#ifdef LOG_WARNING
503890792Sgshapiro		if (sm_strcasecmp("WARNING", priority) == 0)
503938032Speter			map->map_prio = LOG_WARNING;
504038032Speter		else
504164562Sgshapiro#endif /* LOG_WARNING */
504238032Speter#ifdef LOG_NOTICE
504390792Sgshapiro		if (sm_strcasecmp("NOTICE", priority) == 0)
504438032Speter			map->map_prio = LOG_NOTICE;
504538032Speter		else
504664562Sgshapiro#endif /* LOG_NOTICE */
504738032Speter#ifdef LOG_INFO
504890792Sgshapiro		if (sm_strcasecmp("INFO", priority) == 0)
504938032Speter			map->map_prio = LOG_INFO;
505038032Speter		else
505164562Sgshapiro#endif /* LOG_INFO */
505238032Speter#ifdef LOG_DEBUG
505390792Sgshapiro		if (sm_strcasecmp("DEBUG", priority) == 0)
505438032Speter			map->map_prio = LOG_DEBUG;
505538032Speter		else
505664562Sgshapiro#endif /* LOG_DEBUG */
505738032Speter		{
505890792Sgshapiro			syserr("syslog_map_parseargs: Unknown priority %s",
505938032Speter			       priority);
506090792Sgshapiro			return false;
506138032Speter		}
506238032Speter	}
506390792Sgshapiro	return true;
506438032Speter}
506538032Speter
506638032Speter/*
506742575Speter**  SYSLOG_MAP_LOOKUP -- rewrite and syslog message.  Always return empty string
506838032Speter*/
506938032Speter
507038032Speterchar *
507138032Spetersyslog_map_lookup(map, string, args, statp)
507238032Speter	MAP *map;
507338032Speter	char *string;
507438032Speter	char **args;
507538032Speter	int *statp;
507638032Speter{
507738032Speter	char *ptr = map_rewrite(map, string, strlen(string), args);
507838032Speter
507938032Speter	if (ptr != NULL)
508038032Speter	{
508138032Speter		if (tTd(38, 20))
508290792Sgshapiro			sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
508364562Sgshapiro				map->map_mname, map->map_prio, ptr);
508438032Speter
508538032Speter		sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
508638032Speter	}
508738032Speter
508838032Speter	*statp = EX_OK;
508938032Speter	return "";
509038032Speter}
509138032Speter
509290792Sgshapiro/*
509338032Speter**  HESIOD Modules
509438032Speter*/
509538032Speter
509690792Sgshapiro#if HESIOD
509738032Speter
509838032Speterbool
509938032Speterhes_map_open(map, mode)
510038032Speter	MAP *map;
510138032Speter	int mode;
510238032Speter{
510338032Speter	if (tTd(38, 2))
510490792Sgshapiro		sm_dprintf("hes_map_open(%s, %s, %d)\n",
510538032Speter			map->map_mname, map->map_file, mode);
510638032Speter
510738032Speter	if (mode != O_RDONLY)
510838032Speter	{
510938032Speter		/* issue a pseudo-error message */
511090792Sgshapiro		errno = SM_EMAPCANTWRITE;
511190792Sgshapiro		return false;
511238032Speter	}
511338032Speter
511464562Sgshapiro# ifdef HESIOD_INIT
511538032Speter	if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
511690792Sgshapiro		return true;
511738032Speter
511838032Speter	if (!bitset(MF_OPTIONAL, map->map_mflags))
511994334Sgshapiro		syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
512090792Sgshapiro			sm_errstring(errno));
512190792Sgshapiro	return false;
512264562Sgshapiro# else /* HESIOD_INIT */
512338032Speter	if (hes_error() == HES_ER_UNINIT)
512438032Speter		hes_init();
512538032Speter	switch (hes_error())
512638032Speter	{
512738032Speter	  case HES_ER_OK:
512838032Speter	  case HES_ER_NOTFOUND:
512990792Sgshapiro		return true;
513038032Speter	}
513138032Speter
513238032Speter	if (!bitset(MF_OPTIONAL, map->map_mflags))
513394334Sgshapiro		syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
513438032Speter
513590792Sgshapiro	return false;
513664562Sgshapiro# endif /* HESIOD_INIT */
513738032Speter}
513838032Speter
513938032Speterchar *
514038032Speterhes_map_lookup(map, name, av, statp)
514138032Speter	MAP *map;
514238032Speter	char *name;
514338032Speter	char **av;
514438032Speter	int *statp;
514538032Speter{
514638032Speter	char **hp;
514738032Speter
514838032Speter	if (tTd(38, 20))
514990792Sgshapiro		sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
515038032Speter
515138032Speter	if (name[0] == '\\')
515238032Speter	{
515338032Speter		char *np;
515438032Speter		int nl;
515577349Sgshapiro		int save_errno;
515638032Speter		char nbuf[MAXNAME];
515738032Speter
515838032Speter		nl = strlen(name);
515938032Speter		if (nl < sizeof nbuf - 1)
516038032Speter			np = nbuf;
516138032Speter		else
516238032Speter			np = xalloc(strlen(name) + 2);
516338032Speter		np[0] = '\\';
516490792Sgshapiro		(void) sm_strlcpy(&np[1], name, (sizeof nbuf) - 1);
516564562Sgshapiro# ifdef HESIOD_INIT
516638032Speter		hp = hesiod_resolve(HesiodContext, np, map->map_file);
516764562Sgshapiro# else /* HESIOD_INIT */
516838032Speter		hp = hes_resolve(np, map->map_file);
516964562Sgshapiro# endif /* HESIOD_INIT */
517077349Sgshapiro		save_errno = errno;
517138032Speter		if (np != nbuf)
517290792Sgshapiro			sm_free(np); /* XXX */
517377349Sgshapiro		errno = save_errno;
517438032Speter	}
517538032Speter	else
517638032Speter	{
517764562Sgshapiro# ifdef HESIOD_INIT
517838032Speter		hp = hesiod_resolve(HesiodContext, name, map->map_file);
517964562Sgshapiro# else /* HESIOD_INIT */
518038032Speter		hp = hes_resolve(name, map->map_file);
518164562Sgshapiro# endif /* HESIOD_INIT */
518238032Speter	}
518364562Sgshapiro# ifdef HESIOD_INIT
518477349Sgshapiro	if (hp == NULL || *hp == NULL)
518538032Speter	{
518638032Speter		switch (errno)
518738032Speter		{
518838032Speter		  case ENOENT:
518938032Speter			  *statp = EX_NOTFOUND;
519038032Speter			  break;
519138032Speter		  case ECONNREFUSED:
519238032Speter			  *statp = EX_TEMPFAIL;
519338032Speter			  break;
519490792Sgshapiro		  case EMSGSIZE:
519538032Speter		  case ENOMEM:
519638032Speter		  default:
519738032Speter			  *statp = EX_UNAVAILABLE;
519838032Speter			  break;
519938032Speter		}
520082017Sgshapiro		if (hp != NULL)
520182017Sgshapiro			hesiod_free_list(HesiodContext, hp);
520238032Speter		return NULL;
520338032Speter	}
520464562Sgshapiro# else /* HESIOD_INIT */
520538032Speter	if (hp == NULL || hp[0] == NULL)
520638032Speter	{
520738032Speter		switch (hes_error())
520838032Speter		{
520938032Speter		  case HES_ER_OK:
521038032Speter			*statp = EX_OK;
521138032Speter			break;
521238032Speter
521338032Speter		  case HES_ER_NOTFOUND:
521438032Speter			*statp = EX_NOTFOUND;
521538032Speter			break;
521638032Speter
521738032Speter		  case HES_ER_CONFIG:
521838032Speter			*statp = EX_UNAVAILABLE;
521938032Speter			break;
522038032Speter
522138032Speter		  case HES_ER_NET:
522238032Speter			*statp = EX_TEMPFAIL;
522338032Speter			break;
522438032Speter		}
522538032Speter		return NULL;
522638032Speter	}
522764562Sgshapiro# endif /* HESIOD_INIT */
522838032Speter
522938032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
523038032Speter		return map_rewrite(map, name, strlen(name), NULL);
523138032Speter	else
523238032Speter		return map_rewrite(map, hp[0], strlen(hp[0]), av);
523338032Speter}
523438032Speter
523590792Sgshapiro/*
523690792Sgshapiro**  HES_MAP_CLOSE -- free the Hesiod context
523790792Sgshapiro*/
523890792Sgshapiro
523990792Sgshapirovoid
524090792Sgshapirohes_map_close(map)
524190792Sgshapiro	MAP *map;
524290792Sgshapiro{
524390792Sgshapiro	if (tTd(38, 20))
524490792Sgshapiro		sm_dprintf("hes_map_close(%s)\n", map->map_file);
524590792Sgshapiro
524690792Sgshapiro# ifdef HESIOD_INIT
524790792Sgshapiro	/* Free the hesiod context */
524890792Sgshapiro	if (HesiodContext != NULL)
524990792Sgshapiro	{
525090792Sgshapiro		hesiod_end(HesiodContext);
525190792Sgshapiro		HesiodContext = NULL;
525290792Sgshapiro	}
525390792Sgshapiro# endif /* HESIOD_INIT */
525490792Sgshapiro}
525590792Sgshapiro
525664562Sgshapiro#endif /* HESIOD */
525790792Sgshapiro/*
525838032Speter**  NeXT NETINFO Modules
525938032Speter*/
526038032Speter
526138032Speter#if NETINFO
526238032Speter
526338032Speter# define NETINFO_DEFAULT_DIR		"/aliases"
526438032Speter# define NETINFO_DEFAULT_PROPERTY	"members"
526538032Speter
526638032Speter/*
526738032Speter**  NI_MAP_OPEN -- open NetInfo Aliases
526838032Speter*/
526938032Speter
527038032Speterbool
527138032Speterni_map_open(map, mode)
527238032Speter	MAP *map;
527338032Speter	int mode;
527438032Speter{
527538032Speter	if (tTd(38, 2))
527690792Sgshapiro		sm_dprintf("ni_map_open(%s, %s, %d)\n",
527738032Speter			map->map_mname, map->map_file, mode);
527838032Speter	mode &= O_ACCMODE;
527938032Speter
528038032Speter	if (*map->map_file == '\0')
528138032Speter		map->map_file = NETINFO_DEFAULT_DIR;
528238032Speter
528338032Speter	if (map->map_valcolnm == NULL)
528438032Speter		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
528538032Speter
528690792Sgshapiro	if (map->map_coldelim == '\0')
528790792Sgshapiro	{
528890792Sgshapiro		if (bitset(MF_ALIAS, map->map_mflags))
528990792Sgshapiro			map->map_coldelim = ',';
529090792Sgshapiro		else if (bitset(MF_FILECLASS, map->map_mflags))
529190792Sgshapiro			map->map_coldelim = ' ';
529290792Sgshapiro	}
529390792Sgshapiro	return true;
529438032Speter}
529538032Speter
529638032Speter
529738032Speter/*
529838032Speter**  NI_MAP_LOOKUP -- look up a datum in NetInfo
529938032Speter*/
530038032Speter
530138032Speterchar *
530238032Speterni_map_lookup(map, name, av, statp)
530338032Speter	MAP *map;
530438032Speter	char *name;
530538032Speter	char **av;
530638032Speter	int *statp;
530738032Speter{
530838032Speter	char *res;
530938032Speter	char *propval;
531038032Speter
531138032Speter	if (tTd(38, 20))
531290792Sgshapiro		sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
531338032Speter
531438032Speter	propval = ni_propval(map->map_file, map->map_keycolnm, name,
531538032Speter			     map->map_valcolnm, map->map_coldelim);
531638032Speter
531738032Speter	if (propval == NULL)
531838032Speter		return NULL;
531938032Speter
532090792Sgshapiro	SM_TRY
532190792Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
532290792Sgshapiro			res = map_rewrite(map, name, strlen(name), NULL);
532390792Sgshapiro		else
532490792Sgshapiro			res = map_rewrite(map, propval, strlen(propval), av);
532590792Sgshapiro	SM_FINALLY
532690792Sgshapiro		sm_free(propval);
532790792Sgshapiro	SM_END_TRY
532838032Speter	return res;
532938032Speter}
533038032Speter
533138032Speter
533264562Sgshapirostatic bool
533338032Speterni_getcanonname(name, hbsize, statp)
533438032Speter	char *name;
533538032Speter	int hbsize;
533638032Speter	int *statp;
533738032Speter{
533838032Speter	char *vptr;
533938032Speter	char *ptr;
534038032Speter	char nbuf[MAXNAME + 1];
534138032Speter
534238032Speter	if (tTd(38, 20))
534390792Sgshapiro		sm_dprintf("ni_getcanonname(%s)\n", name);
534438032Speter
534590792Sgshapiro	if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
534638032Speter	{
534738032Speter		*statp = EX_UNAVAILABLE;
534890792Sgshapiro		return false;
534938032Speter	}
535073188Sgshapiro	(void) shorten_hostname(nbuf);
535138032Speter
535238032Speter	/* we only accept single token search key */
535338032Speter	if (strchr(nbuf, '.'))
535438032Speter	{
535538032Speter		*statp = EX_NOHOST;
535690792Sgshapiro		return false;
535738032Speter	}
535838032Speter
535938032Speter	/* Do the search */
536038032Speter	vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
536138032Speter
536238032Speter	if (vptr == NULL)
536338032Speter	{
536438032Speter		*statp = EX_NOHOST;
536590792Sgshapiro		return false;
536638032Speter	}
536738032Speter
536838032Speter	/* Only want the first machine name */
536938032Speter	if ((ptr = strchr(vptr, '\n')) != NULL)
537038032Speter		*ptr = '\0';
537138032Speter
537290792Sgshapiro	if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
537338032Speter	{
537477349Sgshapiro		sm_free(vptr);
537590792Sgshapiro		*statp = EX_UNAVAILABLE;
537690792Sgshapiro		return true;
537738032Speter	}
537877349Sgshapiro	sm_free(vptr);
537990792Sgshapiro	*statp = EX_OK;
538090792Sgshapiro	return false;
538138032Speter}
538290792Sgshapiro#endif /* NETINFO */
538338032Speter/*
538438032Speter**  TEXT (unindexed text file) Modules
538538032Speter**
538638032Speter**	This code donated by Sun Microsystems.
538738032Speter*/
538838032Speter
538938032Speter#define map_sff		map_lockfd	/* overload field */
539038032Speter
539138032Speter
539238032Speter/*
539338032Speter**  TEXT_MAP_OPEN -- open text table
539438032Speter*/
539538032Speter
539638032Speterbool
539738032Spetertext_map_open(map, mode)
539838032Speter	MAP *map;
539938032Speter	int mode;
540038032Speter{
540164562Sgshapiro	long sff;
540238032Speter	int i;
540338032Speter
540438032Speter	if (tTd(38, 2))
540590792Sgshapiro		sm_dprintf("text_map_open(%s, %s, %d)\n",
540638032Speter			map->map_mname, map->map_file, mode);
540738032Speter
540838032Speter	mode &= O_ACCMODE;
540938032Speter	if (mode != O_RDONLY)
541038032Speter	{
541138032Speter		errno = EPERM;
541290792Sgshapiro		return false;
541338032Speter	}
541438032Speter
541538032Speter	if (*map->map_file == '\0')
541638032Speter	{
541738032Speter		syserr("text map \"%s\": file name required",
541838032Speter			map->map_mname);
541990792Sgshapiro		return false;
542038032Speter	}
542138032Speter
542238032Speter	if (map->map_file[0] != '/')
542338032Speter	{
542438032Speter		syserr("text map \"%s\": file name must be fully qualified",
542538032Speter			map->map_mname);
542690792Sgshapiro		return false;
542738032Speter	}
542838032Speter
542938032Speter	sff = SFF_ROOTOK|SFF_REGONLY;
543064562Sgshapiro	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
543138032Speter		sff |= SFF_NOWLINK;
543264562Sgshapiro	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
543338032Speter		sff |= SFF_SAFEDIRPATH;
543438032Speter	if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
543538032Speter			  sff, S_IRUSR, NULL)) != 0)
543638032Speter	{
543764562Sgshapiro		int save_errno = errno;
543864562Sgshapiro
543938032Speter		/* cannot open this map */
544038032Speter		if (tTd(38, 2))
544190792Sgshapiro			sm_dprintf("\tunsafe map file: %d\n", i);
544264562Sgshapiro		errno = save_errno;
544338032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
544438032Speter			syserr("text map \"%s\": unsafe map file %s",
544538032Speter				map->map_mname, map->map_file);
544690792Sgshapiro		return false;
544738032Speter	}
544838032Speter
544938032Speter	if (map->map_keycolnm == NULL)
545038032Speter		map->map_keycolno = 0;
545138032Speter	else
545238032Speter	{
545338032Speter		if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
545438032Speter		{
545538032Speter			syserr("text map \"%s\", file %s: -k should specify a number, not %s",
545638032Speter				map->map_mname, map->map_file,
545738032Speter				map->map_keycolnm);
545890792Sgshapiro			return false;
545938032Speter		}
546038032Speter		map->map_keycolno = atoi(map->map_keycolnm);
546138032Speter	}
546238032Speter
546338032Speter	if (map->map_valcolnm == NULL)
546438032Speter		map->map_valcolno = 0;
546538032Speter	else
546638032Speter	{
546738032Speter		if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
546838032Speter		{
546938032Speter			syserr("text map \"%s\", file %s: -v should specify a number, not %s",
547038032Speter					map->map_mname, map->map_file,
547138032Speter					map->map_valcolnm);
547290792Sgshapiro			return false;
547338032Speter		}
547438032Speter		map->map_valcolno = atoi(map->map_valcolnm);
547538032Speter	}
547638032Speter
547738032Speter	if (tTd(38, 2))
547838032Speter	{
547990792Sgshapiro		sm_dprintf("text_map_open(%s, %s): delimiter = ",
548038032Speter			map->map_mname, map->map_file);
548138032Speter		if (map->map_coldelim == '\0')
548290792Sgshapiro			sm_dprintf("(white space)\n");
548338032Speter		else
548490792Sgshapiro			sm_dprintf("%c\n", map->map_coldelim);
548538032Speter	}
548638032Speter
548738032Speter	map->map_sff = sff;
548890792Sgshapiro	return true;
548938032Speter}
549038032Speter
549138032Speter
549238032Speter/*
549338032Speter**  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
549438032Speter*/
549538032Speter
549638032Speterchar *
549738032Spetertext_map_lookup(map, name, av, statp)
549838032Speter	MAP *map;
549938032Speter	char *name;
550038032Speter	char **av;
550138032Speter	int *statp;
550238032Speter{
550338032Speter	char *vp;
550438032Speter	auto int vsize;
550538032Speter	int buflen;
550690792Sgshapiro	SM_FILE_T *f;
550738032Speter	char delim;
550838032Speter	int key_idx;
550938032Speter	bool found_it;
551064562Sgshapiro	long sff = map->map_sff;
551138032Speter	char search_key[MAXNAME + 1];
551238032Speter	char linebuf[MAXLINE];
551338032Speter	char buf[MAXNAME + 1];
551438032Speter
551590792Sgshapiro	found_it = false;
551638032Speter	if (tTd(38, 20))
551790792Sgshapiro		sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname,  name);
551838032Speter
551938032Speter	buflen = strlen(name);
552038032Speter	if (buflen > sizeof search_key - 1)
552190792Sgshapiro		buflen = sizeof search_key - 1;	/* XXX just cut if off? */
552264562Sgshapiro	memmove(search_key, name, buflen);
552338032Speter	search_key[buflen] = '\0';
552438032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
552538032Speter		makelower(search_key);
552638032Speter
552738032Speter	f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
552838032Speter	if (f == NULL)
552938032Speter	{
553038032Speter		map->map_mflags &= ~(MF_VALID|MF_OPEN);
553138032Speter		*statp = EX_UNAVAILABLE;
553238032Speter		return NULL;
553338032Speter	}
553438032Speter	key_idx = map->map_keycolno;
553538032Speter	delim = map->map_coldelim;
553698121Sgshapiro	while (sm_io_fgets(f, SM_TIME_DEFAULT,
553798121Sgshapiro			   linebuf, sizeof linebuf) != NULL)
553838032Speter	{
553938032Speter		char *p;
554038032Speter
554138032Speter		/* skip comment line */
554238032Speter		if (linebuf[0] == '#')
554338032Speter			continue;
554438032Speter		p = strchr(linebuf, '\n');
554538032Speter		if (p != NULL)
554638032Speter			*p = '\0';
554738032Speter		p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
554890792Sgshapiro		if (p != NULL && sm_strcasecmp(search_key, p) == 0)
554938032Speter		{
555090792Sgshapiro			found_it = true;
555138032Speter			break;
555238032Speter		}
555338032Speter	}
555490792Sgshapiro	(void) sm_io_close(f, SM_TIME_DEFAULT);
555538032Speter	if (!found_it)
555638032Speter	{
555738032Speter		*statp = EX_NOTFOUND;
555838032Speter		return NULL;
555938032Speter	}
556038032Speter	vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
556142575Speter	if (vp == NULL)
556242575Speter	{
556342575Speter		*statp = EX_NOTFOUND;
556442575Speter		return NULL;
556542575Speter	}
556638032Speter	vsize = strlen(vp);
556738032Speter	*statp = EX_OK;
556838032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
556938032Speter		return map_rewrite(map, name, strlen(name), NULL);
557038032Speter	else
557138032Speter		return map_rewrite(map, vp, vsize, av);
557238032Speter}
557338032Speter
557438032Speter/*
557538032Speter**  TEXT_GETCANONNAME -- look up canonical name in hosts file
557638032Speter*/
557738032Speter
557864562Sgshapirostatic bool
557938032Spetertext_getcanonname(name, hbsize, statp)
558038032Speter	char *name;
558138032Speter	int hbsize;
558238032Speter	int *statp;
558338032Speter{
558438032Speter	bool found;
558573188Sgshapiro	char *dot;
558690792Sgshapiro	SM_FILE_T *f;
558738032Speter	char linebuf[MAXLINE];
558838032Speter	char cbuf[MAXNAME + 1];
558938032Speter	char nbuf[MAXNAME + 1];
559038032Speter
559138032Speter	if (tTd(38, 20))
559290792Sgshapiro		sm_dprintf("text_getcanonname(%s)\n", name);
559338032Speter
559490792Sgshapiro	if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
559538032Speter	{
559638032Speter		*statp = EX_UNAVAILABLE;
559790792Sgshapiro		return false;
559838032Speter	}
559973188Sgshapiro	dot = shorten_hostname(nbuf);
560038032Speter
560190792Sgshapiro	f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
560290792Sgshapiro		       NULL);
560338032Speter	if (f == NULL)
560438032Speter	{
560538032Speter		*statp = EX_UNAVAILABLE;
560690792Sgshapiro		return false;
560738032Speter	}
560890792Sgshapiro	found = false;
560990792Sgshapiro	while (!found &&
561098121Sgshapiro		sm_io_fgets(f, SM_TIME_DEFAULT,
561198121Sgshapiro			    linebuf, sizeof linebuf) != NULL)
561238032Speter	{
561338032Speter		char *p = strpbrk(linebuf, "#\n");
561438032Speter
561538032Speter		if (p != NULL)
561638032Speter			*p = '\0';
561738032Speter		if (linebuf[0] != '\0')
561873188Sgshapiro			found = extract_canonname(nbuf, dot, linebuf,
561973188Sgshapiro						  cbuf, sizeof cbuf);
562038032Speter	}
562190792Sgshapiro	(void) sm_io_close(f, SM_TIME_DEFAULT);
562238032Speter	if (!found)
562338032Speter	{
562438032Speter		*statp = EX_NOHOST;
562590792Sgshapiro		return false;
562638032Speter	}
562738032Speter
562890792Sgshapiro	if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
562938032Speter	{
563090792Sgshapiro		*statp = EX_UNAVAILABLE;
563190792Sgshapiro		return false;
563238032Speter	}
563390792Sgshapiro	*statp = EX_OK;
563490792Sgshapiro	return true;
563538032Speter}
563690792Sgshapiro/*
563738032Speter**  STAB (Symbol Table) Modules
563838032Speter*/
563938032Speter
564038032Speter
564138032Speter/*
564238032Speter**  STAB_MAP_LOOKUP -- look up alias in symbol table
564338032Speter*/
564438032Speter
564538032Speter/* ARGSUSED2 */
564638032Speterchar *
564738032Speterstab_map_lookup(map, name, av, pstat)
564838032Speter	register MAP *map;
564938032Speter	char *name;
565038032Speter	char **av;
565138032Speter	int *pstat;
565238032Speter{
565338032Speter	register STAB *s;
565438032Speter
565538032Speter	if (tTd(38, 20))
565690792Sgshapiro		sm_dprintf("stab_lookup(%s, %s)\n",
565738032Speter			map->map_mname, name);
565838032Speter
565938032Speter	s = stab(name, ST_ALIAS, ST_FIND);
566038032Speter	if (s != NULL)
566164562Sgshapiro		return s->s_alias;
566264562Sgshapiro	return NULL;
566338032Speter}
566438032Speter
566538032Speter
566638032Speter/*
566738032Speter**  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
566838032Speter*/
566938032Speter
567038032Spetervoid
567138032Speterstab_map_store(map, lhs, rhs)
567238032Speter	register MAP *map;
567338032Speter	char *lhs;
567438032Speter	char *rhs;
567538032Speter{
567638032Speter	register STAB *s;
567738032Speter
567838032Speter	s = stab(lhs, ST_ALIAS, ST_ENTER);
567938032Speter	s->s_alias = newstr(rhs);
568038032Speter}
568138032Speter
568238032Speter
568338032Speter/*
568438032Speter**  STAB_MAP_OPEN -- initialize (reads data file)
568538032Speter**
568638032Speter**	This is a wierd case -- it is only intended as a fallback for
568738032Speter**	aliases.  For this reason, opens for write (only during a
568838032Speter**	"newaliases") always fails, and opens for read open the
568938032Speter**	actual underlying text file instead of the database.
569038032Speter*/
569138032Speter
569238032Speterbool
569338032Speterstab_map_open(map, mode)
569438032Speter	register MAP *map;
569538032Speter	int mode;
569638032Speter{
569790792Sgshapiro	SM_FILE_T *af;
569864562Sgshapiro	long sff;
569938032Speter	struct stat st;
570038032Speter
570138032Speter	if (tTd(38, 2))
570290792Sgshapiro		sm_dprintf("stab_map_open(%s, %s, %d)\n",
570338032Speter			map->map_mname, map->map_file, mode);
570438032Speter
570538032Speter	mode &= O_ACCMODE;
570638032Speter	if (mode != O_RDONLY)
570738032Speter	{
570838032Speter		errno = EPERM;
570990792Sgshapiro		return false;
571038032Speter	}
571138032Speter
571238032Speter	sff = SFF_ROOTOK|SFF_REGONLY;
571364562Sgshapiro	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
571438032Speter		sff |= SFF_NOWLINK;
571564562Sgshapiro	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
571638032Speter		sff |= SFF_SAFEDIRPATH;
571738032Speter	af = safefopen(map->map_file, O_RDONLY, 0444, sff);
571838032Speter	if (af == NULL)
571990792Sgshapiro		return false;
572090792Sgshapiro	readaliases(map, af, false, false);
572138032Speter
572290792Sgshapiro	if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
572338032Speter		map->map_mtime = st.st_mtime;
572490792Sgshapiro	(void) sm_io_close(af, SM_TIME_DEFAULT);
572538032Speter
572690792Sgshapiro	return true;
572738032Speter}
572890792Sgshapiro/*
572938032Speter**  Implicit Modules
573038032Speter**
573138032Speter**	Tries several types.  For back compatibility of aliases.
573238032Speter*/
573338032Speter
573438032Speter
573538032Speter/*
573638032Speter**  IMPL_MAP_LOOKUP -- lookup in best open database
573738032Speter*/
573838032Speter
573938032Speterchar *
574038032Speterimpl_map_lookup(map, name, av, pstat)
574138032Speter	MAP *map;
574238032Speter	char *name;
574338032Speter	char **av;
574438032Speter	int *pstat;
574538032Speter{
574638032Speter	if (tTd(38, 20))
574790792Sgshapiro		sm_dprintf("impl_map_lookup(%s, %s)\n",
574838032Speter			map->map_mname, name);
574938032Speter
575090792Sgshapiro#if NEWDB
575138032Speter	if (bitset(MF_IMPL_HASH, map->map_mflags))
575238032Speter		return db_map_lookup(map, name, av, pstat);
575364562Sgshapiro#endif /* NEWDB */
575490792Sgshapiro#if NDBM
575538032Speter	if (bitset(MF_IMPL_NDBM, map->map_mflags))
575638032Speter		return ndbm_map_lookup(map, name, av, pstat);
575764562Sgshapiro#endif /* NDBM */
575838032Speter	return stab_map_lookup(map, name, av, pstat);
575938032Speter}
576038032Speter
576138032Speter/*
576238032Speter**  IMPL_MAP_STORE -- store in open databases
576338032Speter*/
576438032Speter
576538032Spetervoid
576638032Speterimpl_map_store(map, lhs, rhs)
576738032Speter	MAP *map;
576838032Speter	char *lhs;
576938032Speter	char *rhs;
577038032Speter{
577138032Speter	if (tTd(38, 12))
577290792Sgshapiro		sm_dprintf("impl_map_store(%s, %s, %s)\n",
577338032Speter			map->map_mname, lhs, rhs);
577490792Sgshapiro#if NEWDB
577538032Speter	if (bitset(MF_IMPL_HASH, map->map_mflags))
577638032Speter		db_map_store(map, lhs, rhs);
577764562Sgshapiro#endif /* NEWDB */
577890792Sgshapiro#if NDBM
577938032Speter	if (bitset(MF_IMPL_NDBM, map->map_mflags))
578038032Speter		ndbm_map_store(map, lhs, rhs);
578164562Sgshapiro#endif /* NDBM */
578238032Speter	stab_map_store(map, lhs, rhs);
578338032Speter}
578438032Speter
578538032Speter/*
578638032Speter**  IMPL_MAP_OPEN -- implicit database open
578738032Speter*/
578838032Speter
578938032Speterbool
579038032Speterimpl_map_open(map, mode)
579138032Speter	MAP *map;
579238032Speter	int mode;
579338032Speter{
579438032Speter	if (tTd(38, 2))
579590792Sgshapiro		sm_dprintf("impl_map_open(%s, %s, %d)\n",
579638032Speter			map->map_mname, map->map_file, mode);
579738032Speter
579838032Speter	mode &= O_ACCMODE;
579990792Sgshapiro#if NEWDB
580038032Speter	map->map_mflags |= MF_IMPL_HASH;
580138032Speter	if (hash_map_open(map, mode))
580238032Speter	{
580338032Speter# ifdef NDBM_YP_COMPAT
580438032Speter		if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
580564562Sgshapiro# endif /* NDBM_YP_COMPAT */
580690792Sgshapiro			return true;
580738032Speter	}
580838032Speter	else
580938032Speter		map->map_mflags &= ~MF_IMPL_HASH;
581064562Sgshapiro#endif /* NEWDB */
581190792Sgshapiro#if NDBM
581238032Speter	map->map_mflags |= MF_IMPL_NDBM;
581338032Speter	if (ndbm_map_open(map, mode))
581438032Speter	{
581590792Sgshapiro		return true;
581638032Speter	}
581738032Speter	else
581838032Speter		map->map_mflags &= ~MF_IMPL_NDBM;
581964562Sgshapiro#endif /* NDBM */
582038032Speter
582138032Speter#if defined(NEWDB) || defined(NDBM)
582238032Speter	if (Verbose)
582338032Speter		message("WARNING: cannot open alias database %s%s",
582438032Speter			map->map_file,
582538032Speter			mode == O_RDONLY ? "; reading text version" : "");
582664562Sgshapiro#else /* defined(NEWDB) || defined(NDBM) */
582738032Speter	if (mode != O_RDONLY)
582838032Speter		usrerr("Cannot rebuild aliases: no database format defined");
582964562Sgshapiro#endif /* defined(NEWDB) || defined(NDBM) */
583038032Speter
583138032Speter	if (mode == O_RDONLY)
583238032Speter		return stab_map_open(map, mode);
583338032Speter	else
583490792Sgshapiro		return false;
583538032Speter}
583638032Speter
583738032Speter
583838032Speter/*
583938032Speter**  IMPL_MAP_CLOSE -- close any open database(s)
584038032Speter*/
584138032Speter
584238032Spetervoid
584338032Speterimpl_map_close(map)
584438032Speter	MAP *map;
584538032Speter{
584638032Speter	if (tTd(38, 9))
584790792Sgshapiro		sm_dprintf("impl_map_close(%s, %s, %lx)\n",
584838032Speter			map->map_mname, map->map_file, map->map_mflags);
584990792Sgshapiro#if NEWDB
585038032Speter	if (bitset(MF_IMPL_HASH, map->map_mflags))
585138032Speter	{
585238032Speter		db_map_close(map);
585338032Speter		map->map_mflags &= ~MF_IMPL_HASH;
585438032Speter	}
585564562Sgshapiro#endif /* NEWDB */
585638032Speter
585790792Sgshapiro#if NDBM
585838032Speter	if (bitset(MF_IMPL_NDBM, map->map_mflags))
585938032Speter	{
586038032Speter		ndbm_map_close(map);
586138032Speter		map->map_mflags &= ~MF_IMPL_NDBM;
586238032Speter	}
586364562Sgshapiro#endif /* NDBM */
586438032Speter}
586590792Sgshapiro/*
586638032Speter**  User map class.
586738032Speter**
586838032Speter**	Provides access to the system password file.
586938032Speter*/
587038032Speter
587138032Speter/*
587238032Speter**  USER_MAP_OPEN -- open user map
587338032Speter**
587438032Speter**	Really just binds field names to field numbers.
587538032Speter*/
587638032Speter
587738032Speterbool
587838032Speteruser_map_open(map, mode)
587938032Speter	MAP *map;
588038032Speter	int mode;
588138032Speter{
588238032Speter	if (tTd(38, 2))
588390792Sgshapiro		sm_dprintf("user_map_open(%s, %d)\n",
588438032Speter			map->map_mname, mode);
588538032Speter
588638032Speter	mode &= O_ACCMODE;
588738032Speter	if (mode != O_RDONLY)
588838032Speter	{
588938032Speter		/* issue a pseudo-error message */
589090792Sgshapiro		errno = SM_EMAPCANTWRITE;
589190792Sgshapiro		return false;
589238032Speter	}
589338032Speter	if (map->map_valcolnm == NULL)
589464562Sgshapiro		/* EMPTY */
589538032Speter		/* nothing */ ;
589690792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
589738032Speter		map->map_valcolno = 1;
589890792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
589938032Speter		map->map_valcolno = 2;
590090792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
590138032Speter		map->map_valcolno = 3;
590290792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
590338032Speter		map->map_valcolno = 4;
590490792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
590538032Speter		map->map_valcolno = 5;
590690792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
590738032Speter		map->map_valcolno = 6;
590890792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
590938032Speter		map->map_valcolno = 7;
591038032Speter	else
591138032Speter	{
591238032Speter		syserr("User map %s: unknown column name %s",
591338032Speter			map->map_mname, map->map_valcolnm);
591490792Sgshapiro		return false;
591538032Speter	}
591690792Sgshapiro	return true;
591738032Speter}
591838032Speter
591938032Speter
592038032Speter/*
592138032Speter**  USER_MAP_LOOKUP -- look up a user in the passwd file.
592238032Speter*/
592338032Speter
592438032Speter/* ARGSUSED3 */
592538032Speterchar *
592638032Speteruser_map_lookup(map, key, av, statp)
592738032Speter	MAP *map;
592838032Speter	char *key;
592938032Speter	char **av;
593038032Speter	int *statp;
593138032Speter{
593238032Speter	auto bool fuzzy;
593390792Sgshapiro	SM_MBDB_T user;
593438032Speter
593538032Speter	if (tTd(38, 20))
593690792Sgshapiro		sm_dprintf("user_map_lookup(%s, %s)\n",
593738032Speter			map->map_mname, key);
593838032Speter
593990792Sgshapiro	*statp = finduser(key, &fuzzy, &user);
594090792Sgshapiro	if (*statp != EX_OK)
594138032Speter		return NULL;
594238032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
594338032Speter		return map_rewrite(map, key, strlen(key), NULL);
594438032Speter	else
594538032Speter	{
594638032Speter		char *rwval = NULL;
594738032Speter		char buf[30];
594838032Speter
594938032Speter		switch (map->map_valcolno)
595038032Speter		{
595138032Speter		  case 0:
595238032Speter		  case 1:
595390792Sgshapiro			rwval = user.mbdb_name;
595438032Speter			break;
595538032Speter
595638032Speter		  case 2:
595790792Sgshapiro			rwval = "x";	/* passwd no longer supported */
595838032Speter			break;
595938032Speter
596038032Speter		  case 3:
596190792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%d",
596290792Sgshapiro					   (int) user.mbdb_uid);
596338032Speter			rwval = buf;
596438032Speter			break;
596538032Speter
596638032Speter		  case 4:
596790792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%d",
596890792Sgshapiro					   (int) user.mbdb_gid);
596938032Speter			rwval = buf;
597038032Speter			break;
597138032Speter
597238032Speter		  case 5:
597390792Sgshapiro			rwval = user.mbdb_fullname;
597438032Speter			break;
597538032Speter
597638032Speter		  case 6:
597790792Sgshapiro			rwval = user.mbdb_homedir;
597838032Speter			break;
597938032Speter
598038032Speter		  case 7:
598190792Sgshapiro			rwval = user.mbdb_shell;
598238032Speter			break;
598338032Speter		}
598438032Speter		return map_rewrite(map, rwval, strlen(rwval), av);
598538032Speter	}
598638032Speter}
598790792Sgshapiro/*
598838032Speter**  Program map type.
598938032Speter**
599038032Speter**	This provides access to arbitrary programs.  It should be used
599138032Speter**	only very sparingly, since there is no way to bound the cost
599238032Speter**	of invoking an arbitrary program.
599338032Speter*/
599438032Speter
599538032Speterchar *
599638032Speterprog_map_lookup(map, name, av, statp)
599738032Speter	MAP *map;
599838032Speter	char *name;
599938032Speter	char **av;
600038032Speter	int *statp;
600138032Speter{
600238032Speter	int i;
600364562Sgshapiro	int save_errno;
600438032Speter	int fd;
600564562Sgshapiro	int status;
600638032Speter	auto pid_t pid;
600764562Sgshapiro	register char *p;
600838032Speter	char *rval;
600938032Speter	char *argv[MAXPV + 1];
601038032Speter	char buf[MAXLINE];
601138032Speter
601238032Speter	if (tTd(38, 20))
601390792Sgshapiro		sm_dprintf("prog_map_lookup(%s, %s) %s\n",
601438032Speter			map->map_mname, name, map->map_file);
601538032Speter
601638032Speter	i = 0;
601738032Speter	argv[i++] = map->map_file;
601838032Speter	if (map->map_rebuild != NULL)
601938032Speter	{
602090792Sgshapiro		(void) sm_strlcpy(buf, map->map_rebuild, sizeof buf);
602138032Speter		for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
602238032Speter		{
602338032Speter			if (i >= MAXPV - 1)
602438032Speter				break;
602538032Speter			argv[i++] = p;
602638032Speter		}
602738032Speter	}
602838032Speter	argv[i++] = name;
602938032Speter	argv[i] = NULL;
603038032Speter	if (tTd(38, 21))
603138032Speter	{
603290792Sgshapiro		sm_dprintf("prog_open:");
603338032Speter		for (i = 0; argv[i] != NULL; i++)
603490792Sgshapiro			sm_dprintf(" %s", argv[i]);
603590792Sgshapiro		sm_dprintf("\n");
603638032Speter	}
603790792Sgshapiro	(void) sm_blocksignal(SIGCHLD);
603838032Speter	pid = prog_open(argv, &fd, CurEnv);
603938032Speter	if (pid < 0)
604038032Speter	{
604138032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
604238032Speter			syserr("prog_map_lookup(%s) failed (%s) -- closing",
604390792Sgshapiro			       map->map_mname, sm_errstring(errno));
604438032Speter		else if (tTd(38, 9))
604590792Sgshapiro			sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
604690792Sgshapiro				   map->map_mname, sm_errstring(errno));
604738032Speter		map->map_mflags &= ~(MF_VALID|MF_OPEN);
604838032Speter		*statp = EX_OSFILE;
604938032Speter		return NULL;
605038032Speter	}
605138032Speter	i = read(fd, buf, sizeof buf - 1);
605238032Speter	if (i < 0)
605338032Speter	{
605490792Sgshapiro		syserr("prog_map_lookup(%s): read error %s",
605590792Sgshapiro		       map->map_mname, sm_errstring(errno));
605638032Speter		rval = NULL;
605738032Speter	}
605838032Speter	else if (i == 0)
605938032Speter	{
606038032Speter		if (tTd(38, 20))
606190792Sgshapiro			sm_dprintf("prog_map_lookup(%s): empty answer\n",
606290792Sgshapiro				   map->map_mname);
606338032Speter		rval = NULL;
606438032Speter	}
606538032Speter	else
606638032Speter	{
606738032Speter		buf[i] = '\0';
606838032Speter		p = strchr(buf, '\n');
606938032Speter		if (p != NULL)
607038032Speter			*p = '\0';
607138032Speter
607238032Speter		/* collect the return value */
607338032Speter		if (bitset(MF_MATCHONLY, map->map_mflags))
607438032Speter			rval = map_rewrite(map, name, strlen(name), NULL);
607538032Speter		else
607677349Sgshapiro			rval = map_rewrite(map, buf, strlen(buf), av);
607738032Speter
607838032Speter		/* now flush any additional output */
607938032Speter		while ((i = read(fd, buf, sizeof buf)) > 0)
608038032Speter			continue;
608138032Speter	}
608238032Speter
608338032Speter	/* wait for the process to terminate */
608464562Sgshapiro	(void) close(fd);
608564562Sgshapiro	status = waitfor(pid);
608664562Sgshapiro	save_errno = errno;
608790792Sgshapiro	(void) sm_releasesignal(SIGCHLD);
608864562Sgshapiro	errno = save_errno;
608938032Speter
609064562Sgshapiro	if (status == -1)
609138032Speter	{
609290792Sgshapiro		syserr("prog_map_lookup(%s): wait error %s",
609390792Sgshapiro		       map->map_mname, sm_errstring(errno));
609438032Speter		*statp = EX_SOFTWARE;
609538032Speter		rval = NULL;
609638032Speter	}
609764562Sgshapiro	else if (WIFEXITED(status))
609838032Speter	{
609964562Sgshapiro		if ((*statp = WEXITSTATUS(status)) != EX_OK)
610038032Speter			rval = NULL;
610138032Speter	}
610238032Speter	else
610338032Speter	{
610438032Speter		syserr("prog_map_lookup(%s): child died on signal %d",
610590792Sgshapiro		       map->map_mname, status);
610638032Speter		*statp = EX_UNAVAILABLE;
610738032Speter		rval = NULL;
610838032Speter	}
610938032Speter	return rval;
611038032Speter}
611190792Sgshapiro/*
611238032Speter**  Sequenced map type.
611338032Speter**
611438032Speter**	Tries each map in order until something matches, much like
611538032Speter**	implicit.  Stores go to the first map in the list that can
611638032Speter**	support storing.
611738032Speter**
611838032Speter**	This is slightly unusual in that there are two interfaces.
611938032Speter**	The "sequence" interface lets you stack maps arbitrarily.
612038032Speter**	The "switch" interface builds a sequence map by looking
612138032Speter**	at a system-dependent configuration file such as
612238032Speter**	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
612338032Speter**
612438032Speter**	We don't need an explicit open, since all maps are
612590792Sgshapiro**	opened on demand.
612638032Speter*/
612738032Speter
612838032Speter/*
612938032Speter**  SEQ_MAP_PARSE -- Sequenced map parsing
613038032Speter*/
613138032Speter
613238032Speterbool
613338032Speterseq_map_parse(map, ap)
613438032Speter	MAP *map;
613538032Speter	char *ap;
613638032Speter{
613738032Speter	int maxmap;
613838032Speter
613938032Speter	if (tTd(38, 2))
614090792Sgshapiro		sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
614138032Speter	maxmap = 0;
614238032Speter	while (*ap != '\0')
614338032Speter	{
614438032Speter		register char *p;
614538032Speter		STAB *s;
614638032Speter
614738032Speter		/* find beginning of map name */
614838032Speter		while (isascii(*ap) && isspace(*ap))
614938032Speter			ap++;
615064562Sgshapiro		for (p = ap;
615164562Sgshapiro		     (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
615264562Sgshapiro		     p++)
615338032Speter			continue;
615438032Speter		if (*p != '\0')
615538032Speter			*p++ = '\0';
615638032Speter		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
615738032Speter			p++;
615838032Speter		if (*ap == '\0')
615938032Speter		{
616038032Speter			ap = p;
616138032Speter			continue;
616238032Speter		}
616338032Speter		s = stab(ap, ST_MAP, ST_FIND);
616438032Speter		if (s == NULL)
616538032Speter		{
616638032Speter			syserr("Sequence map %s: unknown member map %s",
616738032Speter				map->map_mname, ap);
616838032Speter		}
616990792Sgshapiro		else if (maxmap >= MAXMAPSTACK)
617038032Speter		{
617138032Speter			syserr("Sequence map %s: too many member maps (%d max)",
617238032Speter				map->map_mname, MAXMAPSTACK);
617338032Speter			maxmap++;
617438032Speter		}
617538032Speter		else if (maxmap < MAXMAPSTACK)
617638032Speter		{
617738032Speter			map->map_stack[maxmap++] = &s->s_map;
617838032Speter		}
617938032Speter		ap = p;
618038032Speter	}
618190792Sgshapiro	return true;
618238032Speter}
618338032Speter
618438032Speter/*
618538032Speter**  SWITCH_MAP_OPEN -- open a switched map
618638032Speter**
618738032Speter**	This looks at the system-dependent configuration and builds
618838032Speter**	a sequence map that does the same thing.
618938032Speter**
619038032Speter**	Every system must define a switch_map_find routine in conf.c
619138032Speter**	that will return the list of service types associated with a
619238032Speter**	given service class.
619338032Speter*/
619438032Speter
619538032Speterbool
619638032Speterswitch_map_open(map, mode)
619738032Speter	MAP *map;
619838032Speter	int mode;
619938032Speter{
620038032Speter	int mapno;
620138032Speter	int nmaps;
620238032Speter	char *maptype[MAXMAPSTACK];
620338032Speter
620438032Speter	if (tTd(38, 2))
620590792Sgshapiro		sm_dprintf("switch_map_open(%s, %s, %d)\n",
620638032Speter			map->map_mname, map->map_file, mode);
620738032Speter
620838032Speter	mode &= O_ACCMODE;
620938032Speter	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
621038032Speter	if (tTd(38, 19))
621138032Speter	{
621290792Sgshapiro		sm_dprintf("\tswitch_map_find => %d\n", nmaps);
621338032Speter		for (mapno = 0; mapno < nmaps; mapno++)
621490792Sgshapiro			sm_dprintf("\t\t%s\n", maptype[mapno]);
621538032Speter	}
621638032Speter	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
621790792Sgshapiro		return false;
621838032Speter
621938032Speter	for (mapno = 0; mapno < nmaps; mapno++)
622038032Speter	{
622138032Speter		register STAB *s;
622238032Speter		char nbuf[MAXNAME + 1];
622338032Speter
622438032Speter		if (maptype[mapno] == NULL)
622538032Speter			continue;
622690792Sgshapiro		(void) sm_strlcpyn(nbuf, sizeof nbuf, 3,
622790792Sgshapiro				   map->map_mname, ".", maptype[mapno]);
622838032Speter		s = stab(nbuf, ST_MAP, ST_FIND);
622938032Speter		if (s == NULL)
623038032Speter		{
623138032Speter			syserr("Switch map %s: unknown member map %s",
623238032Speter				map->map_mname, nbuf);
623338032Speter		}
623438032Speter		else
623538032Speter		{
623638032Speter			map->map_stack[mapno] = &s->s_map;
623738032Speter			if (tTd(38, 4))
623890792Sgshapiro				sm_dprintf("\tmap_stack[%d] = %s:%s\n",
623990792Sgshapiro					   mapno,
624090792Sgshapiro					   s->s_map.map_class->map_cname,
624190792Sgshapiro					   nbuf);
624238032Speter		}
624338032Speter	}
624490792Sgshapiro	return true;
624538032Speter}
624638032Speter
624790792Sgshapiro#if 0
624838032Speter/*
624938032Speter**  SEQ_MAP_CLOSE -- close all underlying maps
625038032Speter*/
625138032Speter
625238032Spetervoid
625338032Speterseq_map_close(map)
625438032Speter	MAP *map;
625538032Speter{
625638032Speter	int mapno;
625738032Speter
625838032Speter	if (tTd(38, 9))
625990792Sgshapiro		sm_dprintf("seq_map_close(%s)\n", map->map_mname);
626038032Speter
626138032Speter	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
626238032Speter	{
626338032Speter		MAP *mm = map->map_stack[mapno];
626438032Speter
626538032Speter		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
626638032Speter			continue;
626777349Sgshapiro		mm->map_mflags |= MF_CLOSING;
626838032Speter		mm->map_class->map_close(mm);
626977349Sgshapiro		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
627038032Speter	}
627138032Speter}
627290792Sgshapiro#endif /* 0 */
627338032Speter
627438032Speter/*
627538032Speter**  SEQ_MAP_LOOKUP -- sequenced map lookup
627638032Speter*/
627738032Speter
627838032Speterchar *
627938032Speterseq_map_lookup(map, key, args, pstat)
628038032Speter	MAP *map;
628138032Speter	char *key;
628238032Speter	char **args;
628338032Speter	int *pstat;
628438032Speter{
628538032Speter	int mapno;
628638032Speter	int mapbit = 0x01;
628790792Sgshapiro	bool tempfail = false;
628838032Speter
628938032Speter	if (tTd(38, 20))
629090792Sgshapiro		sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
629138032Speter
629238032Speter	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
629338032Speter	{
629438032Speter		MAP *mm = map->map_stack[mapno];
629538032Speter		char *rv;
629638032Speter
629738032Speter		if (mm == NULL)
629838032Speter			continue;
629964562Sgshapiro		if (!bitset(MF_OPEN, mm->map_mflags) &&
630064562Sgshapiro		    !openmap(mm))
630138032Speter		{
630238032Speter			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
630338032Speter			{
630438032Speter				*pstat = EX_UNAVAILABLE;
630538032Speter				return NULL;
630638032Speter			}
630738032Speter			continue;
630838032Speter		}
630938032Speter		*pstat = EX_OK;
631038032Speter		rv = mm->map_class->map_lookup(mm, key, args, pstat);
631138032Speter		if (rv != NULL)
631238032Speter			return rv;
631338032Speter		if (*pstat == EX_TEMPFAIL)
631438032Speter		{
631538032Speter			if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
631638032Speter				return NULL;
631790792Sgshapiro			tempfail = true;
631838032Speter		}
631938032Speter		else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
632038032Speter			break;
632138032Speter	}
632238032Speter	if (tempfail)
632338032Speter		*pstat = EX_TEMPFAIL;
632438032Speter	else if (*pstat == EX_OK)
632538032Speter		*pstat = EX_NOTFOUND;
632638032Speter	return NULL;
632738032Speter}
632838032Speter
632938032Speter/*
633038032Speter**  SEQ_MAP_STORE -- sequenced map store
633138032Speter*/
633238032Speter
633338032Spetervoid
633438032Speterseq_map_store(map, key, val)
633538032Speter	MAP *map;
633638032Speter	char *key;
633738032Speter	char *val;
633838032Speter{
633938032Speter	int mapno;
634038032Speter
634138032Speter	if (tTd(38, 12))
634290792Sgshapiro		sm_dprintf("seq_map_store(%s, %s, %s)\n",
634338032Speter			map->map_mname, key, val);
634438032Speter
634538032Speter	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
634638032Speter	{
634738032Speter		MAP *mm = map->map_stack[mapno];
634838032Speter
634938032Speter		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
635038032Speter			continue;
635138032Speter
635238032Speter		mm->map_class->map_store(mm, key, val);
635338032Speter		return;
635438032Speter	}
635538032Speter	syserr("seq_map_store(%s, %s, %s): no writable map",
635638032Speter		map->map_mname, key, val);
635738032Speter}
635890792Sgshapiro/*
635938032Speter**  NULL stubs
636038032Speter*/
636138032Speter
636238032Speter/* ARGSUSED */
636338032Speterbool
636438032Speternull_map_open(map, mode)
636538032Speter	MAP *map;
636638032Speter	int mode;
636738032Speter{
636890792Sgshapiro	return true;
636938032Speter}
637038032Speter
637138032Speter/* ARGSUSED */
637238032Spetervoid
637338032Speternull_map_close(map)
637438032Speter	MAP *map;
637538032Speter{
637638032Speter	return;
637738032Speter}
637838032Speter
637938032Speterchar *
638038032Speternull_map_lookup(map, key, args, pstat)
638138032Speter	MAP *map;
638238032Speter	char *key;
638338032Speter	char **args;
638438032Speter	int *pstat;
638538032Speter{
638638032Speter	*pstat = EX_NOTFOUND;
638738032Speter	return NULL;
638838032Speter}
638938032Speter
639038032Speter/* ARGSUSED */
639138032Spetervoid
639238032Speternull_map_store(map, key, val)
639338032Speter	MAP *map;
639438032Speter	char *key;
639538032Speter	char *val;
639638032Speter{
639738032Speter	return;
639838032Speter}
639938032Speter
640038032Speter/*
640138032Speter**  BOGUS stubs
640238032Speter*/
640338032Speter
640438032Speterchar *
640538032Speterbogus_map_lookup(map, key, args, pstat)
640638032Speter	MAP *map;
640738032Speter	char *key;
640838032Speter	char **args;
640938032Speter	int *pstat;
641038032Speter{
641138032Speter	*pstat = EX_TEMPFAIL;
641238032Speter	return NULL;
641338032Speter}
641438032Speter
641538032SpeterMAPCLASS	BogusMapClass =
641638032Speter{
641790792Sgshapiro	"bogus-map",		NULL,			0,
641890792Sgshapiro	NULL,			bogus_map_lookup,	null_map_store,
641990792Sgshapiro	null_map_open,		null_map_close,
642038032Speter};
642190792Sgshapiro/*
642264562Sgshapiro**  MACRO modules
642364562Sgshapiro*/
642464562Sgshapiro
642564562Sgshapirochar *
642664562Sgshapiromacro_map_lookup(map, name, av, statp)
642764562Sgshapiro	MAP *map;
642864562Sgshapiro	char *name;
642964562Sgshapiro	char **av;
643064562Sgshapiro	int *statp;
643164562Sgshapiro{
643264562Sgshapiro	int mid;
643364562Sgshapiro
643464562Sgshapiro	if (tTd(38, 20))
643590792Sgshapiro		sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
643664562Sgshapiro			name == NULL ? "NULL" : name);
643764562Sgshapiro
643864562Sgshapiro	if (name == NULL ||
643964562Sgshapiro	    *name == '\0' ||
644090792Sgshapiro	    (mid = macid(name)) == 0)
644164562Sgshapiro	{
644264562Sgshapiro		*statp = EX_CONFIG;
644364562Sgshapiro		return NULL;
644464562Sgshapiro	}
644564562Sgshapiro
644664562Sgshapiro	if (av[1] == NULL)
644790792Sgshapiro		macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
644864562Sgshapiro	else
644990792Sgshapiro		macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
645064562Sgshapiro
645164562Sgshapiro	*statp = EX_OK;
645264562Sgshapiro	return "";
645364562Sgshapiro}
645490792Sgshapiro/*
645538032Speter**  REGEX modules
645638032Speter*/
645738032Speter
645890792Sgshapiro#if MAP_REGEX
645938032Speter
646038032Speter# include <regex.h>
646138032Speter
646238032Speter# define DEFAULT_DELIM	CONDELSE
646338032Speter# define END_OF_FIELDS	-1
646438032Speter# define ERRBUF_SIZE	80
646538032Speter# define MAX_MATCH	32
646638032Speter
646764562Sgshapiro# define xnalloc(s)	memset(xalloc(s), '\0', s);
646838032Speter
646938032Speterstruct regex_map
647038032Speter{
647171345Sgshapiro	regex_t	*regex_pattern_buf;	/* xalloc it */
647238032Speter	int	*regex_subfields;	/* move to type MAP */
647364562Sgshapiro	char	*regex_delim;		/* move to type MAP */
647438032Speter};
647538032Speter
6476141858Sgshapirostatic int	parse_fields __P((char *, int *, int, int));
6477141858Sgshapirostatic char	*regex_map_rewrite __P((MAP *, const char*, size_t, char **));
6478141858Sgshapiro
647938032Speterstatic int
648038032Speterparse_fields(s, ibuf, blen, nr_substrings)
648138032Speter	char *s;
648238032Speter	int *ibuf;		/* array */
648338032Speter	int blen;		/* number of elements in ibuf */
648438032Speter	int nr_substrings;	/* number of substrings in the pattern */
648538032Speter{
648638032Speter	register char *cp;
648738032Speter	int i = 0;
648890792Sgshapiro	bool lastone = false;
648938032Speter
649038032Speter	blen--;		/* for terminating END_OF_FIELDS */
649138032Speter	cp = s;
649238032Speter	do
649338032Speter	{
649438032Speter		for (;; cp++)
649538032Speter		{
649638032Speter			if (*cp == ',')
649738032Speter			{
649838032Speter				*cp = '\0';
649938032Speter				break;
650038032Speter			}
650138032Speter			if (*cp == '\0')
650238032Speter			{
650390792Sgshapiro				lastone = true;
650438032Speter				break;
650538032Speter			}
650638032Speter		}
650738032Speter		if (i < blen)
650838032Speter		{
650938032Speter			int val = atoi(s);
651038032Speter
651138032Speter			if (val < 0 || val >= nr_substrings)
651238032Speter			{
651338032Speter				syserr("field (%d) out of range, only %d substrings in pattern",
651438032Speter				       val, nr_substrings);
651538032Speter				return -1;
651638032Speter			}
651738032Speter			ibuf[i++] = val;
651838032Speter		}
651938032Speter		else
652038032Speter		{
652190792Sgshapiro			syserr("too many fields, %d max", blen);
652238032Speter			return -1;
652338032Speter		}
652438032Speter		s = ++cp;
652538032Speter	} while (!lastone);
652638032Speter	ibuf[i] = END_OF_FIELDS;
652738032Speter	return i;
652838032Speter}
652938032Speter
653038032Speterbool
653138032Speterregex_map_init(map, ap)
653238032Speter	MAP *map;
653338032Speter	char *ap;
653438032Speter{
653538032Speter	int regerr;
653638032Speter	struct regex_map *map_p;
653738032Speter	register char *p;
653838032Speter	char *sub_param = NULL;
653938032Speter	int pflags;
654090792Sgshapiro	static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
654138032Speter
654238032Speter	if (tTd(38, 2))
654390792Sgshapiro		sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
654464562Sgshapiro			map->map_mname, ap);
654538032Speter
654638032Speter	pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
654738032Speter	p = ap;
654864562Sgshapiro	map_p = (struct regex_map *) xnalloc(sizeof *map_p);
654971345Sgshapiro	map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
655038032Speter
655138032Speter	for (;;)
655264562Sgshapiro	{
655338032Speter		while (isascii(*p) && isspace(*p))
655438032Speter			p++;
655538032Speter		if (*p != '-')
655638032Speter			break;
655738032Speter		switch (*++p)
655838032Speter		{
655938032Speter		  case 'n':	/* not */
656038032Speter			map->map_mflags |= MF_REGEX_NOT;
656138032Speter			break;
656238032Speter
656338032Speter		  case 'f':	/* case sensitive */
656438032Speter			map->map_mflags |= MF_NOFOLDCASE;
656538032Speter			pflags &= ~REG_ICASE;
656638032Speter			break;
656738032Speter
656838032Speter		  case 'b':	/* basic regular expressions */
656938032Speter			pflags &= ~REG_EXTENDED;
657038032Speter			break;
657138032Speter
657238032Speter		  case 's':	/* substring match () syntax */
657338032Speter			sub_param = ++p;
657438032Speter			pflags &= ~REG_NOSUB;
657538032Speter			break;
657638032Speter
657738032Speter		  case 'd':	/* delimiter */
657864562Sgshapiro			map_p->regex_delim = ++p;
657938032Speter			break;
658038032Speter
658138032Speter		  case 'a':	/* map append */
658238032Speter			map->map_app = ++p;
658338032Speter			break;
658438032Speter
658538032Speter		  case 'm':	/* matchonly */
658638032Speter			map->map_mflags |= MF_MATCHONLY;
658738032Speter			break;
658838032Speter
6589120256Sgshapiro		  case 'q':
6590120256Sgshapiro			map->map_mflags |= MF_KEEPQUOTES;
6591120256Sgshapiro			break;
6592120256Sgshapiro
659364562Sgshapiro		  case 'S':
659464562Sgshapiro			map->map_spacesub = *++p;
659564562Sgshapiro			break;
659664562Sgshapiro
659764562Sgshapiro		  case 'D':
659864562Sgshapiro			map->map_mflags |= MF_DEFER;
659964562Sgshapiro			break;
660064562Sgshapiro
660138032Speter		}
660264562Sgshapiro		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
660364562Sgshapiro			p++;
660464562Sgshapiro		if (*p != '\0')
660564562Sgshapiro			*p++ = '\0';
660638032Speter	}
660738032Speter	if (tTd(38, 3))
660890792Sgshapiro		sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
660938032Speter
661071345Sgshapiro	if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
661138032Speter	{
661238032Speter		/* Errorhandling */
661338032Speter		char errbuf[ERRBUF_SIZE];
661438032Speter
661571345Sgshapiro		(void) regerror(regerr, map_p->regex_pattern_buf,
661690792Sgshapiro			 errbuf, sizeof errbuf);
661790792Sgshapiro		syserr("pattern-compile-error: %s", errbuf);
661890792Sgshapiro		sm_free(map_p->regex_pattern_buf); /* XXX */
661990792Sgshapiro		sm_free(map_p); /* XXX */
662090792Sgshapiro		return false;
662138032Speter	}
662238032Speter
662338032Speter	if (map->map_app != NULL)
662438032Speter		map->map_app = newstr(map->map_app);
662564562Sgshapiro	if (map_p->regex_delim != NULL)
662664562Sgshapiro		map_p->regex_delim = newstr(map_p->regex_delim);
662738032Speter	else
662864562Sgshapiro		map_p->regex_delim = defdstr;
662938032Speter
663038032Speter	if (!bitset(REG_NOSUB, pflags))
663138032Speter	{
663238032Speter		/* substring matching */
663338032Speter		int substrings;
663464562Sgshapiro		int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
663538032Speter
663671345Sgshapiro		substrings = map_p->regex_pattern_buf->re_nsub + 1;
663738032Speter
663838032Speter		if (tTd(38, 3))
663990792Sgshapiro			sm_dprintf("regex_map_init: nr of substrings %d\n",
664064562Sgshapiro				substrings);
664138032Speter
664238032Speter		if (substrings >= MAX_MATCH)
664338032Speter		{
664490792Sgshapiro			syserr("too many substrings, %d max", MAX_MATCH);
664590792Sgshapiro			sm_free(map_p->regex_pattern_buf); /* XXX */
664690792Sgshapiro			sm_free(map_p); /* XXX */
664790792Sgshapiro			return false;
664838032Speter		}
664938032Speter		if (sub_param != NULL && sub_param[0] != '\0')
665038032Speter		{
665138032Speter			/* optional parameter -sfields */
665238032Speter			if (parse_fields(sub_param, fields,
665338032Speter					 MAX_MATCH + 1, substrings) == -1)
665490792Sgshapiro				return false;
665538032Speter		}
665638032Speter		else
665738032Speter		{
665838032Speter			int i;
665938032Speter
666090792Sgshapiro			/* set default fields */
666138032Speter			for (i = 0; i < substrings; i++)
666238032Speter				fields[i] = i;
666338032Speter			fields[i] = END_OF_FIELDS;
666438032Speter		}
666538032Speter		map_p->regex_subfields = fields;
666638032Speter		if (tTd(38, 3))
666738032Speter		{
666838032Speter			int *ip;
666938032Speter
667090792Sgshapiro			sm_dprintf("regex_map_init: subfields");
667138032Speter			for (ip = fields; *ip != END_OF_FIELDS; ip++)
667290792Sgshapiro				sm_dprintf(" %d", *ip);
667390792Sgshapiro			sm_dprintf("\n");
667438032Speter		}
667538032Speter	}
667690792Sgshapiro	map->map_db1 = (ARBPTR_T) map_p;	/* dirty hack */
667790792Sgshapiro	return true;
667838032Speter}
667938032Speter
668038032Speterstatic char *
668138032Speterregex_map_rewrite(map, s, slen, av)
668238032Speter	MAP *map;
668338032Speter	const char *s;
668438032Speter	size_t slen;
668538032Speter	char **av;
668638032Speter{
668738032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
668838032Speter		return map_rewrite(map, av[0], strlen(av[0]), NULL);
668938032Speter	else
669077349Sgshapiro		return map_rewrite(map, s, slen, av);
669138032Speter}
669238032Speter
669338032Speterchar *
669438032Speterregex_map_lookup(map, name, av, statp)
669538032Speter	MAP *map;
669638032Speter	char *name;
669738032Speter	char **av;
669838032Speter	int *statp;
669938032Speter{
670038032Speter	int reg_res;
670138032Speter	struct regex_map *map_p;
670238032Speter	regmatch_t pmatch[MAX_MATCH];
670338032Speter
670438032Speter	if (tTd(38, 20))
670538032Speter	{
670638032Speter		char **cpp;
670738032Speter
670890792Sgshapiro		sm_dprintf("regex_map_lookup: key '%s'\n", name);
670964562Sgshapiro		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
671090792Sgshapiro			sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
671138032Speter	}
671238032Speter
671338032Speter	map_p = (struct regex_map *)(map->map_db1);
671471345Sgshapiro	reg_res = regexec(map_p->regex_pattern_buf,
671564562Sgshapiro			  name, MAX_MATCH, pmatch, 0);
671638032Speter
671738032Speter	if (bitset(MF_REGEX_NOT, map->map_mflags))
671838032Speter	{
671938032Speter		/* option -n */
672038032Speter		if (reg_res == REG_NOMATCH)
672190792Sgshapiro			return regex_map_rewrite(map, "", (size_t) 0, av);
672238032Speter		else
672338032Speter			return NULL;
672438032Speter	}
672538032Speter	if (reg_res == REG_NOMATCH)
672638032Speter		return NULL;
672738032Speter
672838032Speter	if (map_p->regex_subfields != NULL)
672938032Speter	{
673038032Speter		/* option -s */
673138032Speter		static char retbuf[MAXNAME];
673238032Speter		int fields[MAX_MATCH + 1];
673390792Sgshapiro		bool first = true;
673438032Speter		int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
673590792Sgshapiro		bool quotemode = false, bslashmode = false;
673638032Speter		register char *dp, *sp;
673738032Speter		char *endp, *ldp;
673838032Speter		int *ip;
673938032Speter
674038032Speter		dp = retbuf;
674138032Speter		ldp = retbuf + sizeof(retbuf) - 1;
674238032Speter
674338032Speter		if (av[1] != NULL)
674438032Speter		{
674538032Speter			if (parse_fields(av[1], fields, MAX_MATCH + 1,
674671345Sgshapiro					 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
674738032Speter			{
674838032Speter				*statp = EX_CONFIG;
674938032Speter				return NULL;
675038032Speter			}
675138032Speter			ip = fields;
675238032Speter		}
675338032Speter		else
675438032Speter			ip = map_p->regex_subfields;
675538032Speter
675638032Speter		for ( ; *ip != END_OF_FIELDS; ip++)
675738032Speter		{
675838032Speter			if (!first)
675938032Speter			{
676064562Sgshapiro				for (sp = map_p->regex_delim; *sp; sp++)
676138032Speter				{
676238032Speter					if (dp < ldp)
676338032Speter						*dp++ = *sp;
676438032Speter				}
676538032Speter			}
676638032Speter			else
676790792Sgshapiro				first = false;
676838032Speter
676971345Sgshapiro			if (*ip >= MAX_MATCH ||
677071345Sgshapiro			    pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
677138032Speter				continue;
677238032Speter
677338032Speter			sp = name + pmatch[*ip].rm_so;
677438032Speter			endp = name + pmatch[*ip].rm_eo;
677538032Speter			for (; endp > sp; sp++)
677638032Speter			{
677738032Speter				if (dp < ldp)
677838032Speter				{
677964562Sgshapiro					if (bslashmode)
678064562Sgshapiro					{
678138032Speter						*dp++ = *sp;
678290792Sgshapiro						bslashmode = false;
678338032Speter					}
678464562Sgshapiro					else if (quotemode && *sp != '"' &&
678538032Speter						*sp != '\\')
678638032Speter					{
678738032Speter						*dp++ = *sp;
678838032Speter					}
678990792Sgshapiro					else switch (*dp++ = *sp)
679038032Speter					{
679190792Sgshapiro					  case '\\':
679290792Sgshapiro						bslashmode = true;
679338032Speter						break;
679438032Speter
679590792Sgshapiro					  case '(':
679638032Speter						cmntcnt++;
679738032Speter						break;
679838032Speter
679990792Sgshapiro					  case ')':
680038032Speter						cmntcnt--;
680138032Speter						break;
680238032Speter
680390792Sgshapiro					  case '<':
680438032Speter						anglecnt++;
680538032Speter						break;
680638032Speter
680790792Sgshapiro					  case '>':
680838032Speter						anglecnt--;
680938032Speter						break;
681038032Speter
681190792Sgshapiro					  case ' ':
681238032Speter						spacecnt++;
681338032Speter						break;
681438032Speter
681590792Sgshapiro					  case '"':
681638032Speter						quotemode = !quotemode;
681738032Speter						break;
681838032Speter					}
681938032Speter				}
682038032Speter			}
682138032Speter		}
682238032Speter		if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
682338032Speter		    bslashmode || spacecnt != 0)
682438032Speter		{
682564562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
682664562Sgshapiro				  "Warning: regex may cause prescan() failure map=%s lookup=%s",
682764562Sgshapiro				  map->map_mname, name);
682838032Speter			return NULL;
682938032Speter		}
683038032Speter
683138032Speter		*dp = '\0';
683238032Speter
683338032Speter		return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
683438032Speter	}
683538032Speter	return regex_map_rewrite(map, "", (size_t)0, av);
683638032Speter}
683738032Speter#endif /* MAP_REGEX */
683890792Sgshapiro/*
683964562Sgshapiro**  NSD modules
684064562Sgshapiro*/
684190792Sgshapiro#if MAP_NSD
684264562Sgshapiro
684364562Sgshapiro# include <ndbm.h>
684464562Sgshapiro# define _DATUM_DEFINED
684564562Sgshapiro# include <ns_api.h>
684664562Sgshapiro
684764562Sgshapirotypedef struct ns_map_list
684864562Sgshapiro{
684990792Sgshapiro	ns_map_t		*map;		/* XXX ns_ ? */
685090792Sgshapiro	char			*mapname;
685190792Sgshapiro	struct ns_map_list	*next;
685264562Sgshapiro} ns_map_list_t;
685364562Sgshapiro
685464562Sgshapirostatic ns_map_t *
685564562Sgshapirons_map_t_find(mapname)
685664562Sgshapiro	char *mapname;
685764562Sgshapiro{
685864562Sgshapiro	static ns_map_list_t *ns_maps = NULL;
685964562Sgshapiro	ns_map_list_t *ns_map;
686064562Sgshapiro
686164562Sgshapiro	/* walk the list of maps looking for the correctly named map */
686264562Sgshapiro	for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
686364562Sgshapiro	{
686464562Sgshapiro		if (strcmp(ns_map->mapname, mapname) == 0)
686564562Sgshapiro			break;
686664562Sgshapiro	}
686764562Sgshapiro
686864562Sgshapiro	/* if we are looking at a NULL ns_map_list_t, then create a new one */
686964562Sgshapiro	if (ns_map == NULL)
687064562Sgshapiro	{
687164562Sgshapiro		ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map);
687264562Sgshapiro		ns_map->mapname = newstr(mapname);
687364562Sgshapiro		ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map);
6874102528Sgshapiro		memset(ns_map->map, '\0', sizeof *ns_map->map);
687564562Sgshapiro		ns_map->next = ns_maps;
687664562Sgshapiro		ns_maps = ns_map;
687764562Sgshapiro	}
687864562Sgshapiro	return ns_map->map;
687964562Sgshapiro}
688064562Sgshapiro
688164562Sgshapirochar *
688264562Sgshapironsd_map_lookup(map, name, av, statp)
688364562Sgshapiro	MAP *map;
688464562Sgshapiro	char *name;
688564562Sgshapiro	char **av;
688664562Sgshapiro	int *statp;
688764562Sgshapiro{
688871345Sgshapiro	int buflen, r;
688964562Sgshapiro	char *p;
689064562Sgshapiro	ns_map_t *ns_map;
689164562Sgshapiro	char keybuf[MAXNAME + 1];
689264562Sgshapiro	char buf[MAXLINE];
689364562Sgshapiro
689464562Sgshapiro	if (tTd(38, 20))
689590792Sgshapiro		sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
689664562Sgshapiro
689764562Sgshapiro	buflen = strlen(name);
689864562Sgshapiro	if (buflen > sizeof keybuf - 1)
689990792Sgshapiro		buflen = sizeof keybuf - 1;	/* XXX simply cut off? */
690064562Sgshapiro	memmove(keybuf, name, buflen);
690164562Sgshapiro	keybuf[buflen] = '\0';
690264562Sgshapiro	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
690364562Sgshapiro		makelower(keybuf);
690464562Sgshapiro
690564562Sgshapiro	ns_map = ns_map_t_find(map->map_file);
690664562Sgshapiro	if (ns_map == NULL)
690764562Sgshapiro	{
690864562Sgshapiro		if (tTd(38, 20))
690990792Sgshapiro			sm_dprintf("nsd_map_t_find failed\n");
691071345Sgshapiro		*statp = EX_UNAVAILABLE;
691164562Sgshapiro		return NULL;
691264562Sgshapiro	}
691398121Sgshapiro	r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
691498121Sgshapiro		      buf, sizeof buf);
691571345Sgshapiro	if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
691671345Sgshapiro	{
691771345Sgshapiro		*statp = EX_TEMPFAIL;
691864562Sgshapiro		return NULL;
691971345Sgshapiro	}
692077349Sgshapiro	if (r == NS_BADREQ
692177349Sgshapiro# ifdef NS_NOPERM
692277349Sgshapiro	    || r == NS_NOPERM
692377349Sgshapiro# endif /* NS_NOPERM */
692477349Sgshapiro	    )
692571345Sgshapiro	{
692671345Sgshapiro		*statp = EX_CONFIG;
692771345Sgshapiro		return NULL;
692871345Sgshapiro	}
692971345Sgshapiro	if (r != NS_SUCCESS)
693071345Sgshapiro	{
693171345Sgshapiro		*statp = EX_NOTFOUND;
693271345Sgshapiro		return NULL;
693371345Sgshapiro	}
693464562Sgshapiro
693571345Sgshapiro	*statp = EX_OK;
693671345Sgshapiro
693764562Sgshapiro	/* Null out trailing \n */
693864562Sgshapiro	if ((p = strchr(buf, '\n')) != NULL)
693964562Sgshapiro		*p = '\0';
694064562Sgshapiro
694164562Sgshapiro	return map_rewrite(map, buf, strlen(buf), av);
694264562Sgshapiro}
694364562Sgshapiro#endif /* MAP_NSD */
694464562Sgshapiro
694564562Sgshapirochar *
694664562Sgshapiroarith_map_lookup(map, name, av, statp)
694764562Sgshapiro	MAP *map;
694864562Sgshapiro	char *name;
694964562Sgshapiro	char **av;
695064562Sgshapiro	int *statp;
695164562Sgshapiro{
695264562Sgshapiro	long r;
695364562Sgshapiro	long v[2];
695490792Sgshapiro	bool res = false;
695564562Sgshapiro	bool boolres;
695664562Sgshapiro	static char result[16];
695764562Sgshapiro	char **cpp;
695864562Sgshapiro
695964562Sgshapiro	if (tTd(38, 2))
696064562Sgshapiro	{
696190792Sgshapiro		sm_dprintf("arith_map_lookup: key '%s'\n", name);
696264562Sgshapiro		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
696390792Sgshapiro			sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
696464562Sgshapiro	}
696564562Sgshapiro	r = 0;
696690792Sgshapiro	boolres = false;
696764562Sgshapiro	cpp = av;
696864562Sgshapiro	*statp = EX_OK;
696964562Sgshapiro
697064562Sgshapiro	/*
697164562Sgshapiro	**  read arguments for arith map
697264562Sgshapiro	**  - no check is made whether they are really numbers
697364562Sgshapiro	**  - just ignores args after the second
697464562Sgshapiro	*/
697590792Sgshapiro
697664562Sgshapiro	for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
697764562Sgshapiro		v[r++] = strtol(*cpp, NULL, 0);
697864562Sgshapiro
697964562Sgshapiro	/* operator and (at least) two operands given? */
698064562Sgshapiro	if (name != NULL && r == 2)
698164562Sgshapiro	{
698290792Sgshapiro		switch (*name)
698364562Sgshapiro		{
698464562Sgshapiro		  case '|':
698564562Sgshapiro			r = v[0] | v[1];
698664562Sgshapiro			break;
698764562Sgshapiro
698864562Sgshapiro		  case '&':
698964562Sgshapiro			r = v[0] & v[1];
699064562Sgshapiro			break;
699164562Sgshapiro
699264562Sgshapiro		  case '%':
699364562Sgshapiro			if (v[1] == 0)
699464562Sgshapiro				return NULL;
699564562Sgshapiro			r = v[0] % v[1];
699664562Sgshapiro			break;
699764562Sgshapiro		  case '+':
699864562Sgshapiro			r = v[0] + v[1];
699964562Sgshapiro			break;
700064562Sgshapiro
700164562Sgshapiro		  case '-':
700264562Sgshapiro			r = v[0] - v[1];
700364562Sgshapiro			break;
700464562Sgshapiro
700564562Sgshapiro		  case '*':
700664562Sgshapiro			r = v[0] * v[1];
700764562Sgshapiro			break;
700864562Sgshapiro
700964562Sgshapiro		  case '/':
701064562Sgshapiro			if (v[1] == 0)
701164562Sgshapiro				return NULL;
701264562Sgshapiro			r = v[0] / v[1];
701364562Sgshapiro			break;
701464562Sgshapiro
701564562Sgshapiro		  case 'l':
701664562Sgshapiro			res = v[0] < v[1];
701790792Sgshapiro			boolres = true;
701864562Sgshapiro			break;
701964562Sgshapiro
702064562Sgshapiro		  case '=':
702164562Sgshapiro			res = v[0] == v[1];
702290792Sgshapiro			boolres = true;
702364562Sgshapiro			break;
702464562Sgshapiro
702564562Sgshapiro		  default:
702664562Sgshapiro			/* XXX */
702764562Sgshapiro			*statp = EX_CONFIG;
702864562Sgshapiro			if (LogLevel > 10)
702964562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
703064562Sgshapiro					  "arith_map: unknown operator %c",
703164562Sgshapiro					  isprint(*name) ? *name : '?');
703264562Sgshapiro			return NULL;
703364562Sgshapiro		}
703464562Sgshapiro		if (boolres)
703590792Sgshapiro			(void) sm_snprintf(result, sizeof result,
703690792Sgshapiro				res ? "TRUE" : "FALSE");
703764562Sgshapiro		else
703890792Sgshapiro			(void) sm_snprintf(result, sizeof result, "%ld", r);
703964562Sgshapiro		return result;
704064562Sgshapiro	}
704164562Sgshapiro	*statp = EX_CONFIG;
704264562Sgshapiro	return NULL;
704364562Sgshapiro}
7044132943Sgshapiro
7045132943Sgshapiro#if SOCKETMAP
7046132943Sgshapiro
7047132943Sgshapiro# if NETINET || NETINET6
7048132943Sgshapiro#  include <arpa/inet.h>
7049132943Sgshapiro# endif /* NETINET || NETINET6 */
7050132943Sgshapiro
7051132943Sgshapiro# define socket_map_next map_stack[0]
7052132943Sgshapiro
7053132943Sgshapiro/*
7054132943Sgshapiro**  SOCKET_MAP_OPEN -- open socket table
7055132943Sgshapiro*/
7056132943Sgshapiro
7057132943Sgshapirobool
7058132943Sgshapirosocket_map_open(map, mode)
7059132943Sgshapiro	MAP *map;
7060132943Sgshapiro	int mode;
7061132943Sgshapiro{
7062132943Sgshapiro	STAB *s;
7063132943Sgshapiro	int sock = 0;
7064132943Sgshapiro	SOCKADDR_LEN_T addrlen = 0;
7065132943Sgshapiro	int addrno = 0;
7066132943Sgshapiro	int save_errno;
7067132943Sgshapiro	char *p;
7068132943Sgshapiro	char *colon;
7069132943Sgshapiro	char *at;
7070132943Sgshapiro	struct hostent *hp = NULL;
7071132943Sgshapiro	SOCKADDR addr;
7072132943Sgshapiro
7073132943Sgshapiro	if (tTd(38, 2))
7074132943Sgshapiro		sm_dprintf("socket_map_open(%s, %s, %d)\n",
7075132943Sgshapiro			map->map_mname, map->map_file, mode);
7076132943Sgshapiro
7077132943Sgshapiro	mode &= O_ACCMODE;
7078132943Sgshapiro
7079132943Sgshapiro	/* sendmail doesn't have the ability to write to SOCKET (yet) */
7080132943Sgshapiro	if (mode != O_RDONLY)
7081132943Sgshapiro	{
7082132943Sgshapiro		/* issue a pseudo-error message */
7083132943Sgshapiro		errno = SM_EMAPCANTWRITE;
7084132943Sgshapiro		return false;
7085132943Sgshapiro	}
7086132943Sgshapiro
7087132943Sgshapiro	if (*map->map_file == '\0')
7088132943Sgshapiro	{
7089132943Sgshapiro		syserr("socket map \"%s\": empty or missing socket information",
7090132943Sgshapiro			map->map_mname);
7091132943Sgshapiro		return false;
7092132943Sgshapiro	}
7093132943Sgshapiro
7094132943Sgshapiro	s = socket_map_findconn(map->map_file);
7095132943Sgshapiro	if (s->s_socketmap != NULL)
7096132943Sgshapiro	{
7097132943Sgshapiro		/* Copy open connection */
7098132943Sgshapiro		map->map_db1 = s->s_socketmap->map_db1;
7099132943Sgshapiro
7100132943Sgshapiro		/* Add this map as head of linked list */
7101132943Sgshapiro		map->socket_map_next = s->s_socketmap;
7102132943Sgshapiro		s->s_socketmap = map;
7103132943Sgshapiro
7104132943Sgshapiro		if (tTd(38, 2))
7105132943Sgshapiro			sm_dprintf("using cached connection\n");
7106132943Sgshapiro		return true;
7107132943Sgshapiro	}
7108132943Sgshapiro
7109132943Sgshapiro	if (tTd(38, 2))
7110132943Sgshapiro		sm_dprintf("opening new connection\n");
7111132943Sgshapiro
7112132943Sgshapiro	/* following code is ripped from milter.c */
7113132943Sgshapiro	/* XXX It should be put in a library... */
7114132943Sgshapiro
7115132943Sgshapiro	/* protocol:filename or protocol:port@host */
7116132943Sgshapiro	memset(&addr, '\0', sizeof addr);
7117132943Sgshapiro	p = map->map_file;
7118132943Sgshapiro	colon = strchr(p, ':');
7119132943Sgshapiro	if (colon != NULL)
7120132943Sgshapiro	{
7121132943Sgshapiro		*colon = '\0';
7122132943Sgshapiro
7123132943Sgshapiro		if (*p == '\0')
7124132943Sgshapiro		{
7125132943Sgshapiro# if NETUNIX
7126132943Sgshapiro			/* default to AF_UNIX */
7127132943Sgshapiro			addr.sa.sa_family = AF_UNIX;
7128132943Sgshapiro# else /* NETUNIX */
7129132943Sgshapiro#  if NETINET
7130132943Sgshapiro			/* default to AF_INET */
7131132943Sgshapiro			addr.sa.sa_family = AF_INET;
7132132943Sgshapiro#  else /* NETINET */
7133132943Sgshapiro#   if NETINET6
7134132943Sgshapiro			/* default to AF_INET6 */
7135132943Sgshapiro			addr.sa.sa_family = AF_INET6;
7136132943Sgshapiro#   else /* NETINET6 */
7137132943Sgshapiro			/* no protocols available */
7138132943Sgshapiro			syserr("socket map \"%s\": no valid socket protocols available",
7139132943Sgshapiro			map->map_mname);
7140132943Sgshapiro			return false;
7141132943Sgshapiro#   endif /* NETINET6 */
7142132943Sgshapiro#  endif /* NETINET */
7143132943Sgshapiro# endif /* NETUNIX */
7144132943Sgshapiro		}
7145132943Sgshapiro# if NETUNIX
7146132943Sgshapiro		else if (sm_strcasecmp(p, "unix") == 0 ||
7147132943Sgshapiro			 sm_strcasecmp(p, "local") == 0)
7148132943Sgshapiro			addr.sa.sa_family = AF_UNIX;
7149132943Sgshapiro# endif /* NETUNIX */
7150132943Sgshapiro# if NETINET
7151132943Sgshapiro		else if (sm_strcasecmp(p, "inet") == 0)
7152132943Sgshapiro			addr.sa.sa_family = AF_INET;
7153132943Sgshapiro# endif /* NETINET */
7154132943Sgshapiro# if NETINET6
7155132943Sgshapiro		else if (sm_strcasecmp(p, "inet6") == 0)
7156132943Sgshapiro			addr.sa.sa_family = AF_INET6;
7157132943Sgshapiro# endif /* NETINET6 */
7158132943Sgshapiro		else
7159132943Sgshapiro		{
7160132943Sgshapiro# ifdef EPROTONOSUPPORT
7161132943Sgshapiro			errno = EPROTONOSUPPORT;
7162132943Sgshapiro# else /* EPROTONOSUPPORT */
7163132943Sgshapiro			errno = EINVAL;
7164132943Sgshapiro# endif /* EPROTONOSUPPORT */
7165132943Sgshapiro			syserr("socket map \"%s\": unknown socket type %s",
7166132943Sgshapiro			       map->map_mname, p);
7167132943Sgshapiro			return false;
7168132943Sgshapiro		}
7169132943Sgshapiro		*colon++ = ':';
7170132943Sgshapiro	}
7171132943Sgshapiro	else
7172132943Sgshapiro	{
7173132943Sgshapiro		colon = p;
7174132943Sgshapiro#if NETUNIX
7175132943Sgshapiro		/* default to AF_UNIX */
7176132943Sgshapiro		addr.sa.sa_family = AF_UNIX;
7177132943Sgshapiro#else /* NETUNIX */
7178132943Sgshapiro# if NETINET
7179132943Sgshapiro		/* default to AF_INET */
7180132943Sgshapiro		addr.sa.sa_family = AF_INET;
7181132943Sgshapiro# else /* NETINET */
7182132943Sgshapiro#  if NETINET6
7183132943Sgshapiro		/* default to AF_INET6 */
7184132943Sgshapiro		addr.sa.sa_family = AF_INET6;
7185132943Sgshapiro#  else /* NETINET6 */
7186132943Sgshapiro		syserr("socket map \"%s\": unknown socket type %s",
7187132943Sgshapiro		       map->map_mname, p);
7188132943Sgshapiro		return false;
7189132943Sgshapiro#  endif /* NETINET6 */
7190132943Sgshapiro# endif /* NETINET */
7191132943Sgshapiro#endif /* NETUNIX */
7192132943Sgshapiro	}
7193132943Sgshapiro
7194132943Sgshapiro# if NETUNIX
7195132943Sgshapiro	if (addr.sa.sa_family == AF_UNIX)
7196132943Sgshapiro	{
7197132943Sgshapiro		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7198132943Sgshapiro
7199132943Sgshapiro		at = colon;
7200132943Sgshapiro		if (strlen(colon) >= sizeof addr.sunix.sun_path)
7201132943Sgshapiro		{
7202132943Sgshapiro			syserr("socket map \"%s\": local socket name %s too long",
7203132943Sgshapiro			       map->map_mname, colon);
7204132943Sgshapiro			return false;
7205132943Sgshapiro		}
7206132943Sgshapiro		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7207132943Sgshapiro				 S_IRUSR|S_IWUSR, NULL);
7208132943Sgshapiro
7209132943Sgshapiro		if (errno != 0)
7210132943Sgshapiro		{
7211132943Sgshapiro			/* if not safe, don't create */
7212132943Sgshapiro				syserr("socket map \"%s\": local socket name %s unsafe",
7213132943Sgshapiro			       map->map_mname, colon);
7214132943Sgshapiro			return false;
7215132943Sgshapiro		}
7216132943Sgshapiro
7217132943Sgshapiro		(void) sm_strlcpy(addr.sunix.sun_path, colon,
7218132943Sgshapiro			       sizeof addr.sunix.sun_path);
7219132943Sgshapiro		addrlen = sizeof (struct sockaddr_un);
7220132943Sgshapiro	}
7221132943Sgshapiro	else
7222132943Sgshapiro# endif /* NETUNIX */
7223132943Sgshapiro# if NETINET || NETINET6
7224132943Sgshapiro	if (false
7225132943Sgshapiro#  if NETINET
7226132943Sgshapiro		 || addr.sa.sa_family == AF_INET
7227132943Sgshapiro#  endif /* NETINET */
7228132943Sgshapiro#  if NETINET6
7229132943Sgshapiro		 || addr.sa.sa_family == AF_INET6
7230132943Sgshapiro#  endif /* NETINET6 */
7231132943Sgshapiro		 )
7232132943Sgshapiro	{
7233132943Sgshapiro		unsigned short port;
7234132943Sgshapiro
7235132943Sgshapiro		/* Parse port@host */
7236132943Sgshapiro		at = strchr(colon, '@');
7237132943Sgshapiro		if (at == NULL)
7238132943Sgshapiro		{
7239132943Sgshapiro			syserr("socket map \"%s\": bad address %s (expected port@host)",
7240132943Sgshapiro				       map->map_mname, colon);
7241132943Sgshapiro			return false;
7242132943Sgshapiro		}
7243132943Sgshapiro		*at = '\0';
7244132943Sgshapiro		if (isascii(*colon) && isdigit(*colon))
7245132943Sgshapiro			port = htons((unsigned short) atoi(colon));
7246132943Sgshapiro		else
7247132943Sgshapiro		{
7248132943Sgshapiro#  ifdef NO_GETSERVBYNAME
7249132943Sgshapiro			syserr("socket map \"%s\": invalid port number %s",
7250132943Sgshapiro				       map->map_mname, colon);
7251132943Sgshapiro			return false;
7252132943Sgshapiro#  else /* NO_GETSERVBYNAME */
7253132943Sgshapiro			register struct servent *sp;
7254132943Sgshapiro
7255132943Sgshapiro			sp = getservbyname(colon, "tcp");
7256132943Sgshapiro			if (sp == NULL)
7257132943Sgshapiro			{
7258132943Sgshapiro				syserr("socket map \"%s\": unknown port name %s",
7259132943Sgshapiro					       map->map_mname, colon);
7260132943Sgshapiro				return false;
7261132943Sgshapiro			}
7262132943Sgshapiro			port = sp->s_port;
7263132943Sgshapiro#  endif /* NO_GETSERVBYNAME */
7264132943Sgshapiro		}
7265132943Sgshapiro		*at++ = '@';
7266132943Sgshapiro		if (*at == '[')
7267132943Sgshapiro		{
7268132943Sgshapiro			char *end;
7269132943Sgshapiro
7270132943Sgshapiro			end = strchr(at, ']');
7271132943Sgshapiro			if (end != NULL)
7272132943Sgshapiro			{
7273132943Sgshapiro				bool found = false;
7274132943Sgshapiro#  if NETINET
7275132943Sgshapiro				unsigned long hid = INADDR_NONE;
7276132943Sgshapiro#  endif /* NETINET */
7277132943Sgshapiro#  if NETINET6
7278132943Sgshapiro				struct sockaddr_in6 hid6;
7279132943Sgshapiro#  endif /* NETINET6 */
7280132943Sgshapiro
7281132943Sgshapiro				*end = '\0';
7282132943Sgshapiro#  if NETINET
7283132943Sgshapiro				if (addr.sa.sa_family == AF_INET &&
7284132943Sgshapiro				    (hid = inet_addr(&at[1])) != INADDR_NONE)
7285132943Sgshapiro				{
7286132943Sgshapiro					addr.sin.sin_addr.s_addr = hid;
7287132943Sgshapiro					addr.sin.sin_port = port;
7288132943Sgshapiro					found = true;
7289132943Sgshapiro				}
7290132943Sgshapiro#  endif /* NETINET */
7291132943Sgshapiro#  if NETINET6
7292132943Sgshapiro				(void) memset(&hid6, '\0', sizeof hid6);
7293132943Sgshapiro				if (addr.sa.sa_family == AF_INET6 &&
7294132943Sgshapiro				    anynet_pton(AF_INET6, &at[1],
7295132943Sgshapiro						&hid6.sin6_addr) == 1)
7296132943Sgshapiro				{
7297132943Sgshapiro					addr.sin6.sin6_addr = hid6.sin6_addr;
7298132943Sgshapiro					addr.sin6.sin6_port = port;
7299132943Sgshapiro					found = true;
7300132943Sgshapiro				}
7301132943Sgshapiro#  endif /* NETINET6 */
7302132943Sgshapiro				*end = ']';
7303132943Sgshapiro				if (!found)
7304132943Sgshapiro				{
7305132943Sgshapiro					syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7306132943Sgshapiro					       map->map_mname, at);
7307132943Sgshapiro					return false;
7308132943Sgshapiro				}
7309132943Sgshapiro			}
7310132943Sgshapiro			else
7311132943Sgshapiro			{
7312132943Sgshapiro				syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7313132943Sgshapiro				       map->map_mname, at);
7314132943Sgshapiro				return false;
7315132943Sgshapiro			}
7316132943Sgshapiro		}
7317132943Sgshapiro		else
7318132943Sgshapiro		{
7319132943Sgshapiro			hp = sm_gethostbyname(at, addr.sa.sa_family);
7320132943Sgshapiro			if (hp == NULL)
7321132943Sgshapiro			{
7322132943Sgshapiro				syserr("socket map \"%s\": Unknown host name %s",
7323132943Sgshapiro					map->map_mname, at);
7324132943Sgshapiro				return false;
7325132943Sgshapiro			}
7326132943Sgshapiro			addr.sa.sa_family = hp->h_addrtype;
7327132943Sgshapiro			switch (hp->h_addrtype)
7328132943Sgshapiro			{
7329132943Sgshapiro#  if NETINET
7330132943Sgshapiro			  case AF_INET:
7331132943Sgshapiro				memmove(&addr.sin.sin_addr,
7332132943Sgshapiro					hp->h_addr, INADDRSZ);
7333132943Sgshapiro				addr.sin.sin_port = port;
7334132943Sgshapiro				addrlen = sizeof (struct sockaddr_in);
7335132943Sgshapiro				addrno = 1;
7336132943Sgshapiro				break;
7337132943Sgshapiro#  endif /* NETINET */
7338132943Sgshapiro
7339132943Sgshapiro#  if NETINET6
7340132943Sgshapiro			  case AF_INET6:
7341132943Sgshapiro				memmove(&addr.sin6.sin6_addr,
7342132943Sgshapiro					hp->h_addr, IN6ADDRSZ);
7343132943Sgshapiro				addr.sin6.sin6_port = port;
7344132943Sgshapiro				addrlen = sizeof (struct sockaddr_in6);
7345132943Sgshapiro				addrno = 1;
7346132943Sgshapiro				break;
7347132943Sgshapiro#  endif /* NETINET6 */
7348132943Sgshapiro
7349132943Sgshapiro			  default:
7350132943Sgshapiro				syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7351132943Sgshapiro					map->map_mname, at, hp->h_addrtype);
7352132943Sgshapiro#  if NETINET6
7353132943Sgshapiro				freehostent(hp);
7354132943Sgshapiro#  endif /* NETINET6 */
7355132943Sgshapiro				return false;
7356132943Sgshapiro			}
7357132943Sgshapiro		}
7358132943Sgshapiro	}
7359132943Sgshapiro	else
7360132943Sgshapiro# endif /* NETINET || NETINET6 */
7361132943Sgshapiro	{
7362132943Sgshapiro		syserr("socket map \"%s\": unknown socket protocol",
7363132943Sgshapiro			map->map_mname);
7364132943Sgshapiro		return false;
7365132943Sgshapiro	}
7366132943Sgshapiro
7367132943Sgshapiro	/* nope, actually connecting */
7368132943Sgshapiro	for (;;)
7369132943Sgshapiro	{
7370132943Sgshapiro		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
7371132943Sgshapiro		if (sock < 0)
7372132943Sgshapiro		{
7373132943Sgshapiro			save_errno = errno;
7374132943Sgshapiro			if (tTd(38, 5))
7375132943Sgshapiro				sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7376132943Sgshapiro					   map->map_mname,
7377132943Sgshapiro					   sm_errstring(save_errno));
7378132943Sgshapiro# if NETINET6
7379132943Sgshapiro			if (hp != NULL)
7380132943Sgshapiro				freehostent(hp);
7381132943Sgshapiro# endif /* NETINET6 */
7382132943Sgshapiro			return false;
7383132943Sgshapiro		}
7384132943Sgshapiro
7385132943Sgshapiro		if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
7386132943Sgshapiro			break;
7387132943Sgshapiro
7388132943Sgshapiro		/* couldn't connect.... try next address */
7389132943Sgshapiro		save_errno = errno;
7390132943Sgshapiro		p = CurHostName;
7391132943Sgshapiro		CurHostName = at;
7392132943Sgshapiro		if (tTd(38, 5))
7393132943Sgshapiro			sm_dprintf("socket_open (%s): open %s failed: %s\n",
7394132943Sgshapiro				map->map_mname, at, sm_errstring(save_errno));
7395132943Sgshapiro		CurHostName = p;
7396132943Sgshapiro		(void) close(sock);
7397132943Sgshapiro
7398132943Sgshapiro		/* try next address */
7399132943Sgshapiro		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
7400132943Sgshapiro		{
7401132943Sgshapiro			switch (addr.sa.sa_family)
7402132943Sgshapiro			{
7403132943Sgshapiro# if NETINET
7404132943Sgshapiro			  case AF_INET:
7405132943Sgshapiro				memmove(&addr.sin.sin_addr,
7406132943Sgshapiro					hp->h_addr_list[addrno++],
7407132943Sgshapiro					INADDRSZ);
7408132943Sgshapiro				break;
7409132943Sgshapiro# endif /* NETINET */
7410132943Sgshapiro
7411132943Sgshapiro# if NETINET6
7412132943Sgshapiro			  case AF_INET6:
7413132943Sgshapiro				memmove(&addr.sin6.sin6_addr,
7414132943Sgshapiro					hp->h_addr_list[addrno++],
7415132943Sgshapiro					IN6ADDRSZ);
7416132943Sgshapiro				break;
7417132943Sgshapiro# endif /* NETINET6 */
7418132943Sgshapiro
7419132943Sgshapiro			  default:
7420132943Sgshapiro				if (tTd(38, 5))
7421132943Sgshapiro					sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7422132943Sgshapiro						   map->map_mname, at,
7423132943Sgshapiro						   hp->h_addrtype);
7424132943Sgshapiro# if NETINET6
7425132943Sgshapiro				freehostent(hp);
7426132943Sgshapiro# endif /* NETINET6 */
7427132943Sgshapiro				return false;
7428132943Sgshapiro			}
7429132943Sgshapiro			continue;
7430132943Sgshapiro		}
7431132943Sgshapiro		p = CurHostName;
7432132943Sgshapiro		CurHostName = at;
7433132943Sgshapiro		if (tTd(38, 5))
7434132943Sgshapiro			sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7435132943Sgshapiro				   map->map_mname, sm_errstring(save_errno));
7436132943Sgshapiro		CurHostName = p;
7437132943Sgshapiro# if NETINET6
7438132943Sgshapiro		if (hp != NULL)
7439132943Sgshapiro			freehostent(hp);
7440132943Sgshapiro# endif /* NETINET6 */
7441132943Sgshapiro		return false;
7442132943Sgshapiro	}
7443132943Sgshapiro# if NETINET6
7444132943Sgshapiro	if (hp != NULL)
7445132943Sgshapiro	{
7446132943Sgshapiro		freehostent(hp);
7447132943Sgshapiro		hp = NULL;
7448132943Sgshapiro	}
7449132943Sgshapiro# endif /* NETINET6 */
7450132943Sgshapiro	if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
7451132943Sgshapiro						  SM_TIME_DEFAULT,
7452132943Sgshapiro						  (void *) &sock,
7453132943Sgshapiro						  SM_IO_RDWR,
7454132943Sgshapiro						  NULL)) == NULL)
7455132943Sgshapiro	{
7456132943Sgshapiro		close(sock);
7457132943Sgshapiro		if (tTd(38, 2))
7458132943Sgshapiro		    sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7459132943Sgshapiro			       map->map_mname, sm_errstring(errno));
7460132943Sgshapiro		return false;
7461132943Sgshapiro	}
7462132943Sgshapiro
7463132943Sgshapiro	/* Save connection for reuse */
7464132943Sgshapiro	s->s_socketmap = map;
7465132943Sgshapiro	return true;
7466132943Sgshapiro}
7467132943Sgshapiro
7468132943Sgshapiro/*
7469132943Sgshapiro**  SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7470132943Sgshapiro**
7471132943Sgshapiro**	Cache SOCKET connections based on the connection specifier
7472132943Sgshapiro**	and PID so we don't have multiple connections open to
7473132943Sgshapiro**	the same server for different maps.  Need a separate connection
7474132943Sgshapiro**	per PID since a parent process may close the map before the
7475132943Sgshapiro**	child is done with it.
7476132943Sgshapiro**
7477132943Sgshapiro**	Parameters:
7478132943Sgshapiro**		conn -- SOCKET map connection specifier
7479132943Sgshapiro**
7480132943Sgshapiro**	Returns:
7481132943Sgshapiro**		Symbol table entry for the SOCKET connection.
7482132943Sgshapiro*/
7483132943Sgshapiro
7484132943Sgshapirostatic STAB *
7485132943Sgshapirosocket_map_findconn(conn)
7486132943Sgshapiro	const char *conn;
7487132943Sgshapiro{
7488132943Sgshapiro	char *nbuf;
7489132943Sgshapiro	STAB *SM_NONVOLATILE s = NULL;
7490132943Sgshapiro
7491132943Sgshapiro	nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
7492132943Sgshapiro	SM_TRY
7493132943Sgshapiro		s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
7494132943Sgshapiro	SM_FINALLY
7495132943Sgshapiro		sm_free(nbuf);
7496132943Sgshapiro	SM_END_TRY
7497132943Sgshapiro	return s;
7498132943Sgshapiro}
7499132943Sgshapiro
7500132943Sgshapiro/*
7501132943Sgshapiro**  SOCKET_MAP_CLOSE -- close the socket
7502132943Sgshapiro*/
7503132943Sgshapiro
7504132943Sgshapirovoid
7505132943Sgshapirosocket_map_close(map)
7506132943Sgshapiro	MAP *map;
7507132943Sgshapiro{
7508132943Sgshapiro	STAB *s;
7509132943Sgshapiro	MAP *smap;
7510132943Sgshapiro
7511132943Sgshapiro	if (tTd(38, 20))
7512132943Sgshapiro		sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
7513132943Sgshapiro			(long) CurrentPid);
7514132943Sgshapiro
7515132943Sgshapiro	/* Check if already closed */
7516132943Sgshapiro	if (map->map_db1 == NULL)
7517132943Sgshapiro	{
7518132943Sgshapiro		if (tTd(38, 20))
7519132943Sgshapiro			sm_dprintf("socket_map_close(%s) already closed\n",
7520132943Sgshapiro				map->map_file);
7521132943Sgshapiro		return;
7522132943Sgshapiro	}
7523132943Sgshapiro	sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
7524132943Sgshapiro
7525132943Sgshapiro	/* Mark all the maps that share the connection as closed */
7526132943Sgshapiro	s = socket_map_findconn(map->map_file);
7527132943Sgshapiro	smap = s->s_socketmap;
7528132943Sgshapiro	while (smap != NULL)
7529132943Sgshapiro	{
7530132943Sgshapiro		MAP *next;
7531132943Sgshapiro
7532132943Sgshapiro		if (tTd(38, 2) && smap != map)
7533132943Sgshapiro			sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7534132943Sgshapiro				map->map_mname, smap->map_mname);
7535132943Sgshapiro
7536132943Sgshapiro		smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
7537132943Sgshapiro		smap->map_db1 = NULL;
7538132943Sgshapiro		next = smap->socket_map_next;
7539132943Sgshapiro		smap->socket_map_next = NULL;
7540132943Sgshapiro		smap = next;
7541132943Sgshapiro	}
7542132943Sgshapiro	s->s_socketmap = NULL;
7543132943Sgshapiro}
7544132943Sgshapiro
7545132943Sgshapiro/*
7546132943Sgshapiro** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7547132943Sgshapiro*/
7548132943Sgshapiro
7549132943Sgshapirochar *
7550132943Sgshapirosocket_map_lookup(map, name, av, statp)
7551132943Sgshapiro	MAP *map;
7552132943Sgshapiro	char *name;
7553132943Sgshapiro	char **av;
7554132943Sgshapiro	int *statp;
7555132943Sgshapiro{
7556132943Sgshapiro	unsigned int nettolen, replylen, recvlen;
7557132943Sgshapiro	char *replybuf, *rval, *value, *status;
7558132943Sgshapiro	SM_FILE_T *f;
7559132943Sgshapiro
7560132943Sgshapiro	replybuf = NULL;
7561132943Sgshapiro	rval = NULL;
7562132943Sgshapiro	f = (SM_FILE_T *)map->map_db1;
7563132943Sgshapiro	if (tTd(38, 20))
7564132943Sgshapiro		sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7565132943Sgshapiro			map->map_mname, name, map->map_file);
7566132943Sgshapiro
7567132943Sgshapiro	nettolen = strlen(map->map_mname) + 1 + strlen(name);
7568132943Sgshapiro	SM_ASSERT(nettolen > strlen(map->map_mname));
7569132943Sgshapiro	SM_ASSERT(nettolen > strlen(name));
7570132943Sgshapiro	if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
7571132943Sgshapiro			   nettolen, map->map_mname, name) == SM_IO_EOF) ||
7572132943Sgshapiro	    (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
7573132943Sgshapiro	    (sm_io_error(f)))
7574132943Sgshapiro	{
7575132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
7576132943Sgshapiro			map->map_mname);
7577132943Sgshapiro		*statp = EX_TEMPFAIL;
7578132943Sgshapiro		goto errcl;
7579132943Sgshapiro	}
7580132943Sgshapiro
7581132943Sgshapiro	if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
7582132943Sgshapiro	{
7583132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
7584132943Sgshapiro			map->map_mname);
7585132943Sgshapiro		*statp = EX_TEMPFAIL;
7586132943Sgshapiro		goto errcl;
7587132943Sgshapiro	}
7588132943Sgshapiro	if (replylen > SOCKETMAP_MAXL)
7589132943Sgshapiro	{
7590132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
7591132943Sgshapiro			   map->map_mname, replylen);
7592132943Sgshapiro		*statp = EX_TEMPFAIL;
7593132943Sgshapiro		goto errcl;
7594132943Sgshapiro	}
7595132943Sgshapiro	if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
7596132943Sgshapiro	{
7597132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
7598132943Sgshapiro			map->map_mname);
7599132943Sgshapiro		*statp = EX_TEMPFAIL;
7600132943Sgshapiro		goto error;
7601132943Sgshapiro	}
7602132943Sgshapiro
7603132943Sgshapiro	replybuf = (char *) sm_malloc(replylen + 1);
7604132943Sgshapiro	if (replybuf == NULL)
7605132943Sgshapiro	{
7606132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
7607132943Sgshapiro			map->map_mname, replylen + 1);
7608132943Sgshapiro		*statp = EX_OSERR;
7609132943Sgshapiro		goto error;
7610132943Sgshapiro	}
7611132943Sgshapiro
7612132943Sgshapiro	recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
7613132943Sgshapiro	if (recvlen < replylen)
7614132943Sgshapiro	{
7615132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
7616132943Sgshapiro			   map->map_mname, recvlen, replylen);
7617132943Sgshapiro		*statp = EX_TEMPFAIL;
7618132943Sgshapiro		goto errcl;
7619132943Sgshapiro	}
7620132943Sgshapiro	if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
7621132943Sgshapiro	{
7622132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
7623132943Sgshapiro			map->map_mname);
7624132943Sgshapiro		*statp = EX_TEMPFAIL;
7625132943Sgshapiro		goto errcl;
7626132943Sgshapiro	}
7627132943Sgshapiro	status = replybuf;
7628132943Sgshapiro	replybuf[recvlen] = '\0';
7629132943Sgshapiro	value = strchr(replybuf, ' ');
7630132943Sgshapiro	if (value != NULL)
7631132943Sgshapiro	{
7632132943Sgshapiro		*value = '\0';
7633132943Sgshapiro		value++;
7634132943Sgshapiro	}
7635132943Sgshapiro	if (strcmp(status, "OK") == 0)
7636132943Sgshapiro	{
7637132943Sgshapiro		*statp = EX_OK;
7638132943Sgshapiro
7639132943Sgshapiro		/* collect the return value */
7640132943Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
7641132943Sgshapiro			rval = map_rewrite(map, name, strlen(name), NULL);
7642132943Sgshapiro		else
7643132943Sgshapiro			rval = map_rewrite(map, value, strlen(value), av);
7644132943Sgshapiro	}
7645132943Sgshapiro	else if (strcmp(status, "NOTFOUND") == 0)
7646132943Sgshapiro	{
7647132943Sgshapiro		*statp = EX_NOTFOUND;
7648132943Sgshapiro		if (tTd(38, 20))
7649132943Sgshapiro			sm_dprintf("socket_map_lookup(%s): %s not found\n",
7650132943Sgshapiro				map->map_mname, name);
7651132943Sgshapiro	}
7652132943Sgshapiro	else
7653132943Sgshapiro	{
7654132943Sgshapiro		if (tTd(38, 5))
7655132943Sgshapiro			sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
7656132943Sgshapiro				map->map_mname, name, status,
7657132943Sgshapiro				value ? value : "");
7658132943Sgshapiro		if ((strcmp(status, "TEMP") == 0) ||
7659132943Sgshapiro		    (strcmp(status, "TIMEOUT") == 0))
7660132943Sgshapiro			*statp = EX_TEMPFAIL;
7661132943Sgshapiro		else if(strcmp(status, "PERM") == 0)
7662132943Sgshapiro			*statp = EX_UNAVAILABLE;
7663132943Sgshapiro		else
7664132943Sgshapiro			*statp = EX_PROTOCOL;
7665132943Sgshapiro	}
7666132943Sgshapiro
7667132943Sgshapiro	if (replybuf != NULL)
7668132943Sgshapiro		sm_free(replybuf);
7669132943Sgshapiro	return rval;
7670132943Sgshapiro
7671132943Sgshapiro  errcl:
7672132943Sgshapiro	socket_map_close(map);
7673132943Sgshapiro  error:
7674132943Sgshapiro	if (replybuf != NULL)
7675132943Sgshapiro		sm_free(replybuf);
7676132943Sgshapiro	return rval;
7677132943Sgshapiro}
7678132943Sgshapiro#endif /* SOCKETMAP */
7679