map.c revision 147078
138032Speter/*
2147078Sgshapiro * Copyright (c) 1998-2005 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
16147078SgshapiroSM_RCSID("@(#)$Id: map.c,v 8.669 2005/02/09 01:46:35 ca Exp $")
1764562Sgshapiro
1890792Sgshapiro#if LDAPMAP
1990792Sgshapiro# include <sm/ldap.h>
2090792Sgshapiro#endif /* LDAPMAP */
2190792Sgshapiro
2290792Sgshapiro#if NDBM
2338032Speter# include <ndbm.h>
2438032Speter# ifdef R_FIRST
2538032Speter  ERROR README:	You are running the Berkeley DB version of ndbm.h.  See
2638032Speter  ERROR README:	the README file about tweaking Berkeley DB so it can
2738032Speter  ERROR README:	coexist with NDBM, or delete -DNDBM from the Makefile
2838032Speter  ERROR README: and use -DNEWDB instead.
2964562Sgshapiro# endif /* R_FIRST */
3064562Sgshapiro#endif /* NDBM */
3190792Sgshapiro#if NEWDB
32110560Sgshapiro# include "sm/bdb.h"
3364562Sgshapiro#endif /* NEWDB */
3490792Sgshapiro#if NIS
3538032Speter  struct dom_binding;	/* forward reference needed on IRIX */
3638032Speter# include <rpcsvc/ypclnt.h>
3790792Sgshapiro# if NDBM
3838032Speter#  define NDBM_YP_COMPAT	/* create YP-compatible NDBM files */
3964562Sgshapiro# endif /* NDBM */
4064562Sgshapiro#endif /* NIS */
4138032Speter
4290792Sgshapiro#if NEWDB
4364562Sgshapiro# if DB_VERSION_MAJOR < 2
4464562Sgshapirostatic bool	db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
4564562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
4664562Sgshapiro# if DB_VERSION_MAJOR == 2
4764562Sgshapirostatic bool	db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
4864562Sgshapiro# endif /* DB_VERSION_MAJOR == 2 */
4964562Sgshapiro# if DB_VERSION_MAJOR > 2
5064562Sgshapirostatic bool	db_map_open __P((MAP *, int, char *, DBTYPE, void **));
5164562Sgshapiro# endif /* DB_VERSION_MAJOR > 2 */
5264562Sgshapiro#endif /* NEWDB */
5373188Sgshapirostatic bool	extract_canonname __P((char *, char *, char *, char[], int));
5490792Sgshapirostatic void	map_close __P((STAB *, int));
5590792Sgshapirostatic void	map_init __P((STAB *, int));
5664562Sgshapiro#ifdef LDAPMAP
5790792Sgshapirostatic STAB *	ldapmap_findconn __P((SM_LDAP_STRUCT *));
5864562Sgshapiro#endif /* LDAPMAP */
5990792Sgshapiro#if NISPLUS
6064562Sgshapirostatic bool	nisplus_getcanonname __P((char *, int, int *));
6164562Sgshapiro#endif /* NISPLUS */
6290792Sgshapiro#if NIS
6364562Sgshapirostatic bool	nis_getcanonname __P((char *, int, int *));
6464562Sgshapiro#endif /* NIS */
6564562Sgshapiro#if NETINFO
6664562Sgshapirostatic bool	ni_getcanonname __P((char *, int, int *));
6764562Sgshapiro#endif /* NETINFO */
6864562Sgshapirostatic bool	text_getcanonname __P((char *, int, int *));
69132943Sgshapiro#if SOCKETMAP
70132943Sgshapirostatic STAB	*socket_map_findconn __P((const char*));
7164562Sgshapiro
72132943Sgshapiro/* XXX arbitrary limit for sanity */
73132943Sgshapiro# define SOCKETMAP_MAXL 1000000
74132943Sgshapiro#endif /* SOCKETMAP */
75132943Sgshapiro
7690792Sgshapiro/* default error message for trying to open a map in write mode */
7790792Sgshapiro#ifdef ENOSYS
7890792Sgshapiro# define SM_EMAPCANTWRITE	ENOSYS
7990792Sgshapiro#else /* ENOSYS */
8090792Sgshapiro# ifdef EFTYPE
8190792Sgshapiro#  define SM_EMAPCANTWRITE	EFTYPE
8290792Sgshapiro# else /* EFTYPE */
8390792Sgshapiro#  define SM_EMAPCANTWRITE	ENXIO
8490792Sgshapiro# endif /* EFTYPE */
8590792Sgshapiro#endif /* ENOSYS */
8690792Sgshapiro
8738032Speter/*
8838032Speter**  MAP.C -- implementations for various map classes.
8938032Speter**
9038032Speter**	Each map class implements a series of functions:
9138032Speter**
9238032Speter**	bool map_parse(MAP *map, char *args)
9390792Sgshapiro**		Parse the arguments from the config file.  Return true
9490792Sgshapiro**		if they were ok, false otherwise.  Fill in map with the
9538032Speter**		values.
9638032Speter**
9738032Speter**	char *map_lookup(MAP *map, char *key, char **args, int *pstat)
9838032Speter**		Look up the key in the given map.  If found, do any
9938032Speter**		rewriting the map wants (including "args" if desired)
10038032Speter**		and return the value.  Set *pstat to the appropriate status
10138032Speter**		on error and return NULL.  Args will be NULL if called
10238032Speter**		from the alias routines, although this should probably
10338032Speter**		not be relied upon.  It is suggested you call map_rewrite
10438032Speter**		to return the results -- it takes care of null termination
10538032Speter**		and uses a dynamically expanded buffer as needed.
10638032Speter**
10738032Speter**	void map_store(MAP *map, char *key, char *value)
10838032Speter**		Store the key:value pair in the map.
10938032Speter**
11038032Speter**	bool map_open(MAP *map, int mode)
11138032Speter**		Open the map for the indicated mode.  Mode should
11290792Sgshapiro**		be either O_RDONLY or O_RDWR.  Return true if it
11390792Sgshapiro**		was opened successfully, false otherwise.  If the open
11490792Sgshapiro**		failed and the MF_OPTIONAL flag is not set, it should
11538032Speter**		also print an error.  If the MF_ALIAS bit is set
11638032Speter**		and this map class understands the @:@ convention, it
11738032Speter**		should call aliaswait() before returning.
11838032Speter**
11938032Speter**	void map_close(MAP *map)
12038032Speter**		Close the map.
12138032Speter**
12238032Speter**	This file also includes the implementation for getcanonname.
12338032Speter**	It is currently implemented in a pretty ad-hoc manner; it ought
12438032Speter**	to be more properly integrated into the map structure.
12538032Speter*/
12638032Speter
12738032Speter#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
12838032Speter# define LOCK_ON_OPEN	1	/* we can open/create a locked file */
12964562Sgshapiro#else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
13038032Speter# define LOCK_ON_OPEN	0	/* no such luck -- bend over backwards */
13164562Sgshapiro#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
13238032Speter
13390792Sgshapiro/*
13438032Speter**  MAP_PARSEARGS -- parse config line arguments for database lookup
13538032Speter**
13638032Speter**	This is a generic version of the map_parse method.
13738032Speter**
13838032Speter**	Parameters:
13938032Speter**		map -- the map being initialized.
14038032Speter**		ap -- a pointer to the args on the config line.
14138032Speter**
14238032Speter**	Returns:
14390792Sgshapiro**		true -- if everything parsed OK.
14490792Sgshapiro**		false -- otherwise.
14538032Speter**
14638032Speter**	Side Effects:
14738032Speter**		null terminates the filename; stores it in map
14838032Speter*/
14938032Speter
15038032Speterbool
15138032Spetermap_parseargs(map, ap)
15238032Speter	MAP *map;
15338032Speter	char *ap;
15438032Speter{
15538032Speter	register char *p = ap;
15638032Speter
15764562Sgshapiro	/*
15890792Sgshapiro	**  There is no check whether there is really an argument,
15990792Sgshapiro	**  but that's not important enough to warrant extra code.
16064562Sgshapiro	*/
16190792Sgshapiro
16290792Sgshapiro	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
16364562Sgshapiro	map->map_spacesub = SpaceSub;	/* default value */
16438032Speter	for (;;)
16538032Speter	{
16638032Speter		while (isascii(*p) && isspace(*p))
16738032Speter			p++;
16838032Speter		if (*p != '-')
16938032Speter			break;
17038032Speter		switch (*++p)
17138032Speter		{
17238032Speter		  case 'N':
17338032Speter			map->map_mflags |= MF_INCLNULL;
17438032Speter			map->map_mflags &= ~MF_TRY0NULL;
17538032Speter			break;
17638032Speter
17738032Speter		  case 'O':
17838032Speter			map->map_mflags &= ~MF_TRY1NULL;
17938032Speter			break;
18038032Speter
18138032Speter		  case 'o':
18238032Speter			map->map_mflags |= MF_OPTIONAL;
18338032Speter			break;
18438032Speter
18538032Speter		  case 'f':
18638032Speter			map->map_mflags |= MF_NOFOLDCASE;
18738032Speter			break;
18838032Speter
18938032Speter		  case 'm':
19038032Speter			map->map_mflags |= MF_MATCHONLY;
19138032Speter			break;
19238032Speter
19338032Speter		  case 'A':
19438032Speter			map->map_mflags |= MF_APPEND;
19538032Speter			break;
19638032Speter
19738032Speter		  case 'q':
19838032Speter			map->map_mflags |= MF_KEEPQUOTES;
19938032Speter			break;
20038032Speter
20138032Speter		  case 'a':
20238032Speter			map->map_app = ++p;
20338032Speter			break;
20438032Speter
20538032Speter		  case 'T':
20638032Speter			map->map_tapp = ++p;
20738032Speter			break;
20838032Speter
20938032Speter		  case 'k':
21038032Speter			while (isascii(*++p) && isspace(*p))
21138032Speter				continue;
21238032Speter			map->map_keycolnm = p;
21338032Speter			break;
21438032Speter
21538032Speter		  case 'v':
21638032Speter			while (isascii(*++p) && isspace(*p))
21738032Speter				continue;
21838032Speter			map->map_valcolnm = p;
21938032Speter			break;
22038032Speter
22138032Speter		  case 'z':
22238032Speter			if (*++p != '\\')
22338032Speter				map->map_coldelim = *p;
22438032Speter			else
22538032Speter			{
22638032Speter				switch (*++p)
22738032Speter				{
22838032Speter				  case 'n':
22938032Speter					map->map_coldelim = '\n';
23038032Speter					break;
23138032Speter
23238032Speter				  case 't':
23338032Speter					map->map_coldelim = '\t';
23438032Speter					break;
23538032Speter
23638032Speter				  default:
23738032Speter					map->map_coldelim = '\\';
23838032Speter				}
23938032Speter			}
24038032Speter			break;
24138032Speter
24238032Speter		  case 't':
24338032Speter			map->map_mflags |= MF_NODEFER;
24438032Speter			break;
24538032Speter
24664562Sgshapiro
24764562Sgshapiro		  case 'S':
24864562Sgshapiro			map->map_spacesub = *++p;
24938032Speter			break;
25038032Speter
25164562Sgshapiro		  case 'D':
25264562Sgshapiro			map->map_mflags |= MF_DEFER;
25338032Speter			break;
25464562Sgshapiro
25564562Sgshapiro		  default:
25664562Sgshapiro			syserr("Illegal option %c map %s", *p, map->map_mname);
25764562Sgshapiro			break;
25838032Speter		}
25938032Speter		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
26038032Speter			p++;
26138032Speter		if (*p != '\0')
26238032Speter			*p++ = '\0';
26338032Speter	}
26438032Speter	if (map->map_app != NULL)
26538032Speter		map->map_app = newstr(map->map_app);
26638032Speter	if (map->map_tapp != NULL)
26738032Speter		map->map_tapp = newstr(map->map_tapp);
26838032Speter	if (map->map_keycolnm != NULL)
26938032Speter		map->map_keycolnm = newstr(map->map_keycolnm);
27038032Speter	if (map->map_valcolnm != NULL)
27138032Speter		map->map_valcolnm = newstr(map->map_valcolnm);
27238032Speter
27338032Speter	if (*p != '\0')
27438032Speter	{
27538032Speter		map->map_file = p;
27638032Speter		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
27738032Speter			p++;
27838032Speter		if (*p != '\0')
27938032Speter			*p++ = '\0';
28038032Speter		map->map_file = newstr(map->map_file);
28138032Speter	}
28238032Speter
28338032Speter	while (*p != '\0' && isascii(*p) && isspace(*p))
28438032Speter		p++;
28538032Speter	if (*p != '\0')
28638032Speter		map->map_rebuild = newstr(p);
28738032Speter
28838032Speter	if (map->map_file == NULL &&
28938032Speter	    !bitset(MCF_OPTFILE, map->map_class->map_cflags))
29038032Speter	{
29138032Speter		syserr("No file name for %s map %s",
29238032Speter			map->map_class->map_cname, map->map_mname);
29390792Sgshapiro		return false;
29438032Speter	}
29590792Sgshapiro	return true;
29638032Speter}
29790792Sgshapiro/*
29838032Speter**  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
29938032Speter**
30038032Speter**	It also adds the map_app string.  It can be used as a utility
30138032Speter**	in the map_lookup method.
30238032Speter**
30338032Speter**	Parameters:
30438032Speter**		map -- the map that causes this.
30538032Speter**		s -- the string to rewrite, NOT necessarily null terminated.
30638032Speter**		slen -- the length of s.
30738032Speter**		av -- arguments to interpolate into buf.
30838032Speter**
30938032Speter**	Returns:
31038032Speter**		Pointer to rewritten result.  This is static data that
31138032Speter**		should be copied if it is to be saved!
31238032Speter*/
31338032Speter
31438032Speterchar *
31538032Spetermap_rewrite(map, s, slen, av)
31638032Speter	register MAP *map;
31738032Speter	register const char *s;
31838032Speter	size_t slen;
31938032Speter	char **av;
32038032Speter{
32138032Speter	register char *bp;
32238032Speter	register char c;
32338032Speter	char **avp;
32438032Speter	register char *ap;
32538032Speter	size_t l;
32638032Speter	size_t len;
32738032Speter	static size_t buflen = 0;
32838032Speter	static char *buf = NULL;
32938032Speter
33038032Speter	if (tTd(39, 1))
33138032Speter	{
33290792Sgshapiro		sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
33338032Speter		if (av == NULL)
33490792Sgshapiro			sm_dprintf(" (nullv)");
33538032Speter		else
33638032Speter		{
33738032Speter			for (avp = av; *avp != NULL; avp++)
33890792Sgshapiro				sm_dprintf("\n\t%s", *avp);
33938032Speter		}
34090792Sgshapiro		sm_dprintf("\n");
34138032Speter	}
34238032Speter
34338032Speter	/* count expected size of output (can safely overestimate) */
34438032Speter	l = len = slen;
34538032Speter	if (av != NULL)
34638032Speter	{
34738032Speter		const char *sp = s;
34838032Speter
34938032Speter		while (l-- > 0 && (c = *sp++) != '\0')
35038032Speter		{
35138032Speter			if (c != '%')
35238032Speter				continue;
35338032Speter			if (l-- <= 0)
35438032Speter				break;
35538032Speter			c = *sp++;
35638032Speter			if (!(isascii(c) && isdigit(c)))
35738032Speter				continue;
35838032Speter			for (avp = av; --c >= '0' && *avp != NULL; avp++)
35938032Speter				continue;
36038032Speter			if (*avp == NULL)
36138032Speter				continue;
36238032Speter			len += strlen(*avp);
36338032Speter		}
36438032Speter	}
36538032Speter	if (map->map_app != NULL)
36638032Speter		len += strlen(map->map_app);
36738032Speter	if (buflen < ++len)
36838032Speter	{
36938032Speter		/* need to malloc additional space */
37038032Speter		buflen = len;
37138032Speter		if (buf != NULL)
37277349Sgshapiro			sm_free(buf);
37390792Sgshapiro		buf = sm_pmalloc_x(buflen);
37438032Speter	}
37538032Speter
37638032Speter	bp = buf;
37738032Speter	if (av == NULL)
37838032Speter	{
37964562Sgshapiro		memmove(bp, s, slen);
38038032Speter		bp += slen;
38164562Sgshapiro
38264562Sgshapiro		/* assert(len > slen); */
38364562Sgshapiro		len -= slen;
38438032Speter	}
38538032Speter	else
38638032Speter	{
38738032Speter		while (slen-- > 0 && (c = *s++) != '\0')
38838032Speter		{
38938032Speter			if (c != '%')
39038032Speter			{
39138032Speter  pushc:
392120256Sgshapiro				if (len-- <= 1)
39390792Sgshapiro				     break;
39438032Speter				*bp++ = c;
39538032Speter				continue;
39638032Speter			}
39738032Speter			if (slen-- <= 0 || (c = *s++) == '\0')
39838032Speter				c = '%';
39938032Speter			if (c == '%')
40038032Speter				goto pushc;
40138032Speter			if (!(isascii(c) && isdigit(c)))
40238032Speter			{
403120256Sgshapiro				if (len-- <= 1)
404120256Sgshapiro				     break;
40538032Speter				*bp++ = '%';
40638032Speter				goto pushc;
40738032Speter			}
40838032Speter			for (avp = av; --c >= '0' && *avp != NULL; avp++)
40938032Speter				continue;
41038032Speter			if (*avp == NULL)
41138032Speter				continue;
41238032Speter
41338032Speter			/* transliterate argument into output string */
41464562Sgshapiro			for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
41538032Speter				*bp++ = c;
41638032Speter		}
41738032Speter	}
41864562Sgshapiro	if (map->map_app != NULL && len > 0)
41990792Sgshapiro		(void) sm_strlcpy(bp, map->map_app, len);
42038032Speter	else
42138032Speter		*bp = '\0';
42238032Speter	if (tTd(39, 1))
42390792Sgshapiro		sm_dprintf("map_rewrite => %s\n", buf);
42438032Speter	return buf;
42538032Speter}
42690792Sgshapiro/*
42764562Sgshapiro**  INITMAPS -- rebuild alias maps
42838032Speter**
42938032Speter**	Parameters:
43064562Sgshapiro**		none.
43138032Speter**
43238032Speter**	Returns:
43338032Speter**		none.
43438032Speter*/
43538032Speter
43638032Spetervoid
43764562Sgshapiroinitmaps()
43838032Speter{
43938032Speter#if XDEBUG
44038032Speter	checkfd012("entering initmaps");
44164562Sgshapiro#endif /* XDEBUG */
44238032Speter	stabapply(map_init, 0);
44338032Speter#if XDEBUG
44438032Speter	checkfd012("exiting initmaps");
44564562Sgshapiro#endif /* XDEBUG */
44638032Speter}
44790792Sgshapiro/*
44864562Sgshapiro**  MAP_INIT -- rebuild a map
44964562Sgshapiro**
45064562Sgshapiro**	Parameters:
45164562Sgshapiro**		s -- STAB entry: if map: try to rebuild
45264562Sgshapiro**		unused -- unused variable
45364562Sgshapiro**
45464562Sgshapiro**	Returns:
45564562Sgshapiro**		none.
45664562Sgshapiro**
45764562Sgshapiro**	Side Effects:
45864562Sgshapiro**		will close already open rebuildable map.
45964562Sgshapiro*/
46038032Speter
46164562Sgshapiro/* ARGSUSED1 */
46264562Sgshapirostatic void
46364562Sgshapiromap_init(s, unused)
46438032Speter	register STAB *s;
46564562Sgshapiro	int unused;
46638032Speter{
46738032Speter	register MAP *map;
46838032Speter
46938032Speter	/* has to be a map */
47090792Sgshapiro	if (s->s_symtype != ST_MAP)
47138032Speter		return;
47238032Speter
47338032Speter	map = &s->s_map;
47438032Speter	if (!bitset(MF_VALID, map->map_mflags))
47538032Speter		return;
47638032Speter
47738032Speter	if (tTd(38, 2))
47890792Sgshapiro		sm_dprintf("map_init(%s:%s, %s)\n",
47938032Speter			map->map_class->map_cname == NULL ? "NULL" :
48038032Speter				map->map_class->map_cname,
48138032Speter			map->map_mname == NULL ? "NULL" : map->map_mname,
48264562Sgshapiro			map->map_file == NULL ? "NULL" : map->map_file);
48338032Speter
48464562Sgshapiro	if (!bitset(MF_ALIAS, map->map_mflags) ||
48564562Sgshapiro	    !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
48638032Speter	{
48738032Speter		if (tTd(38, 3))
48890792Sgshapiro			sm_dprintf("\tnot rebuildable\n");
48938032Speter		return;
49038032Speter	}
49138032Speter
49238032Speter	/* if already open, close it (for nested open) */
49338032Speter	if (bitset(MF_OPEN, map->map_mflags))
49438032Speter	{
49577349Sgshapiro		map->map_mflags |= MF_CLOSING;
49638032Speter		map->map_class->map_close(map);
49777349Sgshapiro		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
49838032Speter	}
49938032Speter
50090792Sgshapiro	(void) rebuildaliases(map, false);
50164562Sgshapiro	return;
50264562Sgshapiro}
50390792Sgshapiro/*
50464562Sgshapiro**  OPENMAP -- open a map
50564562Sgshapiro**
50664562Sgshapiro**	Parameters:
50764562Sgshapiro**		map -- map to open (it must not be open).
50864562Sgshapiro**
50964562Sgshapiro**	Returns:
51064562Sgshapiro**		whether open succeeded.
51164562Sgshapiro*/
51264562Sgshapiro
51364562Sgshapirobool
51464562Sgshapiroopenmap(map)
51564562Sgshapiro	MAP *map;
51664562Sgshapiro{
51790792Sgshapiro	bool restore = false;
51864562Sgshapiro	bool savehold = HoldErrs;
51964562Sgshapiro	bool savequick = QuickAbort;
52064562Sgshapiro	int saveerrors = Errors;
52164562Sgshapiro
52264562Sgshapiro	if (!bitset(MF_VALID, map->map_mflags))
52390792Sgshapiro		return false;
52464562Sgshapiro
52564562Sgshapiro	/* better safe than sorry... */
52664562Sgshapiro	if (bitset(MF_OPEN, map->map_mflags))
52790792Sgshapiro		return true;
52864562Sgshapiro
52964562Sgshapiro	/* Don't send a map open error out via SMTP */
53064562Sgshapiro	if ((OnlyOneError || QuickAbort) &&
53164562Sgshapiro	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
53238032Speter	{
53390792Sgshapiro		restore = true;
53490792Sgshapiro		HoldErrs = true;
53590792Sgshapiro		QuickAbort = false;
53638032Speter	}
53738032Speter
53864562Sgshapiro	errno = 0;
53938032Speter	if (map->map_class->map_open(map, O_RDONLY))
54038032Speter	{
54138032Speter		if (tTd(38, 4))
54290792Sgshapiro			sm_dprintf("openmap()\t%s:%s %s: valid\n",
54338032Speter				map->map_class->map_cname == NULL ? "NULL" :
54438032Speter					map->map_class->map_cname,
54538032Speter				map->map_mname == NULL ? "NULL" :
54638032Speter					map->map_mname,
54738032Speter				map->map_file == NULL ? "NULL" :
54838032Speter					map->map_file);
54938032Speter		map->map_mflags |= MF_OPEN;
55090792Sgshapiro		map->map_pid = CurrentPid;
55138032Speter	}
55238032Speter	else
55338032Speter	{
55438032Speter		if (tTd(38, 4))
55590792Sgshapiro			sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
55638032Speter				map->map_class->map_cname == NULL ? "NULL" :
55738032Speter					map->map_class->map_cname,
55838032Speter				map->map_mname == NULL ? "NULL" :
55938032Speter					map->map_mname,
56038032Speter				map->map_file == NULL ? "NULL" :
56138032Speter					map->map_file,
56264562Sgshapiro				errno == 0 ? "" : ": ",
56390792Sgshapiro				errno == 0 ? "" : sm_errstring(errno));
56438032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
56538032Speter		{
56638032Speter			extern MAPCLASS BogusMapClass;
56738032Speter
56890792Sgshapiro			map->map_orgclass = map->map_class;
56938032Speter			map->map_class = &BogusMapClass;
57090792Sgshapiro			map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
57190792Sgshapiro			map->map_pid = CurrentPid;
57238032Speter		}
57364562Sgshapiro		else
57464562Sgshapiro		{
57564562Sgshapiro			/* don't try again */
57664562Sgshapiro			map->map_mflags &= ~MF_VALID;
57764562Sgshapiro		}
57838032Speter	}
57964562Sgshapiro
58064562Sgshapiro	if (restore)
58164562Sgshapiro	{
58264562Sgshapiro		Errors = saveerrors;
58364562Sgshapiro		HoldErrs = savehold;
58464562Sgshapiro		QuickAbort = savequick;
58564562Sgshapiro	}
58664562Sgshapiro
58764562Sgshapiro	return bitset(MF_OPEN, map->map_mflags);
58838032Speter}
58990792Sgshapiro/*
59042575Speter**  CLOSEMAPS -- close all open maps opened by the current pid.
59142575Speter**
59242575Speter**	Parameters:
59390792Sgshapiro**		bogus -- only close bogus maps.
59442575Speter**
59542575Speter**	Returns:
59642575Speter**		none.
59742575Speter*/
59842575Speter
59942575Spetervoid
60090792Sgshapiroclosemaps(bogus)
60190792Sgshapiro	bool bogus;
60242575Speter{
60390792Sgshapiro	stabapply(map_close, bogus);
60442575Speter}
60590792Sgshapiro/*
60664562Sgshapiro**  MAP_CLOSE -- close a map opened by the current pid.
60764562Sgshapiro**
60864562Sgshapiro**	Parameters:
60990792Sgshapiro**		s -- STAB entry: if map: try to close
61090792Sgshapiro**		bogus -- only close bogus maps or MCF_NOTPERSIST maps.
61164562Sgshapiro**
61264562Sgshapiro**	Returns:
61364562Sgshapiro**		none.
61464562Sgshapiro*/
61542575Speter
61642575Speter/* ARGSUSED1 */
61764562Sgshapirostatic void
61890792Sgshapiromap_close(s, bogus)
61942575Speter	register STAB *s;
62090792Sgshapiro	int bogus;	/* int because of stabapply(), used as bool */
62142575Speter{
62242575Speter	MAP *map;
62390792Sgshapiro	extern MAPCLASS BogusMapClass;
62442575Speter
62590792Sgshapiro	if (s->s_symtype != ST_MAP)
62642575Speter		return;
62764562Sgshapiro
62842575Speter	map = &s->s_map;
62942575Speter
63090792Sgshapiro	/*
63190792Sgshapiro	**  close the map iff:
63290792Sgshapiro	**  it is valid and open and opened by this process
63390792Sgshapiro	**  and (!bogus or it's a bogus map or it is not persistent)
63490792Sgshapiro	**  negate this: return iff
63590792Sgshapiro	**  it is not valid or it is not open or not opened by this process
63690792Sgshapiro	**  or (bogus and it's not a bogus map and it's not not-persistent)
63790792Sgshapiro	*/
63890792Sgshapiro
63942575Speter	if (!bitset(MF_VALID, map->map_mflags) ||
64042575Speter	    !bitset(MF_OPEN, map->map_mflags) ||
64177349Sgshapiro	    bitset(MF_CLOSING, map->map_mflags) ||
64290792Sgshapiro	    map->map_pid != CurrentPid ||
64390792Sgshapiro	    (bogus && map->map_class != &BogusMapClass &&
64490792Sgshapiro	     !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
64542575Speter		return;
64664562Sgshapiro
64790792Sgshapiro	if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
64890792Sgshapiro	    map->map_orgclass != &BogusMapClass)
64990792Sgshapiro		map->map_class = map->map_orgclass;
65042575Speter	if (tTd(38, 5))
65190792Sgshapiro		sm_dprintf("closemaps: closing %s (%s)\n",
65264562Sgshapiro			map->map_mname == NULL ? "NULL" : map->map_mname,
65364562Sgshapiro			map->map_file == NULL ? "NULL" : map->map_file);
65464562Sgshapiro
65590792Sgshapiro	if (!bitset(MF_OPENBOGUS, map->map_mflags))
65690792Sgshapiro	{
65790792Sgshapiro		map->map_mflags |= MF_CLOSING;
65890792Sgshapiro		map->map_class->map_close(map);
65990792Sgshapiro	}
66090792Sgshapiro	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
66142575Speter}
66290792Sgshapiro/*
66338032Speter**  GETCANONNAME -- look up name using service switch
66438032Speter**
66538032Speter**	Parameters:
66638032Speter**		host -- the host name to look up.
66738032Speter**		hbsize -- the size of the host buffer.
66838032Speter**		trymx -- if set, try MX records.
66990792Sgshapiro**		pttl -- pointer to return TTL (can be NULL).
67038032Speter**
67138032Speter**	Returns:
67290792Sgshapiro**		true -- if the host was found.
67390792Sgshapiro**		false -- otherwise.
67438032Speter*/
67538032Speter
67638032Speterbool
67790792Sgshapirogetcanonname(host, hbsize, trymx, pttl)
67838032Speter	char *host;
67938032Speter	int hbsize;
68038032Speter	bool trymx;
68190792Sgshapiro	int *pttl;
68238032Speter{
68338032Speter	int nmaps;
68438032Speter	int mapno;
68590792Sgshapiro	bool found = false;
68690792Sgshapiro	bool got_tempfail = false;
68764562Sgshapiro	auto int status;
68838032Speter	char *maptype[MAXMAPSTACK];
68938032Speter	short mapreturn[MAXMAPACTIONS];
69038032Speter
69138032Speter	nmaps = switch_map_find("hosts", maptype, mapreturn);
69290792Sgshapiro	if (pttl != 0)
69390792Sgshapiro		*pttl = SM_DEFAULT_TTL;
69438032Speter	for (mapno = 0; mapno < nmaps; mapno++)
69538032Speter	{
69638032Speter		int i;
69738032Speter
69838032Speter		if (tTd(38, 20))
69990792Sgshapiro			sm_dprintf("getcanonname(%s), trying %s\n",
70038032Speter				host, maptype[mapno]);
70138032Speter		if (strcmp("files", maptype[mapno]) == 0)
70238032Speter		{
70364562Sgshapiro			found = text_getcanonname(host, hbsize, &status);
70438032Speter		}
70590792Sgshapiro#if NIS
70638032Speter		else if (strcmp("nis", maptype[mapno]) == 0)
70738032Speter		{
70864562Sgshapiro			found = nis_getcanonname(host, hbsize, &status);
70938032Speter		}
71064562Sgshapiro#endif /* NIS */
71190792Sgshapiro#if NISPLUS
71238032Speter		else if (strcmp("nisplus", maptype[mapno]) == 0)
71338032Speter		{
71464562Sgshapiro			found = nisplus_getcanonname(host, hbsize, &status);
71538032Speter		}
71664562Sgshapiro#endif /* NISPLUS */
71738032Speter#if NAMED_BIND
71838032Speter		else if (strcmp("dns", maptype[mapno]) == 0)
71938032Speter		{
72090792Sgshapiro			found = dns_getcanonname(host, hbsize, trymx, &status,							 pttl);
72138032Speter		}
72264562Sgshapiro#endif /* NAMED_BIND */
72338032Speter#if NETINFO
72438032Speter		else if (strcmp("netinfo", maptype[mapno]) == 0)
72538032Speter		{
72664562Sgshapiro			found = ni_getcanonname(host, hbsize, &status);
72738032Speter		}
72864562Sgshapiro#endif /* NETINFO */
72938032Speter		else
73038032Speter		{
73190792Sgshapiro			found = false;
73264562Sgshapiro			status = EX_UNAVAILABLE;
73338032Speter		}
73438032Speter
73538032Speter		/*
73638032Speter		**  Heuristic: if $m is not set, we are running during system
73738032Speter		**  startup.  In this case, when a name is apparently found
73838032Speter		**  but has no dot, treat is as not found.  This avoids
73938032Speter		**  problems if /etc/hosts has no FQDN but is listed first
74038032Speter		**  in the service switch.
74138032Speter		*/
74238032Speter
74338032Speter		if (found &&
74438032Speter		    (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
74538032Speter			break;
74638032Speter
74738032Speter		/* see if we should continue */
74864562Sgshapiro		if (status == EX_TEMPFAIL)
74938032Speter		{
75038032Speter			i = MA_TRYAGAIN;
75190792Sgshapiro			got_tempfail = true;
75238032Speter		}
75364562Sgshapiro		else if (status == EX_NOTFOUND)
75438032Speter			i = MA_NOTFOUND;
75538032Speter		else
75638032Speter			i = MA_UNAVAIL;
75738032Speter		if (bitset(1 << mapno, mapreturn[i]))
75838032Speter			break;
75938032Speter	}
76038032Speter
76138032Speter	if (found)
76238032Speter	{
76338032Speter		char *d;
76438032Speter
76538032Speter		if (tTd(38, 20))
76690792Sgshapiro			sm_dprintf("getcanonname(%s), found\n", host);
76738032Speter
76838032Speter		/*
76938032Speter		**  If returned name is still single token, compensate
77038032Speter		**  by tagging on $m.  This is because some sites set
77138032Speter		**  up their DNS or NIS databases wrong.
77238032Speter		*/
77338032Speter
77438032Speter		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
77538032Speter		{
77638032Speter			d = macvalue('m', CurEnv);
77738032Speter			if (d != NULL &&
77838032Speter			    hbsize > (int) (strlen(host) + strlen(d) + 1))
77938032Speter			{
78038032Speter				if (host[strlen(host) - 1] != '.')
78190792Sgshapiro					(void) sm_strlcat2(host, ".", d,
78290792Sgshapiro							   hbsize);
78390792Sgshapiro				else
78490792Sgshapiro					(void) sm_strlcat(host, d, hbsize);
78538032Speter			}
78638032Speter			else
78790792Sgshapiro				return false;
78838032Speter		}
78990792Sgshapiro		return true;
79038032Speter	}
79138032Speter
79238032Speter	if (tTd(38, 20))
79390792Sgshapiro		sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
79490792Sgshapiro			status);
79538032Speter
79638032Speter	if (got_tempfail)
79773188Sgshapiro		SM_SET_H_ERRNO(TRY_AGAIN);
79838032Speter	else
79973188Sgshapiro		SM_SET_H_ERRNO(HOST_NOT_FOUND);
80090792Sgshapiro
80190792Sgshapiro	return false;
80238032Speter}
80390792Sgshapiro/*
80438032Speter**  EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
80538032Speter**
80638032Speter**	Parameters:
80738032Speter**		name -- the name against which to match.
80873188Sgshapiro**		dot -- where to reinsert '.' to get FQDN
80938032Speter**		line -- the /etc/hosts line.
81038032Speter**		cbuf -- the location to store the result.
81138032Speter**		cbuflen -- the size of cbuf.
81238032Speter**
81338032Speter**	Returns:
81490792Sgshapiro**		true -- if the line matched the desired name.
81590792Sgshapiro**		false -- otherwise.
81638032Speter*/
81738032Speter
81864562Sgshapirostatic bool
81973188Sgshapiroextract_canonname(name, dot, line, cbuf, cbuflen)
82038032Speter	char *name;
82173188Sgshapiro	char *dot;
82238032Speter	char *line;
82338032Speter	char cbuf[];
82438032Speter	int cbuflen;
82538032Speter{
82638032Speter	int i;
82738032Speter	char *p;
82890792Sgshapiro	bool found = false;
82938032Speter
83038032Speter	cbuf[0] = '\0';
83138032Speter	if (line[0] == '#')
83290792Sgshapiro		return false;
83338032Speter
83438032Speter	for (i = 1; ; i++)
83538032Speter	{
83638032Speter		char nbuf[MAXNAME + 1];
83738032Speter
83838032Speter		p = get_column(line, i, '\0', nbuf, sizeof nbuf);
83938032Speter		if (p == NULL)
84038032Speter			break;
84138032Speter		if (*p == '\0')
84238032Speter			continue;
84338032Speter		if (cbuf[0] == '\0' ||
84438032Speter		    (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
84538032Speter		{
84690792Sgshapiro			(void) sm_strlcpy(cbuf, p, cbuflen);
84738032Speter		}
84890792Sgshapiro		if (sm_strcasecmp(name, p) == 0)
84990792Sgshapiro			found = true;
85073188Sgshapiro		else if (dot != NULL)
85173188Sgshapiro		{
85273188Sgshapiro			/* try looking for the FQDN as well */
85373188Sgshapiro			*dot = '.';
85490792Sgshapiro			if (sm_strcasecmp(name, p) == 0)
85590792Sgshapiro				found = true;
85673188Sgshapiro			*dot = '\0';
85773188Sgshapiro		}
85838032Speter	}
85938032Speter	if (found && strchr(cbuf, '.') == NULL)
86038032Speter	{
86138032Speter		/* try to add a domain on the end of the name */
86238032Speter		char *domain = macvalue('m', CurEnv);
86338032Speter
86438032Speter		if (domain != NULL &&
86564562Sgshapiro		    strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
86638032Speter		{
86764562Sgshapiro			p = &cbuf[i];
86838032Speter			*p++ = '.';
86990792Sgshapiro			(void) sm_strlcpy(p, domain, cbuflen - i - 1);
87038032Speter		}
87138032Speter	}
87238032Speter	return found;
87338032Speter}
87490792Sgshapiro
87590792Sgshapiro/*
87690792Sgshapiro**  DNS modules
87790792Sgshapiro*/
87890792Sgshapiro
87990792Sgshapiro#if NAMED_BIND
88090792Sgshapiro# if DNSMAP
88190792Sgshapiro
88290792Sgshapiro#  include "sm_resolve.h"
88390792Sgshapiro#  if NETINET || NETINET6
88490792Sgshapiro#   include <arpa/inet.h>
88590792Sgshapiro#  endif /* NETINET || NETINET6 */
88690792Sgshapiro
88790792Sgshapiro/*
88890792Sgshapiro**  DNS_MAP_OPEN -- stub to check proper value for dns map type
88990792Sgshapiro*/
89090792Sgshapiro
89190792Sgshapirobool
89290792Sgshapirodns_map_open(map, mode)
89390792Sgshapiro	MAP *map;
89490792Sgshapiro	int mode;
89590792Sgshapiro{
89690792Sgshapiro	if (tTd(38,2))
89790792Sgshapiro		sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
89890792Sgshapiro
89990792Sgshapiro	mode &= O_ACCMODE;
90090792Sgshapiro	if (mode != O_RDONLY)
90190792Sgshapiro	{
90290792Sgshapiro		/* issue a pseudo-error message */
90390792Sgshapiro		errno = SM_EMAPCANTWRITE;
90490792Sgshapiro		return false;
90590792Sgshapiro	}
90690792Sgshapiro	return true;
90790792Sgshapiro}
90890792Sgshapiro
90990792Sgshapiro/*
91090792Sgshapiro**  DNS_MAP_PARSEARGS -- parse dns map definition args.
91190792Sgshapiro**
91290792Sgshapiro**	Parameters:
91390792Sgshapiro**		map -- pointer to MAP
91490792Sgshapiro**		args -- pointer to the args on the config line.
91590792Sgshapiro**
91690792Sgshapiro**	Returns:
91790792Sgshapiro**		true -- if everything parsed OK.
91890792Sgshapiro**		false -- otherwise.
91990792Sgshapiro*/
92090792Sgshapiro
92190792Sgshapiro#  if _FFR_DNSMAP_MULTILIMIT
92290792Sgshapiro#   if !_FFR_DNSMAP_MULTI
92390792Sgshapiro  ERROR README:	You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT
92490792Sgshapiro#   endif /* ! _FFR_DNSMAP_MULTI */
92590792Sgshapiro#  endif /* _FFR_DNSMAP_MULTILIMIT */
92690792Sgshapiro
92790792Sgshapiro#  if _FFR_DNSMAP_MULTI
92890792Sgshapiro#   if _FFR_DNSMAP_MULTILIMIT
92990792Sgshapiro#    define map_sizelimit	map_lockfd	/* overload field */
93090792Sgshapiro#   endif /* _FFR_DNSMAP_MULTILIMIT */
93190792Sgshapiro#  endif /* _FFR_DNSMAP_MULTI */
93290792Sgshapiro
93390792Sgshapirostruct dns_map
93490792Sgshapiro{
93590792Sgshapiro	int dns_m_type;
93690792Sgshapiro};
93790792Sgshapiro
93890792Sgshapirobool
93990792Sgshapirodns_map_parseargs(map,args)
94090792Sgshapiro	MAP *map;
94190792Sgshapiro	char *args;
94290792Sgshapiro{
94390792Sgshapiro	register char *p = args;
94490792Sgshapiro	struct dns_map *map_p;
94590792Sgshapiro
94690792Sgshapiro	map_p = (struct dns_map *) xalloc(sizeof *map_p);
94790792Sgshapiro	map_p->dns_m_type = -1;
94890792Sgshapiro	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
94990792Sgshapiro
95090792Sgshapiro	for (;;)
95190792Sgshapiro	{
95290792Sgshapiro		while (isascii(*p) && isspace(*p))
95390792Sgshapiro			p++;
95490792Sgshapiro		if (*p != '-')
95590792Sgshapiro			break;
95690792Sgshapiro		switch (*++p)
95790792Sgshapiro		{
95890792Sgshapiro		  case 'N':
95990792Sgshapiro			map->map_mflags |= MF_INCLNULL;
96090792Sgshapiro			map->map_mflags &= ~MF_TRY0NULL;
96190792Sgshapiro			break;
96290792Sgshapiro
96390792Sgshapiro		  case 'O':
96490792Sgshapiro			map->map_mflags &= ~MF_TRY1NULL;
96590792Sgshapiro			break;
96690792Sgshapiro
96790792Sgshapiro		  case 'o':
96890792Sgshapiro			map->map_mflags |= MF_OPTIONAL;
96990792Sgshapiro			break;
97090792Sgshapiro
97190792Sgshapiro		  case 'f':
97290792Sgshapiro			map->map_mflags |= MF_NOFOLDCASE;
97390792Sgshapiro			break;
97490792Sgshapiro
97590792Sgshapiro		  case 'm':
97690792Sgshapiro			map->map_mflags |= MF_MATCHONLY;
97790792Sgshapiro			break;
97890792Sgshapiro
97990792Sgshapiro		  case 'A':
98090792Sgshapiro			map->map_mflags |= MF_APPEND;
98190792Sgshapiro			break;
98290792Sgshapiro
98390792Sgshapiro		  case 'q':
98490792Sgshapiro			map->map_mflags |= MF_KEEPQUOTES;
98590792Sgshapiro			break;
98690792Sgshapiro
98790792Sgshapiro		  case 't':
98890792Sgshapiro			map->map_mflags |= MF_NODEFER;
98990792Sgshapiro			break;
99090792Sgshapiro
99190792Sgshapiro		  case 'a':
99290792Sgshapiro			map->map_app = ++p;
99390792Sgshapiro			break;
99490792Sgshapiro
99590792Sgshapiro		  case 'T':
99690792Sgshapiro			map->map_tapp = ++p;
99790792Sgshapiro			break;
99890792Sgshapiro
99990792Sgshapiro		  case 'd':
100090792Sgshapiro			{
100190792Sgshapiro				char *h;
100290792Sgshapiro
100390792Sgshapiro				++p;
100490792Sgshapiro				h = strchr(p, ' ');
100590792Sgshapiro				if (h != NULL)
100690792Sgshapiro					*h = '\0';
100790792Sgshapiro				map->map_timeout = convtime(p, 's');
100890792Sgshapiro				if (h != NULL)
100990792Sgshapiro					*h = ' ';
101090792Sgshapiro			}
101190792Sgshapiro			break;
101290792Sgshapiro
101390792Sgshapiro		  case 'r':
101490792Sgshapiro			while (isascii(*++p) && isspace(*p))
101590792Sgshapiro				continue;
101690792Sgshapiro			map->map_retry = atoi(p);
101790792Sgshapiro			break;
101890792Sgshapiro
101990792Sgshapiro#  if _FFR_DNSMAP_MULTI
102090792Sgshapiro		  case 'z':
102190792Sgshapiro			if (*++p != '\\')
102290792Sgshapiro				map->map_coldelim = *p;
102390792Sgshapiro			else
102490792Sgshapiro			{
102590792Sgshapiro				switch (*++p)
102690792Sgshapiro				{
102790792Sgshapiro				  case 'n':
102890792Sgshapiro					map->map_coldelim = '\n';
102990792Sgshapiro					break;
103090792Sgshapiro
103190792Sgshapiro				  case 't':
103290792Sgshapiro					map->map_coldelim = '\t';
103390792Sgshapiro					break;
103490792Sgshapiro
103590792Sgshapiro				  default:
103690792Sgshapiro					map->map_coldelim = '\\';
103790792Sgshapiro				}
103890792Sgshapiro			}
103990792Sgshapiro			break;
104090792Sgshapiro
104190792Sgshapiro#   if _FFR_DNSMAP_MULTILIMIT
104290792Sgshapiro		  case 'Z':
104390792Sgshapiro			while (isascii(*++p) && isspace(*p))
104490792Sgshapiro				continue;
104590792Sgshapiro			map->map_sizelimit = atoi(p);
104690792Sgshapiro			break;
104790792Sgshapiro#   endif /* _FFR_DNSMAP_MULTILIMIT */
104890792Sgshapiro#  endif /* _FFR_DNSMAP_MULTI */
104990792Sgshapiro
105090792Sgshapiro			/* Start of dns_map specific args */
105190792Sgshapiro		  case 'R':		/* search field */
105290792Sgshapiro			{
105390792Sgshapiro				char *h;
105490792Sgshapiro
105590792Sgshapiro				while (isascii(*++p) && isspace(*p))
105690792Sgshapiro					continue;
105790792Sgshapiro				h = strchr(p, ' ');
105890792Sgshapiro				if (h != NULL)
105990792Sgshapiro					*h = '\0';
106090792Sgshapiro				map_p->dns_m_type = dns_string_to_type(p);
106190792Sgshapiro				if (h != NULL)
106290792Sgshapiro					*h = ' ';
106390792Sgshapiro				if (map_p->dns_m_type < 0)
106490792Sgshapiro					syserr("dns map %s: wrong type %s",
106590792Sgshapiro						map->map_mname, p);
106690792Sgshapiro			}
106790792Sgshapiro			break;
106890792Sgshapiro
106990792Sgshapiro#  if _FFR_DNSMAP_BASE
107090792Sgshapiro		  case 'B':		/* base domain */
107190792Sgshapiro			{
107290792Sgshapiro				char *h;
107390792Sgshapiro
107490792Sgshapiro				while (isascii(*++p) && isspace(*p))
107590792Sgshapiro					continue;
107690792Sgshapiro				h = strchr(p, ' ');
107790792Sgshapiro				if (h != NULL)
107890792Sgshapiro					*h = '\0';
107990792Sgshapiro
108090792Sgshapiro				/*
108190792Sgshapiro				**  slight abuse of map->map_file; it isn't
108290792Sgshapiro				**	used otherwise in this map type.
108390792Sgshapiro				*/
108490792Sgshapiro
108590792Sgshapiro				map->map_file = newstr(p);
108690792Sgshapiro				if (h != NULL)
108790792Sgshapiro					*h = ' ';
108890792Sgshapiro			}
108990792Sgshapiro			break;
109090792Sgshapiro#  endif /* _FFR_DNSMAP_BASE */
109190792Sgshapiro
109290792Sgshapiro		}
109390792Sgshapiro		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
109490792Sgshapiro			p++;
109590792Sgshapiro		if (*p != '\0')
109690792Sgshapiro			*p++ = '\0';
109790792Sgshapiro	}
109890792Sgshapiro	if (map_p->dns_m_type < 0)
109990792Sgshapiro		syserr("dns map %s: missing -R type", map->map_mname);
110090792Sgshapiro	if (map->map_app != NULL)
110190792Sgshapiro		map->map_app = newstr(map->map_app);
110290792Sgshapiro	if (map->map_tapp != NULL)
110390792Sgshapiro		map->map_tapp = newstr(map->map_tapp);
110490792Sgshapiro
110590792Sgshapiro	/*
110690792Sgshapiro	**  Assumption: assert(sizeof int <= sizeof(ARBPTR_T));
110790792Sgshapiro	**  Even if this assumption is wrong, we use only one byte,
110890792Sgshapiro	**  so it doesn't really matter.
110990792Sgshapiro	*/
111090792Sgshapiro
111190792Sgshapiro	map->map_db1 = (ARBPTR_T) map_p;
111290792Sgshapiro	return true;
111390792Sgshapiro}
111490792Sgshapiro
111590792Sgshapiro/*
111690792Sgshapiro**  DNS_MAP_LOOKUP -- perform dns map lookup.
111790792Sgshapiro**
111890792Sgshapiro**	Parameters:
111990792Sgshapiro**		map -- pointer to MAP
112090792Sgshapiro**		name -- name to lookup
112190792Sgshapiro**		av -- arguments to interpolate into buf.
112290792Sgshapiro**		statp -- pointer to status (EX_)
112390792Sgshapiro**
112490792Sgshapiro**	Returns:
112590792Sgshapiro**		result of lookup if succeeded.
112690792Sgshapiro**		NULL -- otherwise.
112790792Sgshapiro*/
112890792Sgshapiro
112990792Sgshapirochar *
113090792Sgshapirodns_map_lookup(map, name, av, statp)
113190792Sgshapiro	MAP *map;
113290792Sgshapiro	char *name;
113390792Sgshapiro	char **av;
113490792Sgshapiro	int *statp;
113590792Sgshapiro{
113690792Sgshapiro#  if _FFR_DNSMAP_MULTI
113790792Sgshapiro#   if _FFR_DNSMAP_MULTILIMIT
113890792Sgshapiro	int resnum = 0;
113990792Sgshapiro#   endif /* _FFR_DNSMAP_MULTILIMIT */
114090792Sgshapiro#  endif /* _FFR_DNSMAP_MULTI */
114190792Sgshapiro	char *vp = NULL, *result = NULL;
114290792Sgshapiro	size_t vsize;
114390792Sgshapiro	struct dns_map *map_p;
114490792Sgshapiro	RESOURCE_RECORD_T *rr = NULL;
114590792Sgshapiro	DNS_REPLY_T *r = NULL;
114690792Sgshapiro#  if NETINET6
114790792Sgshapiro	static char buf6[INET6_ADDRSTRLEN];
114890792Sgshapiro#  endif /* NETINET6 */
114990792Sgshapiro
115090792Sgshapiro	if (tTd(38, 20))
115190792Sgshapiro		sm_dprintf("dns_map_lookup(%s, %s)\n",
115290792Sgshapiro			   map->map_mname, name);
115390792Sgshapiro
115490792Sgshapiro	map_p = (struct dns_map *)(map->map_db1);
115590792Sgshapiro#  if _FFR_DNSMAP_BASE
115690792Sgshapiro	if (map->map_file != NULL && *map->map_file != '\0')
115790792Sgshapiro	{
115890792Sgshapiro		size_t len;
115990792Sgshapiro		char *appdomain;
116090792Sgshapiro
116190792Sgshapiro		len = strlen(map->map_file) + strlen(name) + 2;
116290792Sgshapiro		appdomain = (char *) sm_malloc(len);
116390792Sgshapiro		if (appdomain == NULL)
116490792Sgshapiro		{
116590792Sgshapiro			*statp = EX_UNAVAILABLE;
116690792Sgshapiro			return NULL;
116790792Sgshapiro		}
116890792Sgshapiro		(void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
116990792Sgshapiro		r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
117090792Sgshapiro				   map->map_timeout, map->map_retry);
117190792Sgshapiro		sm_free(appdomain);
117290792Sgshapiro	}
117390792Sgshapiro	else
117490792Sgshapiro#  endif /* _FFR_DNSMAP_BASE */
117590792Sgshapiro	{
117690792Sgshapiro		r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
117790792Sgshapiro				   map->map_timeout, map->map_retry);
117890792Sgshapiro	}
117990792Sgshapiro
118090792Sgshapiro	if (r == NULL)
118190792Sgshapiro	{
118290792Sgshapiro		result = NULL;
1183120256Sgshapiro		if (h_errno == TRY_AGAIN || transienterror(errno))
118490792Sgshapiro			*statp = EX_TEMPFAIL;
118590792Sgshapiro		else
118690792Sgshapiro			*statp = EX_NOTFOUND;
118790792Sgshapiro		goto cleanup;
118890792Sgshapiro	}
118990792Sgshapiro	*statp = EX_OK;
119090792Sgshapiro	for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
119190792Sgshapiro	{
119290792Sgshapiro		char *type = NULL;
119390792Sgshapiro		char *value = NULL;
119490792Sgshapiro
119590792Sgshapiro		switch (rr->rr_type)
119690792Sgshapiro		{
119790792Sgshapiro		  case T_NS:
119890792Sgshapiro			type = "T_NS";
119990792Sgshapiro			value = rr->rr_u.rr_txt;
120090792Sgshapiro			break;
120190792Sgshapiro		  case T_CNAME:
120290792Sgshapiro			type = "T_CNAME";
120390792Sgshapiro			value = rr->rr_u.rr_txt;
120490792Sgshapiro			break;
120590792Sgshapiro		  case T_AFSDB:
120690792Sgshapiro			type = "T_AFSDB";
120790792Sgshapiro			value = rr->rr_u.rr_mx->mx_r_domain;
120890792Sgshapiro			break;
120990792Sgshapiro		  case T_SRV:
121090792Sgshapiro			type = "T_SRV";
121190792Sgshapiro			value = rr->rr_u.rr_srv->srv_r_target;
121290792Sgshapiro			break;
121390792Sgshapiro		  case T_PTR:
121490792Sgshapiro			type = "T_PTR";
121590792Sgshapiro			value = rr->rr_u.rr_txt;
121690792Sgshapiro			break;
121790792Sgshapiro		  case T_TXT:
121890792Sgshapiro			type = "T_TXT";
121990792Sgshapiro			value = rr->rr_u.rr_txt;
122090792Sgshapiro			break;
122190792Sgshapiro		  case T_MX:
122290792Sgshapiro			type = "T_MX";
122390792Sgshapiro			value = rr->rr_u.rr_mx->mx_r_domain;
122490792Sgshapiro			break;
122590792Sgshapiro#  if NETINET
122690792Sgshapiro		  case T_A:
122790792Sgshapiro			type = "T_A";
122890792Sgshapiro			value = inet_ntoa(*(rr->rr_u.rr_a));
122990792Sgshapiro			break;
123090792Sgshapiro#  endif /* NETINET */
123190792Sgshapiro#  if NETINET6
123290792Sgshapiro		  case T_AAAA:
123390792Sgshapiro			type = "T_AAAA";
123490792Sgshapiro			value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
123590792Sgshapiro					    sizeof buf6);
123690792Sgshapiro			break;
123790792Sgshapiro#  endif /* NETINET6 */
123890792Sgshapiro		}
123990792Sgshapiro
124098841Sgshapiro		(void) strreplnonprt(value, 'X');
124190792Sgshapiro		if (map_p->dns_m_type != rr->rr_type)
124290792Sgshapiro		{
124390792Sgshapiro			if (tTd(38, 40))
124490792Sgshapiro				sm_dprintf("\tskipping type %s (%d) value %s\n",
124590792Sgshapiro					   type != NULL ? type : "<UNKNOWN>",
124690792Sgshapiro					   rr->rr_type,
124790792Sgshapiro					   value != NULL ? value : "<NO VALUE>");
124890792Sgshapiro			continue;
124990792Sgshapiro		}
125090792Sgshapiro
125190792Sgshapiro#  if NETINET6
125290792Sgshapiro		if (rr->rr_type == T_AAAA && value == NULL)
125390792Sgshapiro		{
125490792Sgshapiro			result = NULL;
125590792Sgshapiro			*statp = EX_DATAERR;
125690792Sgshapiro			if (tTd(38, 40))
125790792Sgshapiro				sm_dprintf("\tbad T_AAAA conversion\n");
125890792Sgshapiro			goto cleanup;
125990792Sgshapiro		}
126090792Sgshapiro#  endif /* NETINET6 */
126190792Sgshapiro		if (tTd(38, 40))
126290792Sgshapiro			sm_dprintf("\tfound type %s (%d) value %s\n",
126390792Sgshapiro				   type != NULL ? type : "<UNKNOWN>",
126490792Sgshapiro				   rr->rr_type,
126590792Sgshapiro				   value != NULL ? value : "<NO VALUE>");
126690792Sgshapiro#  if _FFR_DNSMAP_MULTI
126790792Sgshapiro		if (value != NULL &&
126890792Sgshapiro		    (map->map_coldelim == '\0' ||
126990792Sgshapiro#   if _FFR_DNSMAP_MULTILIMIT
127090792Sgshapiro		     map->map_sizelimit == 1 ||
127190792Sgshapiro#   endif /* _FFR_DNSMAP_MULTILIMIT */
127290792Sgshapiro		     bitset(MF_MATCHONLY, map->map_mflags)))
127390792Sgshapiro		{
127490792Sgshapiro			/* Only care about the first match */
127590792Sgshapiro			vp = newstr(value);
127690792Sgshapiro			break;
127790792Sgshapiro		}
127890792Sgshapiro		else if (vp == NULL)
127990792Sgshapiro		{
128090792Sgshapiro			/* First result */
128190792Sgshapiro			vp = newstr(value);
128290792Sgshapiro		}
128390792Sgshapiro		else
128490792Sgshapiro		{
128590792Sgshapiro			/* concatenate the results */
128690792Sgshapiro			int sz;
128790792Sgshapiro			char *new;
128890792Sgshapiro
128990792Sgshapiro			sz = strlen(vp) + strlen(value) + 2;
129090792Sgshapiro			new = xalloc(sz);
129190792Sgshapiro			(void) sm_snprintf(new, sz, "%s%c%s",
129290792Sgshapiro					   vp, map->map_coldelim, value);
129390792Sgshapiro			sm_free(vp);
129490792Sgshapiro			vp = new;
129590792Sgshapiro#   if _FFR_DNSMAP_MULTILIMIT
129690792Sgshapiro			if (map->map_sizelimit > 0 &&
129790792Sgshapiro			    ++resnum >= map->map_sizelimit)
129890792Sgshapiro				break;
129990792Sgshapiro#   endif /* _FFR_DNSMAP_MULTILIMIT */
130090792Sgshapiro		}
130190792Sgshapiro#  else /* _FFR_DNSMAP_MULTI */
130290792Sgshapiro		vp = value;
130390792Sgshapiro		break;
130490792Sgshapiro#  endif /* _FFR_DNSMAP_MULTI */
130590792Sgshapiro	}
130690792Sgshapiro	if (vp == NULL)
130790792Sgshapiro	{
130890792Sgshapiro		result = NULL;
130990792Sgshapiro		*statp = EX_NOTFOUND;
131090792Sgshapiro		if (tTd(38, 40))
131190792Sgshapiro			sm_dprintf("\tno match found\n");
131290792Sgshapiro		goto cleanup;
131390792Sgshapiro	}
131490792Sgshapiro
131590792Sgshapiro#  if _FFR_DNSMAP_MULTI
131690792Sgshapiro	/* Cleanly truncate for rulesets */
131790792Sgshapiro	truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
131890792Sgshapiro#  endif /* _FFR_DNSMAP_MULTI */
131990792Sgshapiro
132090792Sgshapiro	vsize = strlen(vp);
132190792Sgshapiro
132290792Sgshapiro	if (LogLevel > 9)
132390792Sgshapiro		sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
132490792Sgshapiro			  name, vp);
132590792Sgshapiro	if (bitset(MF_MATCHONLY, map->map_mflags))
132690792Sgshapiro		result = map_rewrite(map, name, strlen(name), NULL);
132790792Sgshapiro	else
132890792Sgshapiro		result = map_rewrite(map, vp, vsize, av);
132990792Sgshapiro
133090792Sgshapiro  cleanup:
133190792Sgshapiro#  if _FFR_DNSMAP_MULTI
133290792Sgshapiro	if (vp != NULL)
133390792Sgshapiro		sm_free(vp);
133490792Sgshapiro#  endif /* _FFR_DNSMAP_MULTI */
133590792Sgshapiro	if (r != NULL)
133690792Sgshapiro		dns_free_data(r);
133790792Sgshapiro	return result;
133890792Sgshapiro}
133990792Sgshapiro# endif /* DNSMAP */
134090792Sgshapiro#endif /* NAMED_BIND */
134190792Sgshapiro
134290792Sgshapiro/*
134338032Speter**  NDBM modules
134438032Speter*/
134538032Speter
134690792Sgshapiro#if NDBM
134738032Speter
134838032Speter/*
134938032Speter**  NDBM_MAP_OPEN -- DBM-style map open
135038032Speter*/
135138032Speter
135238032Speterbool
135338032Speterndbm_map_open(map, mode)
135438032Speter	MAP *map;
135538032Speter	int mode;
135638032Speter{
135738032Speter	register DBM *dbm;
135864562Sgshapiro	int save_errno;
135938032Speter	int dfd;
136038032Speter	int pfd;
136164562Sgshapiro	long sff;
136238032Speter	int ret;
136338032Speter	int smode = S_IREAD;
136498121Sgshapiro	char dirfile[MAXPATHLEN];
136598121Sgshapiro	char pagfile[MAXPATHLEN];
136664562Sgshapiro	struct stat st;
136738032Speter	struct stat std, stp;
136838032Speter
136938032Speter	if (tTd(38, 2))
137090792Sgshapiro		sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
137138032Speter			map->map_mname, map->map_file, mode);
137238032Speter	map->map_lockfd = -1;
137338032Speter	mode &= O_ACCMODE;
137438032Speter
137538032Speter	/* do initial file and directory checks */
137698121Sgshapiro	if (sm_strlcpyn(dirfile, sizeof dirfile, 2,
137798121Sgshapiro			map->map_file, ".dir") >= sizeof dirfile ||
137898121Sgshapiro	    sm_strlcpyn(pagfile, sizeof pagfile, 2,
137998121Sgshapiro			map->map_file, ".pag") >= sizeof pagfile)
138098121Sgshapiro	{
138198121Sgshapiro		errno = 0;
138298121Sgshapiro		if (!bitset(MF_OPTIONAL, map->map_mflags))
138398121Sgshapiro			syserr("dbm map \"%s\": map file %s name too long",
138498121Sgshapiro				map->map_mname, map->map_file);
138598121Sgshapiro		return false;
138698121Sgshapiro	}
138738032Speter	sff = SFF_ROOTOK|SFF_REGONLY;
138838032Speter	if (mode == O_RDWR)
138938032Speter	{
139038032Speter		sff |= SFF_CREAT;
139164562Sgshapiro		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
139238032Speter			sff |= SFF_NOSLINK;
139364562Sgshapiro		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
139438032Speter			sff |= SFF_NOHLINK;
139538032Speter		smode = S_IWRITE;
139638032Speter	}
139738032Speter	else
139838032Speter	{
139964562Sgshapiro		if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
140038032Speter			sff |= SFF_NOWLINK;
140138032Speter	}
140264562Sgshapiro	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
140338032Speter		sff |= SFF_SAFEDIRPATH;
140438032Speter	ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
140590792Sgshapiro		       sff, smode, &std);
140638032Speter	if (ret == 0)
140738032Speter		ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
140838032Speter			       sff, smode, &stp);
140964562Sgshapiro
141038032Speter	if (ret != 0)
141138032Speter	{
141238032Speter		char *prob = "unsafe";
141338032Speter
141438032Speter		/* cannot open this map */
141538032Speter		if (ret == ENOENT)
141638032Speter			prob = "missing";
141738032Speter		if (tTd(38, 2))
141890792Sgshapiro			sm_dprintf("\t%s map file: %d\n", prob, ret);
141938032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
142038032Speter			syserr("dbm map \"%s\": %s map file %s",
142138032Speter				map->map_mname, prob, map->map_file);
142290792Sgshapiro		return false;
142338032Speter	}
142438032Speter	if (std.st_mode == ST_MODE_NOFILE)
142538032Speter		mode |= O_CREAT|O_EXCL;
142638032Speter
142764562Sgshapiro# if LOCK_ON_OPEN
142838032Speter	if (mode == O_RDONLY)
142938032Speter		mode |= O_SHLOCK;
143038032Speter	else
143138032Speter		mode |= O_TRUNC|O_EXLOCK;
143264562Sgshapiro# else /* LOCK_ON_OPEN */
143338032Speter	if ((mode & O_ACCMODE) == O_RDWR)
143438032Speter	{
143564562Sgshapiro#  if NOFTRUNCATE
143638032Speter		/*
143738032Speter		**  Warning: race condition.  Try to lock the file as
143838032Speter		**  quickly as possible after opening it.
143938032Speter		**	This may also have security problems on some systems,
144038032Speter		**	but there isn't anything we can do about it.
144138032Speter		*/
144238032Speter
144338032Speter		mode |= O_TRUNC;
144464562Sgshapiro#  else /* NOFTRUNCATE */
144538032Speter		/*
144638032Speter		**  This ugly code opens the map without truncating it,
144738032Speter		**  locks the file, then truncates it.  Necessary to
144838032Speter		**  avoid race conditions.
144938032Speter		*/
145038032Speter
145138032Speter		int dirfd;
145238032Speter		int pagfd;
145364562Sgshapiro		long sff = SFF_CREAT|SFF_OPENASROOT;
145438032Speter
145564562Sgshapiro		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
145638032Speter			sff |= SFF_NOSLINK;
145764562Sgshapiro		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
145838032Speter			sff |= SFF_NOHLINK;
145938032Speter
146038032Speter		dirfd = safeopen(dirfile, mode, DBMMODE, sff);
146138032Speter		pagfd = safeopen(pagfile, mode, DBMMODE, sff);
146238032Speter
146338032Speter		if (dirfd < 0 || pagfd < 0)
146438032Speter		{
146564562Sgshapiro			save_errno = errno;
146638032Speter			if (dirfd >= 0)
146738032Speter				(void) close(dirfd);
146838032Speter			if (pagfd >= 0)
146938032Speter				(void) close(pagfd);
147038032Speter			errno = save_errno;
147138032Speter			syserr("ndbm_map_open: cannot create database %s",
147238032Speter				map->map_file);
147390792Sgshapiro			return false;
147438032Speter		}
147538032Speter		if (ftruncate(dirfd, (off_t) 0) < 0 ||
147638032Speter		    ftruncate(pagfd, (off_t) 0) < 0)
147738032Speter		{
147864562Sgshapiro			save_errno = errno;
147938032Speter			(void) close(dirfd);
148038032Speter			(void) close(pagfd);
148138032Speter			errno = save_errno;
148238032Speter			syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
148338032Speter				map->map_file);
148490792Sgshapiro			return false;
148538032Speter		}
148638032Speter
148738032Speter		/* if new file, get "before" bits for later filechanged check */
148838032Speter		if (std.st_mode == ST_MODE_NOFILE &&
148938032Speter		    (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
149038032Speter		{
149164562Sgshapiro			save_errno = errno;
149238032Speter			(void) close(dirfd);
149338032Speter			(void) close(pagfd);
149438032Speter			errno = save_errno;
149538032Speter			syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
149638032Speter				map->map_file);
149790792Sgshapiro			return false;
149838032Speter		}
149938032Speter
150038032Speter		/* have to save the lock for the duration (bletch) */
150138032Speter		map->map_lockfd = dirfd;
150264562Sgshapiro		(void) close(pagfd);
150338032Speter
150438032Speter		/* twiddle bits for dbm_open */
150538032Speter		mode &= ~(O_CREAT|O_EXCL);
150664562Sgshapiro#  endif /* NOFTRUNCATE */
150738032Speter	}
150864562Sgshapiro# endif /* LOCK_ON_OPEN */
150938032Speter
151038032Speter	/* open the database */
151138032Speter	dbm = dbm_open(map->map_file, mode, DBMMODE);
151238032Speter	if (dbm == NULL)
151338032Speter	{
151464562Sgshapiro		save_errno = errno;
151538032Speter		if (bitset(MF_ALIAS, map->map_mflags) &&
151690792Sgshapiro		    aliaswait(map, ".pag", false))
151790792Sgshapiro			return true;
151864562Sgshapiro# if !LOCK_ON_OPEN && !NOFTRUNCATE
151938032Speter		if (map->map_lockfd >= 0)
152064562Sgshapiro			(void) close(map->map_lockfd);
152164562Sgshapiro# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
152238032Speter		errno = save_errno;
152338032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
152438032Speter			syserr("Cannot open DBM database %s", map->map_file);
152590792Sgshapiro		return false;
152638032Speter	}
152738032Speter	dfd = dbm_dirfno(dbm);
152838032Speter	pfd = dbm_pagfno(dbm);
152938032Speter	if (dfd == pfd)
153038032Speter	{
153138032Speter		/* heuristic: if files are linked, this is actually gdbm */
153238032Speter		dbm_close(dbm);
153364562Sgshapiro# if !LOCK_ON_OPEN && !NOFTRUNCATE
153438032Speter		if (map->map_lockfd >= 0)
153564562Sgshapiro			(void) close(map->map_lockfd);
153664562Sgshapiro# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
153738032Speter		errno = 0;
153838032Speter		syserr("dbm map \"%s\": cannot support GDBM",
153938032Speter			map->map_mname);
154090792Sgshapiro		return false;
154138032Speter	}
154238032Speter
154338032Speter	if (filechanged(dirfile, dfd, &std) ||
154438032Speter	    filechanged(pagfile, pfd, &stp))
154538032Speter	{
154664562Sgshapiro		save_errno = errno;
154738032Speter		dbm_close(dbm);
154864562Sgshapiro# if !LOCK_ON_OPEN && !NOFTRUNCATE
154938032Speter		if (map->map_lockfd >= 0)
155064562Sgshapiro			(void) close(map->map_lockfd);
155164562Sgshapiro# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
155238032Speter		errno = save_errno;
155338032Speter		syserr("ndbm_map_open(%s): file changed after open",
155438032Speter			map->map_file);
155590792Sgshapiro		return false;
155638032Speter	}
155738032Speter
155838032Speter	map->map_db1 = (ARBPTR_T) dbm;
155964562Sgshapiro
156064562Sgshapiro	/*
156164562Sgshapiro	**  Need to set map_mtime before the call to aliaswait()
156264562Sgshapiro	**  as aliaswait() will call map_lookup() which requires
156364562Sgshapiro	**  map_mtime to be set
156464562Sgshapiro	*/
156564562Sgshapiro
156677349Sgshapiro	if (fstat(pfd, &st) >= 0)
156764562Sgshapiro		map->map_mtime = st.st_mtime;
156864562Sgshapiro
156938032Speter	if (mode == O_RDONLY)
157038032Speter	{
157164562Sgshapiro# if LOCK_ON_OPEN
157238032Speter		if (dfd >= 0)
157338032Speter			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
157438032Speter		if (pfd >= 0)
157538032Speter			(void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
157664562Sgshapiro# endif /* LOCK_ON_OPEN */
157738032Speter		if (bitset(MF_ALIAS, map->map_mflags) &&
157890792Sgshapiro		    !aliaswait(map, ".pag", true))
157990792Sgshapiro			return false;
158038032Speter	}
158138032Speter	else
158238032Speter	{
158338032Speter		map->map_mflags |= MF_LOCKED;
158442575Speter		if (geteuid() == 0 && TrustedUid != 0)
158538032Speter		{
158664562Sgshapiro#  if HASFCHOWN
158742575Speter			if (fchown(dfd, TrustedUid, -1) < 0 ||
158842575Speter			    fchown(pfd, TrustedUid, -1) < 0)
158938032Speter			{
159038032Speter				int err = errno;
159138032Speter
159238032Speter				sm_syslog(LOG_ALERT, NOQID,
159338032Speter					  "ownership change on %s failed: %s",
159490792Sgshapiro					  map->map_file, sm_errstring(err));
159538032Speter				message("050 ownership change on %s failed: %s",
159690792Sgshapiro					map->map_file, sm_errstring(err));
159738032Speter			}
159890792Sgshapiro#  else /* HASFCHOWN */
159990792Sgshapiro			sm_syslog(LOG_ALERT, NOQID,
160090792Sgshapiro				  "no fchown(): cannot change ownership on %s",
160190792Sgshapiro				  map->map_file);
160290792Sgshapiro			message("050 no fchown(): cannot change ownership on %s",
160390792Sgshapiro				map->map_file);
160464562Sgshapiro#  endif /* HASFCHOWN */
160538032Speter		}
160638032Speter	}
160790792Sgshapiro	return true;
160838032Speter}
160938032Speter
161038032Speter
161138032Speter/*
161238032Speter**  NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
161338032Speter*/
161438032Speter
161538032Speterchar *
161638032Speterndbm_map_lookup(map, name, av, statp)
161738032Speter	MAP *map;
161838032Speter	char *name;
161938032Speter	char **av;
162038032Speter	int *statp;
162138032Speter{
162238032Speter	datum key, val;
162377349Sgshapiro	int dfd, pfd;
162438032Speter	char keybuf[MAXNAME + 1];
162538032Speter	struct stat stbuf;
162638032Speter
162738032Speter	if (tTd(38, 20))
162890792Sgshapiro		sm_dprintf("ndbm_map_lookup(%s, %s)\n",
162938032Speter			map->map_mname, name);
163038032Speter
163138032Speter	key.dptr = name;
163238032Speter	key.dsize = strlen(name);
163338032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
163438032Speter	{
163538032Speter		if (key.dsize > sizeof keybuf - 1)
163638032Speter			key.dsize = sizeof keybuf - 1;
163764562Sgshapiro		memmove(keybuf, key.dptr, key.dsize);
163838032Speter		keybuf[key.dsize] = '\0';
163938032Speter		makelower(keybuf);
164038032Speter		key.dptr = keybuf;
164138032Speter	}
164238032Speterlockdbm:
164377349Sgshapiro	dfd = dbm_dirfno((DBM *) map->map_db1);
164477349Sgshapiro	if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
164577349Sgshapiro		(void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
164677349Sgshapiro	pfd = dbm_pagfno((DBM *) map->map_db1);
164777349Sgshapiro	if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
164877349Sgshapiro	    stbuf.st_mtime > map->map_mtime)
164938032Speter	{
165038032Speter		/* Reopen the database to sync the cache */
165138032Speter		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
165238032Speter								 : O_RDONLY;
165338032Speter
165477349Sgshapiro		if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
165577349Sgshapiro			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
165677349Sgshapiro		map->map_mflags |= MF_CLOSING;
165738032Speter		map->map_class->map_close(map);
165877349Sgshapiro		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
165938032Speter		if (map->map_class->map_open(map, omode))
166038032Speter		{
166138032Speter			map->map_mflags |= MF_OPEN;
166290792Sgshapiro			map->map_pid = CurrentPid;
166338032Speter			if ((omode && O_ACCMODE) == O_RDWR)
166438032Speter				map->map_mflags |= MF_WRITABLE;
166538032Speter			goto lockdbm;
166638032Speter		}
166738032Speter		else
166838032Speter		{
166938032Speter			if (!bitset(MF_OPTIONAL, map->map_mflags))
167038032Speter			{
167138032Speter				extern MAPCLASS BogusMapClass;
167238032Speter
167338032Speter				*statp = EX_TEMPFAIL;
167490792Sgshapiro				map->map_orgclass = map->map_class;
167538032Speter				map->map_class = &BogusMapClass;
167638032Speter				map->map_mflags |= MF_OPEN;
167790792Sgshapiro				map->map_pid = CurrentPid;
167838032Speter				syserr("Cannot reopen NDBM database %s",
167938032Speter					map->map_file);
168038032Speter			}
168138032Speter			return NULL;
168238032Speter		}
168338032Speter	}
168438032Speter	val.dptr = NULL;
168538032Speter	if (bitset(MF_TRY0NULL, map->map_mflags))
168638032Speter	{
168738032Speter		val = dbm_fetch((DBM *) map->map_db1, key);
168838032Speter		if (val.dptr != NULL)
168938032Speter			map->map_mflags &= ~MF_TRY1NULL;
169038032Speter	}
169138032Speter	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
169238032Speter	{
169338032Speter		key.dsize++;
169438032Speter		val = dbm_fetch((DBM *) map->map_db1, key);
169538032Speter		if (val.dptr != NULL)
169638032Speter			map->map_mflags &= ~MF_TRY0NULL;
169738032Speter	}
169877349Sgshapiro	if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
169977349Sgshapiro		(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
170038032Speter	if (val.dptr == NULL)
170138032Speter		return NULL;
170238032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
170338032Speter		return map_rewrite(map, name, strlen(name), NULL);
170438032Speter	else
170538032Speter		return map_rewrite(map, val.dptr, val.dsize, av);
170638032Speter}
170738032Speter
170838032Speter
170938032Speter/*
171038032Speter**  NDBM_MAP_STORE -- store a datum in the database
171138032Speter*/
171238032Speter
171338032Spetervoid
171438032Speterndbm_map_store(map, lhs, rhs)
171538032Speter	register MAP *map;
171638032Speter	char *lhs;
171738032Speter	char *rhs;
171838032Speter{
171938032Speter	datum key;
172038032Speter	datum data;
172164562Sgshapiro	int status;
172238032Speter	char keybuf[MAXNAME + 1];
172338032Speter
172438032Speter	if (tTd(38, 12))
172590792Sgshapiro		sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
172638032Speter			map->map_mname, lhs, rhs);
172738032Speter
172838032Speter	key.dsize = strlen(lhs);
172938032Speter	key.dptr = lhs;
173038032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
173138032Speter	{
173238032Speter		if (key.dsize > sizeof keybuf - 1)
173338032Speter			key.dsize = sizeof keybuf - 1;
173464562Sgshapiro		memmove(keybuf, key.dptr, key.dsize);
173538032Speter		keybuf[key.dsize] = '\0';
173638032Speter		makelower(keybuf);
173738032Speter		key.dptr = keybuf;
173838032Speter	}
173938032Speter
174038032Speter	data.dsize = strlen(rhs);
174138032Speter	data.dptr = rhs;
174238032Speter
174338032Speter	if (bitset(MF_INCLNULL, map->map_mflags))
174438032Speter	{
174538032Speter		key.dsize++;
174638032Speter		data.dsize++;
174738032Speter	}
174838032Speter
174964562Sgshapiro	status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
175064562Sgshapiro	if (status > 0)
175138032Speter	{
175238032Speter		if (!bitset(MF_APPEND, map->map_mflags))
175338032Speter			message("050 Warning: duplicate alias name %s", lhs);
175438032Speter		else
175538032Speter		{
175638032Speter			static char *buf = NULL;
175738032Speter			static int bufsiz = 0;
175838032Speter			auto int xstat;
175938032Speter			datum old;
176038032Speter
176138032Speter			old.dptr = ndbm_map_lookup(map, key.dptr,
176290792Sgshapiro						   (char **) NULL, &xstat);
176338032Speter			if (old.dptr != NULL && *(char *) old.dptr != '\0')
176438032Speter			{
176538032Speter				old.dsize = strlen(old.dptr);
176638032Speter				if (data.dsize + old.dsize + 2 > bufsiz)
176738032Speter				{
176838032Speter					if (buf != NULL)
176990792Sgshapiro						(void) sm_free(buf);
177038032Speter					bufsiz = data.dsize + old.dsize + 2;
177190792Sgshapiro					buf = sm_pmalloc_x(bufsiz);
177238032Speter				}
177390792Sgshapiro				(void) sm_strlcpyn(buf, bufsiz, 3,
177490792Sgshapiro					data.dptr, ",", old.dptr);
177538032Speter				data.dsize = data.dsize + old.dsize + 1;
177638032Speter				data.dptr = buf;
177738032Speter				if (tTd(38, 9))
177890792Sgshapiro					sm_dprintf("ndbm_map_store append=%s\n",
177964562Sgshapiro						data.dptr);
178038032Speter			}
178138032Speter		}
178264562Sgshapiro		status = dbm_store((DBM *) map->map_db1,
178364562Sgshapiro				   key, data, DBM_REPLACE);
178438032Speter	}
178564562Sgshapiro	if (status != 0)
178664562Sgshapiro		syserr("readaliases: dbm put (%s): %d", lhs, status);
178738032Speter}
178838032Speter
178938032Speter
179038032Speter/*
179138032Speter**  NDBM_MAP_CLOSE -- close the database
179238032Speter*/
179338032Speter
179438032Spetervoid
179538032Speterndbm_map_close(map)
179638032Speter	register MAP  *map;
179738032Speter{
179838032Speter	if (tTd(38, 9))
179990792Sgshapiro		sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
180038032Speter			map->map_mname, map->map_file, map->map_mflags);
180138032Speter
180238032Speter	if (bitset(MF_WRITABLE, map->map_mflags))
180338032Speter	{
180464562Sgshapiro# ifdef NDBM_YP_COMPAT
180538032Speter		bool inclnull;
180642575Speter		char buf[MAXHOSTNAMELEN];
180738032Speter
180838032Speter		inclnull = bitset(MF_INCLNULL, map->map_mflags);
180938032Speter		map->map_mflags &= ~MF_INCLNULL;
181038032Speter
181138032Speter		if (strstr(map->map_file, "/yp/") != NULL)
181238032Speter		{
181338032Speter			long save_mflags = map->map_mflags;
181438032Speter
181538032Speter			map->map_mflags |= MF_NOFOLDCASE;
181638032Speter
181790792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%010ld", curtime());
181838032Speter			ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
181938032Speter
182038032Speter			(void) gethostname(buf, sizeof buf);
182138032Speter			ndbm_map_store(map, "YP_MASTER_NAME", buf);
182238032Speter
182338032Speter			map->map_mflags = save_mflags;
182438032Speter		}
182538032Speter
182638032Speter		if (inclnull)
182738032Speter			map->map_mflags |= MF_INCLNULL;
182864562Sgshapiro# endif /* NDBM_YP_COMPAT */
182938032Speter
183038032Speter		/* write out the distinguished alias */
183138032Speter		ndbm_map_store(map, "@", "@");
183238032Speter	}
183338032Speter	dbm_close((DBM *) map->map_db1);
183438032Speter
183538032Speter	/* release lock (if needed) */
183664562Sgshapiro# if !LOCK_ON_OPEN
183738032Speter	if (map->map_lockfd >= 0)
183838032Speter		(void) close(map->map_lockfd);
183964562Sgshapiro# endif /* !LOCK_ON_OPEN */
184038032Speter}
184138032Speter
184264562Sgshapiro#endif /* NDBM */
184390792Sgshapiro/*
184438032Speter**  NEWDB (Hash and BTree) Modules
184538032Speter*/
184638032Speter
184790792Sgshapiro#if NEWDB
184838032Speter
184938032Speter/*
185038032Speter**  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
185138032Speter**
185238032Speter**	These do rather bizarre locking.  If you can lock on open,
185338032Speter**	do that to avoid the condition of opening a database that
185438032Speter**	is being rebuilt.  If you don't, we'll try to fake it, but
185538032Speter**	there will be a race condition.  If opening for read-only,
185638032Speter**	we immediately release the lock to avoid freezing things up.
185738032Speter**	We really ought to hold the lock, but guarantee that we won't
185838032Speter**	be pokey about it.  That's hard to do.
185938032Speter*/
186038032Speter
186138032Speter/* these should be K line arguments */
186264562Sgshapiro# if DB_VERSION_MAJOR < 2
186364562Sgshapiro#  define db_cachesize	cachesize
186464562Sgshapiro#  define h_nelem	nelem
186564562Sgshapiro#  ifndef DB_CACHE_SIZE
186664562Sgshapiro#   define DB_CACHE_SIZE	(1024 * 1024)	/* database memory cache size */
186764562Sgshapiro#  endif /* ! DB_CACHE_SIZE */
186864562Sgshapiro#  ifndef DB_HASH_NELEM
186964562Sgshapiro#   define DB_HASH_NELEM	4096		/* (starting) size of hash table */
187064562Sgshapiro#  endif /* ! DB_HASH_NELEM */
187164562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
187238032Speter
187338032Speterbool
187438032Speterbt_map_open(map, mode)
187538032Speter	MAP *map;
187638032Speter	int mode;
187738032Speter{
187864562Sgshapiro# if DB_VERSION_MAJOR < 2
187938032Speter	BTREEINFO btinfo;
188064562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
188164562Sgshapiro# if DB_VERSION_MAJOR == 2
188238032Speter	DB_INFO btinfo;
188364562Sgshapiro# endif /* DB_VERSION_MAJOR == 2 */
188464562Sgshapiro# if DB_VERSION_MAJOR > 2
188564562Sgshapiro	void *btinfo = NULL;
188664562Sgshapiro# endif /* DB_VERSION_MAJOR > 2 */
188738032Speter
188838032Speter	if (tTd(38, 2))
188990792Sgshapiro		sm_dprintf("bt_map_open(%s, %s, %d)\n",
189038032Speter			map->map_mname, map->map_file, mode);
189138032Speter
189264562Sgshapiro# if DB_VERSION_MAJOR < 3
189364562Sgshapiro	memset(&btinfo, '\0', sizeof btinfo);
189464562Sgshapiro#  ifdef DB_CACHE_SIZE
189538032Speter	btinfo.db_cachesize = DB_CACHE_SIZE;
189664562Sgshapiro#  endif /* DB_CACHE_SIZE */
189764562Sgshapiro# endif /* DB_VERSION_MAJOR < 3 */
189864562Sgshapiro
189938032Speter	return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
190038032Speter}
190138032Speter
190238032Speterbool
190338032Speterhash_map_open(map, mode)
190438032Speter	MAP *map;
190538032Speter	int mode;
190638032Speter{
190764562Sgshapiro# if DB_VERSION_MAJOR < 2
190838032Speter	HASHINFO hinfo;
190964562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
191064562Sgshapiro# if DB_VERSION_MAJOR == 2
191138032Speter	DB_INFO hinfo;
191264562Sgshapiro# endif /* DB_VERSION_MAJOR == 2 */
191364562Sgshapiro# if DB_VERSION_MAJOR > 2
191464562Sgshapiro	void *hinfo = NULL;
191564562Sgshapiro# endif /* DB_VERSION_MAJOR > 2 */
191638032Speter
191738032Speter	if (tTd(38, 2))
191890792Sgshapiro		sm_dprintf("hash_map_open(%s, %s, %d)\n",
191938032Speter			map->map_mname, map->map_file, mode);
192038032Speter
192164562Sgshapiro# if DB_VERSION_MAJOR < 3
192264562Sgshapiro	memset(&hinfo, '\0', sizeof hinfo);
192364562Sgshapiro#  ifdef DB_HASH_NELEM
192438032Speter	hinfo.h_nelem = DB_HASH_NELEM;
192564562Sgshapiro#  endif /* DB_HASH_NELEM */
192664562Sgshapiro#  ifdef DB_CACHE_SIZE
192738032Speter	hinfo.db_cachesize = DB_CACHE_SIZE;
192864562Sgshapiro#  endif /* DB_CACHE_SIZE */
192964562Sgshapiro# endif /* DB_VERSION_MAJOR < 3 */
193064562Sgshapiro
193138032Speter	return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
193238032Speter}
193338032Speter
193464562Sgshapirostatic bool
193538032Speterdb_map_open(map, mode, mapclassname, dbtype, openinfo)
193638032Speter	MAP *map;
193738032Speter	int mode;
193838032Speter	char *mapclassname;
193938032Speter	DBTYPE dbtype;
194064562Sgshapiro# if DB_VERSION_MAJOR < 2
194138032Speter	const void *openinfo;
194264562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
194364562Sgshapiro# if DB_VERSION_MAJOR == 2
194438032Speter	DB_INFO *openinfo;
194564562Sgshapiro# endif /* DB_VERSION_MAJOR == 2 */
194664562Sgshapiro# if DB_VERSION_MAJOR > 2
194764562Sgshapiro	void **openinfo;
194864562Sgshapiro# endif /* DB_VERSION_MAJOR > 2 */
194938032Speter{
195038032Speter	DB *db = NULL;
195138032Speter	int i;
195238032Speter	int omode;
195338032Speter	int smode = S_IREAD;
195438032Speter	int fd;
195564562Sgshapiro	long sff;
195664562Sgshapiro	int save_errno;
195738032Speter	struct stat st;
195898121Sgshapiro	char buf[MAXPATHLEN];
195938032Speter
196038032Speter	/* do initial file and directory checks */
196198121Sgshapiro	if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
196298121Sgshapiro	{
196398121Sgshapiro		errno = 0;
196498121Sgshapiro		if (!bitset(MF_OPTIONAL, map->map_mflags))
196598121Sgshapiro			syserr("map \"%s\": map file %s name too long",
196698121Sgshapiro				map->map_mname, map->map_file);
196798121Sgshapiro		return false;
196898121Sgshapiro	}
196938032Speter	i = strlen(buf);
197038032Speter	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
197198121Sgshapiro	{
197298121Sgshapiro		if (sm_strlcat(buf, ".db", sizeof buf) >= sizeof buf)
197398121Sgshapiro		{
197498121Sgshapiro			errno = 0;
197598121Sgshapiro			if (!bitset(MF_OPTIONAL, map->map_mflags))
197698121Sgshapiro				syserr("map \"%s\": map file %s name too long",
197798121Sgshapiro					map->map_mname, map->map_file);
197898121Sgshapiro			return false;
197998121Sgshapiro		}
198098121Sgshapiro	}
198138032Speter
198238032Speter	mode &= O_ACCMODE;
198338032Speter	omode = mode;
198438032Speter
198538032Speter	sff = SFF_ROOTOK|SFF_REGONLY;
198638032Speter	if (mode == O_RDWR)
198738032Speter	{
198838032Speter		sff |= SFF_CREAT;
198964562Sgshapiro		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
199038032Speter			sff |= SFF_NOSLINK;
199164562Sgshapiro		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
199238032Speter			sff |= SFF_NOHLINK;
199338032Speter		smode = S_IWRITE;
199438032Speter	}
199538032Speter	else
199638032Speter	{
199764562Sgshapiro		if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
199838032Speter			sff |= SFF_NOWLINK;
199938032Speter	}
200064562Sgshapiro	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
200138032Speter		sff |= SFF_SAFEDIRPATH;
200238032Speter	i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
200364562Sgshapiro
200438032Speter	if (i != 0)
200538032Speter	{
200638032Speter		char *prob = "unsafe";
200738032Speter
200838032Speter		/* cannot open this map */
200938032Speter		if (i == ENOENT)
201038032Speter			prob = "missing";
201138032Speter		if (tTd(38, 2))
201290792Sgshapiro			sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
201338032Speter		errno = i;
201438032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
201538032Speter			syserr("%s map \"%s\": %s map file %s",
201638032Speter				mapclassname, map->map_mname, prob, buf);
201790792Sgshapiro		return false;
201838032Speter	}
201938032Speter	if (st.st_mode == ST_MODE_NOFILE)
202038032Speter		omode |= O_CREAT|O_EXCL;
202138032Speter
202238032Speter	map->map_lockfd = -1;
202338032Speter
202464562Sgshapiro# if LOCK_ON_OPEN
202538032Speter	if (mode == O_RDWR)
202638032Speter		omode |= O_TRUNC|O_EXLOCK;
202738032Speter	else
202838032Speter		omode |= O_SHLOCK;
202964562Sgshapiro# else /* LOCK_ON_OPEN */
203038032Speter	/*
203138032Speter	**  Pre-lock the file to avoid race conditions.  In particular,
203238032Speter	**  since dbopen returns NULL if the file is zero length, we
203338032Speter	**  must have a locked instance around the dbopen.
203438032Speter	*/
203538032Speter
203638032Speter	fd = open(buf, omode, DBMMODE);
203738032Speter	if (fd < 0)
203838032Speter	{
203938032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
204038032Speter			syserr("db_map_open: cannot pre-open database %s", buf);
204190792Sgshapiro		return false;
204238032Speter	}
204338032Speter
204438032Speter	/* make sure no baddies slipped in just before the open... */
204538032Speter	if (filechanged(buf, fd, &st))
204638032Speter	{
204764562Sgshapiro		save_errno = errno;
204838032Speter		(void) close(fd);
204938032Speter		errno = save_errno;
205038032Speter		syserr("db_map_open(%s): file changed after pre-open", buf);
205190792Sgshapiro		return false;
205238032Speter	}
205338032Speter
205438032Speter	/* if new file, get the "before" bits for later filechanged check */
205538032Speter	if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
205638032Speter	{
205764562Sgshapiro		save_errno = errno;
205838032Speter		(void) close(fd);
205938032Speter		errno = save_errno;
206038032Speter		syserr("db_map_open(%s): cannot fstat pre-opened file",
206138032Speter			buf);
206290792Sgshapiro		return false;
206338032Speter	}
206438032Speter
206538032Speter	/* actually lock the pre-opened file */
206638032Speter	if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
206738032Speter		syserr("db_map_open: cannot lock %s", buf);
206838032Speter
206938032Speter	/* set up mode bits for dbopen */
207038032Speter	if (mode == O_RDWR)
207138032Speter		omode |= O_TRUNC;
207238032Speter	omode &= ~(O_EXCL|O_CREAT);
207364562Sgshapiro# endif /* LOCK_ON_OPEN */
207438032Speter
207564562Sgshapiro# if DB_VERSION_MAJOR < 2
207638032Speter	db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
207764562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
207838032Speter	{
207938032Speter		int flags = 0;
208064562Sgshapiro#  if DB_VERSION_MAJOR > 2
208164562Sgshapiro		int ret;
208264562Sgshapiro#  endif /* DB_VERSION_MAJOR > 2 */
208338032Speter
208438032Speter		if (mode == O_RDONLY)
208538032Speter			flags |= DB_RDONLY;
208638032Speter		if (bitset(O_CREAT, omode))
208738032Speter			flags |= DB_CREATE;
208838032Speter		if (bitset(O_TRUNC, omode))
208938032Speter			flags |= DB_TRUNCATE;
2090110560Sgshapiro		SM_DB_FLAG_ADD(flags);
209138032Speter
209264562Sgshapiro#  if DB_VERSION_MAJOR > 2
209364562Sgshapiro		ret = db_create(&db, NULL, 0);
209464562Sgshapiro#  ifdef DB_CACHE_SIZE
209564562Sgshapiro		if (ret == 0 && db != NULL)
209664562Sgshapiro		{
209764562Sgshapiro			ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
209864562Sgshapiro			if (ret != 0)
209964562Sgshapiro			{
210064562Sgshapiro				(void) db->close(db, 0);
210164562Sgshapiro				db = NULL;
210264562Sgshapiro			}
210364562Sgshapiro		}
210464562Sgshapiro#  endif /* DB_CACHE_SIZE */
210564562Sgshapiro#  ifdef DB_HASH_NELEM
210664562Sgshapiro		if (dbtype == DB_HASH && ret == 0 && db != NULL)
210764562Sgshapiro		{
210864562Sgshapiro			ret = db->set_h_nelem(db, DB_HASH_NELEM);
210964562Sgshapiro			if (ret != 0)
211064562Sgshapiro			{
211164562Sgshapiro				(void) db->close(db, 0);
211264562Sgshapiro				db = NULL;
211364562Sgshapiro			}
211464562Sgshapiro		}
211564562Sgshapiro#  endif /* DB_HASH_NELEM */
211664562Sgshapiro		if (ret == 0 && db != NULL)
211764562Sgshapiro		{
2118110560Sgshapiro			ret = db->open(db,
2119110560Sgshapiro					DBTXN	/* transaction for DB 4.1 */
2120110560Sgshapiro					buf, NULL, dbtype, flags, DBMMODE);
212164562Sgshapiro			if (ret != 0)
212264562Sgshapiro			{
212373188Sgshapiro#ifdef DB_OLD_VERSION
212473188Sgshapiro				if (ret == DB_OLD_VERSION)
212573188Sgshapiro					ret = EINVAL;
212673188Sgshapiro#endif /* DB_OLD_VERSION */
212764562Sgshapiro				(void) db->close(db, 0);
212864562Sgshapiro				db = NULL;
212964562Sgshapiro			}
213064562Sgshapiro		}
213164562Sgshapiro		errno = ret;
213264562Sgshapiro#  else /* DB_VERSION_MAJOR > 2 */
213338032Speter		errno = db_open(buf, dbtype, flags, DBMMODE,
213438032Speter				NULL, openinfo, &db);
213564562Sgshapiro#  endif /* DB_VERSION_MAJOR > 2 */
213638032Speter	}
213764562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
213864562Sgshapiro	save_errno = errno;
213938032Speter
214064562Sgshapiro# if !LOCK_ON_OPEN
214138032Speter	if (mode == O_RDWR)
214238032Speter		map->map_lockfd = fd;
214338032Speter	else
214438032Speter		(void) close(fd);
214564562Sgshapiro# endif /* !LOCK_ON_OPEN */
214638032Speter
214738032Speter	if (db == NULL)
214838032Speter	{
214938032Speter		if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
215090792Sgshapiro		    aliaswait(map, ".db", false))
215190792Sgshapiro			return true;
215264562Sgshapiro# if !LOCK_ON_OPEN
215338032Speter		if (map->map_lockfd >= 0)
215438032Speter			(void) close(map->map_lockfd);
215564562Sgshapiro# endif /* !LOCK_ON_OPEN */
215664562Sgshapiro		errno = save_errno;
215738032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
215838032Speter			syserr("Cannot open %s database %s",
215938032Speter				mapclassname, buf);
216090792Sgshapiro		return false;
216138032Speter	}
216238032Speter
216364562Sgshapiro# if DB_VERSION_MAJOR < 2
216438032Speter	fd = db->fd(db);
216564562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
216638032Speter	fd = -1;
216738032Speter	errno = db->fd(db, &fd);
216864562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
216938032Speter	if (filechanged(buf, fd, &st))
217038032Speter	{
217164562Sgshapiro		save_errno = errno;
217264562Sgshapiro# if DB_VERSION_MAJOR < 2
217364562Sgshapiro		(void) db->close(db);
217464562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
217538032Speter		errno = db->close(db, 0);
217664562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
217764562Sgshapiro# if !LOCK_ON_OPEN
217838032Speter		if (map->map_lockfd >= 0)
217964562Sgshapiro			(void) close(map->map_lockfd);
218064562Sgshapiro# endif /* !LOCK_ON_OPEN */
218138032Speter		errno = save_errno;
218238032Speter		syserr("db_map_open(%s): file changed after open", buf);
218390792Sgshapiro		return false;
218438032Speter	}
218538032Speter
218638032Speter	if (mode == O_RDWR)
218738032Speter		map->map_mflags |= MF_LOCKED;
218864562Sgshapiro# if LOCK_ON_OPEN
218938032Speter	if (fd >= 0 && mode == O_RDONLY)
219038032Speter	{
219138032Speter		(void) lockfile(fd, buf, NULL, LOCK_UN);
219238032Speter	}
219364562Sgshapiro# endif /* LOCK_ON_OPEN */
219438032Speter
219538032Speter	/* try to make sure that at least the database header is on disk */
219638032Speter	if (mode == O_RDWR)
219738032Speter	{
219838032Speter		(void) db->sync(db, 0);
219942575Speter		if (geteuid() == 0 && TrustedUid != 0)
220038032Speter		{
220164562Sgshapiro#  if HASFCHOWN
220242575Speter			if (fchown(fd, TrustedUid, -1) < 0)
220338032Speter			{
220438032Speter				int err = errno;
220538032Speter
220638032Speter				sm_syslog(LOG_ALERT, NOQID,
220738032Speter					  "ownership change on %s failed: %s",
220890792Sgshapiro					  buf, sm_errstring(err));
220938032Speter				message("050 ownership change on %s failed: %s",
221090792Sgshapiro					buf, sm_errstring(err));
221138032Speter			}
221290792Sgshapiro#  else /* HASFCHOWN */
221390792Sgshapiro			sm_syslog(LOG_ALERT, NOQID,
221490792Sgshapiro				  "no fchown(): cannot change ownership on %s",
221590792Sgshapiro				  map->map_file);
221690792Sgshapiro			message("050 no fchown(): cannot change ownership on %s",
221790792Sgshapiro				map->map_file);
221864562Sgshapiro#  endif /* HASFCHOWN */
221938032Speter		}
222038032Speter	}
222138032Speter
222264562Sgshapiro	map->map_db2 = (ARBPTR_T) db;
222364562Sgshapiro
222464562Sgshapiro	/*
222564562Sgshapiro	**  Need to set map_mtime before the call to aliaswait()
222664562Sgshapiro	**  as aliaswait() will call map_lookup() which requires
222764562Sgshapiro	**  map_mtime to be set
222864562Sgshapiro	*/
222964562Sgshapiro
223038032Speter	if (fd >= 0 && fstat(fd, &st) >= 0)
223138032Speter		map->map_mtime = st.st_mtime;
223238032Speter
223338032Speter	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
223490792Sgshapiro	    !aliaswait(map, ".db", true))
223590792Sgshapiro		return false;
223690792Sgshapiro	return true;
223738032Speter}
223838032Speter
223938032Speter
224038032Speter/*
224138032Speter**  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
224238032Speter*/
224338032Speter
224438032Speterchar *
224538032Speterdb_map_lookup(map, name, av, statp)
224638032Speter	MAP *map;
224738032Speter	char *name;
224838032Speter	char **av;
224938032Speter	int *statp;
225038032Speter{
225138032Speter	DBT key, val;
225238032Speter	register DB *db = (DB *) map->map_db2;
225338032Speter	int i;
225438032Speter	int st;
225564562Sgshapiro	int save_errno;
225638032Speter	int fd;
225738032Speter	struct stat stbuf;
225838032Speter	char keybuf[MAXNAME + 1];
225998121Sgshapiro	char buf[MAXPATHLEN];
226038032Speter
226164562Sgshapiro	memset(&key, '\0', sizeof key);
226264562Sgshapiro	memset(&val, '\0', sizeof val);
226338032Speter
226438032Speter	if (tTd(38, 20))
226590792Sgshapiro		sm_dprintf("db_map_lookup(%s, %s)\n",
226638032Speter			map->map_mname, name);
226738032Speter
226898121Sgshapiro	if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
226998121Sgshapiro	{
227098121Sgshapiro		errno = 0;
227198121Sgshapiro		if (!bitset(MF_OPTIONAL, map->map_mflags))
227298121Sgshapiro			syserr("map \"%s\": map file %s name too long",
227398121Sgshapiro				map->map_mname, map->map_file);
227498121Sgshapiro		return NULL;
227598121Sgshapiro	}
227698121Sgshapiro	i = strlen(buf);
227738032Speter	if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
227838032Speter		buf[i - 3] = '\0';
227938032Speter
228038032Speter	key.size = strlen(name);
228138032Speter	if (key.size > sizeof keybuf - 1)
228238032Speter		key.size = sizeof keybuf - 1;
228338032Speter	key.data = keybuf;
228464562Sgshapiro	memmove(keybuf, name, key.size);
228538032Speter	keybuf[key.size] = '\0';
228638032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
228738032Speter		makelower(keybuf);
228838032Speter  lockdb:
228964562Sgshapiro# if DB_VERSION_MAJOR < 2
229038032Speter	fd = db->fd(db);
229164562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
229238032Speter	fd = -1;
229338032Speter	errno = db->fd(db, &fd);
229464562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
229538032Speter	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
229638032Speter		(void) lockfile(fd, buf, ".db", LOCK_SH);
229738032Speter	if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
229838032Speter	{
229938032Speter		/* Reopen the database to sync the cache */
230038032Speter		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
230138032Speter								 : O_RDONLY;
230238032Speter
230364562Sgshapiro		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
230464562Sgshapiro			(void) lockfile(fd, buf, ".db", LOCK_UN);
230577349Sgshapiro		map->map_mflags |= MF_CLOSING;
230638032Speter		map->map_class->map_close(map);
230777349Sgshapiro		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
230838032Speter		if (map->map_class->map_open(map, omode))
230938032Speter		{
231038032Speter			map->map_mflags |= MF_OPEN;
231190792Sgshapiro			map->map_pid = CurrentPid;
231238032Speter			if ((omode && O_ACCMODE) == O_RDWR)
231338032Speter				map->map_mflags |= MF_WRITABLE;
231438032Speter			db = (DB *) map->map_db2;
231538032Speter			goto lockdb;
231638032Speter		}
231738032Speter		else
231838032Speter		{
231938032Speter			if (!bitset(MF_OPTIONAL, map->map_mflags))
232038032Speter			{
232138032Speter				extern MAPCLASS BogusMapClass;
232238032Speter
232338032Speter				*statp = EX_TEMPFAIL;
232490792Sgshapiro				map->map_orgclass = map->map_class;
232538032Speter				map->map_class = &BogusMapClass;
232638032Speter				map->map_mflags |= MF_OPEN;
232790792Sgshapiro				map->map_pid = CurrentPid;
232838032Speter				syserr("Cannot reopen DB database %s",
232938032Speter					map->map_file);
233038032Speter			}
233138032Speter			return NULL;
233238032Speter		}
233338032Speter	}
233438032Speter
233538032Speter	st = 1;
233638032Speter	if (bitset(MF_TRY0NULL, map->map_mflags))
233738032Speter	{
233864562Sgshapiro# if DB_VERSION_MAJOR < 2
233938032Speter		st = db->get(db, &key, &val, 0);
234064562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
234138032Speter		errno = db->get(db, NULL, &key, &val, 0);
234238032Speter		switch (errno)
234338032Speter		{
234438032Speter		  case DB_NOTFOUND:
234538032Speter		  case DB_KEYEMPTY:
234638032Speter			st = 1;
234738032Speter			break;
234838032Speter
234938032Speter		  case 0:
235038032Speter			st = 0;
235138032Speter			break;
235238032Speter
235338032Speter		  default:
235438032Speter			st = -1;
235538032Speter			break;
235638032Speter		}
235764562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
235838032Speter		if (st == 0)
235938032Speter			map->map_mflags &= ~MF_TRY1NULL;
236038032Speter	}
236138032Speter	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
236238032Speter	{
236338032Speter		key.size++;
236464562Sgshapiro# if DB_VERSION_MAJOR < 2
236538032Speter		st = db->get(db, &key, &val, 0);
236664562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
236738032Speter		errno = db->get(db, NULL, &key, &val, 0);
236838032Speter		switch (errno)
236938032Speter		{
237038032Speter		  case DB_NOTFOUND:
237138032Speter		  case DB_KEYEMPTY:
237238032Speter			st = 1;
237338032Speter			break;
237438032Speter
237538032Speter		  case 0:
237638032Speter			st = 0;
237738032Speter			break;
237838032Speter
237938032Speter		  default:
238038032Speter			st = -1;
238138032Speter			break;
238238032Speter		}
238364562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
238438032Speter		if (st == 0)
238538032Speter			map->map_mflags &= ~MF_TRY0NULL;
238638032Speter	}
238764562Sgshapiro	save_errno = errno;
238838032Speter	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
238938032Speter		(void) lockfile(fd, buf, ".db", LOCK_UN);
239038032Speter	if (st != 0)
239138032Speter	{
239264562Sgshapiro		errno = save_errno;
239338032Speter		if (st < 0)
239438032Speter			syserr("db_map_lookup: get (%s)", name);
239538032Speter		return NULL;
239638032Speter	}
239738032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
239838032Speter		return map_rewrite(map, name, strlen(name), NULL);
239938032Speter	else
240038032Speter		return map_rewrite(map, val.data, val.size, av);
240138032Speter}
240238032Speter
240338032Speter
240438032Speter/*
240538032Speter**  DB_MAP_STORE -- store a datum in the NEWDB database
240638032Speter*/
240738032Speter
240838032Spetervoid
240938032Speterdb_map_store(map, lhs, rhs)
241038032Speter	register MAP *map;
241138032Speter	char *lhs;
241238032Speter	char *rhs;
241338032Speter{
241464562Sgshapiro	int status;
241538032Speter	DBT key;
241638032Speter	DBT data;
241738032Speter	register DB *db = map->map_db2;
241838032Speter	char keybuf[MAXNAME + 1];
241938032Speter
242064562Sgshapiro	memset(&key, '\0', sizeof key);
242164562Sgshapiro	memset(&data, '\0', sizeof data);
242238032Speter
242338032Speter	if (tTd(38, 12))
242490792Sgshapiro		sm_dprintf("db_map_store(%s, %s, %s)\n",
242538032Speter			map->map_mname, lhs, rhs);
242638032Speter
242738032Speter	key.size = strlen(lhs);
242838032Speter	key.data = lhs;
242938032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
243038032Speter	{
243138032Speter		if (key.size > sizeof keybuf - 1)
243238032Speter			key.size = sizeof keybuf - 1;
243364562Sgshapiro		memmove(keybuf, key.data, key.size);
243438032Speter		keybuf[key.size] = '\0';
243538032Speter		makelower(keybuf);
243638032Speter		key.data = keybuf;
243738032Speter	}
243838032Speter
243938032Speter	data.size = strlen(rhs);
244038032Speter	data.data = rhs;
244138032Speter
244238032Speter	if (bitset(MF_INCLNULL, map->map_mflags))
244338032Speter	{
244438032Speter		key.size++;
244538032Speter		data.size++;
244638032Speter	}
244738032Speter
244864562Sgshapiro# if DB_VERSION_MAJOR < 2
244964562Sgshapiro	status = db->put(db, &key, &data, R_NOOVERWRITE);
245064562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
245138032Speter	errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
245238032Speter	switch (errno)
245338032Speter	{
245438032Speter	  case DB_KEYEXIST:
245564562Sgshapiro		status = 1;
245638032Speter		break;
245738032Speter
245838032Speter	  case 0:
245964562Sgshapiro		status = 0;
246038032Speter		break;
246138032Speter
246238032Speter	  default:
246364562Sgshapiro		status = -1;
246438032Speter		break;
246538032Speter	}
246664562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
246764562Sgshapiro	if (status > 0)
246838032Speter	{
246938032Speter		if (!bitset(MF_APPEND, map->map_mflags))
247038032Speter			message("050 Warning: duplicate alias name %s", lhs);
247138032Speter		else
247238032Speter		{
247338032Speter			static char *buf = NULL;
247438032Speter			static int bufsiz = 0;
247538032Speter			DBT old;
247638032Speter
247764562Sgshapiro			memset(&old, '\0', sizeof old);
247838032Speter
247964562Sgshapiro			old.data = db_map_lookup(map, key.data,
248090792Sgshapiro						 (char **) NULL, &status);
248138032Speter			if (old.data != NULL)
248238032Speter			{
248338032Speter				old.size = strlen(old.data);
248490792Sgshapiro				if (data.size + old.size + 2 > (size_t) bufsiz)
248538032Speter				{
248638032Speter					if (buf != NULL)
248777349Sgshapiro						sm_free(buf);
248838032Speter					bufsiz = data.size + old.size + 2;
248990792Sgshapiro					buf = sm_pmalloc_x(bufsiz);
249038032Speter				}
249190792Sgshapiro				(void) sm_strlcpyn(buf, bufsiz, 3,
249290792Sgshapiro					(char *) data.data, ",",
249390792Sgshapiro					(char *) old.data);
249438032Speter				data.size = data.size + old.size + 1;
249538032Speter				data.data = buf;
249638032Speter				if (tTd(38, 9))
249790792Sgshapiro					sm_dprintf("db_map_store append=%s\n",
249864562Sgshapiro						(char *) data.data);
249938032Speter			}
250038032Speter		}
250164562Sgshapiro# if DB_VERSION_MAJOR < 2
250264562Sgshapiro		status = db->put(db, &key, &data, 0);
250364562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
250464562Sgshapiro		status = errno = db->put(db, NULL, &key, &data, 0);
250564562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
250638032Speter	}
250764562Sgshapiro	if (status != 0)
250838032Speter		syserr("readaliases: db put (%s)", lhs);
250938032Speter}
251038032Speter
251138032Speter
251238032Speter/*
251338032Speter**  DB_MAP_CLOSE -- add distinguished entries and close the database
251438032Speter*/
251538032Speter
251638032Spetervoid
251738032Speterdb_map_close(map)
251838032Speter	MAP *map;
251938032Speter{
252038032Speter	register DB *db = map->map_db2;
252138032Speter
252238032Speter	if (tTd(38, 9))
252390792Sgshapiro		sm_dprintf("db_map_close(%s, %s, %lx)\n",
252438032Speter			map->map_mname, map->map_file, map->map_mflags);
252538032Speter
252638032Speter	if (bitset(MF_WRITABLE, map->map_mflags))
252738032Speter	{
252838032Speter		/* write out the distinguished alias */
252938032Speter		db_map_store(map, "@", "@");
253038032Speter	}
253138032Speter
253238032Speter	(void) db->sync(db, 0);
253338032Speter
253464562Sgshapiro# if !LOCK_ON_OPEN
253538032Speter	if (map->map_lockfd >= 0)
253638032Speter		(void) close(map->map_lockfd);
253764562Sgshapiro# endif /* !LOCK_ON_OPEN */
253838032Speter
253964562Sgshapiro# if DB_VERSION_MAJOR < 2
254038032Speter	if (db->close(db) != 0)
254164562Sgshapiro# else /* DB_VERSION_MAJOR < 2 */
254242575Speter	/*
254342575Speter	**  Berkeley DB can use internal shared memory
254442575Speter	**  locking for its memory pool.  Closing a map
254542575Speter	**  opened by another process will interfere
254642575Speter	**  with the shared memory and locks of the parent
254742575Speter	**  process leaving things in a bad state.
254843730Speter	*/
254943730Speter
255043730Speter	/*
255142575Speter	**  If this map was not opened by the current
255243730Speter	**  process, do not close the map but recover
255342575Speter	**  the file descriptor.
255442575Speter	*/
255590792Sgshapiro
255690792Sgshapiro	if (map->map_pid != CurrentPid)
255742575Speter	{
255842575Speter		int fd = -1;
255942575Speter
256042575Speter		errno = db->fd(db, &fd);
256142575Speter		if (fd >= 0)
256242575Speter			(void) close(fd);
256342575Speter		return;
256442575Speter	}
256542575Speter
256638032Speter	if ((errno = db->close(db, 0)) != 0)
256764562Sgshapiro# endif /* DB_VERSION_MAJOR < 2 */
256842575Speter		syserr("db_map_close(%s, %s, %lx): db close failure",
256942575Speter			map->map_mname, map->map_file, map->map_mflags);
257038032Speter}
257164562Sgshapiro#endif /* NEWDB */
257290792Sgshapiro/*
257338032Speter**  NIS Modules
257438032Speter*/
257538032Speter
257690792Sgshapiro#if NIS
257738032Speter
257838032Speter# ifndef YPERR_BUSY
257938032Speter#  define YPERR_BUSY	16
258064562Sgshapiro# endif /* ! YPERR_BUSY */
258138032Speter
258238032Speter/*
258338032Speter**  NIS_MAP_OPEN -- open DBM map
258438032Speter*/
258538032Speter
258638032Speterbool
258738032Speternis_map_open(map, mode)
258838032Speter	MAP *map;
258938032Speter	int mode;
259038032Speter{
259138032Speter	int yperr;
259238032Speter	register char *p;
259338032Speter	auto char *vp;
259438032Speter	auto int vsize;
259538032Speter
259638032Speter	if (tTd(38, 2))
259790792Sgshapiro		sm_dprintf("nis_map_open(%s, %s, %d)\n",
259838032Speter			map->map_mname, map->map_file, mode);
259938032Speter
260038032Speter	mode &= O_ACCMODE;
260138032Speter	if (mode != O_RDONLY)
260238032Speter	{
260338032Speter		/* issue a pseudo-error message */
260490792Sgshapiro		errno = SM_EMAPCANTWRITE;
260590792Sgshapiro		return false;
260638032Speter	}
260738032Speter
260838032Speter	p = strchr(map->map_file, '@');
260938032Speter	if (p != NULL)
261038032Speter	{
261138032Speter		*p++ = '\0';
261238032Speter		if (*p != '\0')
261338032Speter			map->map_domain = p;
261438032Speter	}
261538032Speter
261638032Speter	if (*map->map_file == '\0')
261738032Speter		map->map_file = "mail.aliases";
261838032Speter
261938032Speter	if (map->map_domain == NULL)
262038032Speter	{
262138032Speter		yperr = yp_get_default_domain(&map->map_domain);
262238032Speter		if (yperr != 0)
262338032Speter		{
262438032Speter			if (!bitset(MF_OPTIONAL, map->map_mflags))
262594334Sgshapiro				syserr("451 4.3.5 NIS map %s specified, but NIS not running",
262664562Sgshapiro				       map->map_file);
262790792Sgshapiro			return false;
262838032Speter		}
262938032Speter	}
263038032Speter
263138032Speter	/* check to see if this map actually exists */
263264562Sgshapiro	vp = NULL;
263338032Speter	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
263438032Speter			&vp, &vsize);
263538032Speter	if (tTd(38, 10))
263690792Sgshapiro		sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
263738032Speter			map->map_domain, map->map_file, yperr_string(yperr));
263864562Sgshapiro	if (vp != NULL)
263977349Sgshapiro		sm_free(vp);
264064562Sgshapiro
264138032Speter	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
264238032Speter	{
264338032Speter		/*
264438032Speter		**  We ought to be calling aliaswait() here if this is an
264538032Speter		**  alias file, but powerful HP-UX NIS servers  apparently
264638032Speter		**  don't insert the @:@ token into the alias map when it
264738032Speter		**  is rebuilt, so aliaswait() just hangs.  I hate HP-UX.
264838032Speter		*/
264938032Speter
265064562Sgshapiro# if 0
265138032Speter		if (!bitset(MF_ALIAS, map->map_mflags) ||
265290792Sgshapiro		    aliaswait(map, NULL, true))
265364562Sgshapiro# endif /* 0 */
265490792Sgshapiro			return true;
265538032Speter	}
265638032Speter
265738032Speter	if (!bitset(MF_OPTIONAL, map->map_mflags))
265838032Speter	{
265994334Sgshapiro		syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
266038032Speter			map->map_file, map->map_domain, yperr_string(yperr));
266138032Speter	}
266238032Speter
266390792Sgshapiro	return false;
266438032Speter}
266538032Speter
266638032Speter
266738032Speter/*
266838032Speter**  NIS_MAP_LOOKUP -- look up a datum in a NIS map
266938032Speter*/
267038032Speter
267138032Speter/* ARGSUSED3 */
267238032Speterchar *
267338032Speternis_map_lookup(map, name, av, statp)
267438032Speter	MAP *map;
267538032Speter	char *name;
267638032Speter	char **av;
267738032Speter	int *statp;
267838032Speter{
267938032Speter	char *vp;
268038032Speter	auto int vsize;
268138032Speter	int buflen;
268238032Speter	int yperr;
268338032Speter	char keybuf[MAXNAME + 1];
268490792Sgshapiro	char *SM_NONVOLATILE result = NULL;
268538032Speter
268638032Speter	if (tTd(38, 20))
268790792Sgshapiro		sm_dprintf("nis_map_lookup(%s, %s)\n",
268838032Speter			map->map_mname, name);
268938032Speter
269038032Speter	buflen = strlen(name);
269138032Speter	if (buflen > sizeof keybuf - 1)
269238032Speter		buflen = sizeof keybuf - 1;
269364562Sgshapiro	memmove(keybuf, name, buflen);
269438032Speter	keybuf[buflen] = '\0';
269538032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
269638032Speter		makelower(keybuf);
269738032Speter	yperr = YPERR_KEY;
269864562Sgshapiro	vp = NULL;
269938032Speter	if (bitset(MF_TRY0NULL, map->map_mflags))
270038032Speter	{
270138032Speter		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
270238032Speter			     &vp, &vsize);
270338032Speter		if (yperr == 0)
270438032Speter			map->map_mflags &= ~MF_TRY1NULL;
270538032Speter	}
270638032Speter	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
270738032Speter	{
270890792Sgshapiro		SM_FREE_CLR(vp);
270938032Speter		buflen++;
271038032Speter		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
271138032Speter			     &vp, &vsize);
271238032Speter		if (yperr == 0)
271338032Speter			map->map_mflags &= ~MF_TRY0NULL;
271438032Speter	}
271538032Speter	if (yperr != 0)
271638032Speter	{
271738032Speter		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
271838032Speter			map->map_mflags &= ~(MF_VALID|MF_OPEN);
271964562Sgshapiro		if (vp != NULL)
272077349Sgshapiro			sm_free(vp);
272138032Speter		return NULL;
272238032Speter	}
272390792Sgshapiro	SM_TRY
272490792Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
272590792Sgshapiro			result = map_rewrite(map, name, strlen(name), NULL);
272690792Sgshapiro		else
272790792Sgshapiro			result = map_rewrite(map, vp, vsize, av);
272890792Sgshapiro	SM_FINALLY
272964562Sgshapiro		if (vp != NULL)
273077349Sgshapiro			sm_free(vp);
273190792Sgshapiro	SM_END_TRY
273290792Sgshapiro	return result;
273338032Speter}
273438032Speter
273538032Speter
273638032Speter/*
273738032Speter**  NIS_GETCANONNAME -- look up canonical name in NIS
273838032Speter*/
273938032Speter
274064562Sgshapirostatic bool
274138032Speternis_getcanonname(name, hbsize, statp)
274238032Speter	char *name;
274338032Speter	int hbsize;
274438032Speter	int *statp;
274538032Speter{
274638032Speter	char *vp;
274738032Speter	auto int vsize;
274838032Speter	int keylen;
274938032Speter	int yperr;
275090792Sgshapiro	static bool try0null = true;
275190792Sgshapiro	static bool try1null = true;
275238032Speter	static char *yp_domain = NULL;
275338032Speter	char host_record[MAXLINE];
275438032Speter	char cbuf[MAXNAME];
275538032Speter	char nbuf[MAXNAME + 1];
275638032Speter
275738032Speter	if (tTd(38, 20))
275890792Sgshapiro		sm_dprintf("nis_getcanonname(%s)\n", name);
275938032Speter
276090792Sgshapiro	if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
276138032Speter	{
276238032Speter		*statp = EX_UNAVAILABLE;
276390792Sgshapiro		return false;
276438032Speter	}
276573188Sgshapiro	(void) shorten_hostname(nbuf);
276638032Speter	keylen = strlen(nbuf);
276738032Speter
276838032Speter	if (yp_domain == NULL)
276964562Sgshapiro		(void) yp_get_default_domain(&yp_domain);
277038032Speter	makelower(nbuf);
277138032Speter	yperr = YPERR_KEY;
277264562Sgshapiro	vp = NULL;
277338032Speter	if (try0null)
277438032Speter	{
277538032Speter		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
277638032Speter			     &vp, &vsize);
277738032Speter		if (yperr == 0)
277890792Sgshapiro			try1null = false;
277938032Speter	}
278038032Speter	if (yperr == YPERR_KEY && try1null)
278138032Speter	{
278290792Sgshapiro		SM_FREE_CLR(vp);
278338032Speter		keylen++;
278438032Speter		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
278538032Speter			     &vp, &vsize);
278638032Speter		if (yperr == 0)
278790792Sgshapiro			try0null = false;
278838032Speter	}
278938032Speter	if (yperr != 0)
279038032Speter	{
279138032Speter		if (yperr == YPERR_KEY)
279238032Speter			*statp = EX_NOHOST;
279338032Speter		else if (yperr == YPERR_BUSY)
279438032Speter			*statp = EX_TEMPFAIL;
279538032Speter		else
279638032Speter			*statp = EX_UNAVAILABLE;
279764562Sgshapiro		if (vp != NULL)
279877349Sgshapiro			sm_free(vp);
279990792Sgshapiro		return false;
280038032Speter	}
280190792Sgshapiro	(void) sm_strlcpy(host_record, vp, sizeof host_record);
280277349Sgshapiro	sm_free(vp);
280338032Speter	if (tTd(38, 44))
280490792Sgshapiro		sm_dprintf("got record `%s'\n", host_record);
280590792Sgshapiro	vp = strpbrk(host_record, "#\n");
280690792Sgshapiro	if (vp != NULL)
280790792Sgshapiro		*vp = '\0';
280873188Sgshapiro	if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof cbuf))
280938032Speter	{
281038032Speter		/* this should not happen, but.... */
281138032Speter		*statp = EX_NOHOST;
281290792Sgshapiro		return false;
281338032Speter	}
281490792Sgshapiro	if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
281538032Speter	{
281638032Speter		*statp = EX_UNAVAILABLE;
281790792Sgshapiro		return false;
281838032Speter	}
281938032Speter	*statp = EX_OK;
282090792Sgshapiro	return true;
282138032Speter}
282238032Speter
282364562Sgshapiro#endif /* NIS */
282490792Sgshapiro/*
282538032Speter**  NISPLUS Modules
282638032Speter**
282738032Speter**	This code donated by Sun Microsystems.
282838032Speter*/
282938032Speter
283090792Sgshapiro#if NISPLUS
283138032Speter
283264562Sgshapiro# undef NIS		/* symbol conflict in nis.h */
283364562Sgshapiro# undef T_UNSPEC	/* symbol conflict in nis.h -> ... -> sys/tiuser.h */
283464562Sgshapiro# include <rpcsvc/nis.h>
283564562Sgshapiro# include <rpcsvc/nislib.h>
283638032Speter
283764562Sgshapiro# define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
283864562Sgshapiro# define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
283964562Sgshapiro# define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
284064562Sgshapiro# define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
284138032Speter
284238032Speter/*
284338032Speter**  NISPLUS_MAP_OPEN -- open nisplus table
284438032Speter*/
284538032Speter
284638032Speterbool
284738032Speternisplus_map_open(map, mode)
284838032Speter	MAP *map;
284938032Speter	int mode;
285038032Speter{
285138032Speter	nis_result *res = NULL;
285238032Speter	int retry_cnt, max_col, i;
285338032Speter	char qbuf[MAXLINE + NIS_MAXNAMELEN];
285438032Speter
285538032Speter	if (tTd(38, 2))
285690792Sgshapiro		sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
285738032Speter			map->map_mname, map->map_file, mode);
285838032Speter
285938032Speter	mode &= O_ACCMODE;
286038032Speter	if (mode != O_RDONLY)
286138032Speter	{
286238032Speter		errno = EPERM;
286390792Sgshapiro		return false;
286438032Speter	}
286538032Speter
286638032Speter	if (*map->map_file == '\0')
286738032Speter		map->map_file = "mail_aliases.org_dir";
286838032Speter
286938032Speter	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
287038032Speter	{
287138032Speter		/* set default NISPLUS Domain to $m */
287238032Speter		map->map_domain = newstr(nisplus_default_domain());
287338032Speter		if (tTd(38, 2))
287490792Sgshapiro			sm_dprintf("nisplus_map_open(%s): using domain %s\n",
287564562Sgshapiro				map->map_file, map->map_domain);
287638032Speter	}
287738032Speter	if (!PARTIAL_NAME(map->map_file))
287838032Speter	{
287938032Speter		map->map_domain = newstr("");
288090792Sgshapiro		(void) sm_strlcpy(qbuf, map->map_file, sizeof qbuf);
288138032Speter	}
288238032Speter	else
288338032Speter	{
288438032Speter		/* check to see if this map actually exists */
288590792Sgshapiro		(void) sm_strlcpyn(qbuf, sizeof qbuf, 3,
288690792Sgshapiro				   map->map_file, ".", map->map_domain);
288738032Speter	}
288838032Speter
288938032Speter	retry_cnt = 0;
289038032Speter	while (res == NULL || res->status != NIS_SUCCESS)
289138032Speter	{
289238032Speter		res = nis_lookup(qbuf, FOLLOW_LINKS);
289338032Speter		switch (res->status)
289438032Speter		{
289538032Speter		  case NIS_SUCCESS:
289638032Speter			break;
289738032Speter
289838032Speter		  case NIS_TRYAGAIN:
289938032Speter		  case NIS_RPCERROR:
290038032Speter		  case NIS_NAMEUNREACHABLE:
290138032Speter			if (retry_cnt++ > 4)
290238032Speter			{
290338032Speter				errno = EAGAIN;
290490792Sgshapiro				return false;
290538032Speter			}
290638032Speter			/* try not to overwhelm hosed server */
290738032Speter			sleep(2);
290838032Speter			break;
290938032Speter
291038032Speter		  default:		/* all other nisplus errors */
291164562Sgshapiro# if 0
291238032Speter			if (!bitset(MF_OPTIONAL, map->map_mflags))
291394334Sgshapiro				syserr("451 4.3.5 Cannot find table %s.%s: %s",
291438032Speter					map->map_file, map->map_domain,
291538032Speter					nis_sperrno(res->status));
291664562Sgshapiro# endif /* 0 */
291738032Speter			errno = EAGAIN;
291890792Sgshapiro			return false;
291938032Speter		}
292038032Speter	}
292138032Speter
292238032Speter	if (NIS_RES_NUMOBJ(res) != 1 ||
292338032Speter	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
292438032Speter	{
292538032Speter		if (tTd(38, 10))
292690792Sgshapiro			sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
292764562Sgshapiro# if 0
292838032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
292994334Sgshapiro			syserr("451 4.3.5 %s.%s: %s is not a table",
293038032Speter				map->map_file, map->map_domain,
293138032Speter				nis_sperrno(res->status));
293264562Sgshapiro# endif /* 0 */
293338032Speter		errno = EBADF;
293490792Sgshapiro		return false;
293538032Speter	}
293638032Speter	/* default key column is column 0 */
293738032Speter	if (map->map_keycolnm == NULL)
293838032Speter		map->map_keycolnm = newstr(COL_NAME(res,0));
293938032Speter
294038032Speter	max_col = COL_MAX(res);
294138032Speter
294238032Speter	/* verify the key column exist */
294390792Sgshapiro	for (i = 0; i < max_col; i++)
294438032Speter	{
294564562Sgshapiro		if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
294638032Speter			break;
294738032Speter	}
294838032Speter	if (i == max_col)
294938032Speter	{
295038032Speter		if (tTd(38, 2))
295190792Sgshapiro			sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
295238032Speter				map->map_file, map->map_keycolnm);
295338032Speter		errno = ENOENT;
295490792Sgshapiro		return false;
295538032Speter	}
295638032Speter
295738032Speter	/* default value column is the last column */
295838032Speter	if (map->map_valcolnm == NULL)
295938032Speter	{
296038032Speter		map->map_valcolno = max_col - 1;
296190792Sgshapiro		return true;
296238032Speter	}
296338032Speter
296464562Sgshapiro	for (i = 0; i< max_col; i++)
296538032Speter	{
296638032Speter		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
296738032Speter		{
296838032Speter			map->map_valcolno = i;
296990792Sgshapiro			return true;
297038032Speter		}
297138032Speter	}
297238032Speter
297338032Speter	if (tTd(38, 2))
297490792Sgshapiro		sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
297564562Sgshapiro			map->map_file, map->map_keycolnm);
297638032Speter	errno = ENOENT;
297790792Sgshapiro	return false;
297838032Speter}
297938032Speter
298038032Speter
298138032Speter/*
298238032Speter**  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
298338032Speter*/
298438032Speter
298538032Speterchar *
298638032Speternisplus_map_lookup(map, name, av, statp)
298738032Speter	MAP *map;
298838032Speter	char *name;
298938032Speter	char **av;
299038032Speter	int *statp;
299138032Speter{
299238032Speter	char *p;
299338032Speter	auto int vsize;
299438032Speter	char *skp;
299538032Speter	int skleft;
299638032Speter	char search_key[MAXNAME + 4];
299738032Speter	char qbuf[MAXLINE + NIS_MAXNAMELEN];
299838032Speter	nis_result *result;
299938032Speter
300038032Speter	if (tTd(38, 20))
300190792Sgshapiro		sm_dprintf("nisplus_map_lookup(%s, %s)\n",
300238032Speter			map->map_mname, name);
300338032Speter
300438032Speter	if (!bitset(MF_OPEN, map->map_mflags))
300538032Speter	{
300638032Speter		if (nisplus_map_open(map, O_RDONLY))
300742575Speter		{
300838032Speter			map->map_mflags |= MF_OPEN;
300990792Sgshapiro			map->map_pid = CurrentPid;
301042575Speter		}
301138032Speter		else
301238032Speter		{
301338032Speter			*statp = EX_UNAVAILABLE;
301438032Speter			return NULL;
301538032Speter		}
301638032Speter	}
301738032Speter
301838032Speter	/*
301938032Speter	**  Copy the name to the key buffer, escaping double quote characters
302038032Speter	**  by doubling them and quoting "]" and "," to avoid having the
302138032Speter	**  NIS+ parser choke on them.
302238032Speter	*/
302338032Speter
302438032Speter	skleft = sizeof search_key - 4;
302538032Speter	skp = search_key;
302638032Speter	for (p = name; *p != '\0' && skleft > 0; p++)
302738032Speter	{
302838032Speter		switch (*p)
302938032Speter		{
303038032Speter		  case ']':
303138032Speter		  case ',':
303238032Speter			/* quote the character */
303338032Speter			*skp++ = '"';
303438032Speter			*skp++ = *p;
303538032Speter			*skp++ = '"';
303638032Speter			skleft -= 3;
303738032Speter			break;
303838032Speter
303938032Speter		  case '"':
304038032Speter			/* double the quote */
304138032Speter			*skp++ = '"';
304238032Speter			skleft--;
304364562Sgshapiro			/* FALLTHROUGH */
304438032Speter
304538032Speter		  default:
304638032Speter			*skp++ = *p;
304738032Speter			skleft--;
304838032Speter			break;
304938032Speter		}
305038032Speter	}
305138032Speter	*skp = '\0';
305238032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
305338032Speter		makelower(search_key);
305438032Speter
305538032Speter	/* construct the query */
305638032Speter	if (PARTIAL_NAME(map->map_file))
305790792Sgshapiro		(void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
305838032Speter			map->map_keycolnm, search_key, map->map_file,
305938032Speter			map->map_domain);
306038032Speter	else
306190792Sgshapiro		(void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
306238032Speter			map->map_keycolnm, search_key, map->map_file);
306338032Speter
306438032Speter	if (tTd(38, 20))
306590792Sgshapiro		sm_dprintf("qbuf=%s\n", qbuf);
306638032Speter	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
306738032Speter	if (result->status == NIS_SUCCESS)
306838032Speter	{
306938032Speter		int count;
307038032Speter		char *str;
307138032Speter
307238032Speter		if ((count = NIS_RES_NUMOBJ(result)) != 1)
307338032Speter		{
307438032Speter			if (LogLevel > 10)
307538032Speter				sm_syslog(LOG_WARNING, CurEnv->e_id,
307664562Sgshapiro					  "%s: lookup error, expected 1 entry, got %d",
307764562Sgshapiro					  map->map_file, count);
307838032Speter
307938032Speter			/* ignore second entry */
308038032Speter			if (tTd(38, 20))
308190792Sgshapiro				sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
308238032Speter					name, count);
308338032Speter		}
308438032Speter
308538032Speter		p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
308638032Speter		/* set the length of the result */
308738032Speter		if (p == NULL)
308838032Speter			p = "";
308938032Speter		vsize = strlen(p);
309038032Speter		if (tTd(38, 20))
309190792Sgshapiro			sm_dprintf("nisplus_map_lookup(%s), found %s\n",
309238032Speter				name, p);
309338032Speter		if (bitset(MF_MATCHONLY, map->map_mflags))
309438032Speter			str = map_rewrite(map, name, strlen(name), NULL);
309538032Speter		else
309638032Speter			str = map_rewrite(map, p, vsize, av);
309738032Speter		nis_freeresult(result);
309838032Speter		*statp = EX_OK;
309938032Speter		return str;
310038032Speter	}
310138032Speter	else
310238032Speter	{
310338032Speter		if (result->status == NIS_NOTFOUND)
310438032Speter			*statp = EX_NOTFOUND;
310538032Speter		else if (result->status == NIS_TRYAGAIN)
310638032Speter			*statp = EX_TEMPFAIL;
310738032Speter		else
310838032Speter		{
310938032Speter			*statp = EX_UNAVAILABLE;
311038032Speter			map->map_mflags &= ~(MF_VALID|MF_OPEN);
311138032Speter		}
311238032Speter	}
311338032Speter	if (tTd(38, 20))
311490792Sgshapiro		sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
311538032Speter	nis_freeresult(result);
311638032Speter	return NULL;
311738032Speter}
311838032Speter
311938032Speter
312038032Speter
312138032Speter/*
312238032Speter**  NISPLUS_GETCANONNAME -- look up canonical name in NIS+
312338032Speter*/
312438032Speter
312564562Sgshapirostatic bool
312638032Speternisplus_getcanonname(name, hbsize, statp)
312738032Speter	char *name;
312838032Speter	int hbsize;
312938032Speter	int *statp;
313038032Speter{
313138032Speter	char *vp;
313238032Speter	auto int vsize;
313338032Speter	nis_result *result;
313438032Speter	char *p;
313538032Speter	char nbuf[MAXNAME + 1];
313638032Speter	char qbuf[MAXLINE + NIS_MAXNAMELEN];
313738032Speter
313890792Sgshapiro	if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
313938032Speter	{
314038032Speter		*statp = EX_UNAVAILABLE;
314190792Sgshapiro		return false;
314238032Speter	}
314373188Sgshapiro	(void) shorten_hostname(nbuf);
314438032Speter
314538032Speter	p = strchr(nbuf, '.');
314638032Speter	if (p == NULL)
314738032Speter	{
314838032Speter		/* single token */
314990792Sgshapiro		(void) sm_snprintf(qbuf, sizeof qbuf,
315090792Sgshapiro			"[name=%s],hosts.org_dir", nbuf);
315138032Speter	}
315238032Speter	else if (p[1] != '\0')
315338032Speter	{
315438032Speter		/* multi token -- take only first token in nbuf */
315538032Speter		*p = '\0';
315690792Sgshapiro		(void) sm_snprintf(qbuf, sizeof qbuf,
315790792Sgshapiro				   "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
315838032Speter	}
315938032Speter	else
316038032Speter	{
316138032Speter		*statp = EX_NOHOST;
316290792Sgshapiro		return false;
316338032Speter	}
316438032Speter
316538032Speter	if (tTd(38, 20))
316694334Sgshapiro		sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
316790792Sgshapiro			   name, qbuf);
316838032Speter
316938032Speter	result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
317090792Sgshapiro			  NULL, NULL);
317138032Speter
317238032Speter	if (result->status == NIS_SUCCESS)
317338032Speter	{
317438032Speter		int count;
317538032Speter		char *domain;
317638032Speter
317738032Speter		if ((count = NIS_RES_NUMOBJ(result)) != 1)
317838032Speter		{
317938032Speter			if (LogLevel > 10)
318038032Speter				sm_syslog(LOG_WARNING, CurEnv->e_id,
318164562Sgshapiro					  "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
318264562Sgshapiro					  count);
318338032Speter
318438032Speter			/* ignore second entry */
318538032Speter			if (tTd(38, 20))
318694334Sgshapiro				sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
318790792Sgshapiro					   name, count);
318838032Speter		}
318938032Speter
319038032Speter		if (tTd(38, 20))
319194334Sgshapiro			sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
319290792Sgshapiro				   name, (NIS_RES_OBJECT(result))->zo_domain);
319338032Speter
319438032Speter
319538032Speter		vp = ((NIS_RES_OBJECT(result))->EN_col(0));
319638032Speter		vsize = strlen(vp);
319738032Speter		if (tTd(38, 20))
319890792Sgshapiro			sm_dprintf("nisplus_getcanonname(%s), found %s\n",
319990792Sgshapiro				   name, vp);
320038032Speter		if (strchr(vp, '.') != NULL)
320138032Speter		{
320238032Speter			domain = "";
320338032Speter		}
320438032Speter		else
320538032Speter		{
320638032Speter			domain = macvalue('m', CurEnv);
320738032Speter			if (domain == NULL)
320838032Speter				domain = "";
320938032Speter		}
321038032Speter		if (hbsize > vsize + (int) strlen(domain) + 1)
321138032Speter		{
321238032Speter			if (domain[0] == '\0')
321390792Sgshapiro				(void) sm_strlcpy(name, vp, hbsize);
321438032Speter			else
321590792Sgshapiro				(void) sm_snprintf(name, hbsize,
321690792Sgshapiro						   "%s.%s", vp, domain);
321738032Speter			*statp = EX_OK;
321838032Speter		}
321938032Speter		else
322038032Speter			*statp = EX_NOHOST;
322138032Speter		nis_freeresult(result);
322290792Sgshapiro		return true;
322338032Speter	}
322438032Speter	else
322538032Speter	{
322638032Speter		if (result->status == NIS_NOTFOUND)
322738032Speter			*statp = EX_NOHOST;
322838032Speter		else if (result->status == NIS_TRYAGAIN)
322938032Speter			*statp = EX_TEMPFAIL;
323038032Speter		else
323138032Speter			*statp = EX_UNAVAILABLE;
323238032Speter	}
323338032Speter	if (tTd(38, 20))
323490792Sgshapiro		sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
323590792Sgshapiro			   name, result->status, *statp);
323638032Speter	nis_freeresult(result);
323790792Sgshapiro	return false;
323838032Speter}
323938032Speter
324038032Speterchar *
324138032Speternisplus_default_domain()
324238032Speter{
324338032Speter	static char default_domain[MAXNAME + 1] = "";
324438032Speter	char *p;
324538032Speter
324638032Speter	if (default_domain[0] != '\0')
324764562Sgshapiro		return default_domain;
324838032Speter
324938032Speter	p = nis_local_directory();
325090792Sgshapiro	(void) sm_strlcpy(default_domain, p, sizeof default_domain);
325138032Speter	return default_domain;
325238032Speter}
325338032Speter
325438032Speter#endif /* NISPLUS */
325590792Sgshapiro/*
325638032Speter**  LDAP Modules
325738032Speter*/
325838032Speter
325964562Sgshapiro/*
326064562Sgshapiro**  LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
326164562Sgshapiro*/
326264562Sgshapiro
326364562Sgshapiro#if defined(LDAPMAP) || defined(PH_MAP)
326464562Sgshapiro
326590792Sgshapiro# if PH_MAP
326664562Sgshapiro#  define ph_map_dequote ldapmap_dequote
326764562Sgshapiro# endif /* PH_MAP */
326864562Sgshapiro
326990792Sgshapirostatic char *ldapmap_dequote __P((char *));
327090792Sgshapiro
327190792Sgshapirostatic char *
327264562Sgshapiroldapmap_dequote(str)
327364562Sgshapiro	char *str;
327464562Sgshapiro{
327564562Sgshapiro	char *p;
327664562Sgshapiro	char *start;
327764562Sgshapiro
327864562Sgshapiro	if (str == NULL)
327964562Sgshapiro		return NULL;
328064562Sgshapiro
328164562Sgshapiro	p = str;
328264562Sgshapiro	if (*p == '"')
328364562Sgshapiro	{
328464562Sgshapiro		/* Should probably swallow initial whitespace here */
328564562Sgshapiro		start = ++p;
328664562Sgshapiro	}
328764562Sgshapiro	else
328864562Sgshapiro		return str;
328964562Sgshapiro	while (*p != '"' && *p != '\0')
329064562Sgshapiro		p++;
329164562Sgshapiro	if (*p != '\0')
329264562Sgshapiro		*p = '\0';
329364562Sgshapiro	return start;
329464562Sgshapiro}
329564562Sgshapiro#endif /* defined(LDAPMAP) || defined(PH_MAP) */
329664562Sgshapiro
329790792Sgshapiro#if LDAPMAP
329838032Speter
329990792Sgshapirostatic SM_LDAP_STRUCT *LDAPDefaults = NULL;
330038032Speter
330138032Speter/*
330264562Sgshapiro**  LDAPMAP_OPEN -- open LDAP map
330338032Speter**
330464562Sgshapiro**	Connect to the LDAP server.  Re-use existing connections since a
330564562Sgshapiro**	single server connection to a host (with the same host, port,
330664562Sgshapiro**	bind DN, and secret) can answer queries for multiple maps.
330738032Speter*/
330838032Speter
330938032Speterbool
331064562Sgshapiroldapmap_open(map, mode)
331138032Speter	MAP *map;
331238032Speter	int mode;
331338032Speter{
331490792Sgshapiro	SM_LDAP_STRUCT *lmap;
331564562Sgshapiro	STAB *s;
3316132943Sgshapiro	char *id;
331764562Sgshapiro
331838032Speter	if (tTd(38, 2))
331990792Sgshapiro		sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
332038032Speter
332138032Speter	mode &= O_ACCMODE;
332264562Sgshapiro
332364562Sgshapiro	/* sendmail doesn't have the ability to write to LDAP (yet) */
332438032Speter	if (mode != O_RDONLY)
332538032Speter	{
332638032Speter		/* issue a pseudo-error message */
332790792Sgshapiro		errno = SM_EMAPCANTWRITE;
332890792Sgshapiro		return false;
332938032Speter	}
333064562Sgshapiro
333190792Sgshapiro	lmap = (SM_LDAP_STRUCT *) map->map_db1;
333264562Sgshapiro
333364562Sgshapiro	s = ldapmap_findconn(lmap);
333477349Sgshapiro	if (s->s_lmap != NULL)
333564562Sgshapiro	{
333664562Sgshapiro		/* Already have a connection open to this LDAP server */
333790792Sgshapiro		lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
333890792Sgshapiro		lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
333977349Sgshapiro
334077349Sgshapiro		/* Add this map as head of linked list */
334177349Sgshapiro		lmap->ldap_next = s->s_lmap;
334277349Sgshapiro		s->s_lmap = map;
334377349Sgshapiro
334466494Sgshapiro		if (tTd(38, 2))
334590792Sgshapiro			sm_dprintf("using cached connection\n");
334690792Sgshapiro		return true;
334764562Sgshapiro	}
334864562Sgshapiro
334966494Sgshapiro	if (tTd(38, 2))
335090792Sgshapiro		sm_dprintf("opening new connection\n");
335166494Sgshapiro
3352132943Sgshapiro	if (lmap->ldap_host != NULL)
3353132943Sgshapiro		id = lmap->ldap_host;
3354132943Sgshapiro	else if (lmap->ldap_uri != NULL)
3355132943Sgshapiro		id = lmap->ldap_uri;
3356132943Sgshapiro	else
3357132943Sgshapiro		id = "localhost";
3358132943Sgshapiro
335964562Sgshapiro	/* No connection yet, connect */
336090792Sgshapiro	if (!sm_ldap_start(map->map_mname, lmap))
336138032Speter	{
336290792Sgshapiro		if (errno == ETIMEDOUT)
336338032Speter		{
336438032Speter			if (LogLevel > 1)
336538032Speter				sm_syslog(LOG_NOTICE, CurEnv->e_id,
336664562Sgshapiro					  "timeout conning to LDAP server %.100s",
3367132943Sgshapiro					  id);
336838032Speter		}
336938032Speter
337038032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
337138032Speter		{
337264562Sgshapiro			if (bitset(MF_NODEFER, map->map_mflags))
3373132943Sgshapiro			{
337464562Sgshapiro				syserr("%s failed to %s in map %s",
337564562Sgshapiro# if USE_LDAP_INIT
337690792Sgshapiro				       "ldap_init/ldap_bind",
337764562Sgshapiro# else /* USE_LDAP_INIT */
337864562Sgshapiro				       "ldap_open",
337964562Sgshapiro# endif /* USE_LDAP_INIT */
3380132943Sgshapiro				       id, map->map_mname);
3381132943Sgshapiro			}
338264562Sgshapiro			else
3383132943Sgshapiro			{
338494334Sgshapiro				syserr("451 4.3.5 %s failed to %s in map %s",
338564562Sgshapiro# if USE_LDAP_INIT
338690792Sgshapiro				       "ldap_init/ldap_bind",
338764562Sgshapiro# else /* USE_LDAP_INIT */
338864562Sgshapiro				       "ldap_open",
338964562Sgshapiro# endif /* USE_LDAP_INIT */
3390132943Sgshapiro				       id, map->map_mname);
3391132943Sgshapiro			}
339238032Speter		}
339390792Sgshapiro		return false;
339438032Speter	}
339538032Speter
339690792Sgshapiro	/* Save connection for reuse */
339790792Sgshapiro	s->s_lmap = map;
339890792Sgshapiro	return true;
339938032Speter}
340038032Speter
340138032Speter/*
340264562Sgshapiro**  LDAPMAP_CLOSE -- close ldap map
340338032Speter*/
340438032Speter
340538032Spetervoid
340664562Sgshapiroldapmap_close(map)
340738032Speter	MAP *map;
340838032Speter{
340990792Sgshapiro	SM_LDAP_STRUCT *lmap;
341064562Sgshapiro	STAB *s;
341143730Speter
341264562Sgshapiro	if (tTd(38, 2))
341390792Sgshapiro		sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
341464562Sgshapiro
341590792Sgshapiro	lmap = (SM_LDAP_STRUCT *) map->map_db1;
341664562Sgshapiro
341764562Sgshapiro	/* Check if already closed */
341864562Sgshapiro	if (lmap->ldap_ld == NULL)
341964562Sgshapiro		return;
342064562Sgshapiro
342177349Sgshapiro	/* Close the LDAP connection */
342290792Sgshapiro	sm_ldap_close(lmap);
342377349Sgshapiro
342477349Sgshapiro	/* Mark all the maps that share the connection as closed */
342564562Sgshapiro	s = ldapmap_findconn(lmap);
342664562Sgshapiro
342777349Sgshapiro	while (s->s_lmap != NULL)
342877349Sgshapiro	{
342977349Sgshapiro		MAP *smap = s->s_lmap;
343064562Sgshapiro
343177349Sgshapiro		if (tTd(38, 2) && smap != map)
343290792Sgshapiro			sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
343390792Sgshapiro				   map->map_mname, smap->map_mname);
343477349Sgshapiro		smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
343590792Sgshapiro		lmap = (SM_LDAP_STRUCT *) smap->map_db1;
343664562Sgshapiro		lmap->ldap_ld = NULL;
343777349Sgshapiro		s->s_lmap = lmap->ldap_next;
343877349Sgshapiro		lmap->ldap_next = NULL;
343943730Speter	}
344038032Speter}
344138032Speter
344264562Sgshapiro# ifdef SUNET_ID
344343730Speter/*
344490792Sgshapiro**  SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
344542575Speter**  This only makes sense at Stanford University.
344638032Speter*/
344738032Speter
344890792Sgshapirostatic char *
344938032Spetersunet_id_hash(str)
345038032Speter	char *str;
345138032Speter{
345238032Speter	char *p, *p_last;
345338032Speter
345438032Speter	p = str;
345538032Speter	p_last = p;
345638032Speter	while (*p != '\0')
345738032Speter	{
345838032Speter		if (islower(*p) || isdigit(*p))
345938032Speter		{
346038032Speter			*p_last = *p;
346138032Speter			p_last++;
346238032Speter		}
346338032Speter		else if (isupper(*p))
346438032Speter		{
346538032Speter			*p_last = tolower(*p);
346638032Speter			p_last++;
346738032Speter		}
346838032Speter		++p;
346938032Speter	}
347038032Speter	if (*p_last != '\0')
347138032Speter		*p_last = '\0';
347264562Sgshapiro	return str;
347338032Speter}
347464562Sgshapiro# endif /* SUNET_ID */
347538032Speter
347638032Speter/*
347764562Sgshapiro**  LDAPMAP_LOOKUP -- look up a datum in a LDAP map
347838032Speter*/
347938032Speter
348038032Speterchar *
348164562Sgshapiroldapmap_lookup(map, name, av, statp)
348238032Speter	MAP *map;
348338032Speter	char *name;
348438032Speter	char **av;
348538032Speter	int *statp;
348638032Speter{
3487132943Sgshapiro	int flags;
348894334Sgshapiro	int plen = 0;
348994334Sgshapiro	int psize = 0;
349064562Sgshapiro	int msgid;
349190792Sgshapiro	int save_errno;
349290792Sgshapiro	char *vp, *p;
349364562Sgshapiro	char *result = NULL;
3494132943Sgshapiro	SM_RPOOL_T *rpool;
349590792Sgshapiro	SM_LDAP_STRUCT *lmap = NULL;
349638032Speter	char keybuf[MAXNAME + 1];
349738032Speter
349838032Speter	if (tTd(38, 20))
349990792Sgshapiro		sm_dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name);
350038032Speter
350138032Speter	/* Get ldap struct pointer from map */
350290792Sgshapiro	lmap = (SM_LDAP_STRUCT *) map->map_db1;
350390792Sgshapiro	sm_ldap_setopts(lmap->ldap_ld, lmap);
350438032Speter
350590792Sgshapiro	(void) sm_strlcpy(keybuf, name, sizeof keybuf);
350638032Speter
350738032Speter	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
350864562Sgshapiro	{
350964562Sgshapiro# ifdef SUNET_ID
351038032Speter		sunet_id_hash(keybuf);
351164562Sgshapiro# else /* SUNET_ID */
351238032Speter		makelower(keybuf);
351364562Sgshapiro# endif /* SUNET_ID */
351464562Sgshapiro	}
351538032Speter
351690792Sgshapiro	msgid = sm_ldap_search(lmap, keybuf);
351764562Sgshapiro	if (msgid == -1)
351838032Speter	{
351990792Sgshapiro		errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
352077349Sgshapiro		save_errno = errno;
352164562Sgshapiro		if (!bitset(MF_OPTIONAL, map->map_mflags))
352238032Speter		{
352364562Sgshapiro			if (bitset(MF_NODEFER, map->map_mflags))
352466494Sgshapiro				syserr("Error in ldap_search using %s in map %s",
352590792Sgshapiro				       keybuf, map->map_mname);
352664562Sgshapiro			else
352794334Sgshapiro				syserr("451 4.3.5 Error in ldap_search using %s in map %s",
352890792Sgshapiro				       keybuf, map->map_mname);
352938032Speter		}
353064562Sgshapiro		*statp = EX_TEMPFAIL;
353190792Sgshapiro		switch (save_errno - E_LDAPBASE)
353290792Sgshapiro		{
353394334Sgshapiro# ifdef LDAP_SERVER_DOWN
353490792Sgshapiro		  case LDAP_SERVER_DOWN:
353594334Sgshapiro# endif /* LDAP_SERVER_DOWN */
353690792Sgshapiro		  case LDAP_TIMEOUT:
353790792Sgshapiro		  case LDAP_UNAVAILABLE:
353866494Sgshapiro			/* server disappeared, try reopen on next search */
353977349Sgshapiro			ldapmap_close(map);
354090792Sgshapiro			break;
354166494Sgshapiro		}
354277349Sgshapiro		errno = save_errno;
354364562Sgshapiro		return NULL;
354464562Sgshapiro	}
354564562Sgshapiro
354664562Sgshapiro	*statp = EX_NOTFOUND;
354764562Sgshapiro	vp = NULL;
354864562Sgshapiro
3549132943Sgshapiro	flags = 0;
3550132943Sgshapiro	if (bitset(MF_SINGLEMATCH, map->map_mflags))
3551132943Sgshapiro		flags |= SM_LDAP_SINGLEMATCH;
3552132943Sgshapiro	if (bitset(MF_MATCHONLY, map->map_mflags))
3553132943Sgshapiro		flags |= SM_LDAP_MATCHONLY;
355490792Sgshapiro
3555132943Sgshapiro	/* Create an rpool for search related memory usage */
3556132943Sgshapiro	rpool = sm_rpool_new_x(NULL);
355790792Sgshapiro
3558132943Sgshapiro	p = NULL;
3559132943Sgshapiro	*statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
3560132943Sgshapiro				 rpool, &p, &plen, &psize, NULL);
3561132943Sgshapiro	save_errno = errno;
356290792Sgshapiro
3563132943Sgshapiro	/* Copy result so rpool can be freed */
3564132943Sgshapiro	if (*statp == EX_OK && p != NULL)
3565132943Sgshapiro		vp = newstr(p);
3566132943Sgshapiro	sm_rpool_free(rpool);
356790792Sgshapiro
3568132943Sgshapiro	/* need to restart LDAP connection? */
3569132943Sgshapiro	if (*statp == EX_RESTART)
357064562Sgshapiro	{
3571132943Sgshapiro		*statp = EX_TEMPFAIL;
3572132943Sgshapiro		ldapmap_close(map);
357338032Speter	}
357438032Speter
3575132943Sgshapiro	errno = save_errno;
3576132943Sgshapiro	if (*statp != EX_OK && *statp != EX_NOTFOUND)
357738032Speter	{
357864562Sgshapiro		if (!bitset(MF_OPTIONAL, map->map_mflags))
357964562Sgshapiro		{
358064562Sgshapiro			if (bitset(MF_NODEFER, map->map_mflags))
358164562Sgshapiro				syserr("Error getting LDAP results in map %s",
358264562Sgshapiro				       map->map_mname);
358364562Sgshapiro			else
358494334Sgshapiro				syserr("451 4.3.5 Error getting LDAP results in map %s",
358564562Sgshapiro				       map->map_mname);
358664562Sgshapiro		}
358777349Sgshapiro		errno = save_errno;
358864562Sgshapiro		return NULL;
358938032Speter	}
359090792Sgshapiro
359164562Sgshapiro	/* Did we match anything? */
359271345Sgshapiro	if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
359364562Sgshapiro		return NULL;
359438032Speter
359564562Sgshapiro	if (*statp == EX_OK)
359664562Sgshapiro	{
359764562Sgshapiro		if (LogLevel > 9)
359864562Sgshapiro			sm_syslog(LOG_INFO, CurEnv->e_id,
359971345Sgshapiro				  "ldap %.100s => %s", name,
360071345Sgshapiro				  vp == NULL ? "<NULL>" : vp);
360164562Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
360264562Sgshapiro			result = map_rewrite(map, name, strlen(name), NULL);
360364562Sgshapiro		else
360471345Sgshapiro		{
360571345Sgshapiro			/* vp != NULL according to test above */
360664562Sgshapiro			result = map_rewrite(map, vp, strlen(vp), av);
360771345Sgshapiro		}
360871345Sgshapiro		if (vp != NULL)
360990792Sgshapiro			sm_free(vp); /* XXX */
361064562Sgshapiro	}
361164562Sgshapiro	return result;
361238032Speter}
361338032Speter
361438032Speter/*
361564562Sgshapiro**  LDAPMAP_FINDCONN -- find an LDAP connection to the server
361664562Sgshapiro**
361764562Sgshapiro**	Cache LDAP connections based on the host, port, bind DN,
361866494Sgshapiro**	secret, and PID so we don't have multiple connections open to
361966494Sgshapiro**	the same server for different maps.  Need a separate connection
362066494Sgshapiro**	per PID since a parent process may close the map before the
362166494Sgshapiro**	child is done with it.
362264562Sgshapiro**
362364562Sgshapiro**	Parameters:
362464562Sgshapiro**		lmap -- LDAP map information
362564562Sgshapiro**
362664562Sgshapiro**	Returns:
362764562Sgshapiro**		Symbol table entry for the LDAP connection.
362838032Speter*/
362938032Speter
363064562Sgshapirostatic STAB *
363164562Sgshapiroldapmap_findconn(lmap)
363290792Sgshapiro	SM_LDAP_STRUCT *lmap;
363338032Speter{
363494334Sgshapiro	char *format;
363564562Sgshapiro	char *nbuf;
3636132943Sgshapiro	char *id;
363790792Sgshapiro	STAB *SM_NONVOLATILE s = NULL;
363838032Speter
3639132943Sgshapiro	if (lmap->ldap_host != NULL)
3640132943Sgshapiro		id = lmap->ldap_host;
3641132943Sgshapiro	else if (lmap->ldap_uri != NULL)
3642132943Sgshapiro		id = lmap->ldap_uri;
3643132943Sgshapiro	else
3644132943Sgshapiro		id = "localhost";
3645132943Sgshapiro
364694334Sgshapiro	format = "%s%c%d%c%d%c%s%c%s%d";
364794334Sgshapiro	nbuf = sm_stringf_x(format,
3648132943Sgshapiro			    id,
364990792Sgshapiro			    CONDELSE,
365090792Sgshapiro			    lmap->ldap_port,
365190792Sgshapiro			    CONDELSE,
365294334Sgshapiro			    lmap->ldap_version,
365394334Sgshapiro			    CONDELSE,
365490792Sgshapiro			    (lmap->ldap_binddn == NULL ? ""
365590792Sgshapiro						       : lmap->ldap_binddn),
365690792Sgshapiro			    CONDELSE,
365790792Sgshapiro			    (lmap->ldap_secret == NULL ? ""
365890792Sgshapiro						       : lmap->ldap_secret),
365990792Sgshapiro			    (int) CurrentPid);
366090792Sgshapiro	SM_TRY
366190792Sgshapiro		s = stab(nbuf, ST_LMAP, ST_ENTER);
366290792Sgshapiro	SM_FINALLY
366390792Sgshapiro		sm_free(nbuf);
366490792Sgshapiro	SM_END_TRY
366564562Sgshapiro	return s;
366664562Sgshapiro}
366738032Speter/*
366864562Sgshapiro**  LDAPMAP_PARSEARGS -- parse ldap map definition args.
366964562Sgshapiro*/
367038032Speter
367190792Sgshapirostatic struct lamvalues LDAPAuthMethods[] =
367264562Sgshapiro{
367364562Sgshapiro	{	"none",		LDAP_AUTH_NONE		},
367464562Sgshapiro	{	"simple",	LDAP_AUTH_SIMPLE	},
367564562Sgshapiro# ifdef LDAP_AUTH_KRBV4
367664562Sgshapiro	{	"krbv4",	LDAP_AUTH_KRBV4		},
367764562Sgshapiro# endif /* LDAP_AUTH_KRBV4 */
367864562Sgshapiro	{	NULL,		0			}
367964562Sgshapiro};
368038032Speter
368190792Sgshapirostatic struct ladvalues LDAPAliasDereference[] =
368264562Sgshapiro{
368364562Sgshapiro	{	"never",	LDAP_DEREF_NEVER	},
368464562Sgshapiro	{	"always",	LDAP_DEREF_ALWAYS	},
368564562Sgshapiro	{	"search",	LDAP_DEREF_SEARCHING	},
368664562Sgshapiro	{	"find",		LDAP_DEREF_FINDING	},
368764562Sgshapiro	{	NULL,		0			}
368864562Sgshapiro};
368938032Speter
369090792Sgshapirostatic struct lssvalues LDAPSearchScope[] =
369164562Sgshapiro{
369264562Sgshapiro	{	"base",		LDAP_SCOPE_BASE		},
369364562Sgshapiro	{	"one",		LDAP_SCOPE_ONELEVEL	},
369464562Sgshapiro	{	"sub",		LDAP_SCOPE_SUBTREE	},
369564562Sgshapiro	{	NULL,		0			}
369664562Sgshapiro};
369738032Speter
369864562Sgshapirobool
369964562Sgshapiroldapmap_parseargs(map, args)
370064562Sgshapiro	MAP *map;
370164562Sgshapiro	char *args;
370264562Sgshapiro{
370390792Sgshapiro	bool secretread = true;
3704132943Sgshapiro	bool attrssetup = false;
370564562Sgshapiro	int i;
370664562Sgshapiro	register char *p = args;
370790792Sgshapiro	SM_LDAP_STRUCT *lmap;
370864562Sgshapiro	struct lamvalues *lam;
370964562Sgshapiro	struct ladvalues *lad;
371064562Sgshapiro	struct lssvalues *lss;
371190792Sgshapiro	char ldapfilt[MAXLINE];
371264562Sgshapiro	char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
371364562Sgshapiro
371464562Sgshapiro	/* Get ldap struct pointer from map */
371590792Sgshapiro	lmap = (SM_LDAP_STRUCT *) map->map_db1;
371664562Sgshapiro
371764562Sgshapiro	/* Check if setting the initial LDAP defaults */
371864562Sgshapiro	if (lmap == NULL || lmap != LDAPDefaults)
371964562Sgshapiro	{
372090792Sgshapiro		/* We need to alloc an SM_LDAP_STRUCT struct */
372190792Sgshapiro		lmap = (SM_LDAP_STRUCT *) xalloc(sizeof *lmap);
372264562Sgshapiro		if (LDAPDefaults == NULL)
372390792Sgshapiro			sm_ldap_clear(lmap);
372464562Sgshapiro		else
372564562Sgshapiro			STRUCTCOPY(*LDAPDefaults, *lmap);
372664562Sgshapiro	}
372764562Sgshapiro
372864562Sgshapiro	/* there is no check whether there is really an argument */
372964562Sgshapiro	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
373064562Sgshapiro	map->map_spacesub = SpaceSub;	/* default value */
373190792Sgshapiro
373290792Sgshapiro	/* Check if setting up an alias or file class LDAP map */
373390792Sgshapiro	if (bitset(MF_ALIAS, map->map_mflags))
373490792Sgshapiro	{
373590792Sgshapiro		/* Comma separate if used as an alias file */
373690792Sgshapiro		map->map_coldelim = ',';
373790792Sgshapiro		if (*args == '\0')
373890792Sgshapiro		{
373990792Sgshapiro			int n;
374090792Sgshapiro			char *lc;
374190792Sgshapiro			char jbuf[MAXHOSTNAMELEN];
374290792Sgshapiro			char lcbuf[MAXLINE];
374390792Sgshapiro
374490792Sgshapiro			/* Get $j */
374590792Sgshapiro			expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
374690792Sgshapiro			if (jbuf[0] == '\0')
374790792Sgshapiro			{
374890792Sgshapiro				(void) sm_strlcpy(jbuf, "localhost",
374990792Sgshapiro						  sizeof jbuf);
375090792Sgshapiro			}
375190792Sgshapiro
375290792Sgshapiro			lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
375390792Sgshapiro			if (lc == NULL)
375490792Sgshapiro				lc = "";
375590792Sgshapiro			else
375690792Sgshapiro			{
375790792Sgshapiro				expand(lc, lcbuf, sizeof lcbuf, CurEnv);
375890792Sgshapiro				lc = lcbuf;
375990792Sgshapiro			}
376090792Sgshapiro
376190792Sgshapiro			n = sm_snprintf(ldapfilt, sizeof ldapfilt,
376290792Sgshapiro					"(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
376390792Sgshapiro					lc, jbuf);
376490792Sgshapiro			if (n >= sizeof ldapfilt)
376590792Sgshapiro			{
376690792Sgshapiro				syserr("%s: Default LDAP string too long",
376790792Sgshapiro				       map->map_mname);
376890792Sgshapiro				return false;
376990792Sgshapiro			}
377090792Sgshapiro
377190792Sgshapiro			/* default args for an alias LDAP entry */
377290792Sgshapiro			lmap->ldap_filter = ldapfilt;
3773132943Sgshapiro			lmap->ldap_attr[0] = "objectClass";
3774132943Sgshapiro			lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
3775132943Sgshapiro			lmap->ldap_attr_needobjclass[0] = NULL;
3776132943Sgshapiro			lmap->ldap_attr[1] = "sendmailMTAAliasValue";
3777132943Sgshapiro			lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
3778132943Sgshapiro			lmap->ldap_attr_needobjclass[1] = NULL;
3779132943Sgshapiro			lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
3780132943Sgshapiro			lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
3781132943Sgshapiro			lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
3782132943Sgshapiro			lmap->ldap_attr[3] = "sendmailMTAAliasURL";
3783132943Sgshapiro			lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
3784132943Sgshapiro			lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
3785132943Sgshapiro			lmap->ldap_attr[4] = NULL;
3786132943Sgshapiro			lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
3787132943Sgshapiro			lmap->ldap_attr_needobjclass[4] = NULL;
3788132943Sgshapiro			attrssetup = true;
378990792Sgshapiro		}
379090792Sgshapiro	}
379190792Sgshapiro	else if (bitset(MF_FILECLASS, map->map_mflags))
379290792Sgshapiro	{
379390792Sgshapiro		/* Space separate if used as a file class file */
379490792Sgshapiro		map->map_coldelim = ' ';
379590792Sgshapiro	}
379690792Sgshapiro
379738032Speter	for (;;)
379838032Speter	{
379938032Speter		while (isascii(*p) && isspace(*p))
380038032Speter			p++;
380138032Speter		if (*p != '-')
380238032Speter			break;
380338032Speter		switch (*++p)
380438032Speter		{
380538032Speter		  case 'N':
380638032Speter			map->map_mflags |= MF_INCLNULL;
380738032Speter			map->map_mflags &= ~MF_TRY0NULL;
380838032Speter			break;
380938032Speter
381038032Speter		  case 'O':
381138032Speter			map->map_mflags &= ~MF_TRY1NULL;
381238032Speter			break;
381338032Speter
381438032Speter		  case 'o':
381538032Speter			map->map_mflags |= MF_OPTIONAL;
381638032Speter			break;
381738032Speter
381838032Speter		  case 'f':
381938032Speter			map->map_mflags |= MF_NOFOLDCASE;
382038032Speter			break;
382138032Speter
382238032Speter		  case 'm':
382338032Speter			map->map_mflags |= MF_MATCHONLY;
382438032Speter			break;
382538032Speter
382638032Speter		  case 'A':
382738032Speter			map->map_mflags |= MF_APPEND;
382838032Speter			break;
382938032Speter
383038032Speter		  case 'q':
383138032Speter			map->map_mflags |= MF_KEEPQUOTES;
383238032Speter			break;
383338032Speter
383438032Speter		  case 'a':
383538032Speter			map->map_app = ++p;
383638032Speter			break;
383738032Speter
383838032Speter		  case 'T':
383938032Speter			map->map_tapp = ++p;
384038032Speter			break;
384138032Speter
384264562Sgshapiro		  case 't':
384364562Sgshapiro			map->map_mflags |= MF_NODEFER;
384464562Sgshapiro			break;
384564562Sgshapiro
384664562Sgshapiro		  case 'S':
384764562Sgshapiro			map->map_spacesub = *++p;
384864562Sgshapiro			break;
384964562Sgshapiro
385064562Sgshapiro		  case 'D':
385164562Sgshapiro			map->map_mflags |= MF_DEFER;
385264562Sgshapiro			break;
385364562Sgshapiro
385464562Sgshapiro		  case 'z':
385564562Sgshapiro			if (*++p != '\\')
385664562Sgshapiro				map->map_coldelim = *p;
385764562Sgshapiro			else
385864562Sgshapiro			{
385964562Sgshapiro				switch (*++p)
386064562Sgshapiro				{
386164562Sgshapiro				  case 'n':
386264562Sgshapiro					map->map_coldelim = '\n';
386364562Sgshapiro					break;
386464562Sgshapiro
386564562Sgshapiro				  case 't':
386664562Sgshapiro					map->map_coldelim = '\t';
386764562Sgshapiro					break;
386864562Sgshapiro
386964562Sgshapiro				  default:
387064562Sgshapiro					map->map_coldelim = '\\';
387164562Sgshapiro				}
387264562Sgshapiro			}
387364562Sgshapiro			break;
387464562Sgshapiro
387564562Sgshapiro			/* Start of ldapmap specific args */
387690792Sgshapiro		  case 'V':
387790792Sgshapiro			if (*++p != '\\')
387890792Sgshapiro				lmap->ldap_attrsep = *p;
387990792Sgshapiro			else
388090792Sgshapiro			{
388190792Sgshapiro				switch (*++p)
388290792Sgshapiro				{
388390792Sgshapiro				  case 'n':
388490792Sgshapiro					lmap->ldap_attrsep = '\n';
388590792Sgshapiro					break;
388690792Sgshapiro
388790792Sgshapiro				  case 't':
388890792Sgshapiro					lmap->ldap_attrsep = '\t';
388990792Sgshapiro					break;
389090792Sgshapiro
389190792Sgshapiro				  default:
389290792Sgshapiro					lmap->ldap_attrsep = '\\';
389390792Sgshapiro				}
389490792Sgshapiro			}
389590792Sgshapiro			break;
389690792Sgshapiro
389738032Speter		  case 'k':		/* search field */
389838032Speter			while (isascii(*++p) && isspace(*p))
389938032Speter				continue;
390064562Sgshapiro			lmap->ldap_filter = p;
390138032Speter			break;
390238032Speter
390338032Speter		  case 'v':		/* attr to return */
390438032Speter			while (isascii(*++p) && isspace(*p))
390538032Speter				continue;
390664562Sgshapiro			lmap->ldap_attr[0] = p;
390764562Sgshapiro			lmap->ldap_attr[1] = NULL;
390838032Speter			break;
390938032Speter
391064562Sgshapiro		  case '1':
391164562Sgshapiro			map->map_mflags |= MF_SINGLEMATCH;
391264562Sgshapiro			break;
391364562Sgshapiro
391438032Speter			/* args stolen from ldapsearch.c */
391538032Speter		  case 'R':		/* don't auto chase referrals */
391664562Sgshapiro# ifdef LDAP_REFERRALS
391738032Speter			lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
391864562Sgshapiro# else /* LDAP_REFERRALS */
391990792Sgshapiro			syserr("compile with -DLDAP_REFERRALS for referral support");
392064562Sgshapiro# endif /* LDAP_REFERRALS */
392138032Speter			break;
392238032Speter
392364562Sgshapiro		  case 'n':		/* retrieve attribute names only */
392464562Sgshapiro			lmap->ldap_attrsonly = LDAPMAP_TRUE;
392538032Speter			break;
392638032Speter
392764562Sgshapiro		  case 'r':		/* alias dereferencing */
392864562Sgshapiro			while (isascii(*++p) && isspace(*p))
392964562Sgshapiro				continue;
393064562Sgshapiro
393190792Sgshapiro			if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
393264562Sgshapiro				p += 11;
393364562Sgshapiro
393464562Sgshapiro			for (lad = LDAPAliasDereference;
393564562Sgshapiro			     lad != NULL && lad->lad_name != NULL; lad++)
393638032Speter			{
393790792Sgshapiro				if (sm_strncasecmp(p, lad->lad_name,
393890792Sgshapiro						   strlen(lad->lad_name)) == 0)
393964562Sgshapiro					break;
394038032Speter			}
394164562Sgshapiro			if (lad->lad_name != NULL)
394264562Sgshapiro				lmap->ldap_deref = lad->lad_code;
394364562Sgshapiro			else
394438032Speter			{
394564562Sgshapiro				/* bad config line */
394664562Sgshapiro				if (!bitset(MCF_OPTFILE,
394764562Sgshapiro					    map->map_class->map_cflags))
394864562Sgshapiro				{
394964562Sgshapiro					char *ptr;
395064562Sgshapiro
395164562Sgshapiro					if ((ptr = strchr(p, ' ')) != NULL)
395264562Sgshapiro						*ptr = '\0';
395373188Sgshapiro					syserr("Deref must be [never|always|search|find] (not %s) in map %s",
395464562Sgshapiro						p, map->map_mname);
395564562Sgshapiro					if (ptr != NULL)
395664562Sgshapiro						*ptr = ' ';
395790792Sgshapiro					return false;
395864562Sgshapiro				}
395938032Speter			}
396064562Sgshapiro			break;
396164562Sgshapiro
396264562Sgshapiro		  case 's':		/* search scope */
396364562Sgshapiro			while (isascii(*++p) && isspace(*p))
396464562Sgshapiro				continue;
396564562Sgshapiro
396690792Sgshapiro			if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
396764562Sgshapiro				p += 11;
396864562Sgshapiro
396964562Sgshapiro			for (lss = LDAPSearchScope;
397064562Sgshapiro			     lss != NULL && lss->lss_name != NULL; lss++)
397138032Speter			{
397290792Sgshapiro				if (sm_strncasecmp(p, lss->lss_name,
397390792Sgshapiro						   strlen(lss->lss_name)) == 0)
397464562Sgshapiro					break;
397538032Speter			}
397664562Sgshapiro			if (lss->lss_name != NULL)
397764562Sgshapiro				lmap->ldap_scope = lss->lss_code;
397838032Speter			else
397964562Sgshapiro			{
398064562Sgshapiro				/* bad config line */
398164562Sgshapiro				if (!bitset(MCF_OPTFILE,
398264562Sgshapiro					    map->map_class->map_cflags))
398338032Speter				{
398438032Speter					char *ptr;
398538032Speter
398638032Speter					if ((ptr = strchr(p, ' ')) != NULL)
398738032Speter						*ptr = '\0';
398873188Sgshapiro					syserr("Scope must be [base|one|sub] (not %s) in map %s",
398938032Speter						p, map->map_mname);
399038032Speter					if (ptr != NULL)
399138032Speter						*ptr = ' ';
399290792Sgshapiro					return false;
399338032Speter				}
399438032Speter			}
399538032Speter			break;
399638032Speter
399738032Speter		  case 'h':		/* ldap host */
399838032Speter			while (isascii(*++p) && isspace(*p))
399938032Speter				continue;
4000132943Sgshapiro			if (lmap->ldap_uri != NULL)
400194334Sgshapiro			{
400294334Sgshapiro				syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
400394334Sgshapiro				       map->map_mname);
400494334Sgshapiro				return false;
400594334Sgshapiro			}
4006132943Sgshapiro			lmap->ldap_host = p;
400738032Speter			break;
400838032Speter
400938032Speter		  case 'b':		/* search base */
401038032Speter			while (isascii(*++p) && isspace(*p))
401138032Speter				continue;
401264562Sgshapiro			lmap->ldap_base = p;
401338032Speter			break;
401438032Speter
401538032Speter		  case 'p':		/* ldap port */
401638032Speter			while (isascii(*++p) && isspace(*p))
401738032Speter				continue;
401864562Sgshapiro			lmap->ldap_port = atoi(p);
401938032Speter			break;
402038032Speter
402138032Speter		  case 'l':		/* time limit */
402238032Speter			while (isascii(*++p) && isspace(*p))
402338032Speter				continue;
402464562Sgshapiro			lmap->ldap_timelimit = atoi(p);
402564562Sgshapiro			lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
402638032Speter			break;
402738032Speter
402864562Sgshapiro		  case 'Z':
402964562Sgshapiro			while (isascii(*++p) && isspace(*p))
403064562Sgshapiro				continue;
403164562Sgshapiro			lmap->ldap_sizelimit = atoi(p);
403264562Sgshapiro			break;
403364562Sgshapiro
403464562Sgshapiro		  case 'd':		/* Dn to bind to server as */
403564562Sgshapiro			while (isascii(*++p) && isspace(*p))
403664562Sgshapiro				continue;
403764562Sgshapiro			lmap->ldap_binddn = p;
403864562Sgshapiro			break;
403964562Sgshapiro
404064562Sgshapiro		  case 'M':		/* Method for binding */
404164562Sgshapiro			while (isascii(*++p) && isspace(*p))
404264562Sgshapiro				continue;
404364562Sgshapiro
404490792Sgshapiro			if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
404564562Sgshapiro				p += 10;
404664562Sgshapiro
404764562Sgshapiro			for (lam = LDAPAuthMethods;
404864562Sgshapiro			     lam != NULL && lam->lam_name != NULL; lam++)
404964562Sgshapiro			{
405090792Sgshapiro				if (sm_strncasecmp(p, lam->lam_name,
405190792Sgshapiro						   strlen(lam->lam_name)) == 0)
405264562Sgshapiro					break;
405364562Sgshapiro			}
405464562Sgshapiro			if (lam->lam_name != NULL)
405564562Sgshapiro				lmap->ldap_method = lam->lam_code;
405664562Sgshapiro			else
405764562Sgshapiro			{
405864562Sgshapiro				/* bad config line */
405964562Sgshapiro				if (!bitset(MCF_OPTFILE,
406064562Sgshapiro					    map->map_class->map_cflags))
406164562Sgshapiro				{
406264562Sgshapiro					char *ptr;
406364562Sgshapiro
406464562Sgshapiro					if ((ptr = strchr(p, ' ')) != NULL)
406564562Sgshapiro						*ptr = '\0';
406673188Sgshapiro					syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
406764562Sgshapiro						p, map->map_mname);
406864562Sgshapiro					if (ptr != NULL)
406964562Sgshapiro						*ptr = ' ';
407090792Sgshapiro					return false;
407164562Sgshapiro				}
407264562Sgshapiro			}
407364562Sgshapiro
407464562Sgshapiro			break;
407564562Sgshapiro
407664562Sgshapiro			/*
407764562Sgshapiro			**  This is a string that is dependent on the
407864562Sgshapiro			**  method used defined above.
407964562Sgshapiro			*/
408064562Sgshapiro
408164562Sgshapiro		  case 'P':		/* Secret password for binding */
408264562Sgshapiro			 while (isascii(*++p) && isspace(*p))
408364562Sgshapiro				continue;
408464562Sgshapiro			lmap->ldap_secret = p;
408590792Sgshapiro			secretread = false;
408664562Sgshapiro			break;
408764562Sgshapiro
408894334Sgshapiro		  case 'H':		/* Use LDAP URI */
408994334Sgshapiro#  if !USE_LDAP_INIT
409094334Sgshapiro			syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
409194334Sgshapiro			       map->map_mname);
409294334Sgshapiro			return false;
4093132943Sgshapiro#   else /* !USE_LDAP_INIT */
4094132943Sgshapiro			if (lmap->ldap_host != NULL)
409594334Sgshapiro			{
409694334Sgshapiro				syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
409794334Sgshapiro				       map->map_mname);
409894334Sgshapiro				return false;
409994334Sgshapiro			}
410094334Sgshapiro			while (isascii(*++p) && isspace(*p))
410194334Sgshapiro				continue;
4102132943Sgshapiro			lmap->ldap_uri = p;
410394334Sgshapiro			break;
410494334Sgshapiro#  endif /* !USE_LDAP_INIT */
410594334Sgshapiro
410694334Sgshapiro		  case 'w':
410794334Sgshapiro			/* -w should be for passwd, -P should be for version */
410894334Sgshapiro			while (isascii(*++p) && isspace(*p))
410994334Sgshapiro				continue;
411094334Sgshapiro			lmap->ldap_version = atoi(p);
4111132943Sgshapiro# ifdef LDAP_VERSION_MAX
411294334Sgshapiro			if (lmap->ldap_version > LDAP_VERSION_MAX)
411394334Sgshapiro			{
411494334Sgshapiro				syserr("LDAP version %d exceeds max of %d in map %s",
411594334Sgshapiro				       lmap->ldap_version, LDAP_VERSION_MAX,
411694334Sgshapiro				       map->map_mname);
411794334Sgshapiro				return false;
411894334Sgshapiro			}
4119132943Sgshapiro# endif /* LDAP_VERSION_MAX */
4120132943Sgshapiro# ifdef LDAP_VERSION_MIN
412194334Sgshapiro			if (lmap->ldap_version < LDAP_VERSION_MIN)
412294334Sgshapiro			{
412394334Sgshapiro				syserr("LDAP version %d is lower than min of %d in map %s",
412494334Sgshapiro				       lmap->ldap_version, LDAP_VERSION_MIN,
412594334Sgshapiro				       map->map_mname);
412694334Sgshapiro				return false;
412794334Sgshapiro			}
4128132943Sgshapiro# endif /* LDAP_VERSION_MIN */
412994334Sgshapiro			break;
413094334Sgshapiro
413164562Sgshapiro		  default:
413264562Sgshapiro			syserr("Illegal option %c map %s", *p, map->map_mname);
413364562Sgshapiro			break;
413438032Speter		}
413538032Speter
413664562Sgshapiro		/* need to account for quoted strings here */
413764562Sgshapiro		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
413838032Speter		{
413938032Speter			if (*p == '"')
414038032Speter			{
414138032Speter				while (*++p != '"' && *p != '\0')
414238032Speter					continue;
414338032Speter				if (*p != '\0')
414438032Speter					p++;
414538032Speter			}
414638032Speter			else
414738032Speter				p++;
414838032Speter		}
414938032Speter
415038032Speter		if (*p != '\0')
415138032Speter			*p++ = '\0';
415238032Speter	}
415338032Speter
415438032Speter	if (map->map_app != NULL)
415564562Sgshapiro		map->map_app = newstr(ldapmap_dequote(map->map_app));
415638032Speter	if (map->map_tapp != NULL)
415764562Sgshapiro		map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
415838032Speter
415938032Speter	/*
416042575Speter	**  We need to swallow up all the stuff into a struct
416142575Speter	**  and dump it into map->map_dbptr1
416238032Speter	*/
416338032Speter
4164132943Sgshapiro	if (lmap->ldap_host != NULL &&
416564562Sgshapiro	    (LDAPDefaults == NULL ||
416664562Sgshapiro	     LDAPDefaults == lmap ||
4167132943Sgshapiro	     LDAPDefaults->ldap_host != lmap->ldap_host))
4168132943Sgshapiro		lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
4169132943Sgshapiro	map->map_domain = lmap->ldap_host;
417064562Sgshapiro
4171132943Sgshapiro	if (lmap->ldap_uri != NULL &&
4172132943Sgshapiro	    (LDAPDefaults == NULL ||
4173132943Sgshapiro	     LDAPDefaults == lmap ||
4174132943Sgshapiro	     LDAPDefaults->ldap_uri != lmap->ldap_uri))
4175132943Sgshapiro		lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
4176132943Sgshapiro	map->map_domain = lmap->ldap_uri;
4177132943Sgshapiro
417864562Sgshapiro	if (lmap->ldap_binddn != NULL &&
417964562Sgshapiro	    (LDAPDefaults == NULL ||
418064562Sgshapiro	     LDAPDefaults == lmap ||
418164562Sgshapiro	     LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
418264562Sgshapiro		lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
418364562Sgshapiro
418464562Sgshapiro	if (lmap->ldap_secret != NULL &&
418564562Sgshapiro	    (LDAPDefaults == NULL ||
418664562Sgshapiro	     LDAPDefaults == lmap ||
418764562Sgshapiro	     LDAPDefaults->ldap_secret != lmap->ldap_secret))
418838032Speter	{
418990792Sgshapiro		SM_FILE_T *sfd;
419064562Sgshapiro		long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
419138032Speter
419264562Sgshapiro		if (DontLockReadFiles)
419364562Sgshapiro			sff |= SFF_NOLOCK;
419438032Speter
419564562Sgshapiro		/* need to use method to map secret to passwd string */
419664562Sgshapiro		switch (lmap->ldap_method)
419764562Sgshapiro		{
419864562Sgshapiro		  case LDAP_AUTH_NONE:
419964562Sgshapiro			/* Do nothing */
420064562Sgshapiro			break;
420138032Speter
420264562Sgshapiro		  case LDAP_AUTH_SIMPLE:
420338032Speter
420464562Sgshapiro			/*
420564562Sgshapiro			**  Secret is the name of a file with
420664562Sgshapiro			**  the first line as the password.
420764562Sgshapiro			*/
420864562Sgshapiro
420964562Sgshapiro			/* Already read in the secret? */
421064562Sgshapiro			if (secretread)
421164562Sgshapiro				break;
421264562Sgshapiro
421364562Sgshapiro			sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
421464562Sgshapiro					O_RDONLY, 0, sff);
421564562Sgshapiro			if (sfd == NULL)
421664562Sgshapiro			{
421764562Sgshapiro				syserr("LDAP map: cannot open secret %s",
421864562Sgshapiro				       ldapmap_dequote(lmap->ldap_secret));
421990792Sgshapiro				return false;
422064562Sgshapiro			}
422198121Sgshapiro			lmap->ldap_secret = sfgets(m_tmp, sizeof m_tmp,
422266494Sgshapiro						   sfd, TimeOuts.to_fileopen,
422366494Sgshapiro						   "ldapmap_parseargs");
422490792Sgshapiro			(void) sm_io_close(sfd, SM_TIME_DEFAULT);
422598121Sgshapiro			if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
422698121Sgshapiro			{
422798121Sgshapiro				syserr("LDAP map: secret in %s too long",
422898121Sgshapiro				       ldapmap_dequote(lmap->ldap_secret));
422998121Sgshapiro				return false;
423098121Sgshapiro			}
423164562Sgshapiro			if (lmap->ldap_secret != NULL &&
423264562Sgshapiro			    strlen(m_tmp) > 0)
423364562Sgshapiro			{
423464562Sgshapiro				/* chomp newline */
423564562Sgshapiro				if (m_tmp[strlen(m_tmp) - 1] == '\n')
423664562Sgshapiro					m_tmp[strlen(m_tmp) - 1] = '\0';
423764562Sgshapiro
423864562Sgshapiro				lmap->ldap_secret = m_tmp;
423964562Sgshapiro			}
424064562Sgshapiro			break;
424164562Sgshapiro
424264562Sgshapiro# ifdef LDAP_AUTH_KRBV4
424364562Sgshapiro		  case LDAP_AUTH_KRBV4:
424464562Sgshapiro
424564562Sgshapiro			/*
424664562Sgshapiro			**  Secret is where the ticket file is
424764562Sgshapiro			**  stashed
424864562Sgshapiro			*/
424964562Sgshapiro
425098121Sgshapiro			(void) sm_snprintf(m_tmp, sizeof m_tmp,
425190792Sgshapiro				"KRBTKFILE=%s",
425290792Sgshapiro				ldapmap_dequote(lmap->ldap_secret));
425364562Sgshapiro			lmap->ldap_secret = m_tmp;
425464562Sgshapiro			break;
425564562Sgshapiro# endif /* LDAP_AUTH_KRBV4 */
425664562Sgshapiro
425764562Sgshapiro		  default:	       /* Should NEVER get here */
425864562Sgshapiro			syserr("LDAP map: Illegal value in lmap method");
425990792Sgshapiro			return false;
426090792Sgshapiro			/* NOTREACHED */
426164562Sgshapiro			break;
426264562Sgshapiro		}
426338032Speter	}
426438032Speter
426564562Sgshapiro	if (lmap->ldap_secret != NULL &&
426664562Sgshapiro	    (LDAPDefaults == NULL ||
426764562Sgshapiro	     LDAPDefaults == lmap ||
426864562Sgshapiro	     LDAPDefaults->ldap_secret != lmap->ldap_secret))
426964562Sgshapiro		lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
427038032Speter
427164562Sgshapiro	if (lmap->ldap_base != NULL &&
427264562Sgshapiro	    (LDAPDefaults == NULL ||
427364562Sgshapiro	     LDAPDefaults == lmap ||
427464562Sgshapiro	     LDAPDefaults->ldap_base != lmap->ldap_base))
427564562Sgshapiro		lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
427664562Sgshapiro
427764562Sgshapiro	/*
427864562Sgshapiro	**  Save the server from extra work.  If request is for a single
427964562Sgshapiro	**  match, tell the server to only return enough records to
428064562Sgshapiro	**  determine if there is a single match or not.  This can not
428164562Sgshapiro	**  be one since the server would only return one and we wouldn't
428264562Sgshapiro	**  know if there were others available.
428364562Sgshapiro	*/
428464562Sgshapiro
428564562Sgshapiro	if (bitset(MF_SINGLEMATCH, map->map_mflags))
428664562Sgshapiro		lmap->ldap_sizelimit = 2;
428764562Sgshapiro
428864562Sgshapiro	/* If setting defaults, don't process ldap_filter and ldap_attr */
428964562Sgshapiro	if (lmap == LDAPDefaults)
429090792Sgshapiro		return true;
429164562Sgshapiro
429264562Sgshapiro	if (lmap->ldap_filter != NULL)
429364562Sgshapiro		lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
429438032Speter	else
429538032Speter	{
429638032Speter		if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
429738032Speter		{
429838032Speter			syserr("No filter given in map %s", map->map_mname);
429990792Sgshapiro			return false;
430038032Speter		}
430138032Speter	}
430264562Sgshapiro
4303132943Sgshapiro	if (!attrssetup && lmap->ldap_attr[0] != NULL)
430438032Speter	{
430590792Sgshapiro		bool recurse = false;
430694334Sgshapiro		bool normalseen = false;
430790792Sgshapiro
430864562Sgshapiro		i = 0;
430964562Sgshapiro		p = ldapmap_dequote(lmap->ldap_attr[0]);
431064562Sgshapiro		lmap->ldap_attr[0] = NULL;
431164562Sgshapiro
431294334Sgshapiro		/* Prime the attr list with the objectClass attribute */
431394334Sgshapiro		lmap->ldap_attr[i] = "objectClass";
431494334Sgshapiro		lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
431594334Sgshapiro		lmap->ldap_attr_needobjclass[i] = NULL;
431694334Sgshapiro		i++;
431794334Sgshapiro
431864562Sgshapiro		while (p != NULL)
431938032Speter		{
432064562Sgshapiro			char *v;
432164562Sgshapiro
432264562Sgshapiro			while (isascii(*p) && isspace(*p))
432364562Sgshapiro				p++;
432464562Sgshapiro			if (*p == '\0')
432564562Sgshapiro				break;
432664562Sgshapiro			v = p;
432764562Sgshapiro			p = strchr(v, ',');
432864562Sgshapiro			if (p != NULL)
432964562Sgshapiro				*p++ = '\0';
433064562Sgshapiro
433171345Sgshapiro			if (i >= LDAPMAP_MAX_ATTR)
433264562Sgshapiro			{
433364562Sgshapiro				syserr("Too many return attributes in %s (max %d)",
433464562Sgshapiro				       map->map_mname, LDAPMAP_MAX_ATTR);
433590792Sgshapiro				return false;
433664562Sgshapiro			}
433764562Sgshapiro			if (*v != '\0')
433890792Sgshapiro			{
433994334Sgshapiro				int j;
434094334Sgshapiro				int use;
434190792Sgshapiro				char *type;
434294334Sgshapiro				char *needobjclass;
434390792Sgshapiro
434490792Sgshapiro				type = strchr(v, ':');
434590792Sgshapiro				if (type != NULL)
434694334Sgshapiro				{
434790792Sgshapiro					*type++ = '\0';
434894334Sgshapiro					needobjclass = strchr(type, ':');
434994334Sgshapiro					if (needobjclass != NULL)
435094334Sgshapiro						*needobjclass++ = '\0';
435194334Sgshapiro				}
435294334Sgshapiro				else
435394334Sgshapiro				{
435494334Sgshapiro					needobjclass = NULL;
435594334Sgshapiro				}
435690792Sgshapiro
435794334Sgshapiro				use = i;
435890792Sgshapiro
435994334Sgshapiro				/* allow override on "objectClass" type */
436094334Sgshapiro				if (sm_strcasecmp(v, "objectClass") == 0 &&
436194334Sgshapiro				    lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
436290792Sgshapiro				{
436394334Sgshapiro					use = 0;
436494334Sgshapiro				}
436594334Sgshapiro				else
436694334Sgshapiro				{
436794334Sgshapiro					/*
436894334Sgshapiro					**  Don't add something to attribute
436994334Sgshapiro					**  list twice.
437094334Sgshapiro					*/
437194334Sgshapiro
437294334Sgshapiro					for (j = 1; j < i; j++)
437390792Sgshapiro					{
437494334Sgshapiro						if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
437594334Sgshapiro						{
437694334Sgshapiro							syserr("Duplicate attribute (%s) in %s",
437794334Sgshapiro							       v, map->map_mname);
437894334Sgshapiro							return false;
437994334Sgshapiro						}
438090792Sgshapiro					}
438194334Sgshapiro
438294334Sgshapiro					lmap->ldap_attr[use] = newstr(v);
438394334Sgshapiro					if (needobjclass != NULL &&
438494334Sgshapiro					    *needobjclass != '\0' &&
438594334Sgshapiro					    *needobjclass != '*')
438690792Sgshapiro					{
438794334Sgshapiro						lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
438894334Sgshapiro					}
438994334Sgshapiro					else
439094334Sgshapiro					{
439194334Sgshapiro						lmap->ldap_attr_needobjclass[use] = NULL;
439294334Sgshapiro					}
439394334Sgshapiro
439494334Sgshapiro				}
439594334Sgshapiro
439694334Sgshapiro				if (type != NULL && *type != '\0')
439794334Sgshapiro				{
439894334Sgshapiro					if (sm_strcasecmp(type, "dn") == 0)
439994334Sgshapiro					{
440090792Sgshapiro						recurse = true;
440194334Sgshapiro						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
440290792Sgshapiro					}
440390792Sgshapiro					else if (sm_strcasecmp(type, "filter") == 0)
440490792Sgshapiro					{
440590792Sgshapiro						recurse = true;
440694334Sgshapiro						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
440790792Sgshapiro					}
440890792Sgshapiro					else if (sm_strcasecmp(type, "url") == 0)
440990792Sgshapiro					{
441090792Sgshapiro						recurse = true;
441194334Sgshapiro						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
441290792Sgshapiro					}
441394334Sgshapiro					else if (sm_strcasecmp(type, "normal") == 0)
441490792Sgshapiro					{
441594334Sgshapiro						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
441694334Sgshapiro						normalseen = true;
441790792Sgshapiro					}
441890792Sgshapiro					else
441990792Sgshapiro					{
442090792Sgshapiro						syserr("Unknown attribute type (%s) in %s",
442190792Sgshapiro						       type, map->map_mname);
442290792Sgshapiro						return false;
442390792Sgshapiro					}
442490792Sgshapiro				}
442590792Sgshapiro				else
442694334Sgshapiro				{
442794334Sgshapiro					lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
442894334Sgshapiro					normalseen = true;
442994334Sgshapiro				}
443090792Sgshapiro				i++;
443190792Sgshapiro			}
443238032Speter		}
443364562Sgshapiro		lmap->ldap_attr[i] = NULL;
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);
5660147078Sgshapiro	if (s == NULL)
5661147078Sgshapiro		return NULL;
5662147078Sgshapiro	if (bitset(MF_MATCHONLY, map->map_mflags))
5663147078Sgshapiro		return map_rewrite(map, name, strlen(name), NULL);
5664147078Sgshapiro	else
5665147078Sgshapiro		return map_rewrite(map, s->s_alias, strlen(s->s_alias), av);
566638032Speter}
566738032Speter
566838032Speter/*
566938032Speter**  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
567038032Speter*/
567138032Speter
567238032Spetervoid
567338032Speterstab_map_store(map, lhs, rhs)
567438032Speter	register MAP *map;
567538032Speter	char *lhs;
567638032Speter	char *rhs;
567738032Speter{
567838032Speter	register STAB *s;
567938032Speter
568038032Speter	s = stab(lhs, ST_ALIAS, ST_ENTER);
568138032Speter	s->s_alias = newstr(rhs);
568238032Speter}
568338032Speter
568438032Speter
568538032Speter/*
568638032Speter**  STAB_MAP_OPEN -- initialize (reads data file)
568738032Speter**
568838032Speter**	This is a wierd case -- it is only intended as a fallback for
568938032Speter**	aliases.  For this reason, opens for write (only during a
569038032Speter**	"newaliases") always fails, and opens for read open the
569138032Speter**	actual underlying text file instead of the database.
569238032Speter*/
569338032Speter
569438032Speterbool
569538032Speterstab_map_open(map, mode)
569638032Speter	register MAP *map;
569738032Speter	int mode;
569838032Speter{
569990792Sgshapiro	SM_FILE_T *af;
570064562Sgshapiro	long sff;
570138032Speter	struct stat st;
570238032Speter
570338032Speter	if (tTd(38, 2))
570490792Sgshapiro		sm_dprintf("stab_map_open(%s, %s, %d)\n",
570538032Speter			map->map_mname, map->map_file, mode);
570638032Speter
570738032Speter	mode &= O_ACCMODE;
570838032Speter	if (mode != O_RDONLY)
570938032Speter	{
571038032Speter		errno = EPERM;
571190792Sgshapiro		return false;
571238032Speter	}
571338032Speter
571438032Speter	sff = SFF_ROOTOK|SFF_REGONLY;
571564562Sgshapiro	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
571638032Speter		sff |= SFF_NOWLINK;
571764562Sgshapiro	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
571838032Speter		sff |= SFF_SAFEDIRPATH;
571938032Speter	af = safefopen(map->map_file, O_RDONLY, 0444, sff);
572038032Speter	if (af == NULL)
572190792Sgshapiro		return false;
572290792Sgshapiro	readaliases(map, af, false, false);
572338032Speter
572490792Sgshapiro	if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
572538032Speter		map->map_mtime = st.st_mtime;
572690792Sgshapiro	(void) sm_io_close(af, SM_TIME_DEFAULT);
572738032Speter
572890792Sgshapiro	return true;
572938032Speter}
573090792Sgshapiro/*
573138032Speter**  Implicit Modules
573238032Speter**
573338032Speter**	Tries several types.  For back compatibility of aliases.
573438032Speter*/
573538032Speter
573638032Speter
573738032Speter/*
573838032Speter**  IMPL_MAP_LOOKUP -- lookup in best open database
573938032Speter*/
574038032Speter
574138032Speterchar *
574238032Speterimpl_map_lookup(map, name, av, pstat)
574338032Speter	MAP *map;
574438032Speter	char *name;
574538032Speter	char **av;
574638032Speter	int *pstat;
574738032Speter{
574838032Speter	if (tTd(38, 20))
574990792Sgshapiro		sm_dprintf("impl_map_lookup(%s, %s)\n",
575038032Speter			map->map_mname, name);
575138032Speter
575290792Sgshapiro#if NEWDB
575338032Speter	if (bitset(MF_IMPL_HASH, map->map_mflags))
575438032Speter		return db_map_lookup(map, name, av, pstat);
575564562Sgshapiro#endif /* NEWDB */
575690792Sgshapiro#if NDBM
575738032Speter	if (bitset(MF_IMPL_NDBM, map->map_mflags))
575838032Speter		return ndbm_map_lookup(map, name, av, pstat);
575964562Sgshapiro#endif /* NDBM */
576038032Speter	return stab_map_lookup(map, name, av, pstat);
576138032Speter}
576238032Speter
576338032Speter/*
576438032Speter**  IMPL_MAP_STORE -- store in open databases
576538032Speter*/
576638032Speter
576738032Spetervoid
576838032Speterimpl_map_store(map, lhs, rhs)
576938032Speter	MAP *map;
577038032Speter	char *lhs;
577138032Speter	char *rhs;
577238032Speter{
577338032Speter	if (tTd(38, 12))
577490792Sgshapiro		sm_dprintf("impl_map_store(%s, %s, %s)\n",
577538032Speter			map->map_mname, lhs, rhs);
577690792Sgshapiro#if NEWDB
577738032Speter	if (bitset(MF_IMPL_HASH, map->map_mflags))
577838032Speter		db_map_store(map, lhs, rhs);
577964562Sgshapiro#endif /* NEWDB */
578090792Sgshapiro#if NDBM
578138032Speter	if (bitset(MF_IMPL_NDBM, map->map_mflags))
578238032Speter		ndbm_map_store(map, lhs, rhs);
578364562Sgshapiro#endif /* NDBM */
578438032Speter	stab_map_store(map, lhs, rhs);
578538032Speter}
578638032Speter
578738032Speter/*
578838032Speter**  IMPL_MAP_OPEN -- implicit database open
578938032Speter*/
579038032Speter
579138032Speterbool
579238032Speterimpl_map_open(map, mode)
579338032Speter	MAP *map;
579438032Speter	int mode;
579538032Speter{
579638032Speter	if (tTd(38, 2))
579790792Sgshapiro		sm_dprintf("impl_map_open(%s, %s, %d)\n",
579838032Speter			map->map_mname, map->map_file, mode);
579938032Speter
580038032Speter	mode &= O_ACCMODE;
580190792Sgshapiro#if NEWDB
580238032Speter	map->map_mflags |= MF_IMPL_HASH;
580338032Speter	if (hash_map_open(map, mode))
580438032Speter	{
580538032Speter# ifdef NDBM_YP_COMPAT
580638032Speter		if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
580764562Sgshapiro# endif /* NDBM_YP_COMPAT */
580890792Sgshapiro			return true;
580938032Speter	}
581038032Speter	else
581138032Speter		map->map_mflags &= ~MF_IMPL_HASH;
581264562Sgshapiro#endif /* NEWDB */
581390792Sgshapiro#if NDBM
581438032Speter	map->map_mflags |= MF_IMPL_NDBM;
581538032Speter	if (ndbm_map_open(map, mode))
581638032Speter	{
581790792Sgshapiro		return true;
581838032Speter	}
581938032Speter	else
582038032Speter		map->map_mflags &= ~MF_IMPL_NDBM;
582164562Sgshapiro#endif /* NDBM */
582238032Speter
582338032Speter#if defined(NEWDB) || defined(NDBM)
582438032Speter	if (Verbose)
582538032Speter		message("WARNING: cannot open alias database %s%s",
582638032Speter			map->map_file,
582738032Speter			mode == O_RDONLY ? "; reading text version" : "");
582864562Sgshapiro#else /* defined(NEWDB) || defined(NDBM) */
582938032Speter	if (mode != O_RDONLY)
583038032Speter		usrerr("Cannot rebuild aliases: no database format defined");
583164562Sgshapiro#endif /* defined(NEWDB) || defined(NDBM) */
583238032Speter
583338032Speter	if (mode == O_RDONLY)
583438032Speter		return stab_map_open(map, mode);
583538032Speter	else
583690792Sgshapiro		return false;
583738032Speter}
583838032Speter
583938032Speter
584038032Speter/*
584138032Speter**  IMPL_MAP_CLOSE -- close any open database(s)
584238032Speter*/
584338032Speter
584438032Spetervoid
584538032Speterimpl_map_close(map)
584638032Speter	MAP *map;
584738032Speter{
584838032Speter	if (tTd(38, 9))
584990792Sgshapiro		sm_dprintf("impl_map_close(%s, %s, %lx)\n",
585038032Speter			map->map_mname, map->map_file, map->map_mflags);
585190792Sgshapiro#if NEWDB
585238032Speter	if (bitset(MF_IMPL_HASH, map->map_mflags))
585338032Speter	{
585438032Speter		db_map_close(map);
585538032Speter		map->map_mflags &= ~MF_IMPL_HASH;
585638032Speter	}
585764562Sgshapiro#endif /* NEWDB */
585838032Speter
585990792Sgshapiro#if NDBM
586038032Speter	if (bitset(MF_IMPL_NDBM, map->map_mflags))
586138032Speter	{
586238032Speter		ndbm_map_close(map);
586338032Speter		map->map_mflags &= ~MF_IMPL_NDBM;
586438032Speter	}
586564562Sgshapiro#endif /* NDBM */
586638032Speter}
586790792Sgshapiro/*
586838032Speter**  User map class.
586938032Speter**
587038032Speter**	Provides access to the system password file.
587138032Speter*/
587238032Speter
587338032Speter/*
587438032Speter**  USER_MAP_OPEN -- open user map
587538032Speter**
587638032Speter**	Really just binds field names to field numbers.
587738032Speter*/
587838032Speter
587938032Speterbool
588038032Speteruser_map_open(map, mode)
588138032Speter	MAP *map;
588238032Speter	int mode;
588338032Speter{
588438032Speter	if (tTd(38, 2))
588590792Sgshapiro		sm_dprintf("user_map_open(%s, %d)\n",
588638032Speter			map->map_mname, mode);
588738032Speter
588838032Speter	mode &= O_ACCMODE;
588938032Speter	if (mode != O_RDONLY)
589038032Speter	{
589138032Speter		/* issue a pseudo-error message */
589290792Sgshapiro		errno = SM_EMAPCANTWRITE;
589390792Sgshapiro		return false;
589438032Speter	}
589538032Speter	if (map->map_valcolnm == NULL)
589664562Sgshapiro		/* EMPTY */
589738032Speter		/* nothing */ ;
589890792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
589938032Speter		map->map_valcolno = 1;
590090792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
590138032Speter		map->map_valcolno = 2;
590290792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
590338032Speter		map->map_valcolno = 3;
590490792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
590538032Speter		map->map_valcolno = 4;
590690792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
590738032Speter		map->map_valcolno = 5;
590890792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
590938032Speter		map->map_valcolno = 6;
591090792Sgshapiro	else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
591138032Speter		map->map_valcolno = 7;
591238032Speter	else
591338032Speter	{
591438032Speter		syserr("User map %s: unknown column name %s",
591538032Speter			map->map_mname, map->map_valcolnm);
591690792Sgshapiro		return false;
591738032Speter	}
591890792Sgshapiro	return true;
591938032Speter}
592038032Speter
592138032Speter
592238032Speter/*
592338032Speter**  USER_MAP_LOOKUP -- look up a user in the passwd file.
592438032Speter*/
592538032Speter
592638032Speter/* ARGSUSED3 */
592738032Speterchar *
592838032Speteruser_map_lookup(map, key, av, statp)
592938032Speter	MAP *map;
593038032Speter	char *key;
593138032Speter	char **av;
593238032Speter	int *statp;
593338032Speter{
593438032Speter	auto bool fuzzy;
593590792Sgshapiro	SM_MBDB_T user;
593638032Speter
593738032Speter	if (tTd(38, 20))
593890792Sgshapiro		sm_dprintf("user_map_lookup(%s, %s)\n",
593938032Speter			map->map_mname, key);
594038032Speter
594190792Sgshapiro	*statp = finduser(key, &fuzzy, &user);
594290792Sgshapiro	if (*statp != EX_OK)
594338032Speter		return NULL;
594438032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
594538032Speter		return map_rewrite(map, key, strlen(key), NULL);
594638032Speter	else
594738032Speter	{
594838032Speter		char *rwval = NULL;
594938032Speter		char buf[30];
595038032Speter
595138032Speter		switch (map->map_valcolno)
595238032Speter		{
595338032Speter		  case 0:
595438032Speter		  case 1:
595590792Sgshapiro			rwval = user.mbdb_name;
595638032Speter			break;
595738032Speter
595838032Speter		  case 2:
595990792Sgshapiro			rwval = "x";	/* passwd no longer supported */
596038032Speter			break;
596138032Speter
596238032Speter		  case 3:
596390792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%d",
596490792Sgshapiro					   (int) user.mbdb_uid);
596538032Speter			rwval = buf;
596638032Speter			break;
596738032Speter
596838032Speter		  case 4:
596990792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%d",
597090792Sgshapiro					   (int) user.mbdb_gid);
597138032Speter			rwval = buf;
597238032Speter			break;
597338032Speter
597438032Speter		  case 5:
597590792Sgshapiro			rwval = user.mbdb_fullname;
597638032Speter			break;
597738032Speter
597838032Speter		  case 6:
597990792Sgshapiro			rwval = user.mbdb_homedir;
598038032Speter			break;
598138032Speter
598238032Speter		  case 7:
598390792Sgshapiro			rwval = user.mbdb_shell;
598438032Speter			break;
598538032Speter		}
598638032Speter		return map_rewrite(map, rwval, strlen(rwval), av);
598738032Speter	}
598838032Speter}
598990792Sgshapiro/*
599038032Speter**  Program map type.
599138032Speter**
599238032Speter**	This provides access to arbitrary programs.  It should be used
599338032Speter**	only very sparingly, since there is no way to bound the cost
599438032Speter**	of invoking an arbitrary program.
599538032Speter*/
599638032Speter
599738032Speterchar *
599838032Speterprog_map_lookup(map, name, av, statp)
599938032Speter	MAP *map;
600038032Speter	char *name;
600138032Speter	char **av;
600238032Speter	int *statp;
600338032Speter{
600438032Speter	int i;
600564562Sgshapiro	int save_errno;
600638032Speter	int fd;
600764562Sgshapiro	int status;
600838032Speter	auto pid_t pid;
600964562Sgshapiro	register char *p;
601038032Speter	char *rval;
601138032Speter	char *argv[MAXPV + 1];
601238032Speter	char buf[MAXLINE];
601338032Speter
601438032Speter	if (tTd(38, 20))
601590792Sgshapiro		sm_dprintf("prog_map_lookup(%s, %s) %s\n",
601638032Speter			map->map_mname, name, map->map_file);
601738032Speter
601838032Speter	i = 0;
601938032Speter	argv[i++] = map->map_file;
602038032Speter	if (map->map_rebuild != NULL)
602138032Speter	{
602290792Sgshapiro		(void) sm_strlcpy(buf, map->map_rebuild, sizeof buf);
602338032Speter		for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
602438032Speter		{
602538032Speter			if (i >= MAXPV - 1)
602638032Speter				break;
602738032Speter			argv[i++] = p;
602838032Speter		}
602938032Speter	}
603038032Speter	argv[i++] = name;
603138032Speter	argv[i] = NULL;
603238032Speter	if (tTd(38, 21))
603338032Speter	{
603490792Sgshapiro		sm_dprintf("prog_open:");
603538032Speter		for (i = 0; argv[i] != NULL; i++)
603690792Sgshapiro			sm_dprintf(" %s", argv[i]);
603790792Sgshapiro		sm_dprintf("\n");
603838032Speter	}
603990792Sgshapiro	(void) sm_blocksignal(SIGCHLD);
604038032Speter	pid = prog_open(argv, &fd, CurEnv);
604138032Speter	if (pid < 0)
604238032Speter	{
604338032Speter		if (!bitset(MF_OPTIONAL, map->map_mflags))
604438032Speter			syserr("prog_map_lookup(%s) failed (%s) -- closing",
604590792Sgshapiro			       map->map_mname, sm_errstring(errno));
604638032Speter		else if (tTd(38, 9))
604790792Sgshapiro			sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
604890792Sgshapiro				   map->map_mname, sm_errstring(errno));
604938032Speter		map->map_mflags &= ~(MF_VALID|MF_OPEN);
605038032Speter		*statp = EX_OSFILE;
605138032Speter		return NULL;
605238032Speter	}
605338032Speter	i = read(fd, buf, sizeof buf - 1);
605438032Speter	if (i < 0)
605538032Speter	{
605690792Sgshapiro		syserr("prog_map_lookup(%s): read error %s",
605790792Sgshapiro		       map->map_mname, sm_errstring(errno));
605838032Speter		rval = NULL;
605938032Speter	}
606038032Speter	else if (i == 0)
606138032Speter	{
606238032Speter		if (tTd(38, 20))
606390792Sgshapiro			sm_dprintf("prog_map_lookup(%s): empty answer\n",
606490792Sgshapiro				   map->map_mname);
606538032Speter		rval = NULL;
606638032Speter	}
606738032Speter	else
606838032Speter	{
606938032Speter		buf[i] = '\0';
607038032Speter		p = strchr(buf, '\n');
607138032Speter		if (p != NULL)
607238032Speter			*p = '\0';
607338032Speter
607438032Speter		/* collect the return value */
607538032Speter		if (bitset(MF_MATCHONLY, map->map_mflags))
607638032Speter			rval = map_rewrite(map, name, strlen(name), NULL);
607738032Speter		else
607877349Sgshapiro			rval = map_rewrite(map, buf, strlen(buf), av);
607938032Speter
608038032Speter		/* now flush any additional output */
608138032Speter		while ((i = read(fd, buf, sizeof buf)) > 0)
608238032Speter			continue;
608338032Speter	}
608438032Speter
608538032Speter	/* wait for the process to terminate */
608664562Sgshapiro	(void) close(fd);
608764562Sgshapiro	status = waitfor(pid);
608864562Sgshapiro	save_errno = errno;
608990792Sgshapiro	(void) sm_releasesignal(SIGCHLD);
609064562Sgshapiro	errno = save_errno;
609138032Speter
609264562Sgshapiro	if (status == -1)
609338032Speter	{
609490792Sgshapiro		syserr("prog_map_lookup(%s): wait error %s",
609590792Sgshapiro		       map->map_mname, sm_errstring(errno));
609638032Speter		*statp = EX_SOFTWARE;
609738032Speter		rval = NULL;
609838032Speter	}
609964562Sgshapiro	else if (WIFEXITED(status))
610038032Speter	{
610164562Sgshapiro		if ((*statp = WEXITSTATUS(status)) != EX_OK)
610238032Speter			rval = NULL;
610338032Speter	}
610438032Speter	else
610538032Speter	{
610638032Speter		syserr("prog_map_lookup(%s): child died on signal %d",
610790792Sgshapiro		       map->map_mname, status);
610838032Speter		*statp = EX_UNAVAILABLE;
610938032Speter		rval = NULL;
611038032Speter	}
611138032Speter	return rval;
611238032Speter}
611390792Sgshapiro/*
611438032Speter**  Sequenced map type.
611538032Speter**
611638032Speter**	Tries each map in order until something matches, much like
611738032Speter**	implicit.  Stores go to the first map in the list that can
611838032Speter**	support storing.
611938032Speter**
612038032Speter**	This is slightly unusual in that there are two interfaces.
612138032Speter**	The "sequence" interface lets you stack maps arbitrarily.
612238032Speter**	The "switch" interface builds a sequence map by looking
612338032Speter**	at a system-dependent configuration file such as
612438032Speter**	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
612538032Speter**
612638032Speter**	We don't need an explicit open, since all maps are
612790792Sgshapiro**	opened on demand.
612838032Speter*/
612938032Speter
613038032Speter/*
613138032Speter**  SEQ_MAP_PARSE -- Sequenced map parsing
613238032Speter*/
613338032Speter
613438032Speterbool
613538032Speterseq_map_parse(map, ap)
613638032Speter	MAP *map;
613738032Speter	char *ap;
613838032Speter{
613938032Speter	int maxmap;
614038032Speter
614138032Speter	if (tTd(38, 2))
614290792Sgshapiro		sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
614338032Speter	maxmap = 0;
614438032Speter	while (*ap != '\0')
614538032Speter	{
614638032Speter		register char *p;
614738032Speter		STAB *s;
614838032Speter
614938032Speter		/* find beginning of map name */
615038032Speter		while (isascii(*ap) && isspace(*ap))
615138032Speter			ap++;
615264562Sgshapiro		for (p = ap;
615364562Sgshapiro		     (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
615464562Sgshapiro		     p++)
615538032Speter			continue;
615638032Speter		if (*p != '\0')
615738032Speter			*p++ = '\0';
615838032Speter		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
615938032Speter			p++;
616038032Speter		if (*ap == '\0')
616138032Speter		{
616238032Speter			ap = p;
616338032Speter			continue;
616438032Speter		}
616538032Speter		s = stab(ap, ST_MAP, ST_FIND);
616638032Speter		if (s == NULL)
616738032Speter		{
616838032Speter			syserr("Sequence map %s: unknown member map %s",
616938032Speter				map->map_mname, ap);
617038032Speter		}
617190792Sgshapiro		else if (maxmap >= MAXMAPSTACK)
617238032Speter		{
617338032Speter			syserr("Sequence map %s: too many member maps (%d max)",
617438032Speter				map->map_mname, MAXMAPSTACK);
617538032Speter			maxmap++;
617638032Speter		}
617738032Speter		else if (maxmap < MAXMAPSTACK)
617838032Speter		{
617938032Speter			map->map_stack[maxmap++] = &s->s_map;
618038032Speter		}
618138032Speter		ap = p;
618238032Speter	}
618390792Sgshapiro	return true;
618438032Speter}
618538032Speter
618638032Speter/*
618738032Speter**  SWITCH_MAP_OPEN -- open a switched map
618838032Speter**
618938032Speter**	This looks at the system-dependent configuration and builds
619038032Speter**	a sequence map that does the same thing.
619138032Speter**
619238032Speter**	Every system must define a switch_map_find routine in conf.c
619338032Speter**	that will return the list of service types associated with a
619438032Speter**	given service class.
619538032Speter*/
619638032Speter
619738032Speterbool
619838032Speterswitch_map_open(map, mode)
619938032Speter	MAP *map;
620038032Speter	int mode;
620138032Speter{
620238032Speter	int mapno;
620338032Speter	int nmaps;
620438032Speter	char *maptype[MAXMAPSTACK];
620538032Speter
620638032Speter	if (tTd(38, 2))
620790792Sgshapiro		sm_dprintf("switch_map_open(%s, %s, %d)\n",
620838032Speter			map->map_mname, map->map_file, mode);
620938032Speter
621038032Speter	mode &= O_ACCMODE;
621138032Speter	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
621238032Speter	if (tTd(38, 19))
621338032Speter	{
621490792Sgshapiro		sm_dprintf("\tswitch_map_find => %d\n", nmaps);
621538032Speter		for (mapno = 0; mapno < nmaps; mapno++)
621690792Sgshapiro			sm_dprintf("\t\t%s\n", maptype[mapno]);
621738032Speter	}
621838032Speter	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
621990792Sgshapiro		return false;
622038032Speter
622138032Speter	for (mapno = 0; mapno < nmaps; mapno++)
622238032Speter	{
622338032Speter		register STAB *s;
622438032Speter		char nbuf[MAXNAME + 1];
622538032Speter
622638032Speter		if (maptype[mapno] == NULL)
622738032Speter			continue;
622890792Sgshapiro		(void) sm_strlcpyn(nbuf, sizeof nbuf, 3,
622990792Sgshapiro				   map->map_mname, ".", maptype[mapno]);
623038032Speter		s = stab(nbuf, ST_MAP, ST_FIND);
623138032Speter		if (s == NULL)
623238032Speter		{
623338032Speter			syserr("Switch map %s: unknown member map %s",
623438032Speter				map->map_mname, nbuf);
623538032Speter		}
623638032Speter		else
623738032Speter		{
623838032Speter			map->map_stack[mapno] = &s->s_map;
623938032Speter			if (tTd(38, 4))
624090792Sgshapiro				sm_dprintf("\tmap_stack[%d] = %s:%s\n",
624190792Sgshapiro					   mapno,
624290792Sgshapiro					   s->s_map.map_class->map_cname,
624390792Sgshapiro					   nbuf);
624438032Speter		}
624538032Speter	}
624690792Sgshapiro	return true;
624738032Speter}
624838032Speter
624990792Sgshapiro#if 0
625038032Speter/*
625138032Speter**  SEQ_MAP_CLOSE -- close all underlying maps
625238032Speter*/
625338032Speter
625438032Spetervoid
625538032Speterseq_map_close(map)
625638032Speter	MAP *map;
625738032Speter{
625838032Speter	int mapno;
625938032Speter
626038032Speter	if (tTd(38, 9))
626190792Sgshapiro		sm_dprintf("seq_map_close(%s)\n", map->map_mname);
626238032Speter
626338032Speter	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
626438032Speter	{
626538032Speter		MAP *mm = map->map_stack[mapno];
626638032Speter
626738032Speter		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
626838032Speter			continue;
626977349Sgshapiro		mm->map_mflags |= MF_CLOSING;
627038032Speter		mm->map_class->map_close(mm);
627177349Sgshapiro		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
627238032Speter	}
627338032Speter}
627490792Sgshapiro#endif /* 0 */
627538032Speter
627638032Speter/*
627738032Speter**  SEQ_MAP_LOOKUP -- sequenced map lookup
627838032Speter*/
627938032Speter
628038032Speterchar *
628138032Speterseq_map_lookup(map, key, args, pstat)
628238032Speter	MAP *map;
628338032Speter	char *key;
628438032Speter	char **args;
628538032Speter	int *pstat;
628638032Speter{
628738032Speter	int mapno;
628838032Speter	int mapbit = 0x01;
628990792Sgshapiro	bool tempfail = false;
629038032Speter
629138032Speter	if (tTd(38, 20))
629290792Sgshapiro		sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
629338032Speter
629438032Speter	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
629538032Speter	{
629638032Speter		MAP *mm = map->map_stack[mapno];
629738032Speter		char *rv;
629838032Speter
629938032Speter		if (mm == NULL)
630038032Speter			continue;
630164562Sgshapiro		if (!bitset(MF_OPEN, mm->map_mflags) &&
630264562Sgshapiro		    !openmap(mm))
630338032Speter		{
630438032Speter			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
630538032Speter			{
630638032Speter				*pstat = EX_UNAVAILABLE;
630738032Speter				return NULL;
630838032Speter			}
630938032Speter			continue;
631038032Speter		}
631138032Speter		*pstat = EX_OK;
631238032Speter		rv = mm->map_class->map_lookup(mm, key, args, pstat);
631338032Speter		if (rv != NULL)
631438032Speter			return rv;
631538032Speter		if (*pstat == EX_TEMPFAIL)
631638032Speter		{
631738032Speter			if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
631838032Speter				return NULL;
631990792Sgshapiro			tempfail = true;
632038032Speter		}
632138032Speter		else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
632238032Speter			break;
632338032Speter	}
632438032Speter	if (tempfail)
632538032Speter		*pstat = EX_TEMPFAIL;
632638032Speter	else if (*pstat == EX_OK)
632738032Speter		*pstat = EX_NOTFOUND;
632838032Speter	return NULL;
632938032Speter}
633038032Speter
633138032Speter/*
633238032Speter**  SEQ_MAP_STORE -- sequenced map store
633338032Speter*/
633438032Speter
633538032Spetervoid
633638032Speterseq_map_store(map, key, val)
633738032Speter	MAP *map;
633838032Speter	char *key;
633938032Speter	char *val;
634038032Speter{
634138032Speter	int mapno;
634238032Speter
634338032Speter	if (tTd(38, 12))
634490792Sgshapiro		sm_dprintf("seq_map_store(%s, %s, %s)\n",
634538032Speter			map->map_mname, key, val);
634638032Speter
634738032Speter	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
634838032Speter	{
634938032Speter		MAP *mm = map->map_stack[mapno];
635038032Speter
635138032Speter		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
635238032Speter			continue;
635338032Speter
635438032Speter		mm->map_class->map_store(mm, key, val);
635538032Speter		return;
635638032Speter	}
635738032Speter	syserr("seq_map_store(%s, %s, %s): no writable map",
635838032Speter		map->map_mname, key, val);
635938032Speter}
636090792Sgshapiro/*
636138032Speter**  NULL stubs
636238032Speter*/
636338032Speter
636438032Speter/* ARGSUSED */
636538032Speterbool
636638032Speternull_map_open(map, mode)
636738032Speter	MAP *map;
636838032Speter	int mode;
636938032Speter{
637090792Sgshapiro	return true;
637138032Speter}
637238032Speter
637338032Speter/* ARGSUSED */
637438032Spetervoid
637538032Speternull_map_close(map)
637638032Speter	MAP *map;
637738032Speter{
637838032Speter	return;
637938032Speter}
638038032Speter
638138032Speterchar *
638238032Speternull_map_lookup(map, key, args, pstat)
638338032Speter	MAP *map;
638438032Speter	char *key;
638538032Speter	char **args;
638638032Speter	int *pstat;
638738032Speter{
638838032Speter	*pstat = EX_NOTFOUND;
638938032Speter	return NULL;
639038032Speter}
639138032Speter
639238032Speter/* ARGSUSED */
639338032Spetervoid
639438032Speternull_map_store(map, key, val)
639538032Speter	MAP *map;
639638032Speter	char *key;
639738032Speter	char *val;
639838032Speter{
639938032Speter	return;
640038032Speter}
640138032Speter
640238032Speter/*
640338032Speter**  BOGUS stubs
640438032Speter*/
640538032Speter
640638032Speterchar *
640738032Speterbogus_map_lookup(map, key, args, pstat)
640838032Speter	MAP *map;
640938032Speter	char *key;
641038032Speter	char **args;
641138032Speter	int *pstat;
641238032Speter{
641338032Speter	*pstat = EX_TEMPFAIL;
641438032Speter	return NULL;
641538032Speter}
641638032Speter
641738032SpeterMAPCLASS	BogusMapClass =
641838032Speter{
641990792Sgshapiro	"bogus-map",		NULL,			0,
642090792Sgshapiro	NULL,			bogus_map_lookup,	null_map_store,
642190792Sgshapiro	null_map_open,		null_map_close,
642238032Speter};
642390792Sgshapiro/*
642464562Sgshapiro**  MACRO modules
642564562Sgshapiro*/
642664562Sgshapiro
642764562Sgshapirochar *
642864562Sgshapiromacro_map_lookup(map, name, av, statp)
642964562Sgshapiro	MAP *map;
643064562Sgshapiro	char *name;
643164562Sgshapiro	char **av;
643264562Sgshapiro	int *statp;
643364562Sgshapiro{
643464562Sgshapiro	int mid;
643564562Sgshapiro
643664562Sgshapiro	if (tTd(38, 20))
643790792Sgshapiro		sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
643864562Sgshapiro			name == NULL ? "NULL" : name);
643964562Sgshapiro
644064562Sgshapiro	if (name == NULL ||
644164562Sgshapiro	    *name == '\0' ||
644290792Sgshapiro	    (mid = macid(name)) == 0)
644364562Sgshapiro	{
644464562Sgshapiro		*statp = EX_CONFIG;
644564562Sgshapiro		return NULL;
644664562Sgshapiro	}
644764562Sgshapiro
644864562Sgshapiro	if (av[1] == NULL)
644990792Sgshapiro		macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
645064562Sgshapiro	else
645190792Sgshapiro		macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
645264562Sgshapiro
645364562Sgshapiro	*statp = EX_OK;
645464562Sgshapiro	return "";
645564562Sgshapiro}
645690792Sgshapiro/*
645738032Speter**  REGEX modules
645838032Speter*/
645938032Speter
646090792Sgshapiro#if MAP_REGEX
646138032Speter
646238032Speter# include <regex.h>
646338032Speter
646438032Speter# define DEFAULT_DELIM	CONDELSE
646538032Speter# define END_OF_FIELDS	-1
646638032Speter# define ERRBUF_SIZE	80
646738032Speter# define MAX_MATCH	32
646838032Speter
646964562Sgshapiro# define xnalloc(s)	memset(xalloc(s), '\0', s);
647038032Speter
647138032Speterstruct regex_map
647238032Speter{
647371345Sgshapiro	regex_t	*regex_pattern_buf;	/* xalloc it */
647438032Speter	int	*regex_subfields;	/* move to type MAP */
647564562Sgshapiro	char	*regex_delim;		/* move to type MAP */
647638032Speter};
647738032Speter
6478141858Sgshapirostatic int	parse_fields __P((char *, int *, int, int));
6479141858Sgshapirostatic char	*regex_map_rewrite __P((MAP *, const char*, size_t, char **));
6480141858Sgshapiro
648138032Speterstatic int
648238032Speterparse_fields(s, ibuf, blen, nr_substrings)
648338032Speter	char *s;
648438032Speter	int *ibuf;		/* array */
648538032Speter	int blen;		/* number of elements in ibuf */
648638032Speter	int nr_substrings;	/* number of substrings in the pattern */
648738032Speter{
648838032Speter	register char *cp;
648938032Speter	int i = 0;
649090792Sgshapiro	bool lastone = false;
649138032Speter
649238032Speter	blen--;		/* for terminating END_OF_FIELDS */
649338032Speter	cp = s;
649438032Speter	do
649538032Speter	{
649638032Speter		for (;; cp++)
649738032Speter		{
649838032Speter			if (*cp == ',')
649938032Speter			{
650038032Speter				*cp = '\0';
650138032Speter				break;
650238032Speter			}
650338032Speter			if (*cp == '\0')
650438032Speter			{
650590792Sgshapiro				lastone = true;
650638032Speter				break;
650738032Speter			}
650838032Speter		}
650938032Speter		if (i < blen)
651038032Speter		{
651138032Speter			int val = atoi(s);
651238032Speter
651338032Speter			if (val < 0 || val >= nr_substrings)
651438032Speter			{
651538032Speter				syserr("field (%d) out of range, only %d substrings in pattern",
651638032Speter				       val, nr_substrings);
651738032Speter				return -1;
651838032Speter			}
651938032Speter			ibuf[i++] = val;
652038032Speter		}
652138032Speter		else
652238032Speter		{
652390792Sgshapiro			syserr("too many fields, %d max", blen);
652438032Speter			return -1;
652538032Speter		}
652638032Speter		s = ++cp;
652738032Speter	} while (!lastone);
652838032Speter	ibuf[i] = END_OF_FIELDS;
652938032Speter	return i;
653038032Speter}
653138032Speter
653238032Speterbool
653338032Speterregex_map_init(map, ap)
653438032Speter	MAP *map;
653538032Speter	char *ap;
653638032Speter{
653738032Speter	int regerr;
653838032Speter	struct regex_map *map_p;
653938032Speter	register char *p;
654038032Speter	char *sub_param = NULL;
654138032Speter	int pflags;
654290792Sgshapiro	static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
654338032Speter
654438032Speter	if (tTd(38, 2))
654590792Sgshapiro		sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
654664562Sgshapiro			map->map_mname, ap);
654738032Speter
654838032Speter	pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
654938032Speter	p = ap;
655064562Sgshapiro	map_p = (struct regex_map *) xnalloc(sizeof *map_p);
655171345Sgshapiro	map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
655238032Speter
655338032Speter	for (;;)
655464562Sgshapiro	{
655538032Speter		while (isascii(*p) && isspace(*p))
655638032Speter			p++;
655738032Speter		if (*p != '-')
655838032Speter			break;
655938032Speter		switch (*++p)
656038032Speter		{
656138032Speter		  case 'n':	/* not */
656238032Speter			map->map_mflags |= MF_REGEX_NOT;
656338032Speter			break;
656438032Speter
656538032Speter		  case 'f':	/* case sensitive */
656638032Speter			map->map_mflags |= MF_NOFOLDCASE;
656738032Speter			pflags &= ~REG_ICASE;
656838032Speter			break;
656938032Speter
657038032Speter		  case 'b':	/* basic regular expressions */
657138032Speter			pflags &= ~REG_EXTENDED;
657238032Speter			break;
657338032Speter
657438032Speter		  case 's':	/* substring match () syntax */
657538032Speter			sub_param = ++p;
657638032Speter			pflags &= ~REG_NOSUB;
657738032Speter			break;
657838032Speter
657938032Speter		  case 'd':	/* delimiter */
658064562Sgshapiro			map_p->regex_delim = ++p;
658138032Speter			break;
658238032Speter
658338032Speter		  case 'a':	/* map append */
658438032Speter			map->map_app = ++p;
658538032Speter			break;
658638032Speter
658738032Speter		  case 'm':	/* matchonly */
658838032Speter			map->map_mflags |= MF_MATCHONLY;
658938032Speter			break;
659038032Speter
6591120256Sgshapiro		  case 'q':
6592120256Sgshapiro			map->map_mflags |= MF_KEEPQUOTES;
6593120256Sgshapiro			break;
6594120256Sgshapiro
659564562Sgshapiro		  case 'S':
659664562Sgshapiro			map->map_spacesub = *++p;
659764562Sgshapiro			break;
659864562Sgshapiro
659964562Sgshapiro		  case 'D':
660064562Sgshapiro			map->map_mflags |= MF_DEFER;
660164562Sgshapiro			break;
660264562Sgshapiro
660338032Speter		}
660464562Sgshapiro		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
660564562Sgshapiro			p++;
660664562Sgshapiro		if (*p != '\0')
660764562Sgshapiro			*p++ = '\0';
660838032Speter	}
660938032Speter	if (tTd(38, 3))
661090792Sgshapiro		sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
661138032Speter
661271345Sgshapiro	if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
661338032Speter	{
661438032Speter		/* Errorhandling */
661538032Speter		char errbuf[ERRBUF_SIZE];
661638032Speter
661771345Sgshapiro		(void) regerror(regerr, map_p->regex_pattern_buf,
661890792Sgshapiro			 errbuf, sizeof errbuf);
661990792Sgshapiro		syserr("pattern-compile-error: %s", errbuf);
662090792Sgshapiro		sm_free(map_p->regex_pattern_buf); /* XXX */
662190792Sgshapiro		sm_free(map_p); /* XXX */
662290792Sgshapiro		return false;
662338032Speter	}
662438032Speter
662538032Speter	if (map->map_app != NULL)
662638032Speter		map->map_app = newstr(map->map_app);
662764562Sgshapiro	if (map_p->regex_delim != NULL)
662864562Sgshapiro		map_p->regex_delim = newstr(map_p->regex_delim);
662938032Speter	else
663064562Sgshapiro		map_p->regex_delim = defdstr;
663138032Speter
663238032Speter	if (!bitset(REG_NOSUB, pflags))
663338032Speter	{
663438032Speter		/* substring matching */
663538032Speter		int substrings;
663664562Sgshapiro		int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
663738032Speter
663871345Sgshapiro		substrings = map_p->regex_pattern_buf->re_nsub + 1;
663938032Speter
664038032Speter		if (tTd(38, 3))
664190792Sgshapiro			sm_dprintf("regex_map_init: nr of substrings %d\n",
664264562Sgshapiro				substrings);
664338032Speter
664438032Speter		if (substrings >= MAX_MATCH)
664538032Speter		{
664690792Sgshapiro			syserr("too many substrings, %d max", MAX_MATCH);
664790792Sgshapiro			sm_free(map_p->regex_pattern_buf); /* XXX */
664890792Sgshapiro			sm_free(map_p); /* XXX */
664990792Sgshapiro			return false;
665038032Speter		}
665138032Speter		if (sub_param != NULL && sub_param[0] != '\0')
665238032Speter		{
665338032Speter			/* optional parameter -sfields */
665438032Speter			if (parse_fields(sub_param, fields,
665538032Speter					 MAX_MATCH + 1, substrings) == -1)
665690792Sgshapiro				return false;
665738032Speter		}
665838032Speter		else
665938032Speter		{
666038032Speter			int i;
666138032Speter
666290792Sgshapiro			/* set default fields */
666338032Speter			for (i = 0; i < substrings; i++)
666438032Speter				fields[i] = i;
666538032Speter			fields[i] = END_OF_FIELDS;
666638032Speter		}
666738032Speter		map_p->regex_subfields = fields;
666838032Speter		if (tTd(38, 3))
666938032Speter		{
667038032Speter			int *ip;
667138032Speter
667290792Sgshapiro			sm_dprintf("regex_map_init: subfields");
667338032Speter			for (ip = fields; *ip != END_OF_FIELDS; ip++)
667490792Sgshapiro				sm_dprintf(" %d", *ip);
667590792Sgshapiro			sm_dprintf("\n");
667638032Speter		}
667738032Speter	}
667890792Sgshapiro	map->map_db1 = (ARBPTR_T) map_p;	/* dirty hack */
667990792Sgshapiro	return true;
668038032Speter}
668138032Speter
668238032Speterstatic char *
668338032Speterregex_map_rewrite(map, s, slen, av)
668438032Speter	MAP *map;
668538032Speter	const char *s;
668638032Speter	size_t slen;
668738032Speter	char **av;
668838032Speter{
668938032Speter	if (bitset(MF_MATCHONLY, map->map_mflags))
669038032Speter		return map_rewrite(map, av[0], strlen(av[0]), NULL);
669138032Speter	else
669277349Sgshapiro		return map_rewrite(map, s, slen, av);
669338032Speter}
669438032Speter
669538032Speterchar *
669638032Speterregex_map_lookup(map, name, av, statp)
669738032Speter	MAP *map;
669838032Speter	char *name;
669938032Speter	char **av;
670038032Speter	int *statp;
670138032Speter{
670238032Speter	int reg_res;
670338032Speter	struct regex_map *map_p;
670438032Speter	regmatch_t pmatch[MAX_MATCH];
670538032Speter
670638032Speter	if (tTd(38, 20))
670738032Speter	{
670838032Speter		char **cpp;
670938032Speter
671090792Sgshapiro		sm_dprintf("regex_map_lookup: key '%s'\n", name);
671164562Sgshapiro		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
671290792Sgshapiro			sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
671338032Speter	}
671438032Speter
671538032Speter	map_p = (struct regex_map *)(map->map_db1);
671671345Sgshapiro	reg_res = regexec(map_p->regex_pattern_buf,
671764562Sgshapiro			  name, MAX_MATCH, pmatch, 0);
671838032Speter
671938032Speter	if (bitset(MF_REGEX_NOT, map->map_mflags))
672038032Speter	{
672138032Speter		/* option -n */
672238032Speter		if (reg_res == REG_NOMATCH)
672390792Sgshapiro			return regex_map_rewrite(map, "", (size_t) 0, av);
672438032Speter		else
672538032Speter			return NULL;
672638032Speter	}
672738032Speter	if (reg_res == REG_NOMATCH)
672838032Speter		return NULL;
672938032Speter
673038032Speter	if (map_p->regex_subfields != NULL)
673138032Speter	{
673238032Speter		/* option -s */
673338032Speter		static char retbuf[MAXNAME];
673438032Speter		int fields[MAX_MATCH + 1];
673590792Sgshapiro		bool first = true;
673638032Speter		int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
673790792Sgshapiro		bool quotemode = false, bslashmode = false;
673838032Speter		register char *dp, *sp;
673938032Speter		char *endp, *ldp;
674038032Speter		int *ip;
674138032Speter
674238032Speter		dp = retbuf;
674338032Speter		ldp = retbuf + sizeof(retbuf) - 1;
674438032Speter
674538032Speter		if (av[1] != NULL)
674638032Speter		{
674738032Speter			if (parse_fields(av[1], fields, MAX_MATCH + 1,
674871345Sgshapiro					 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
674938032Speter			{
675038032Speter				*statp = EX_CONFIG;
675138032Speter				return NULL;
675238032Speter			}
675338032Speter			ip = fields;
675438032Speter		}
675538032Speter		else
675638032Speter			ip = map_p->regex_subfields;
675738032Speter
675838032Speter		for ( ; *ip != END_OF_FIELDS; ip++)
675938032Speter		{
676038032Speter			if (!first)
676138032Speter			{
676264562Sgshapiro				for (sp = map_p->regex_delim; *sp; sp++)
676338032Speter				{
676438032Speter					if (dp < ldp)
676538032Speter						*dp++ = *sp;
676638032Speter				}
676738032Speter			}
676838032Speter			else
676990792Sgshapiro				first = false;
677038032Speter
677171345Sgshapiro			if (*ip >= MAX_MATCH ||
677271345Sgshapiro			    pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
677338032Speter				continue;
677438032Speter
677538032Speter			sp = name + pmatch[*ip].rm_so;
677638032Speter			endp = name + pmatch[*ip].rm_eo;
677738032Speter			for (; endp > sp; sp++)
677838032Speter			{
677938032Speter				if (dp < ldp)
678038032Speter				{
678164562Sgshapiro					if (bslashmode)
678264562Sgshapiro					{
678338032Speter						*dp++ = *sp;
678490792Sgshapiro						bslashmode = false;
678538032Speter					}
678664562Sgshapiro					else if (quotemode && *sp != '"' &&
678738032Speter						*sp != '\\')
678838032Speter					{
678938032Speter						*dp++ = *sp;
679038032Speter					}
679190792Sgshapiro					else switch (*dp++ = *sp)
679238032Speter					{
679390792Sgshapiro					  case '\\':
679490792Sgshapiro						bslashmode = true;
679538032Speter						break;
679638032Speter
679790792Sgshapiro					  case '(':
679838032Speter						cmntcnt++;
679938032Speter						break;
680038032Speter
680190792Sgshapiro					  case ')':
680238032Speter						cmntcnt--;
680338032Speter						break;
680438032Speter
680590792Sgshapiro					  case '<':
680638032Speter						anglecnt++;
680738032Speter						break;
680838032Speter
680990792Sgshapiro					  case '>':
681038032Speter						anglecnt--;
681138032Speter						break;
681238032Speter
681390792Sgshapiro					  case ' ':
681438032Speter						spacecnt++;
681538032Speter						break;
681638032Speter
681790792Sgshapiro					  case '"':
681838032Speter						quotemode = !quotemode;
681938032Speter						break;
682038032Speter					}
682138032Speter				}
682238032Speter			}
682338032Speter		}
682438032Speter		if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
682538032Speter		    bslashmode || spacecnt != 0)
682638032Speter		{
682764562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
682864562Sgshapiro				  "Warning: regex may cause prescan() failure map=%s lookup=%s",
682964562Sgshapiro				  map->map_mname, name);
683038032Speter			return NULL;
683138032Speter		}
683238032Speter
683338032Speter		*dp = '\0';
683438032Speter
683538032Speter		return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
683638032Speter	}
683738032Speter	return regex_map_rewrite(map, "", (size_t)0, av);
683838032Speter}
683938032Speter#endif /* MAP_REGEX */
684090792Sgshapiro/*
684164562Sgshapiro**  NSD modules
684264562Sgshapiro*/
684390792Sgshapiro#if MAP_NSD
684464562Sgshapiro
684564562Sgshapiro# include <ndbm.h>
684664562Sgshapiro# define _DATUM_DEFINED
684764562Sgshapiro# include <ns_api.h>
684864562Sgshapiro
684964562Sgshapirotypedef struct ns_map_list
685064562Sgshapiro{
685190792Sgshapiro	ns_map_t		*map;		/* XXX ns_ ? */
685290792Sgshapiro	char			*mapname;
685390792Sgshapiro	struct ns_map_list	*next;
685464562Sgshapiro} ns_map_list_t;
685564562Sgshapiro
685664562Sgshapirostatic ns_map_t *
685764562Sgshapirons_map_t_find(mapname)
685864562Sgshapiro	char *mapname;
685964562Sgshapiro{
686064562Sgshapiro	static ns_map_list_t *ns_maps = NULL;
686164562Sgshapiro	ns_map_list_t *ns_map;
686264562Sgshapiro
686364562Sgshapiro	/* walk the list of maps looking for the correctly named map */
686464562Sgshapiro	for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
686564562Sgshapiro	{
686664562Sgshapiro		if (strcmp(ns_map->mapname, mapname) == 0)
686764562Sgshapiro			break;
686864562Sgshapiro	}
686964562Sgshapiro
687064562Sgshapiro	/* if we are looking at a NULL ns_map_list_t, then create a new one */
687164562Sgshapiro	if (ns_map == NULL)
687264562Sgshapiro	{
687364562Sgshapiro		ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map);
687464562Sgshapiro		ns_map->mapname = newstr(mapname);
687564562Sgshapiro		ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map);
6876102528Sgshapiro		memset(ns_map->map, '\0', sizeof *ns_map->map);
687764562Sgshapiro		ns_map->next = ns_maps;
687864562Sgshapiro		ns_maps = ns_map;
687964562Sgshapiro	}
688064562Sgshapiro	return ns_map->map;
688164562Sgshapiro}
688264562Sgshapiro
688364562Sgshapirochar *
688464562Sgshapironsd_map_lookup(map, name, av, statp)
688564562Sgshapiro	MAP *map;
688664562Sgshapiro	char *name;
688764562Sgshapiro	char **av;
688864562Sgshapiro	int *statp;
688964562Sgshapiro{
689071345Sgshapiro	int buflen, r;
689164562Sgshapiro	char *p;
689264562Sgshapiro	ns_map_t *ns_map;
689364562Sgshapiro	char keybuf[MAXNAME + 1];
689464562Sgshapiro	char buf[MAXLINE];
689564562Sgshapiro
689664562Sgshapiro	if (tTd(38, 20))
689790792Sgshapiro		sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
689864562Sgshapiro
689964562Sgshapiro	buflen = strlen(name);
690064562Sgshapiro	if (buflen > sizeof keybuf - 1)
690190792Sgshapiro		buflen = sizeof keybuf - 1;	/* XXX simply cut off? */
690264562Sgshapiro	memmove(keybuf, name, buflen);
690364562Sgshapiro	keybuf[buflen] = '\0';
690464562Sgshapiro	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
690564562Sgshapiro		makelower(keybuf);
690664562Sgshapiro
690764562Sgshapiro	ns_map = ns_map_t_find(map->map_file);
690864562Sgshapiro	if (ns_map == NULL)
690964562Sgshapiro	{
691064562Sgshapiro		if (tTd(38, 20))
691190792Sgshapiro			sm_dprintf("nsd_map_t_find failed\n");
691271345Sgshapiro		*statp = EX_UNAVAILABLE;
691364562Sgshapiro		return NULL;
691464562Sgshapiro	}
691598121Sgshapiro	r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
691698121Sgshapiro		      buf, sizeof buf);
691771345Sgshapiro	if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
691871345Sgshapiro	{
691971345Sgshapiro		*statp = EX_TEMPFAIL;
692064562Sgshapiro		return NULL;
692171345Sgshapiro	}
692277349Sgshapiro	if (r == NS_BADREQ
692377349Sgshapiro# ifdef NS_NOPERM
692477349Sgshapiro	    || r == NS_NOPERM
692577349Sgshapiro# endif /* NS_NOPERM */
692677349Sgshapiro	    )
692771345Sgshapiro	{
692871345Sgshapiro		*statp = EX_CONFIG;
692971345Sgshapiro		return NULL;
693071345Sgshapiro	}
693171345Sgshapiro	if (r != NS_SUCCESS)
693271345Sgshapiro	{
693371345Sgshapiro		*statp = EX_NOTFOUND;
693471345Sgshapiro		return NULL;
693571345Sgshapiro	}
693664562Sgshapiro
693771345Sgshapiro	*statp = EX_OK;
693871345Sgshapiro
693964562Sgshapiro	/* Null out trailing \n */
694064562Sgshapiro	if ((p = strchr(buf, '\n')) != NULL)
694164562Sgshapiro		*p = '\0';
694264562Sgshapiro
694364562Sgshapiro	return map_rewrite(map, buf, strlen(buf), av);
694464562Sgshapiro}
694564562Sgshapiro#endif /* MAP_NSD */
694664562Sgshapiro
694764562Sgshapirochar *
694864562Sgshapiroarith_map_lookup(map, name, av, statp)
694964562Sgshapiro	MAP *map;
695064562Sgshapiro	char *name;
695164562Sgshapiro	char **av;
695264562Sgshapiro	int *statp;
695364562Sgshapiro{
695464562Sgshapiro	long r;
695564562Sgshapiro	long v[2];
695690792Sgshapiro	bool res = false;
695764562Sgshapiro	bool boolres;
695864562Sgshapiro	static char result[16];
695964562Sgshapiro	char **cpp;
696064562Sgshapiro
696164562Sgshapiro	if (tTd(38, 2))
696264562Sgshapiro	{
696390792Sgshapiro		sm_dprintf("arith_map_lookup: key '%s'\n", name);
696464562Sgshapiro		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
696590792Sgshapiro			sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
696664562Sgshapiro	}
696764562Sgshapiro	r = 0;
696890792Sgshapiro	boolres = false;
696964562Sgshapiro	cpp = av;
697064562Sgshapiro	*statp = EX_OK;
697164562Sgshapiro
697264562Sgshapiro	/*
697364562Sgshapiro	**  read arguments for arith map
697464562Sgshapiro	**  - no check is made whether they are really numbers
697564562Sgshapiro	**  - just ignores args after the second
697664562Sgshapiro	*/
697790792Sgshapiro
697864562Sgshapiro	for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
697964562Sgshapiro		v[r++] = strtol(*cpp, NULL, 0);
698064562Sgshapiro
698164562Sgshapiro	/* operator and (at least) two operands given? */
698264562Sgshapiro	if (name != NULL && r == 2)
698364562Sgshapiro	{
698490792Sgshapiro		switch (*name)
698564562Sgshapiro		{
698664562Sgshapiro		  case '|':
698764562Sgshapiro			r = v[0] | v[1];
698864562Sgshapiro			break;
698964562Sgshapiro
699064562Sgshapiro		  case '&':
699164562Sgshapiro			r = v[0] & v[1];
699264562Sgshapiro			break;
699364562Sgshapiro
699464562Sgshapiro		  case '%':
699564562Sgshapiro			if (v[1] == 0)
699664562Sgshapiro				return NULL;
699764562Sgshapiro			r = v[0] % v[1];
699864562Sgshapiro			break;
699964562Sgshapiro		  case '+':
700064562Sgshapiro			r = v[0] + v[1];
700164562Sgshapiro			break;
700264562Sgshapiro
700364562Sgshapiro		  case '-':
700464562Sgshapiro			r = v[0] - v[1];
700564562Sgshapiro			break;
700664562Sgshapiro
700764562Sgshapiro		  case '*':
700864562Sgshapiro			r = v[0] * v[1];
700964562Sgshapiro			break;
701064562Sgshapiro
701164562Sgshapiro		  case '/':
701264562Sgshapiro			if (v[1] == 0)
701364562Sgshapiro				return NULL;
701464562Sgshapiro			r = v[0] / v[1];
701564562Sgshapiro			break;
701664562Sgshapiro
701764562Sgshapiro		  case 'l':
701864562Sgshapiro			res = v[0] < v[1];
701990792Sgshapiro			boolres = true;
702064562Sgshapiro			break;
702164562Sgshapiro
702264562Sgshapiro		  case '=':
702364562Sgshapiro			res = v[0] == v[1];
702490792Sgshapiro			boolres = true;
702564562Sgshapiro			break;
702664562Sgshapiro
702764562Sgshapiro		  default:
702864562Sgshapiro			/* XXX */
702964562Sgshapiro			*statp = EX_CONFIG;
703064562Sgshapiro			if (LogLevel > 10)
703164562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
703264562Sgshapiro					  "arith_map: unknown operator %c",
703364562Sgshapiro					  isprint(*name) ? *name : '?');
703464562Sgshapiro			return NULL;
703564562Sgshapiro		}
703664562Sgshapiro		if (boolres)
703790792Sgshapiro			(void) sm_snprintf(result, sizeof result,
703890792Sgshapiro				res ? "TRUE" : "FALSE");
703964562Sgshapiro		else
704090792Sgshapiro			(void) sm_snprintf(result, sizeof result, "%ld", r);
704164562Sgshapiro		return result;
704264562Sgshapiro	}
704364562Sgshapiro	*statp = EX_CONFIG;
704464562Sgshapiro	return NULL;
704564562Sgshapiro}
7046132943Sgshapiro
7047132943Sgshapiro#if SOCKETMAP
7048132943Sgshapiro
7049132943Sgshapiro# if NETINET || NETINET6
7050132943Sgshapiro#  include <arpa/inet.h>
7051132943Sgshapiro# endif /* NETINET || NETINET6 */
7052132943Sgshapiro
7053132943Sgshapiro# define socket_map_next map_stack[0]
7054132943Sgshapiro
7055132943Sgshapiro/*
7056132943Sgshapiro**  SOCKET_MAP_OPEN -- open socket table
7057132943Sgshapiro*/
7058132943Sgshapiro
7059132943Sgshapirobool
7060132943Sgshapirosocket_map_open(map, mode)
7061132943Sgshapiro	MAP *map;
7062132943Sgshapiro	int mode;
7063132943Sgshapiro{
7064132943Sgshapiro	STAB *s;
7065132943Sgshapiro	int sock = 0;
7066132943Sgshapiro	SOCKADDR_LEN_T addrlen = 0;
7067132943Sgshapiro	int addrno = 0;
7068132943Sgshapiro	int save_errno;
7069132943Sgshapiro	char *p;
7070132943Sgshapiro	char *colon;
7071132943Sgshapiro	char *at;
7072132943Sgshapiro	struct hostent *hp = NULL;
7073132943Sgshapiro	SOCKADDR addr;
7074132943Sgshapiro
7075132943Sgshapiro	if (tTd(38, 2))
7076132943Sgshapiro		sm_dprintf("socket_map_open(%s, %s, %d)\n",
7077132943Sgshapiro			map->map_mname, map->map_file, mode);
7078132943Sgshapiro
7079132943Sgshapiro	mode &= O_ACCMODE;
7080132943Sgshapiro
7081132943Sgshapiro	/* sendmail doesn't have the ability to write to SOCKET (yet) */
7082132943Sgshapiro	if (mode != O_RDONLY)
7083132943Sgshapiro	{
7084132943Sgshapiro		/* issue a pseudo-error message */
7085132943Sgshapiro		errno = SM_EMAPCANTWRITE;
7086132943Sgshapiro		return false;
7087132943Sgshapiro	}
7088132943Sgshapiro
7089132943Sgshapiro	if (*map->map_file == '\0')
7090132943Sgshapiro	{
7091132943Sgshapiro		syserr("socket map \"%s\": empty or missing socket information",
7092132943Sgshapiro			map->map_mname);
7093132943Sgshapiro		return false;
7094132943Sgshapiro	}
7095132943Sgshapiro
7096132943Sgshapiro	s = socket_map_findconn(map->map_file);
7097132943Sgshapiro	if (s->s_socketmap != NULL)
7098132943Sgshapiro	{
7099132943Sgshapiro		/* Copy open connection */
7100132943Sgshapiro		map->map_db1 = s->s_socketmap->map_db1;
7101132943Sgshapiro
7102132943Sgshapiro		/* Add this map as head of linked list */
7103132943Sgshapiro		map->socket_map_next = s->s_socketmap;
7104132943Sgshapiro		s->s_socketmap = map;
7105132943Sgshapiro
7106132943Sgshapiro		if (tTd(38, 2))
7107132943Sgshapiro			sm_dprintf("using cached connection\n");
7108132943Sgshapiro		return true;
7109132943Sgshapiro	}
7110132943Sgshapiro
7111132943Sgshapiro	if (tTd(38, 2))
7112132943Sgshapiro		sm_dprintf("opening new connection\n");
7113132943Sgshapiro
7114132943Sgshapiro	/* following code is ripped from milter.c */
7115132943Sgshapiro	/* XXX It should be put in a library... */
7116132943Sgshapiro
7117132943Sgshapiro	/* protocol:filename or protocol:port@host */
7118132943Sgshapiro	memset(&addr, '\0', sizeof addr);
7119132943Sgshapiro	p = map->map_file;
7120132943Sgshapiro	colon = strchr(p, ':');
7121132943Sgshapiro	if (colon != NULL)
7122132943Sgshapiro	{
7123132943Sgshapiro		*colon = '\0';
7124132943Sgshapiro
7125132943Sgshapiro		if (*p == '\0')
7126132943Sgshapiro		{
7127132943Sgshapiro# if NETUNIX
7128132943Sgshapiro			/* default to AF_UNIX */
7129132943Sgshapiro			addr.sa.sa_family = AF_UNIX;
7130132943Sgshapiro# else /* NETUNIX */
7131132943Sgshapiro#  if NETINET
7132132943Sgshapiro			/* default to AF_INET */
7133132943Sgshapiro			addr.sa.sa_family = AF_INET;
7134132943Sgshapiro#  else /* NETINET */
7135132943Sgshapiro#   if NETINET6
7136132943Sgshapiro			/* default to AF_INET6 */
7137132943Sgshapiro			addr.sa.sa_family = AF_INET6;
7138132943Sgshapiro#   else /* NETINET6 */
7139132943Sgshapiro			/* no protocols available */
7140132943Sgshapiro			syserr("socket map \"%s\": no valid socket protocols available",
7141132943Sgshapiro			map->map_mname);
7142132943Sgshapiro			return false;
7143132943Sgshapiro#   endif /* NETINET6 */
7144132943Sgshapiro#  endif /* NETINET */
7145132943Sgshapiro# endif /* NETUNIX */
7146132943Sgshapiro		}
7147132943Sgshapiro# if NETUNIX
7148132943Sgshapiro		else if (sm_strcasecmp(p, "unix") == 0 ||
7149132943Sgshapiro			 sm_strcasecmp(p, "local") == 0)
7150132943Sgshapiro			addr.sa.sa_family = AF_UNIX;
7151132943Sgshapiro# endif /* NETUNIX */
7152132943Sgshapiro# if NETINET
7153132943Sgshapiro		else if (sm_strcasecmp(p, "inet") == 0)
7154132943Sgshapiro			addr.sa.sa_family = AF_INET;
7155132943Sgshapiro# endif /* NETINET */
7156132943Sgshapiro# if NETINET6
7157132943Sgshapiro		else if (sm_strcasecmp(p, "inet6") == 0)
7158132943Sgshapiro			addr.sa.sa_family = AF_INET6;
7159132943Sgshapiro# endif /* NETINET6 */
7160132943Sgshapiro		else
7161132943Sgshapiro		{
7162132943Sgshapiro# ifdef EPROTONOSUPPORT
7163132943Sgshapiro			errno = EPROTONOSUPPORT;
7164132943Sgshapiro# else /* EPROTONOSUPPORT */
7165132943Sgshapiro			errno = EINVAL;
7166132943Sgshapiro# endif /* EPROTONOSUPPORT */
7167132943Sgshapiro			syserr("socket map \"%s\": unknown socket type %s",
7168132943Sgshapiro			       map->map_mname, p);
7169132943Sgshapiro			return false;
7170132943Sgshapiro		}
7171132943Sgshapiro		*colon++ = ':';
7172132943Sgshapiro	}
7173132943Sgshapiro	else
7174132943Sgshapiro	{
7175132943Sgshapiro		colon = p;
7176132943Sgshapiro#if NETUNIX
7177132943Sgshapiro		/* default to AF_UNIX */
7178132943Sgshapiro		addr.sa.sa_family = AF_UNIX;
7179132943Sgshapiro#else /* NETUNIX */
7180132943Sgshapiro# if NETINET
7181132943Sgshapiro		/* default to AF_INET */
7182132943Sgshapiro		addr.sa.sa_family = AF_INET;
7183132943Sgshapiro# else /* NETINET */
7184132943Sgshapiro#  if NETINET6
7185132943Sgshapiro		/* default to AF_INET6 */
7186132943Sgshapiro		addr.sa.sa_family = AF_INET6;
7187132943Sgshapiro#  else /* NETINET6 */
7188132943Sgshapiro		syserr("socket map \"%s\": unknown socket type %s",
7189132943Sgshapiro		       map->map_mname, p);
7190132943Sgshapiro		return false;
7191132943Sgshapiro#  endif /* NETINET6 */
7192132943Sgshapiro# endif /* NETINET */
7193132943Sgshapiro#endif /* NETUNIX */
7194132943Sgshapiro	}
7195132943Sgshapiro
7196132943Sgshapiro# if NETUNIX
7197132943Sgshapiro	if (addr.sa.sa_family == AF_UNIX)
7198132943Sgshapiro	{
7199132943Sgshapiro		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7200132943Sgshapiro
7201132943Sgshapiro		at = colon;
7202132943Sgshapiro		if (strlen(colon) >= sizeof addr.sunix.sun_path)
7203132943Sgshapiro		{
7204132943Sgshapiro			syserr("socket map \"%s\": local socket name %s too long",
7205132943Sgshapiro			       map->map_mname, colon);
7206132943Sgshapiro			return false;
7207132943Sgshapiro		}
7208132943Sgshapiro		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7209132943Sgshapiro				 S_IRUSR|S_IWUSR, NULL);
7210132943Sgshapiro
7211132943Sgshapiro		if (errno != 0)
7212132943Sgshapiro		{
7213132943Sgshapiro			/* if not safe, don't create */
7214132943Sgshapiro				syserr("socket map \"%s\": local socket name %s unsafe",
7215132943Sgshapiro			       map->map_mname, colon);
7216132943Sgshapiro			return false;
7217132943Sgshapiro		}
7218132943Sgshapiro
7219132943Sgshapiro		(void) sm_strlcpy(addr.sunix.sun_path, colon,
7220132943Sgshapiro			       sizeof addr.sunix.sun_path);
7221132943Sgshapiro		addrlen = sizeof (struct sockaddr_un);
7222132943Sgshapiro	}
7223132943Sgshapiro	else
7224132943Sgshapiro# endif /* NETUNIX */
7225132943Sgshapiro# if NETINET || NETINET6
7226132943Sgshapiro	if (false
7227132943Sgshapiro#  if NETINET
7228132943Sgshapiro		 || addr.sa.sa_family == AF_INET
7229132943Sgshapiro#  endif /* NETINET */
7230132943Sgshapiro#  if NETINET6
7231132943Sgshapiro		 || addr.sa.sa_family == AF_INET6
7232132943Sgshapiro#  endif /* NETINET6 */
7233132943Sgshapiro		 )
7234132943Sgshapiro	{
7235132943Sgshapiro		unsigned short port;
7236132943Sgshapiro
7237132943Sgshapiro		/* Parse port@host */
7238132943Sgshapiro		at = strchr(colon, '@');
7239132943Sgshapiro		if (at == NULL)
7240132943Sgshapiro		{
7241132943Sgshapiro			syserr("socket map \"%s\": bad address %s (expected port@host)",
7242132943Sgshapiro				       map->map_mname, colon);
7243132943Sgshapiro			return false;
7244132943Sgshapiro		}
7245132943Sgshapiro		*at = '\0';
7246132943Sgshapiro		if (isascii(*colon) && isdigit(*colon))
7247132943Sgshapiro			port = htons((unsigned short) atoi(colon));
7248132943Sgshapiro		else
7249132943Sgshapiro		{
7250132943Sgshapiro#  ifdef NO_GETSERVBYNAME
7251132943Sgshapiro			syserr("socket map \"%s\": invalid port number %s",
7252132943Sgshapiro				       map->map_mname, colon);
7253132943Sgshapiro			return false;
7254132943Sgshapiro#  else /* NO_GETSERVBYNAME */
7255132943Sgshapiro			register struct servent *sp;
7256132943Sgshapiro
7257132943Sgshapiro			sp = getservbyname(colon, "tcp");
7258132943Sgshapiro			if (sp == NULL)
7259132943Sgshapiro			{
7260132943Sgshapiro				syserr("socket map \"%s\": unknown port name %s",
7261132943Sgshapiro					       map->map_mname, colon);
7262132943Sgshapiro				return false;
7263132943Sgshapiro			}
7264132943Sgshapiro			port = sp->s_port;
7265132943Sgshapiro#  endif /* NO_GETSERVBYNAME */
7266132943Sgshapiro		}
7267132943Sgshapiro		*at++ = '@';
7268132943Sgshapiro		if (*at == '[')
7269132943Sgshapiro		{
7270132943Sgshapiro			char *end;
7271132943Sgshapiro
7272132943Sgshapiro			end = strchr(at, ']');
7273132943Sgshapiro			if (end != NULL)
7274132943Sgshapiro			{
7275132943Sgshapiro				bool found = false;
7276132943Sgshapiro#  if NETINET
7277132943Sgshapiro				unsigned long hid = INADDR_NONE;
7278132943Sgshapiro#  endif /* NETINET */
7279132943Sgshapiro#  if NETINET6
7280132943Sgshapiro				struct sockaddr_in6 hid6;
7281132943Sgshapiro#  endif /* NETINET6 */
7282132943Sgshapiro
7283132943Sgshapiro				*end = '\0';
7284132943Sgshapiro#  if NETINET
7285132943Sgshapiro				if (addr.sa.sa_family == AF_INET &&
7286132943Sgshapiro				    (hid = inet_addr(&at[1])) != INADDR_NONE)
7287132943Sgshapiro				{
7288132943Sgshapiro					addr.sin.sin_addr.s_addr = hid;
7289132943Sgshapiro					addr.sin.sin_port = port;
7290132943Sgshapiro					found = true;
7291132943Sgshapiro				}
7292132943Sgshapiro#  endif /* NETINET */
7293132943Sgshapiro#  if NETINET6
7294132943Sgshapiro				(void) memset(&hid6, '\0', sizeof hid6);
7295132943Sgshapiro				if (addr.sa.sa_family == AF_INET6 &&
7296132943Sgshapiro				    anynet_pton(AF_INET6, &at[1],
7297132943Sgshapiro						&hid6.sin6_addr) == 1)
7298132943Sgshapiro				{
7299132943Sgshapiro					addr.sin6.sin6_addr = hid6.sin6_addr;
7300132943Sgshapiro					addr.sin6.sin6_port = port;
7301132943Sgshapiro					found = true;
7302132943Sgshapiro				}
7303132943Sgshapiro#  endif /* NETINET6 */
7304132943Sgshapiro				*end = ']';
7305132943Sgshapiro				if (!found)
7306132943Sgshapiro				{
7307132943Sgshapiro					syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7308132943Sgshapiro					       map->map_mname, at);
7309132943Sgshapiro					return false;
7310132943Sgshapiro				}
7311132943Sgshapiro			}
7312132943Sgshapiro			else
7313132943Sgshapiro			{
7314132943Sgshapiro				syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7315132943Sgshapiro				       map->map_mname, at);
7316132943Sgshapiro				return false;
7317132943Sgshapiro			}
7318132943Sgshapiro		}
7319132943Sgshapiro		else
7320132943Sgshapiro		{
7321132943Sgshapiro			hp = sm_gethostbyname(at, addr.sa.sa_family);
7322132943Sgshapiro			if (hp == NULL)
7323132943Sgshapiro			{
7324132943Sgshapiro				syserr("socket map \"%s\": Unknown host name %s",
7325132943Sgshapiro					map->map_mname, at);
7326132943Sgshapiro				return false;
7327132943Sgshapiro			}
7328132943Sgshapiro			addr.sa.sa_family = hp->h_addrtype;
7329132943Sgshapiro			switch (hp->h_addrtype)
7330132943Sgshapiro			{
7331132943Sgshapiro#  if NETINET
7332132943Sgshapiro			  case AF_INET:
7333132943Sgshapiro				memmove(&addr.sin.sin_addr,
7334132943Sgshapiro					hp->h_addr, INADDRSZ);
7335132943Sgshapiro				addr.sin.sin_port = port;
7336132943Sgshapiro				addrlen = sizeof (struct sockaddr_in);
7337132943Sgshapiro				addrno = 1;
7338132943Sgshapiro				break;
7339132943Sgshapiro#  endif /* NETINET */
7340132943Sgshapiro
7341132943Sgshapiro#  if NETINET6
7342132943Sgshapiro			  case AF_INET6:
7343132943Sgshapiro				memmove(&addr.sin6.sin6_addr,
7344132943Sgshapiro					hp->h_addr, IN6ADDRSZ);
7345132943Sgshapiro				addr.sin6.sin6_port = port;
7346132943Sgshapiro				addrlen = sizeof (struct sockaddr_in6);
7347132943Sgshapiro				addrno = 1;
7348132943Sgshapiro				break;
7349132943Sgshapiro#  endif /* NETINET6 */
7350132943Sgshapiro
7351132943Sgshapiro			  default:
7352132943Sgshapiro				syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7353132943Sgshapiro					map->map_mname, at, hp->h_addrtype);
7354132943Sgshapiro#  if NETINET6
7355132943Sgshapiro				freehostent(hp);
7356132943Sgshapiro#  endif /* NETINET6 */
7357132943Sgshapiro				return false;
7358132943Sgshapiro			}
7359132943Sgshapiro		}
7360132943Sgshapiro	}
7361132943Sgshapiro	else
7362132943Sgshapiro# endif /* NETINET || NETINET6 */
7363132943Sgshapiro	{
7364132943Sgshapiro		syserr("socket map \"%s\": unknown socket protocol",
7365132943Sgshapiro			map->map_mname);
7366132943Sgshapiro		return false;
7367132943Sgshapiro	}
7368132943Sgshapiro
7369132943Sgshapiro	/* nope, actually connecting */
7370132943Sgshapiro	for (;;)
7371132943Sgshapiro	{
7372132943Sgshapiro		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
7373132943Sgshapiro		if (sock < 0)
7374132943Sgshapiro		{
7375132943Sgshapiro			save_errno = errno;
7376132943Sgshapiro			if (tTd(38, 5))
7377132943Sgshapiro				sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7378132943Sgshapiro					   map->map_mname,
7379132943Sgshapiro					   sm_errstring(save_errno));
7380132943Sgshapiro# if NETINET6
7381132943Sgshapiro			if (hp != NULL)
7382132943Sgshapiro				freehostent(hp);
7383132943Sgshapiro# endif /* NETINET6 */
7384132943Sgshapiro			return false;
7385132943Sgshapiro		}
7386132943Sgshapiro
7387132943Sgshapiro		if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
7388132943Sgshapiro			break;
7389132943Sgshapiro
7390132943Sgshapiro		/* couldn't connect.... try next address */
7391132943Sgshapiro		save_errno = errno;
7392132943Sgshapiro		p = CurHostName;
7393132943Sgshapiro		CurHostName = at;
7394132943Sgshapiro		if (tTd(38, 5))
7395132943Sgshapiro			sm_dprintf("socket_open (%s): open %s failed: %s\n",
7396132943Sgshapiro				map->map_mname, at, sm_errstring(save_errno));
7397132943Sgshapiro		CurHostName = p;
7398132943Sgshapiro		(void) close(sock);
7399132943Sgshapiro
7400132943Sgshapiro		/* try next address */
7401132943Sgshapiro		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
7402132943Sgshapiro		{
7403132943Sgshapiro			switch (addr.sa.sa_family)
7404132943Sgshapiro			{
7405132943Sgshapiro# if NETINET
7406132943Sgshapiro			  case AF_INET:
7407132943Sgshapiro				memmove(&addr.sin.sin_addr,
7408132943Sgshapiro					hp->h_addr_list[addrno++],
7409132943Sgshapiro					INADDRSZ);
7410132943Sgshapiro				break;
7411132943Sgshapiro# endif /* NETINET */
7412132943Sgshapiro
7413132943Sgshapiro# if NETINET6
7414132943Sgshapiro			  case AF_INET6:
7415132943Sgshapiro				memmove(&addr.sin6.sin6_addr,
7416132943Sgshapiro					hp->h_addr_list[addrno++],
7417132943Sgshapiro					IN6ADDRSZ);
7418132943Sgshapiro				break;
7419132943Sgshapiro# endif /* NETINET6 */
7420132943Sgshapiro
7421132943Sgshapiro			  default:
7422132943Sgshapiro				if (tTd(38, 5))
7423132943Sgshapiro					sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7424132943Sgshapiro						   map->map_mname, at,
7425132943Sgshapiro						   hp->h_addrtype);
7426132943Sgshapiro# if NETINET6
7427132943Sgshapiro				freehostent(hp);
7428132943Sgshapiro# endif /* NETINET6 */
7429132943Sgshapiro				return false;
7430132943Sgshapiro			}
7431132943Sgshapiro			continue;
7432132943Sgshapiro		}
7433132943Sgshapiro		p = CurHostName;
7434132943Sgshapiro		CurHostName = at;
7435132943Sgshapiro		if (tTd(38, 5))
7436132943Sgshapiro			sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7437132943Sgshapiro				   map->map_mname, sm_errstring(save_errno));
7438132943Sgshapiro		CurHostName = p;
7439132943Sgshapiro# if NETINET6
7440132943Sgshapiro		if (hp != NULL)
7441132943Sgshapiro			freehostent(hp);
7442132943Sgshapiro# endif /* NETINET6 */
7443132943Sgshapiro		return false;
7444132943Sgshapiro	}
7445132943Sgshapiro# if NETINET6
7446132943Sgshapiro	if (hp != NULL)
7447132943Sgshapiro	{
7448132943Sgshapiro		freehostent(hp);
7449132943Sgshapiro		hp = NULL;
7450132943Sgshapiro	}
7451132943Sgshapiro# endif /* NETINET6 */
7452132943Sgshapiro	if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
7453132943Sgshapiro						  SM_TIME_DEFAULT,
7454132943Sgshapiro						  (void *) &sock,
7455132943Sgshapiro						  SM_IO_RDWR,
7456132943Sgshapiro						  NULL)) == NULL)
7457132943Sgshapiro	{
7458132943Sgshapiro		close(sock);
7459132943Sgshapiro		if (tTd(38, 2))
7460132943Sgshapiro		    sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7461132943Sgshapiro			       map->map_mname, sm_errstring(errno));
7462132943Sgshapiro		return false;
7463132943Sgshapiro	}
7464132943Sgshapiro
7465132943Sgshapiro	/* Save connection for reuse */
7466132943Sgshapiro	s->s_socketmap = map;
7467132943Sgshapiro	return true;
7468132943Sgshapiro}
7469132943Sgshapiro
7470132943Sgshapiro/*
7471132943Sgshapiro**  SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7472132943Sgshapiro**
7473132943Sgshapiro**	Cache SOCKET connections based on the connection specifier
7474132943Sgshapiro**	and PID so we don't have multiple connections open to
7475132943Sgshapiro**	the same server for different maps.  Need a separate connection
7476132943Sgshapiro**	per PID since a parent process may close the map before the
7477132943Sgshapiro**	child is done with it.
7478132943Sgshapiro**
7479132943Sgshapiro**	Parameters:
7480132943Sgshapiro**		conn -- SOCKET map connection specifier
7481132943Sgshapiro**
7482132943Sgshapiro**	Returns:
7483132943Sgshapiro**		Symbol table entry for the SOCKET connection.
7484132943Sgshapiro*/
7485132943Sgshapiro
7486132943Sgshapirostatic STAB *
7487132943Sgshapirosocket_map_findconn(conn)
7488132943Sgshapiro	const char *conn;
7489132943Sgshapiro{
7490132943Sgshapiro	char *nbuf;
7491132943Sgshapiro	STAB *SM_NONVOLATILE s = NULL;
7492132943Sgshapiro
7493132943Sgshapiro	nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
7494132943Sgshapiro	SM_TRY
7495132943Sgshapiro		s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
7496132943Sgshapiro	SM_FINALLY
7497132943Sgshapiro		sm_free(nbuf);
7498132943Sgshapiro	SM_END_TRY
7499132943Sgshapiro	return s;
7500132943Sgshapiro}
7501132943Sgshapiro
7502132943Sgshapiro/*
7503132943Sgshapiro**  SOCKET_MAP_CLOSE -- close the socket
7504132943Sgshapiro*/
7505132943Sgshapiro
7506132943Sgshapirovoid
7507132943Sgshapirosocket_map_close(map)
7508132943Sgshapiro	MAP *map;
7509132943Sgshapiro{
7510132943Sgshapiro	STAB *s;
7511132943Sgshapiro	MAP *smap;
7512132943Sgshapiro
7513132943Sgshapiro	if (tTd(38, 20))
7514132943Sgshapiro		sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
7515132943Sgshapiro			(long) CurrentPid);
7516132943Sgshapiro
7517132943Sgshapiro	/* Check if already closed */
7518132943Sgshapiro	if (map->map_db1 == NULL)
7519132943Sgshapiro	{
7520132943Sgshapiro		if (tTd(38, 20))
7521132943Sgshapiro			sm_dprintf("socket_map_close(%s) already closed\n",
7522132943Sgshapiro				map->map_file);
7523132943Sgshapiro		return;
7524132943Sgshapiro	}
7525132943Sgshapiro	sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
7526132943Sgshapiro
7527132943Sgshapiro	/* Mark all the maps that share the connection as closed */
7528132943Sgshapiro	s = socket_map_findconn(map->map_file);
7529132943Sgshapiro	smap = s->s_socketmap;
7530132943Sgshapiro	while (smap != NULL)
7531132943Sgshapiro	{
7532132943Sgshapiro		MAP *next;
7533132943Sgshapiro
7534132943Sgshapiro		if (tTd(38, 2) && smap != map)
7535132943Sgshapiro			sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7536132943Sgshapiro				map->map_mname, smap->map_mname);
7537132943Sgshapiro
7538132943Sgshapiro		smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
7539132943Sgshapiro		smap->map_db1 = NULL;
7540132943Sgshapiro		next = smap->socket_map_next;
7541132943Sgshapiro		smap->socket_map_next = NULL;
7542132943Sgshapiro		smap = next;
7543132943Sgshapiro	}
7544132943Sgshapiro	s->s_socketmap = NULL;
7545132943Sgshapiro}
7546132943Sgshapiro
7547132943Sgshapiro/*
7548132943Sgshapiro** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7549132943Sgshapiro*/
7550132943Sgshapiro
7551132943Sgshapirochar *
7552132943Sgshapirosocket_map_lookup(map, name, av, statp)
7553132943Sgshapiro	MAP *map;
7554132943Sgshapiro	char *name;
7555132943Sgshapiro	char **av;
7556132943Sgshapiro	int *statp;
7557132943Sgshapiro{
7558132943Sgshapiro	unsigned int nettolen, replylen, recvlen;
7559147078Sgshapiro	char *replybuf, *rval, *value, *status, *key;
7560132943Sgshapiro	SM_FILE_T *f;
7561147078Sgshapiro	char keybuf[MAXNAME + 1];
7562132943Sgshapiro
7563132943Sgshapiro	replybuf = NULL;
7564132943Sgshapiro	rval = NULL;
7565132943Sgshapiro	f = (SM_FILE_T *)map->map_db1;
7566132943Sgshapiro	if (tTd(38, 20))
7567132943Sgshapiro		sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7568132943Sgshapiro			map->map_mname, name, map->map_file);
7569132943Sgshapiro
7570147078Sgshapiro	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7571147078Sgshapiro	{
7572147078Sgshapiro		nettolen = strlen(name);
7573147078Sgshapiro		if (nettolen > sizeof keybuf - 1)
7574147078Sgshapiro			nettolen = sizeof keybuf - 1;
7575147078Sgshapiro		memmove(keybuf, name, nettolen);
7576147078Sgshapiro		keybuf[nettolen] = '\0';
7577147078Sgshapiro		makelower(keybuf);
7578147078Sgshapiro		key = keybuf;
7579147078Sgshapiro	}
7580147078Sgshapiro	else
7581147078Sgshapiro		key = name;
7582147078Sgshapiro
7583147078Sgshapiro	nettolen = strlen(map->map_mname) + 1 + strlen(key);
7584132943Sgshapiro	SM_ASSERT(nettolen > strlen(map->map_mname));
7585147078Sgshapiro	SM_ASSERT(nettolen > strlen(key));
7586132943Sgshapiro	if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
7587147078Sgshapiro			   nettolen, map->map_mname, key) == SM_IO_EOF) ||
7588132943Sgshapiro	    (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
7589132943Sgshapiro	    (sm_io_error(f)))
7590132943Sgshapiro	{
7591132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
7592132943Sgshapiro			map->map_mname);
7593132943Sgshapiro		*statp = EX_TEMPFAIL;
7594132943Sgshapiro		goto errcl;
7595132943Sgshapiro	}
7596132943Sgshapiro
7597132943Sgshapiro	if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
7598132943Sgshapiro	{
7599132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
7600132943Sgshapiro			map->map_mname);
7601132943Sgshapiro		*statp = EX_TEMPFAIL;
7602132943Sgshapiro		goto errcl;
7603132943Sgshapiro	}
7604132943Sgshapiro	if (replylen > SOCKETMAP_MAXL)
7605132943Sgshapiro	{
7606132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
7607132943Sgshapiro			   map->map_mname, replylen);
7608132943Sgshapiro		*statp = EX_TEMPFAIL;
7609132943Sgshapiro		goto errcl;
7610132943Sgshapiro	}
7611132943Sgshapiro	if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
7612132943Sgshapiro	{
7613132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
7614132943Sgshapiro			map->map_mname);
7615132943Sgshapiro		*statp = EX_TEMPFAIL;
7616132943Sgshapiro		goto error;
7617132943Sgshapiro	}
7618132943Sgshapiro
7619132943Sgshapiro	replybuf = (char *) sm_malloc(replylen + 1);
7620132943Sgshapiro	if (replybuf == NULL)
7621132943Sgshapiro	{
7622132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
7623132943Sgshapiro			map->map_mname, replylen + 1);
7624132943Sgshapiro		*statp = EX_OSERR;
7625132943Sgshapiro		goto error;
7626132943Sgshapiro	}
7627132943Sgshapiro
7628132943Sgshapiro	recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
7629132943Sgshapiro	if (recvlen < replylen)
7630132943Sgshapiro	{
7631132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
7632132943Sgshapiro			   map->map_mname, recvlen, replylen);
7633132943Sgshapiro		*statp = EX_TEMPFAIL;
7634132943Sgshapiro		goto errcl;
7635132943Sgshapiro	}
7636132943Sgshapiro	if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
7637132943Sgshapiro	{
7638132943Sgshapiro		syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
7639132943Sgshapiro			map->map_mname);
7640132943Sgshapiro		*statp = EX_TEMPFAIL;
7641132943Sgshapiro		goto errcl;
7642132943Sgshapiro	}
7643132943Sgshapiro	status = replybuf;
7644132943Sgshapiro	replybuf[recvlen] = '\0';
7645132943Sgshapiro	value = strchr(replybuf, ' ');
7646132943Sgshapiro	if (value != NULL)
7647132943Sgshapiro	{
7648132943Sgshapiro		*value = '\0';
7649132943Sgshapiro		value++;
7650132943Sgshapiro	}
7651132943Sgshapiro	if (strcmp(status, "OK") == 0)
7652132943Sgshapiro	{
7653132943Sgshapiro		*statp = EX_OK;
7654132943Sgshapiro
7655132943Sgshapiro		/* collect the return value */
7656132943Sgshapiro		if (bitset(MF_MATCHONLY, map->map_mflags))
7657147078Sgshapiro			rval = map_rewrite(map, key, strlen(key), NULL);
7658132943Sgshapiro		else
7659132943Sgshapiro			rval = map_rewrite(map, value, strlen(value), av);
7660132943Sgshapiro	}
7661132943Sgshapiro	else if (strcmp(status, "NOTFOUND") == 0)
7662132943Sgshapiro	{
7663132943Sgshapiro		*statp = EX_NOTFOUND;
7664132943Sgshapiro		if (tTd(38, 20))
7665132943Sgshapiro			sm_dprintf("socket_map_lookup(%s): %s not found\n",
7666147078Sgshapiro				map->map_mname, key);
7667132943Sgshapiro	}
7668132943Sgshapiro	else
7669132943Sgshapiro	{
7670132943Sgshapiro		if (tTd(38, 5))
7671132943Sgshapiro			sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
7672147078Sgshapiro				map->map_mname, key, status,
7673132943Sgshapiro				value ? value : "");
7674132943Sgshapiro		if ((strcmp(status, "TEMP") == 0) ||
7675132943Sgshapiro		    (strcmp(status, "TIMEOUT") == 0))
7676132943Sgshapiro			*statp = EX_TEMPFAIL;
7677132943Sgshapiro		else if(strcmp(status, "PERM") == 0)
7678132943Sgshapiro			*statp = EX_UNAVAILABLE;
7679132943Sgshapiro		else
7680132943Sgshapiro			*statp = EX_PROTOCOL;
7681132943Sgshapiro	}
7682132943Sgshapiro
7683132943Sgshapiro	if (replybuf != NULL)
7684132943Sgshapiro		sm_free(replybuf);
7685132943Sgshapiro	return rval;
7686132943Sgshapiro
7687132943Sgshapiro  errcl:
7688132943Sgshapiro	socket_map_close(map);
7689132943Sgshapiro  error:
7690132943Sgshapiro	if (replybuf != NULL)
7691132943Sgshapiro		sm_free(replybuf);
7692132943Sgshapiro	return rval;
7693132943Sgshapiro}
7694132943Sgshapiro#endif /* SOCKETMAP */
7695