Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (c) 1998-2008 Sendmail, Inc. and its suppliers.
      3  *	All rights reserved.
      4  * Copyright (c) 1992, 1995-1997 Eric P. Allman.  All rights reserved.
      5  * Copyright (c) 1992, 1993
      6  *	The Regents of the University of California.  All rights reserved.
      7  *
      8  * By using this file, you agree to the terms and conditions set
      9  * forth in the LICENSE file which can be found at the top level of
     10  * the sendmail distribution.
     11  *
     12  */
     13 
     14 /*
     15  * Copyright 1996-2007 Sun Microsystems, Inc.  All rights reserved.
     16  * Use is subject to license terms.
     17  */
     18 
     19 #include <sendmail.h>
     20 
     21 SM_RCSID("@(#)$Id: map.c,v 8.705 2009/08/11 22:22:40 ca Exp $")
     22 
     23 #if LDAPMAP
     24 # include <sm/ldap.h>
     25 #endif /* LDAPMAP */
     26 
     27 #if NDBM
     28 # include <ndbm.h>
     29 # ifdef R_FIRST
     30   ERROR README:	You are running the Berkeley DB version of ndbm.h.  See
     31   ERROR README:	the README file about tweaking Berkeley DB so it can
     32   ERROR README:	coexist with NDBM, or delete -DNDBM from the Makefile
     33   ERROR README: and use -DNEWDB instead.
     34 # endif /* R_FIRST */
     35 #endif /* NDBM */
     36 #if NEWDB
     37 # include "sm/bdb.h"
     38 #endif /* NEWDB */
     39 #if NIS
     40   struct dom_binding;	/* forward reference needed on IRIX */
     41 # include <rpcsvc/ypclnt.h>
     42 # if NDBM
     43 #  define NDBM_YP_COMPAT	/* create YP-compatible NDBM files */
     44 # endif /* NDBM */
     45 #endif /* NIS */
     46 
     47 #include "map.h"
     48 
     49 #if NEWDB
     50 # if DB_VERSION_MAJOR < 2
     51 static bool	db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
     52 # endif /* DB_VERSION_MAJOR < 2 */
     53 # if DB_VERSION_MAJOR == 2
     54 static bool	db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
     55 # endif /* DB_VERSION_MAJOR == 2 */
     56 # if DB_VERSION_MAJOR > 2
     57 static bool	db_map_open __P((MAP *, int, char *, DBTYPE, void **));
     58 # endif /* DB_VERSION_MAJOR > 2 */
     59 #endif /* NEWDB */
     60 static bool	extract_canonname __P((char *, char *, char *, char[], int));
     61 static void	map_close __P((STAB *, int));
     62 static void	map_init __P((STAB *, int));
     63 #ifdef LDAPMAP
     64 static STAB *	ldapmap_findconn __P((SM_LDAP_STRUCT *));
     65 #endif /* LDAPMAP */
     66 #if NISPLUS
     67 static bool	nisplus_getcanonname __P((char *, int, int *));
     68 #endif /* NISPLUS */
     69 #if NIS
     70 static bool	nis_getcanonname __P((char *, int, int *));
     71 #endif /* NIS */
     72 #if NETINFO
     73 static bool	ni_getcanonname __P((char *, int, int *));
     74 #endif /* NETINFO */
     75 static bool	text_getcanonname __P((char *, int, int *));
     76 #if SOCKETMAP
     77 static STAB	*socket_map_findconn __P((const char*));
     78 
     79 /* XXX arbitrary limit for sanity */
     80 # define SOCKETMAP_MAXL 1000000
     81 #endif /* SOCKETMAP */
     82 
     83 /* default error message for trying to open a map in write mode */
     84 #ifdef ENOSYS
     85 # define SM_EMAPCANTWRITE	ENOSYS
     86 #else /* ENOSYS */
     87 # ifdef EFTYPE
     88 #  define SM_EMAPCANTWRITE	EFTYPE
     89 # else /* EFTYPE */
     90 #  define SM_EMAPCANTWRITE	ENXIO
     91 # endif /* EFTYPE */
     92 #endif /* ENOSYS */
     93 
     94 /*
     95 **  MAP.C -- implementations for various map classes.
     96 **
     97 **	Each map class implements a series of functions:
     98 **
     99 **	bool map_parse(MAP *map, char *args)
    100 **		Parse the arguments from the config file.  Return true
    101 **		if they were ok, false otherwise.  Fill in map with the
    102 **		values.
    103 **
    104 **	char *map_lookup(MAP *map, char *key, char **args, int *pstat)
    105 **		Look up the key in the given map.  If found, do any
    106 **		rewriting the map wants (including "args" if desired)
    107 **		and return the value.  Set *pstat to the appropriate status
    108 **		on error and return NULL.  Args will be NULL if called
    109 **		from the alias routines, although this should probably
    110 **		not be relied upon.  It is suggested you call map_rewrite
    111 **		to return the results -- it takes care of null termination
    112 **		and uses a dynamically expanded buffer as needed.
    113 **
    114 **	void map_store(MAP *map, char *key, char *value)
    115 **		Store the key:value pair in the map.
    116 **
    117 **	bool map_open(MAP *map, int mode)
    118 **		Open the map for the indicated mode.  Mode should
    119 **		be either O_RDONLY or O_RDWR.  Return true if it
    120 **		was opened successfully, false otherwise.  If the open
    121 **		failed and the MF_OPTIONAL flag is not set, it should
    122 **		also print an error.  If the MF_ALIAS bit is set
    123 **		and this map class understands the @:@ convention, it
    124 **		should call aliaswait() before returning.
    125 **
    126 **	void map_close(MAP *map)
    127 **		Close the map.
    128 **
    129 **	This file also includes the implementation for getcanonname.
    130 **	It is currently implemented in a pretty ad-hoc manner; it ought
    131 **	to be more properly integrated into the map structure.
    132 */
    133 
    134 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
    135 # define LOCK_ON_OPEN	1	/* we can open/create a locked file */
    136 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
    137 # define LOCK_ON_OPEN	0	/* no such luck -- bend over backwards */
    138 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
    139 
    140 /*
    141 **  MAP_PARSEARGS -- parse config line arguments for database lookup
    142 **
    143 **	This is a generic version of the map_parse method.
    144 **
    145 **	Parameters:
    146 **		map -- the map being initialized.
    147 **		ap -- a pointer to the args on the config line.
    148 **
    149 **	Returns:
    150 **		true -- if everything parsed OK.
    151 **		false -- otherwise.
    152 **
    153 **	Side Effects:
    154 **		null terminates the filename; stores it in map
    155 */
    156 
    157 bool
    158 map_parseargs(map, ap)
    159 	MAP *map;
    160 	char *ap;
    161 {
    162 	register char *p = ap;
    163 
    164 	/*
    165 	**  There is no check whether there is really an argument,
    166 	**  but that's not important enough to warrant extra code.
    167 	*/
    168 
    169 	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
    170 	map->map_spacesub = SpaceSub;	/* default value */
    171 	for (;;)
    172 	{
    173 		while (isascii(*p) && isspace(*p))
    174 			p++;
    175 		if (*p != '-')
    176 			break;
    177 		switch (*++p)
    178 		{
    179 		  case 'N':
    180 			map->map_mflags |= MF_INCLNULL;
    181 			map->map_mflags &= ~MF_TRY0NULL;
    182 			break;
    183 
    184 		  case 'O':
    185 			map->map_mflags &= ~MF_TRY1NULL;
    186 			break;
    187 
    188 		  case 'o':
    189 			map->map_mflags |= MF_OPTIONAL;
    190 			break;
    191 
    192 		  case 'f':
    193 			map->map_mflags |= MF_NOFOLDCASE;
    194 			break;
    195 
    196 		  case 'm':
    197 			map->map_mflags |= MF_MATCHONLY;
    198 			break;
    199 
    200 		  case 'A':
    201 			map->map_mflags |= MF_APPEND;
    202 			break;
    203 
    204 		  case 'q':
    205 			map->map_mflags |= MF_KEEPQUOTES;
    206 			break;
    207 
    208 		  case 'a':
    209 			map->map_app = ++p;
    210 			break;
    211 
    212 		  case 'T':
    213 			map->map_tapp = ++p;
    214 			break;
    215 
    216 		  case 'k':
    217 			while (isascii(*++p) && isspace(*p))
    218 				continue;
    219 			map->map_keycolnm = p;
    220 			break;
    221 
    222 		  case 'v':
    223 			while (isascii(*++p) && isspace(*p))
    224 				continue;
    225 			map->map_valcolnm = p;
    226 			break;
    227 
    228 		  case 'z':
    229 			if (*++p != '\\')
    230 				map->map_coldelim = *p;
    231 			else
    232 			{
    233 				switch (*++p)
    234 				{
    235 				  case 'n':
    236 					map->map_coldelim = '\n';
    237 					break;
    238 
    239 				  case 't':
    240 					map->map_coldelim = '\t';
    241 					break;
    242 
    243 				  default:
    244 					map->map_coldelim = '\\';
    245 				}
    246 			}
    247 			break;
    248 
    249 		  case 't':
    250 			map->map_mflags |= MF_NODEFER;
    251 			break;
    252 
    253 
    254 		  case 'S':
    255 			map->map_spacesub = *++p;
    256 			break;
    257 
    258 		  case 'D':
    259 			map->map_mflags |= MF_DEFER;
    260 			break;
    261 
    262 		  default:
    263 			syserr("Illegal option %c map %s", *p, map->map_mname);
    264 			break;
    265 		}
    266 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
    267 			p++;
    268 		if (*p != '\0')
    269 			*p++ = '\0';
    270 	}
    271 	if (map->map_app != NULL)
    272 		map->map_app = newstr(map->map_app);
    273 	if (map->map_tapp != NULL)
    274 		map->map_tapp = newstr(map->map_tapp);
    275 	if (map->map_keycolnm != NULL)
    276 		map->map_keycolnm = newstr(map->map_keycolnm);
    277 	if (map->map_valcolnm != NULL)
    278 		map->map_valcolnm = newstr(map->map_valcolnm);
    279 
    280 	if (*p != '\0')
    281 	{
    282 		map->map_file = p;
    283 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
    284 			p++;
    285 		if (*p != '\0')
    286 			*p++ = '\0';
    287 		map->map_file = newstr(map->map_file);
    288 	}
    289 
    290 	while (*p != '\0' && isascii(*p) && isspace(*p))
    291 		p++;
    292 	if (*p != '\0')
    293 		map->map_rebuild = newstr(p);
    294 
    295 	if (map->map_file == NULL &&
    296 	    !bitset(MCF_OPTFILE, map->map_class->map_cflags))
    297 	{
    298 		syserr("No file name for %s map %s",
    299 			map->map_class->map_cname, map->map_mname);
    300 		return false;
    301 	}
    302 	return true;
    303 }
    304 /*
    305 **  MAP_REWRITE -- rewrite a database key, interpolating %n indications.
    306 **
    307 **	It also adds the map_app string.  It can be used as a utility
    308 **	in the map_lookup method.
    309 **
    310 **	Parameters:
    311 **		map -- the map that causes this.
    312 **		s -- the string to rewrite, NOT necessarily null terminated.
    313 **		slen -- the length of s.
    314 **		av -- arguments to interpolate into buf.
    315 **
    316 **	Returns:
    317 **		Pointer to rewritten result.  This is static data that
    318 **		should be copied if it is to be saved!
    319 */
    320 
    321 char *
    322 map_rewrite(map, s, slen, av)
    323 	register MAP *map;
    324 	register const char *s;
    325 	size_t slen;
    326 	char **av;
    327 {
    328 	register char *bp;
    329 	register char c;
    330 	char **avp;
    331 	register char *ap;
    332 	size_t l;
    333 	size_t len;
    334 	static size_t buflen = 0;
    335 	static char *buf = NULL;
    336 
    337 	if (tTd(39, 1))
    338 	{
    339 		sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
    340 		if (av == NULL)
    341 			sm_dprintf(" (nullv)");
    342 		else
    343 		{
    344 			for (avp = av; *avp != NULL; avp++)
    345 				sm_dprintf("\n\t%s", *avp);
    346 		}
    347 		sm_dprintf("\n");
    348 	}
    349 
    350 	/* count expected size of output (can safely overestimate) */
    351 	l = len = slen;
    352 	if (av != NULL)
    353 	{
    354 		const char *sp = s;
    355 
    356 		while (l-- > 0 && (c = *sp++) != '\0')
    357 		{
    358 			if (c != '%')
    359 				continue;
    360 			if (l-- <= 0)
    361 				break;
    362 			c = *sp++;
    363 			if (!(isascii(c) && isdigit(c)))
    364 				continue;
    365 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
    366 				continue;
    367 			if (*avp == NULL)
    368 				continue;
    369 			len += strlen(*avp);
    370 		}
    371 	}
    372 	if (map->map_app != NULL)
    373 		len += strlen(map->map_app);
    374 	if (buflen < ++len)
    375 	{
    376 		/* need to malloc additional space */
    377 		buflen = len;
    378 		if (buf != NULL)
    379 			sm_free(buf);
    380 		buf = sm_pmalloc_x(buflen);
    381 	}
    382 
    383 	bp = buf;
    384 	if (av == NULL)
    385 	{
    386 		memmove(bp, s, slen);
    387 		bp += slen;
    388 
    389 		/* assert(len > slen); */
    390 		len -= slen;
    391 	}
    392 	else
    393 	{
    394 		while (slen-- > 0 && (c = *s++) != '\0')
    395 		{
    396 			if (c != '%')
    397 			{
    398   pushc:
    399 				if (len-- <= 1)
    400 				     break;
    401 				*bp++ = c;
    402 				continue;
    403 			}
    404 			if (slen-- <= 0 || (c = *s++) == '\0')
    405 				c = '%';
    406 			if (c == '%')
    407 				goto pushc;
    408 			if (!(isascii(c) && isdigit(c)))
    409 			{
    410 				if (len-- <= 1)
    411 				     break;
    412 				*bp++ = '%';
    413 				goto pushc;
    414 			}
    415 			for (avp = av; --c >= '0' && *avp != NULL; avp++)
    416 				continue;
    417 			if (*avp == NULL)
    418 				continue;
    419 
    420 			/* transliterate argument into output string */
    421 			for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
    422 				*bp++ = c;
    423 		}
    424 	}
    425 	if (map->map_app != NULL && len > 0)
    426 		(void) sm_strlcpy(bp, map->map_app, len);
    427 	else
    428 		*bp = '\0';
    429 	if (tTd(39, 1))
    430 		sm_dprintf("map_rewrite => %s\n", buf);
    431 	return buf;
    432 }
    433 /*
    434 **  INITMAPS -- rebuild alias maps
    435 **
    436 **	Parameters:
    437 **		none.
    438 **
    439 **	Returns:
    440 **		none.
    441 */
    442 
    443 void
    444 initmaps()
    445 {
    446 #if XDEBUG
    447 	checkfd012("entering initmaps");
    448 #endif /* XDEBUG */
    449 	stabapply(map_init, 0);
    450 #if XDEBUG
    451 	checkfd012("exiting initmaps");
    452 #endif /* XDEBUG */
    453 }
    454 /*
    455 **  MAP_INIT -- rebuild a map
    456 **
    457 **	Parameters:
    458 **		s -- STAB entry: if map: try to rebuild
    459 **		unused -- unused variable
    460 **
    461 **	Returns:
    462 **		none.
    463 **
    464 **	Side Effects:
    465 **		will close already open rebuildable map.
    466 */
    467 
    468 /* ARGSUSED1 */
    469 static void
    470 map_init(s, unused)
    471 	register STAB *s;
    472 	int unused;
    473 {
    474 	register MAP *map;
    475 
    476 	/* has to be a map */
    477 	if (s->s_symtype != ST_MAP)
    478 		return;
    479 
    480 	map = &s->s_map;
    481 	if (!bitset(MF_VALID, map->map_mflags))
    482 		return;
    483 
    484 	if (tTd(38, 2))
    485 		sm_dprintf("map_init(%s:%s, %s)\n",
    486 			map->map_class->map_cname == NULL ? "NULL" :
    487 				map->map_class->map_cname,
    488 			map->map_mname == NULL ? "NULL" : map->map_mname,
    489 			map->map_file == NULL ? "NULL" : map->map_file);
    490 
    491 	if (!bitset(MF_ALIAS, map->map_mflags) ||
    492 	    !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
    493 	{
    494 		if (tTd(38, 3))
    495 			sm_dprintf("\tnot rebuildable\n");
    496 		return;
    497 	}
    498 
    499 	/* if already open, close it (for nested open) */
    500 	if (bitset(MF_OPEN, map->map_mflags))
    501 	{
    502 		map->map_mflags |= MF_CLOSING;
    503 		map->map_class->map_close(map);
    504 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
    505 	}
    506 
    507 	(void) rebuildaliases(map, false);
    508 	return;
    509 }
    510 /*
    511 **  OPENMAP -- open a map
    512 **
    513 **	Parameters:
    514 **		map -- map to open (it must not be open).
    515 **
    516 **	Returns:
    517 **		whether open succeeded.
    518 */
    519 
    520 bool
    521 openmap(map)
    522 	MAP *map;
    523 {
    524 	bool restore = false;
    525 	bool savehold = HoldErrs;
    526 	bool savequick = QuickAbort;
    527 	int saveerrors = Errors;
    528 
    529 	if (!bitset(MF_VALID, map->map_mflags))
    530 		return false;
    531 
    532 	/* better safe than sorry... */
    533 	if (bitset(MF_OPEN, map->map_mflags))
    534 		return true;
    535 
    536 	/* Don't send a map open error out via SMTP */
    537 	if ((OnlyOneError || QuickAbort) &&
    538 	    (OpMode == MD_SMTP || OpMode == MD_DAEMON))
    539 	{
    540 		restore = true;
    541 		HoldErrs = true;
    542 		QuickAbort = false;
    543 	}
    544 
    545 	errno = 0;
    546 	if (map->map_class->map_open(map, O_RDONLY))
    547 	{
    548 		if (tTd(38, 4))
    549 			sm_dprintf("openmap()\t%s:%s %s: valid\n",
    550 				map->map_class->map_cname == NULL ? "NULL" :
    551 					map->map_class->map_cname,
    552 				map->map_mname == NULL ? "NULL" :
    553 					map->map_mname,
    554 				map->map_file == NULL ? "NULL" :
    555 					map->map_file);
    556 		map->map_mflags |= MF_OPEN;
    557 		map->map_pid = CurrentPid;
    558 	}
    559 	else
    560 	{
    561 		if (tTd(38, 4))
    562 			sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
    563 				map->map_class->map_cname == NULL ? "NULL" :
    564 					map->map_class->map_cname,
    565 				map->map_mname == NULL ? "NULL" :
    566 					map->map_mname,
    567 				map->map_file == NULL ? "NULL" :
    568 					map->map_file,
    569 				errno == 0 ? "" : ": ",
    570 				errno == 0 ? "" : sm_errstring(errno));
    571 		if (!bitset(MF_OPTIONAL, map->map_mflags))
    572 		{
    573 			extern MAPCLASS BogusMapClass;
    574 
    575 			map->map_orgclass = map->map_class;
    576 			map->map_class = &BogusMapClass;
    577 			map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
    578 			map->map_pid = CurrentPid;
    579 		}
    580 		else
    581 		{
    582 			/* don't try again */
    583 			map->map_mflags &= ~MF_VALID;
    584 		}
    585 	}
    586 
    587 	if (restore)
    588 	{
    589 		Errors = saveerrors;
    590 		HoldErrs = savehold;
    591 		QuickAbort = savequick;
    592 	}
    593 
    594 	return bitset(MF_OPEN, map->map_mflags);
    595 }
    596 /*
    597 **  CLOSEMAPS -- close all open maps opened by the current pid.
    598 **
    599 **	Parameters:
    600 **		bogus -- only close bogus maps.
    601 **
    602 **	Returns:
    603 **		none.
    604 */
    605 
    606 void
    607 closemaps(bogus)
    608 	bool bogus;
    609 {
    610 	stabapply(map_close, bogus);
    611 }
    612 /*
    613 **  MAP_CLOSE -- close a map opened by the current pid.
    614 **
    615 **	Parameters:
    616 **		s -- STAB entry: if map: try to close
    617 **		bogus -- only close bogus maps or MCF_NOTPERSIST maps.
    618 **
    619 **	Returns:
    620 **		none.
    621 */
    622 
    623 /* ARGSUSED1 */
    624 static void
    625 map_close(s, bogus)
    626 	register STAB *s;
    627 	int bogus;	/* int because of stabapply(), used as bool */
    628 {
    629 	MAP *map;
    630 	extern MAPCLASS BogusMapClass;
    631 
    632 	if (s->s_symtype != ST_MAP)
    633 		return;
    634 
    635 	map = &s->s_map;
    636 
    637 	/*
    638 	**  close the map iff:
    639 	**  it is valid and open and opened by this process
    640 	**  and (!bogus or it's a bogus map or it is not persistent)
    641 	**  negate this: return iff
    642 	**  it is not valid or it is not open or not opened by this process
    643 	**  or (bogus and it's not a bogus map and it's not not-persistent)
    644 	*/
    645 
    646 	if (!bitset(MF_VALID, map->map_mflags) ||
    647 	    !bitset(MF_OPEN, map->map_mflags) ||
    648 	    bitset(MF_CLOSING, map->map_mflags) ||
    649 	    map->map_pid != CurrentPid ||
    650 	    (bogus && map->map_class != &BogusMapClass &&
    651 	     !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
    652 		return;
    653 
    654 	if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
    655 	    map->map_orgclass != &BogusMapClass)
    656 		map->map_class = map->map_orgclass;
    657 	if (tTd(38, 5))
    658 		sm_dprintf("closemaps: closing %s (%s)\n",
    659 			map->map_mname == NULL ? "NULL" : map->map_mname,
    660 			map->map_file == NULL ? "NULL" : map->map_file);
    661 
    662 	if (!bitset(MF_OPENBOGUS, map->map_mflags))
    663 	{
    664 		map->map_mflags |= MF_CLOSING;
    665 		map->map_class->map_close(map);
    666 	}
    667 	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
    668 }
    669 
    670 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
    671 extern int getdomainname();
    672 
    673 /* this is mainly for backward compatibility in Sun environment */
    674 static char *
    675 sun_init_domain()
    676 {
    677 	/*
    678 	**  Get the domain name from the kernel.
    679 	**  If it does not start with a leading dot, then remove
    680 	**  the first component.  Since leading dots are funny Unix
    681 	**  files, we treat a leading "+" the same as a leading dot.
    682 	**  Finally, force there to be at least one dot in the domain name
    683 	**  (i.e. top-level domains are not allowed, like "com", must be
    684 	**  something like "sun.com").
    685 	*/
    686 
    687 	char buf[MAXNAME];
    688 	char *period, *autodomain;
    689 
    690 	if (getdomainname(buf, sizeof buf) < 0)
    691 		return NULL;
    692 
    693 	if (buf[0] == '\0')
    694 		return NULL;
    695 
    696 	if (tTd(0, 20))
    697 		printf("domainname = %s\n", buf);
    698 
    699 	if (buf[0] == '+')
    700 		buf[0] = '.';
    701 	period = strchr(buf, '.');
    702 	if (period == NULL)
    703 		autodomain = buf;
    704 	else
    705 		autodomain = period + 1;
    706 	if (strchr(autodomain, '.') == NULL)
    707 		return newstr(buf);
    708 	else
    709 		return newstr(autodomain);
    710 }
    711 #endif /* SUN_EXTENSIONS && SUN_INIT_DOMAIN */
    712 
    713 /*
    714 **  GETCANONNAME -- look up name using service switch
    715 **
    716 **	Parameters:
    717 **		host -- the host name to look up.
    718 **		hbsize -- the size of the host buffer.
    719 **		trymx -- if set, try MX records.
    720 **		pttl -- pointer to return TTL (can be NULL).
    721 **
    722 **	Returns:
    723 **		true -- if the host was found.
    724 **		false -- otherwise.
    725 */
    726 
    727 bool
    728 getcanonname(host, hbsize, trymx, pttl)
    729 	char *host;
    730 	int hbsize;
    731 	bool trymx;
    732 	int *pttl;
    733 {
    734 	int nmaps;
    735 	int mapno;
    736 	bool found = false;
    737 	bool got_tempfail = false;
    738 	auto int status = EX_UNAVAILABLE;
    739 	char *maptype[MAXMAPSTACK];
    740 	short mapreturn[MAXMAPACTIONS];
    741 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
    742 	bool should_try_nis_domain = false;
    743 	static char *nis_domain = NULL;
    744 #endif
    745 
    746 	nmaps = switch_map_find("hosts", maptype, mapreturn);
    747 	if (pttl != 0)
    748 		*pttl = SM_DEFAULT_TTL;
    749 	for (mapno = 0; mapno < nmaps; mapno++)
    750 	{
    751 		int i;
    752 
    753 		if (tTd(38, 20))
    754 			sm_dprintf("getcanonname(%s), trying %s\n",
    755 				host, maptype[mapno]);
    756 		if (strcmp("files", maptype[mapno]) == 0)
    757 		{
    758 			found = text_getcanonname(host, hbsize, &status);
    759 		}
    760 #if NIS
    761 		else if (strcmp("nis", maptype[mapno]) == 0)
    762 		{
    763 			found = nis_getcanonname(host, hbsize, &status);
    764 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
    765 			if (nis_domain == NULL)
    766 				nis_domain = sun_init_domain();
    767 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
    768 		}
    769 #endif /* NIS */
    770 #if NISPLUS
    771 		else if (strcmp("nisplus", maptype[mapno]) == 0)
    772 		{
    773 			found = nisplus_getcanonname(host, hbsize, &status);
    774 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
    775 			if (nis_domain == NULL)
    776 				nis_domain = sun_init_domain();
    777 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
    778 		}
    779 #endif /* NISPLUS */
    780 #if NAMED_BIND
    781 		else if (strcmp("dns", maptype[mapno]) == 0)
    782 		{
    783 			found = dns_getcanonname(host, hbsize, trymx, &status,							 pttl);
    784 		}
    785 #endif /* NAMED_BIND */
    786 #if NETINFO
    787 		else if (strcmp("netinfo", maptype[mapno]) == 0)
    788 		{
    789 			found = ni_getcanonname(host, hbsize, &status);
    790 		}
    791 #endif /* NETINFO */
    792 		else
    793 		{
    794 			found = false;
    795 			status = EX_UNAVAILABLE;
    796 		}
    797 
    798 		/*
    799 		**  Heuristic: if $m is not set, we are running during system
    800 		**  startup.  In this case, when a name is apparently found
    801 		**  but has no dot, treat is as not found.  This avoids
    802 		**  problems if /etc/hosts has no FQDN but is listed first
    803 		**  in the service switch.
    804 		*/
    805 
    806 		if (found &&
    807 		    (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
    808 			break;
    809 
    810 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
    811 		if (found)
    812 			should_try_nis_domain = true;
    813 		/* but don't break, as we need to try all methods first */
    814 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
    815 
    816 		/* see if we should continue */
    817 		if (status == EX_TEMPFAIL)
    818 		{
    819 			i = MA_TRYAGAIN;
    820 			got_tempfail = true;
    821 		}
    822 		else if (status == EX_NOTFOUND)
    823 			i = MA_NOTFOUND;
    824 		else
    825 			i = MA_UNAVAIL;
    826 		if (bitset(1 << mapno, mapreturn[i]))
    827 			break;
    828 	}
    829 
    830 	if (found)
    831 	{
    832 		char *d;
    833 
    834 		if (tTd(38, 20))
    835 			sm_dprintf("getcanonname(%s), found\n", host);
    836 
    837 		/*
    838 		**  If returned name is still single token, compensate
    839 		**  by tagging on $m.  This is because some sites set
    840 		**  up their DNS or NIS databases wrong.
    841 		*/
    842 
    843 		if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
    844 		{
    845 			d = macvalue('m', CurEnv);
    846 			if (d != NULL &&
    847 			    hbsize > (int) (strlen(host) + strlen(d) + 1))
    848 			{
    849 				if (host[strlen(host) - 1] != '.')
    850 					(void) sm_strlcat2(host, ".", d,
    851 							   hbsize);
    852 				else
    853 					(void) sm_strlcat(host, d, hbsize);
    854 			}
    855 			else
    856 			{
    857 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
    858 				if (VendorCode == VENDOR_SUN &&
    859 				    should_try_nis_domain)
    860 				{
    861 					goto try_nis_domain;
    862 				}
    863 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
    864 				return false;
    865 			}
    866 		}
    867 		return true;
    868 	}
    869 
    870 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
    871 	if (VendorCode == VENDOR_SUN && should_try_nis_domain)
    872 	{
    873   try_nis_domain:
    874 		if (nis_domain != NULL &&
    875 		    strlen(nis_domain) + strlen(host) + 1 < hbsize)
    876 		{
    877 			(void) sm_strlcat2(host, ".", nis_domain, hbsize);
    878 			return true;
    879 		}
    880 	}
    881 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
    882 
    883 	if (tTd(38, 20))
    884 		sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
    885 			status);
    886 
    887 	if (got_tempfail)
    888 		SM_SET_H_ERRNO(TRY_AGAIN);
    889 	else
    890 		SM_SET_H_ERRNO(HOST_NOT_FOUND);
    891 
    892 	return false;
    893 }
    894 /*
    895 **  EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
    896 **
    897 **	Parameters:
    898 **		name -- the name against which to match.
    899 **		dot -- where to reinsert '.' to get FQDN
    900 **		line -- the /etc/hosts line.
    901 **		cbuf -- the location to store the result.
    902 **		cbuflen -- the size of cbuf.
    903 **
    904 **	Returns:
    905 **		true -- if the line matched the desired name.
    906 **		false -- otherwise.
    907 */
    908 
    909 static bool
    910 extract_canonname(name, dot, line, cbuf, cbuflen)
    911 	char *name;
    912 	char *dot;
    913 	char *line;
    914 	char cbuf[];
    915 	int cbuflen;
    916 {
    917 	int i;
    918 	char *p;
    919 	bool found = false;
    920 
    921 	cbuf[0] = '\0';
    922 	if (line[0] == '#')
    923 		return false;
    924 
    925 	for (i = 1; ; i++)
    926 	{
    927 		char nbuf[MAXNAME + 1];
    928 
    929 		p = get_column(line, i, '\0', nbuf, sizeof(nbuf));
    930 		if (p == NULL)
    931 			break;
    932 		if (*p == '\0')
    933 			continue;
    934 		if (cbuf[0] == '\0' ||
    935 		    (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
    936 		{
    937 			(void) sm_strlcpy(cbuf, p, cbuflen);
    938 		}
    939 		if (sm_strcasecmp(name, p) == 0)
    940 			found = true;
    941 		else if (dot != NULL)
    942 		{
    943 			/* try looking for the FQDN as well */
    944 			*dot = '.';
    945 			if (sm_strcasecmp(name, p) == 0)
    946 				found = true;
    947 			*dot = '\0';
    948 		}
    949 	}
    950 	if (found && strchr(cbuf, '.') == NULL)
    951 	{
    952 		/* try to add a domain on the end of the name */
    953 		char *domain = macvalue('m', CurEnv);
    954 
    955 		if (domain != NULL &&
    956 		    strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
    957 		{
    958 			p = &cbuf[i];
    959 			*p++ = '.';
    960 			(void) sm_strlcpy(p, domain, cbuflen - i - 1);
    961 		}
    962 	}
    963 	return found;
    964 }
    965 
    966 /*
    967 **  DNS modules
    968 */
    969 
    970 #if NAMED_BIND
    971 # if DNSMAP
    972 
    973 #  include "sm_resolve.h"
    974 #  if NETINET || NETINET6
    975 #   include <arpa/inet.h>
    976 #  endif /* NETINET || NETINET6 */
    977 
    978 /*
    979 **  DNS_MAP_OPEN -- stub to check proper value for dns map type
    980 */
    981 
    982 bool
    983 dns_map_open(map, mode)
    984 	MAP *map;
    985 	int mode;
    986 {
    987 	if (tTd(38,2))
    988 		sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
    989 
    990 	mode &= O_ACCMODE;
    991 	if (mode != O_RDONLY)
    992 	{
    993 		/* issue a pseudo-error message */
    994 		errno = SM_EMAPCANTWRITE;
    995 		return false;
    996 	}
    997 	return true;
    998 }
    999 
   1000 /*
   1001 **  DNS_MAP_PARSEARGS -- parse dns map definition args.
   1002 **
   1003 **	Parameters:
   1004 **		map -- pointer to MAP
   1005 **		args -- pointer to the args on the config line.
   1006 **
   1007 **	Returns:
   1008 **		true -- if everything parsed OK.
   1009 **		false -- otherwise.
   1010 */
   1011 
   1012 #define map_sizelimit	map_lockfd	/* overload field */
   1013 
   1014 struct dns_map
   1015 {
   1016 	int dns_m_type;
   1017 };
   1018 
   1019 bool
   1020 dns_map_parseargs(map,args)
   1021 	MAP *map;
   1022 	char *args;
   1023 {
   1024 	register char *p = args;
   1025 	struct dns_map *map_p;
   1026 
   1027 	map_p = (struct dns_map *) xalloc(sizeof(*map_p));
   1028 	map_p->dns_m_type = -1;
   1029 	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
   1030 
   1031 	for (;;)
   1032 	{
   1033 		while (isascii(*p) && isspace(*p))
   1034 			p++;
   1035 		if (*p != '-')
   1036 			break;
   1037 		switch (*++p)
   1038 		{
   1039 		  case 'N':
   1040 			map->map_mflags |= MF_INCLNULL;
   1041 			map->map_mflags &= ~MF_TRY0NULL;
   1042 			break;
   1043 
   1044 		  case 'O':
   1045 			map->map_mflags &= ~MF_TRY1NULL;
   1046 			break;
   1047 
   1048 		  case 'o':
   1049 			map->map_mflags |= MF_OPTIONAL;
   1050 			break;
   1051 
   1052 		  case 'f':
   1053 			map->map_mflags |= MF_NOFOLDCASE;
   1054 			break;
   1055 
   1056 		  case 'm':
   1057 			map->map_mflags |= MF_MATCHONLY;
   1058 			break;
   1059 
   1060 		  case 'A':
   1061 			map->map_mflags |= MF_APPEND;
   1062 			break;
   1063 
   1064 		  case 'q':
   1065 			map->map_mflags |= MF_KEEPQUOTES;
   1066 			break;
   1067 
   1068 		  case 't':
   1069 			map->map_mflags |= MF_NODEFER;
   1070 			break;
   1071 
   1072 		  case 'a':
   1073 			map->map_app = ++p;
   1074 			break;
   1075 
   1076 		  case 'T':
   1077 			map->map_tapp = ++p;
   1078 			break;
   1079 
   1080 		  case 'd':
   1081 			{
   1082 				char *h;
   1083 
   1084 				++p;
   1085 				h = strchr(p, ' ');
   1086 				if (h != NULL)
   1087 					*h = '\0';
   1088 				map->map_timeout = convtime(p, 's');
   1089 				if (h != NULL)
   1090 					*h = ' ';
   1091 			}
   1092 			break;
   1093 
   1094 		  case 'r':
   1095 			while (isascii(*++p) && isspace(*p))
   1096 				continue;
   1097 			map->map_retry = atoi(p);
   1098 			break;
   1099 
   1100 		  case 'z':
   1101 			if (*++p != '\\')
   1102 				map->map_coldelim = *p;
   1103 			else
   1104 			{
   1105 				switch (*++p)
   1106 				{
   1107 				  case 'n':
   1108 					map->map_coldelim = '\n';
   1109 					break;
   1110 
   1111 				  case 't':
   1112 					map->map_coldelim = '\t';
   1113 					break;
   1114 
   1115 				  default:
   1116 					map->map_coldelim = '\\';
   1117 				}
   1118 			}
   1119 			break;
   1120 
   1121 		  case 'Z':
   1122 			while (isascii(*++p) && isspace(*p))
   1123 				continue;
   1124 			map->map_sizelimit = atoi(p);
   1125 			break;
   1126 
   1127 			/* Start of dns_map specific args */
   1128 		  case 'R':		/* search field */
   1129 			{
   1130 				char *h;
   1131 
   1132 				while (isascii(*++p) && isspace(*p))
   1133 					continue;
   1134 				h = strchr(p, ' ');
   1135 				if (h != NULL)
   1136 					*h = '\0';
   1137 				map_p->dns_m_type = dns_string_to_type(p);
   1138 				if (h != NULL)
   1139 					*h = ' ';
   1140 				if (map_p->dns_m_type < 0)
   1141 					syserr("dns map %s: wrong type %s",
   1142 						map->map_mname, p);
   1143 			}
   1144 			break;
   1145 
   1146 		  case 'B':		/* base domain */
   1147 			{
   1148 				char *h;
   1149 
   1150 				while (isascii(*++p) && isspace(*p))
   1151 					continue;
   1152 				h = strchr(p, ' ');
   1153 				if (h != NULL)
   1154 					*h = '\0';
   1155 
   1156 				/*
   1157 				**  slight abuse of map->map_file; it isn't
   1158 				**	used otherwise in this map type.
   1159 				*/
   1160 
   1161 				map->map_file = newstr(p);
   1162 				if (h != NULL)
   1163 					*h = ' ';
   1164 			}
   1165 			break;
   1166 		}
   1167 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
   1168 			p++;
   1169 		if (*p != '\0')
   1170 			*p++ = '\0';
   1171 	}
   1172 	if (map_p->dns_m_type < 0)
   1173 		syserr("dns map %s: missing -R type", map->map_mname);
   1174 	if (map->map_app != NULL)
   1175 		map->map_app = newstr(map->map_app);
   1176 	if (map->map_tapp != NULL)
   1177 		map->map_tapp = newstr(map->map_tapp);
   1178 
   1179 	/*
   1180 	**  Assumption: assert(sizeof(int) <= sizeof(ARBPTR_T));
   1181 	**  Even if this assumption is wrong, we use only one byte,
   1182 	**  so it doesn't really matter.
   1183 	*/
   1184 
   1185 	map->map_db1 = (ARBPTR_T) map_p;
   1186 	return true;
   1187 }
   1188 
   1189 /*
   1190 **  DNS_MAP_LOOKUP -- perform dns map lookup.
   1191 **
   1192 **	Parameters:
   1193 **		map -- pointer to MAP
   1194 **		name -- name to lookup
   1195 **		av -- arguments to interpolate into buf.
   1196 **		statp -- pointer to status (EX_)
   1197 **
   1198 **	Returns:
   1199 **		result of lookup if succeeded.
   1200 **		NULL -- otherwise.
   1201 */
   1202 
   1203 char *
   1204 dns_map_lookup(map, name, av, statp)
   1205 	MAP *map;
   1206 	char *name;
   1207 	char **av;
   1208 	int *statp;
   1209 {
   1210 	int resnum = 0;
   1211 	char *vp = NULL, *result = NULL;
   1212 	size_t vsize;
   1213 	struct dns_map *map_p;
   1214 	RESOURCE_RECORD_T *rr = NULL;
   1215 	DNS_REPLY_T *r = NULL;
   1216 #  if NETINET6
   1217 	static char buf6[INET6_ADDRSTRLEN];
   1218 #  endif /* NETINET6 */
   1219 
   1220 	if (tTd(38, 20))
   1221 		sm_dprintf("dns_map_lookup(%s, %s)\n",
   1222 			   map->map_mname, name);
   1223 
   1224 	map_p = (struct dns_map *)(map->map_db1);
   1225 	if (map->map_file != NULL && *map->map_file != '\0')
   1226 	{
   1227 		size_t len;
   1228 		char *appdomain;
   1229 
   1230 		len = strlen(map->map_file) + strlen(name) + 2;
   1231 		appdomain = (char *) sm_malloc(len);
   1232 		if (appdomain == NULL)
   1233 		{
   1234 			*statp = EX_UNAVAILABLE;
   1235 			return NULL;
   1236 		}
   1237 		(void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
   1238 		r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
   1239 				   map->map_timeout, map->map_retry);
   1240 		sm_free(appdomain);
   1241 	}
   1242 	else
   1243 	{
   1244 		r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
   1245 				   map->map_timeout, map->map_retry);
   1246 	}
   1247 
   1248 	if (r == NULL)
   1249 	{
   1250 		result = NULL;
   1251 		if (h_errno == TRY_AGAIN || transienterror(errno))
   1252 			*statp = EX_TEMPFAIL;
   1253 		else
   1254 			*statp = EX_NOTFOUND;
   1255 		goto cleanup;
   1256 	}
   1257 	*statp = EX_OK;
   1258 	for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
   1259 	{
   1260 		char *type = NULL;
   1261 		char *value = NULL;
   1262 
   1263 		switch (rr->rr_type)
   1264 		{
   1265 		  case T_NS:
   1266 			type = "T_NS";
   1267 			value = rr->rr_u.rr_txt;
   1268 			break;
   1269 		  case T_CNAME:
   1270 			type = "T_CNAME";
   1271 			value = rr->rr_u.rr_txt;
   1272 			break;
   1273 		  case T_AFSDB:
   1274 			type = "T_AFSDB";
   1275 			value = rr->rr_u.rr_mx->mx_r_domain;
   1276 			break;
   1277 		  case T_SRV:
   1278 			type = "T_SRV";
   1279 			value = rr->rr_u.rr_srv->srv_r_target;
   1280 			break;
   1281 		  case T_PTR:
   1282 			type = "T_PTR";
   1283 			value = rr->rr_u.rr_txt;
   1284 			break;
   1285 		  case T_TXT:
   1286 			type = "T_TXT";
   1287 			value = rr->rr_u.rr_txt;
   1288 			break;
   1289 		  case T_MX:
   1290 			type = "T_MX";
   1291 			value = rr->rr_u.rr_mx->mx_r_domain;
   1292 			break;
   1293 #  if NETINET
   1294 		  case T_A:
   1295 			type = "T_A";
   1296 			value = inet_ntoa(*(rr->rr_u.rr_a));
   1297 			break;
   1298 #  endif /* NETINET */
   1299 #  if NETINET6
   1300 		  case T_AAAA:
   1301 			type = "T_AAAA";
   1302 			value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
   1303 					    sizeof(buf6));
   1304 			break;
   1305 #  endif /* NETINET6 */
   1306 		}
   1307 
   1308 		(void) strreplnonprt(value, 'X');
   1309 		if (map_p->dns_m_type != rr->rr_type)
   1310 		{
   1311 			if (tTd(38, 40))
   1312 				sm_dprintf("\tskipping type %s (%d) value %s\n",
   1313 					   type != NULL ? type : "<UNKNOWN>",
   1314 					   rr->rr_type,
   1315 					   value != NULL ? value : "<NO VALUE>");
   1316 			continue;
   1317 		}
   1318 
   1319 #  if NETINET6
   1320 		if (rr->rr_type == T_AAAA && value == NULL)
   1321 		{
   1322 			result = NULL;
   1323 			*statp = EX_DATAERR;
   1324 			if (tTd(38, 40))
   1325 				sm_dprintf("\tbad T_AAAA conversion\n");
   1326 			goto cleanup;
   1327 		}
   1328 #  endif /* NETINET6 */
   1329 		if (tTd(38, 40))
   1330 			sm_dprintf("\tfound type %s (%d) value %s\n",
   1331 				   type != NULL ? type : "<UNKNOWN>",
   1332 				   rr->rr_type,
   1333 				   value != NULL ? value : "<NO VALUE>");
   1334 		if (value != NULL &&
   1335 		    (map->map_coldelim == '\0' ||
   1336 		     map->map_sizelimit == 1 ||
   1337 		     bitset(MF_MATCHONLY, map->map_mflags)))
   1338 		{
   1339 			/* Only care about the first match */
   1340 			vp = newstr(value);
   1341 			break;
   1342 		}
   1343 		else if (vp == NULL)
   1344 		{
   1345 			/* First result */
   1346 			vp = newstr(value);
   1347 		}
   1348 		else
   1349 		{
   1350 			/* concatenate the results */
   1351 			int sz;
   1352 			char *new;
   1353 
   1354 			sz = strlen(vp) + strlen(value) + 2;
   1355 			new = xalloc(sz);
   1356 			(void) sm_snprintf(new, sz, "%s%c%s",
   1357 					   vp, map->map_coldelim, value);
   1358 			sm_free(vp);
   1359 			vp = new;
   1360 			if (map->map_sizelimit > 0 &&
   1361 			    ++resnum >= map->map_sizelimit)
   1362 				break;
   1363 		}
   1364 	}
   1365 	if (vp == NULL)
   1366 	{
   1367 		result = NULL;
   1368 		*statp = EX_NOTFOUND;
   1369 		if (tTd(38, 40))
   1370 			sm_dprintf("\tno match found\n");
   1371 		goto cleanup;
   1372 	}
   1373 
   1374 	/* Cleanly truncate for rulesets */
   1375 	truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
   1376 
   1377 	vsize = strlen(vp);
   1378 
   1379 	if (LogLevel > 9)
   1380 		sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
   1381 			  name, vp);
   1382 	if (bitset(MF_MATCHONLY, map->map_mflags))
   1383 		result = map_rewrite(map, name, strlen(name), NULL);
   1384 	else
   1385 		result = map_rewrite(map, vp, vsize, av);
   1386 
   1387   cleanup:
   1388 	if (vp != NULL)
   1389 		sm_free(vp);
   1390 	if (r != NULL)
   1391 		dns_free_data(r);
   1392 	return result;
   1393 }
   1394 # endif /* DNSMAP */
   1395 #endif /* NAMED_BIND */
   1396 
   1397 /*
   1398 **  NDBM modules
   1399 */
   1400 
   1401 #if NDBM
   1402 
   1403 /*
   1404 **  NDBM_MAP_OPEN -- DBM-style map open
   1405 */
   1406 
   1407 bool
   1408 ndbm_map_open(map, mode)
   1409 	MAP *map;
   1410 	int mode;
   1411 {
   1412 	register DBM *dbm;
   1413 	int save_errno;
   1414 	int dfd;
   1415 	int pfd;
   1416 	long sff;
   1417 	int ret;
   1418 	int smode = S_IREAD;
   1419 	char dirfile[MAXPATHLEN];
   1420 	char pagfile[MAXPATHLEN];
   1421 	struct stat st;
   1422 	struct stat std, stp;
   1423 
   1424 	if (tTd(38, 2))
   1425 		sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
   1426 			map->map_mname, map->map_file, mode);
   1427 	map->map_lockfd = -1;
   1428 	mode &= O_ACCMODE;
   1429 
   1430 	/* do initial file and directory checks */
   1431 	if (sm_strlcpyn(dirfile, sizeof(dirfile), 2,
   1432 			map->map_file, ".dir") >= sizeof(dirfile) ||
   1433 	    sm_strlcpyn(pagfile, sizeof(pagfile), 2,
   1434 			map->map_file, ".pag") >= sizeof(pagfile))
   1435 	{
   1436 		errno = 0;
   1437 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   1438 			syserr("dbm map \"%s\": map file %s name too long",
   1439 				map->map_mname, map->map_file);
   1440 		return false;
   1441 	}
   1442 	sff = SFF_ROOTOK|SFF_REGONLY;
   1443 	if (mode == O_RDWR)
   1444 	{
   1445 		sff |= SFF_CREAT;
   1446 		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
   1447 			sff |= SFF_NOSLINK;
   1448 		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
   1449 			sff |= SFF_NOHLINK;
   1450 		smode = S_IWRITE;
   1451 	}
   1452 	else
   1453 	{
   1454 		if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
   1455 			sff |= SFF_NOWLINK;
   1456 	}
   1457 	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
   1458 		sff |= SFF_SAFEDIRPATH;
   1459 	ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
   1460 		       sff, smode, &std);
   1461 	if (ret == 0)
   1462 		ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
   1463 			       sff, smode, &stp);
   1464 
   1465 	if (ret != 0)
   1466 	{
   1467 		char *prob = "unsafe";
   1468 
   1469 		/* cannot open this map */
   1470 		if (ret == ENOENT)
   1471 			prob = "missing";
   1472 		if (tTd(38, 2))
   1473 			sm_dprintf("\t%s map file: %d\n", prob, ret);
   1474 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   1475 			syserr("dbm map \"%s\": %s map file %s",
   1476 				map->map_mname, prob, map->map_file);
   1477 		return false;
   1478 	}
   1479 	if (std.st_mode == ST_MODE_NOFILE)
   1480 		mode |= O_CREAT|O_EXCL;
   1481 
   1482 # if LOCK_ON_OPEN
   1483 	if (mode == O_RDONLY)
   1484 		mode |= O_SHLOCK;
   1485 	else
   1486 		mode |= O_TRUNC|O_EXLOCK;
   1487 # else /* LOCK_ON_OPEN */
   1488 	if ((mode & O_ACCMODE) == O_RDWR)
   1489 	{
   1490 #  if NOFTRUNCATE
   1491 		/*
   1492 		**  Warning: race condition.  Try to lock the file as
   1493 		**  quickly as possible after opening it.
   1494 		**	This may also have security problems on some systems,
   1495 		**	but there isn't anything we can do about it.
   1496 		*/
   1497 
   1498 		mode |= O_TRUNC;
   1499 #  else /* NOFTRUNCATE */
   1500 		/*
   1501 		**  This ugly code opens the map without truncating it,
   1502 		**  locks the file, then truncates it.  Necessary to
   1503 		**  avoid race conditions.
   1504 		*/
   1505 
   1506 		int dirfd;
   1507 		int pagfd;
   1508 		long sff = SFF_CREAT|SFF_OPENASROOT;
   1509 
   1510 		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
   1511 			sff |= SFF_NOSLINK;
   1512 		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
   1513 			sff |= SFF_NOHLINK;
   1514 
   1515 		dirfd = safeopen(dirfile, mode, DBMMODE, sff);
   1516 		pagfd = safeopen(pagfile, mode, DBMMODE, sff);
   1517 
   1518 		if (dirfd < 0 || pagfd < 0)
   1519 		{
   1520 			save_errno = errno;
   1521 			if (dirfd >= 0)
   1522 				(void) close(dirfd);
   1523 			if (pagfd >= 0)
   1524 				(void) close(pagfd);
   1525 			errno = save_errno;
   1526 			syserr("ndbm_map_open: cannot create database %s",
   1527 				map->map_file);
   1528 			return false;
   1529 		}
   1530 		if (ftruncate(dirfd, (off_t) 0) < 0 ||
   1531 		    ftruncate(pagfd, (off_t) 0) < 0)
   1532 		{
   1533 			save_errno = errno;
   1534 			(void) close(dirfd);
   1535 			(void) close(pagfd);
   1536 			errno = save_errno;
   1537 			syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
   1538 				map->map_file);
   1539 			return false;
   1540 		}
   1541 
   1542 		/* if new file, get "before" bits for later filechanged check */
   1543 		if (std.st_mode == ST_MODE_NOFILE &&
   1544 		    (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
   1545 		{
   1546 			save_errno = errno;
   1547 			(void) close(dirfd);
   1548 			(void) close(pagfd);
   1549 			errno = save_errno;
   1550 			syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
   1551 				map->map_file);
   1552 			return false;
   1553 		}
   1554 
   1555 		/* have to save the lock for the duration (bletch) */
   1556 		map->map_lockfd = dirfd;
   1557 		(void) close(pagfd);
   1558 
   1559 		/* twiddle bits for dbm_open */
   1560 		mode &= ~(O_CREAT|O_EXCL);
   1561 #  endif /* NOFTRUNCATE */
   1562 	}
   1563 # endif /* LOCK_ON_OPEN */
   1564 
   1565 	/* open the database */
   1566 	dbm = dbm_open(map->map_file, mode, DBMMODE);
   1567 	if (dbm == NULL)
   1568 	{
   1569 		save_errno = errno;
   1570 		if (bitset(MF_ALIAS, map->map_mflags) &&
   1571 		    aliaswait(map, ".pag", false))
   1572 			return true;
   1573 # if !LOCK_ON_OPEN && !NOFTRUNCATE
   1574 		if (map->map_lockfd >= 0)
   1575 			(void) close(map->map_lockfd);
   1576 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
   1577 		errno = save_errno;
   1578 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   1579 			syserr("Cannot open DBM database %s", map->map_file);
   1580 		return false;
   1581 	}
   1582 	dfd = dbm_dirfno(dbm);
   1583 	pfd = dbm_pagfno(dbm);
   1584 	if (dfd == pfd)
   1585 	{
   1586 		/* heuristic: if files are linked, this is actually gdbm */
   1587 		dbm_close(dbm);
   1588 # if !LOCK_ON_OPEN && !NOFTRUNCATE
   1589 		if (map->map_lockfd >= 0)
   1590 			(void) close(map->map_lockfd);
   1591 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
   1592 		errno = 0;
   1593 		syserr("dbm map \"%s\": cannot support GDBM",
   1594 			map->map_mname);
   1595 		return false;
   1596 	}
   1597 
   1598 	if (filechanged(dirfile, dfd, &std) ||
   1599 	    filechanged(pagfile, pfd, &stp))
   1600 	{
   1601 		save_errno = errno;
   1602 		dbm_close(dbm);
   1603 # if !LOCK_ON_OPEN && !NOFTRUNCATE
   1604 		if (map->map_lockfd >= 0)
   1605 			(void) close(map->map_lockfd);
   1606 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
   1607 		errno = save_errno;
   1608 		syserr("ndbm_map_open(%s): file changed after open",
   1609 			map->map_file);
   1610 		return false;
   1611 	}
   1612 
   1613 	map->map_db1 = (ARBPTR_T) dbm;
   1614 
   1615 	/*
   1616 	**  Need to set map_mtime before the call to aliaswait()
   1617 	**  as aliaswait() will call map_lookup() which requires
   1618 	**  map_mtime to be set
   1619 	*/
   1620 
   1621 	if (fstat(pfd, &st) >= 0)
   1622 		map->map_mtime = st.st_mtime;
   1623 
   1624 	if (mode == O_RDONLY)
   1625 	{
   1626 # if LOCK_ON_OPEN
   1627 		if (dfd >= 0)
   1628 			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
   1629 		if (pfd >= 0)
   1630 			(void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
   1631 # endif /* LOCK_ON_OPEN */
   1632 		if (bitset(MF_ALIAS, map->map_mflags) &&
   1633 		    !aliaswait(map, ".pag", true))
   1634 			return false;
   1635 	}
   1636 	else
   1637 	{
   1638 		map->map_mflags |= MF_LOCKED;
   1639 		if (geteuid() == 0 && TrustedUid != 0)
   1640 		{
   1641 #  if HASFCHOWN
   1642 			if (fchown(dfd, TrustedUid, -1) < 0 ||
   1643 			    fchown(pfd, TrustedUid, -1) < 0)
   1644 			{
   1645 				int err = errno;
   1646 
   1647 				sm_syslog(LOG_ALERT, NOQID,
   1648 					  "ownership change on %s failed: %s",
   1649 					  map->map_file, sm_errstring(err));
   1650 				message("050 ownership change on %s failed: %s",
   1651 					map->map_file, sm_errstring(err));
   1652 			}
   1653 #  else /* HASFCHOWN */
   1654 			sm_syslog(LOG_ALERT, NOQID,
   1655 				  "no fchown(): cannot change ownership on %s",
   1656 				  map->map_file);
   1657 			message("050 no fchown(): cannot change ownership on %s",
   1658 				map->map_file);
   1659 #  endif /* HASFCHOWN */
   1660 		}
   1661 	}
   1662 	return true;
   1663 }
   1664 
   1665 
   1666 /*
   1667 **  NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
   1668 */
   1669 
   1670 char *
   1671 ndbm_map_lookup(map, name, av, statp)
   1672 	MAP *map;
   1673 	char *name;
   1674 	char **av;
   1675 	int *statp;
   1676 {
   1677 	datum key, val;
   1678 	int dfd, pfd;
   1679 	char keybuf[MAXNAME + 1];
   1680 	struct stat stbuf;
   1681 
   1682 	if (tTd(38, 20))
   1683 		sm_dprintf("ndbm_map_lookup(%s, %s)\n",
   1684 			map->map_mname, name);
   1685 
   1686 	key.dptr = name;
   1687 	key.dsize = strlen(name);
   1688 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
   1689 	{
   1690 		if (key.dsize > sizeof(keybuf) - 1)
   1691 			key.dsize = sizeof(keybuf) - 1;
   1692 		memmove(keybuf, key.dptr, key.dsize);
   1693 		keybuf[key.dsize] = '\0';
   1694 		makelower(keybuf);
   1695 		key.dptr = keybuf;
   1696 	}
   1697 lockdbm:
   1698 	dfd = dbm_dirfno((DBM *) map->map_db1);
   1699 	if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
   1700 		(void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
   1701 	pfd = dbm_pagfno((DBM *) map->map_db1);
   1702 	if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
   1703 	    stbuf.st_mtime > map->map_mtime)
   1704 	{
   1705 		/* Reopen the database to sync the cache */
   1706 		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
   1707 								 : O_RDONLY;
   1708 
   1709 		if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
   1710 			(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
   1711 		map->map_mflags |= MF_CLOSING;
   1712 		map->map_class->map_close(map);
   1713 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
   1714 		if (map->map_class->map_open(map, omode))
   1715 		{
   1716 			map->map_mflags |= MF_OPEN;
   1717 			map->map_pid = CurrentPid;
   1718 			if ((omode & O_ACCMODE) == O_RDWR)
   1719 				map->map_mflags |= MF_WRITABLE;
   1720 			goto lockdbm;
   1721 		}
   1722 		else
   1723 		{
   1724 			if (!bitset(MF_OPTIONAL, map->map_mflags))
   1725 			{
   1726 				extern MAPCLASS BogusMapClass;
   1727 
   1728 				*statp = EX_TEMPFAIL;
   1729 				map->map_orgclass = map->map_class;
   1730 				map->map_class = &BogusMapClass;
   1731 				map->map_mflags |= MF_OPEN;
   1732 				map->map_pid = CurrentPid;
   1733 				syserr("Cannot reopen NDBM database %s",
   1734 					map->map_file);
   1735 			}
   1736 			return NULL;
   1737 		}
   1738 	}
   1739 	val.dptr = NULL;
   1740 	if (bitset(MF_TRY0NULL, map->map_mflags))
   1741 	{
   1742 		val = dbm_fetch((DBM *) map->map_db1, key);
   1743 		if (val.dptr != NULL)
   1744 			map->map_mflags &= ~MF_TRY1NULL;
   1745 	}
   1746 	if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
   1747 	{
   1748 		key.dsize++;
   1749 		val = dbm_fetch((DBM *) map->map_db1, key);
   1750 		if (val.dptr != NULL)
   1751 			map->map_mflags &= ~MF_TRY0NULL;
   1752 	}
   1753 	if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
   1754 		(void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
   1755 	if (val.dptr == NULL)
   1756 		return NULL;
   1757 	if (bitset(MF_MATCHONLY, map->map_mflags))
   1758 		return map_rewrite(map, name, strlen(name), NULL);
   1759 	else
   1760 		return map_rewrite(map, val.dptr, val.dsize, av);
   1761 }
   1762 
   1763 
   1764 /*
   1765 **  NDBM_MAP_STORE -- store a datum in the database
   1766 */
   1767 
   1768 void
   1769 ndbm_map_store(map, lhs, rhs)
   1770 	register MAP *map;
   1771 	char *lhs;
   1772 	char *rhs;
   1773 {
   1774 	datum key;
   1775 	datum data;
   1776 	int status;
   1777 	char keybuf[MAXNAME + 1];
   1778 
   1779 	if (tTd(38, 12))
   1780 		sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
   1781 			map->map_mname, lhs, rhs);
   1782 
   1783 	key.dsize = strlen(lhs);
   1784 	key.dptr = lhs;
   1785 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
   1786 	{
   1787 		if (key.dsize > sizeof(keybuf) - 1)
   1788 			key.dsize = sizeof(keybuf) - 1;
   1789 		memmove(keybuf, key.dptr, key.dsize);
   1790 		keybuf[key.dsize] = '\0';
   1791 		makelower(keybuf);
   1792 		key.dptr = keybuf;
   1793 	}
   1794 
   1795 	data.dsize = strlen(rhs);
   1796 	data.dptr = rhs;
   1797 
   1798 	if (bitset(MF_INCLNULL, map->map_mflags))
   1799 	{
   1800 		key.dsize++;
   1801 		data.dsize++;
   1802 	}
   1803 
   1804 	status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
   1805 	if (status > 0)
   1806 	{
   1807 		if (!bitset(MF_APPEND, map->map_mflags))
   1808 			message("050 Warning: duplicate alias name %s", lhs);
   1809 		else
   1810 		{
   1811 			static char *buf = NULL;
   1812 			static int bufsiz = 0;
   1813 			auto int xstat;
   1814 			datum old;
   1815 
   1816 			old.dptr = ndbm_map_lookup(map, key.dptr,
   1817 						   (char **) NULL, &xstat);
   1818 			if (old.dptr != NULL && *(char *) old.dptr != '\0')
   1819 			{
   1820 				old.dsize = strlen(old.dptr);
   1821 				if (data.dsize + old.dsize + 2 > bufsiz)
   1822 				{
   1823 					if (buf != NULL)
   1824 						(void) sm_free(buf);
   1825 					bufsiz = data.dsize + old.dsize + 2;
   1826 					buf = sm_pmalloc_x(bufsiz);
   1827 				}
   1828 				(void) sm_strlcpyn(buf, bufsiz, 3,
   1829 					data.dptr, ",", old.dptr);
   1830 				data.dsize = data.dsize + old.dsize + 1;
   1831 				data.dptr = buf;
   1832 				if (tTd(38, 9))
   1833 					sm_dprintf("ndbm_map_store append=%s\n",
   1834 						data.dptr);
   1835 			}
   1836 		}
   1837 		status = dbm_store((DBM *) map->map_db1,
   1838 				   key, data, DBM_REPLACE);
   1839 	}
   1840 	if (status != 0)
   1841 		syserr("readaliases: dbm put (%s): %d", lhs, status);
   1842 }
   1843 
   1844 
   1845 /*
   1846 **  NDBM_MAP_CLOSE -- close the database
   1847 */
   1848 
   1849 void
   1850 ndbm_map_close(map)
   1851 	register MAP  *map;
   1852 {
   1853 	if (tTd(38, 9))
   1854 		sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
   1855 			map->map_mname, map->map_file, map->map_mflags);
   1856 
   1857 	if (bitset(MF_WRITABLE, map->map_mflags))
   1858 	{
   1859 # ifdef NDBM_YP_COMPAT
   1860 		bool inclnull;
   1861 		char buf[MAXHOSTNAMELEN];
   1862 
   1863 		inclnull = bitset(MF_INCLNULL, map->map_mflags);
   1864 		map->map_mflags &= ~MF_INCLNULL;
   1865 
   1866 		if (strstr(map->map_file, "/yp/") != NULL)
   1867 		{
   1868 			long save_mflags = map->map_mflags;
   1869 
   1870 			map->map_mflags |= MF_NOFOLDCASE;
   1871 
   1872 			(void) sm_snprintf(buf, sizeof(buf), "%010ld", curtime());
   1873 			ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
   1874 
   1875 			(void) gethostname(buf, sizeof(buf));
   1876 			ndbm_map_store(map, "YP_MASTER_NAME", buf);
   1877 
   1878 			map->map_mflags = save_mflags;
   1879 		}
   1880 
   1881 		if (inclnull)
   1882 			map->map_mflags |= MF_INCLNULL;
   1883 # endif /* NDBM_YP_COMPAT */
   1884 
   1885 		/* write out the distinguished alias */
   1886 		ndbm_map_store(map, "@", "@");
   1887 	}
   1888 	dbm_close((DBM *) map->map_db1);
   1889 
   1890 	/* release lock (if needed) */
   1891 # if !LOCK_ON_OPEN
   1892 	if (map->map_lockfd >= 0)
   1893 		(void) close(map->map_lockfd);
   1894 # endif /* !LOCK_ON_OPEN */
   1895 }
   1896 
   1897 #endif /* NDBM */
   1898 /*
   1899 **  NEWDB (Hash and BTree) Modules
   1900 */
   1901 
   1902 #if NEWDB
   1903 
   1904 /*
   1905 **  BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
   1906 **
   1907 **	These do rather bizarre locking.  If you can lock on open,
   1908 **	do that to avoid the condition of opening a database that
   1909 **	is being rebuilt.  If you don't, we'll try to fake it, but
   1910 **	there will be a race condition.  If opening for read-only,
   1911 **	we immediately release the lock to avoid freezing things up.
   1912 **	We really ought to hold the lock, but guarantee that we won't
   1913 **	be pokey about it.  That's hard to do.
   1914 */
   1915 
   1916 /* these should be K line arguments */
   1917 # if DB_VERSION_MAJOR < 2
   1918 #  define db_cachesize	cachesize
   1919 #  define h_nelem	nelem
   1920 #  ifndef DB_CACHE_SIZE
   1921 #   define DB_CACHE_SIZE	(1024 * 1024)	/* database memory cache size */
   1922 #  endif /* ! DB_CACHE_SIZE */
   1923 #  ifndef DB_HASH_NELEM
   1924 #   define DB_HASH_NELEM	4096		/* (starting) size of hash table */
   1925 #  endif /* ! DB_HASH_NELEM */
   1926 # endif /* DB_VERSION_MAJOR < 2 */
   1927 
   1928 bool
   1929 bt_map_open(map, mode)
   1930 	MAP *map;
   1931 	int mode;
   1932 {
   1933 # if DB_VERSION_MAJOR < 2
   1934 	BTREEINFO btinfo;
   1935 # endif /* DB_VERSION_MAJOR < 2 */
   1936 # if DB_VERSION_MAJOR == 2
   1937 	DB_INFO btinfo;
   1938 # endif /* DB_VERSION_MAJOR == 2 */
   1939 # if DB_VERSION_MAJOR > 2
   1940 	void *btinfo = NULL;
   1941 # endif /* DB_VERSION_MAJOR > 2 */
   1942 
   1943 	if (tTd(38, 2))
   1944 		sm_dprintf("bt_map_open(%s, %s, %d)\n",
   1945 			map->map_mname, map->map_file, mode);
   1946 
   1947 # if DB_VERSION_MAJOR < 3
   1948 	memset(&btinfo, '\0', sizeof(btinfo));
   1949 #  ifdef DB_CACHE_SIZE
   1950 	btinfo.db_cachesize = DB_CACHE_SIZE;
   1951 #  endif /* DB_CACHE_SIZE */
   1952 # endif /* DB_VERSION_MAJOR < 3 */
   1953 
   1954 	return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
   1955 }
   1956 
   1957 bool
   1958 hash_map_open(map, mode)
   1959 	MAP *map;
   1960 	int mode;
   1961 {
   1962 # if DB_VERSION_MAJOR < 2
   1963 	HASHINFO hinfo;
   1964 # endif /* DB_VERSION_MAJOR < 2 */
   1965 # if DB_VERSION_MAJOR == 2
   1966 	DB_INFO hinfo;
   1967 # endif /* DB_VERSION_MAJOR == 2 */
   1968 # if DB_VERSION_MAJOR > 2
   1969 	void *hinfo = NULL;
   1970 # endif /* DB_VERSION_MAJOR > 2 */
   1971 
   1972 	if (tTd(38, 2))
   1973 		sm_dprintf("hash_map_open(%s, %s, %d)\n",
   1974 			map->map_mname, map->map_file, mode);
   1975 
   1976 # if DB_VERSION_MAJOR < 3
   1977 	memset(&hinfo, '\0', sizeof(hinfo));
   1978 #  ifdef DB_HASH_NELEM
   1979 	hinfo.h_nelem = DB_HASH_NELEM;
   1980 #  endif /* DB_HASH_NELEM */
   1981 #  ifdef DB_CACHE_SIZE
   1982 	hinfo.db_cachesize = DB_CACHE_SIZE;
   1983 #  endif /* DB_CACHE_SIZE */
   1984 # endif /* DB_VERSION_MAJOR < 3 */
   1985 
   1986 	return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
   1987 }
   1988 
   1989 static bool
   1990 db_map_open(map, mode, mapclassname, dbtype, openinfo)
   1991 	MAP *map;
   1992 	int mode;
   1993 	char *mapclassname;
   1994 	DBTYPE dbtype;
   1995 # if DB_VERSION_MAJOR < 2
   1996 	const void *openinfo;
   1997 # endif /* DB_VERSION_MAJOR < 2 */
   1998 # if DB_VERSION_MAJOR == 2
   1999 	DB_INFO *openinfo;
   2000 # endif /* DB_VERSION_MAJOR == 2 */
   2001 # if DB_VERSION_MAJOR > 2
   2002 	void **openinfo;
   2003 # endif /* DB_VERSION_MAJOR > 2 */
   2004 {
   2005 	DB *db = NULL;
   2006 	int i;
   2007 	int omode;
   2008 	int smode = S_IREAD;
   2009 	int fd;
   2010 	long sff;
   2011 	int save_errno;
   2012 	struct stat st;
   2013 	char buf[MAXPATHLEN];
   2014 
   2015 	/* do initial file and directory checks */
   2016 	if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
   2017 	{
   2018 		errno = 0;
   2019 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   2020 			syserr("map \"%s\": map file %s name too long",
   2021 				map->map_mname, map->map_file);
   2022 		return false;
   2023 	}
   2024 	i = strlen(buf);
   2025 	if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
   2026 	{
   2027 		if (sm_strlcat(buf, ".db", sizeof(buf)) >= sizeof(buf))
   2028 		{
   2029 			errno = 0;
   2030 			if (!bitset(MF_OPTIONAL, map->map_mflags))
   2031 				syserr("map \"%s\": map file %s name too long",
   2032 					map->map_mname, map->map_file);
   2033 			return false;
   2034 		}
   2035 	}
   2036 
   2037 	mode &= O_ACCMODE;
   2038 	omode = mode;
   2039 
   2040 	sff = SFF_ROOTOK|SFF_REGONLY;
   2041 	if (mode == O_RDWR)
   2042 	{
   2043 		sff |= SFF_CREAT;
   2044 		if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
   2045 			sff |= SFF_NOSLINK;
   2046 		if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
   2047 			sff |= SFF_NOHLINK;
   2048 		smode = S_IWRITE;
   2049 	}
   2050 	else
   2051 	{
   2052 		if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
   2053 			sff |= SFF_NOWLINK;
   2054 	}
   2055 	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
   2056 		sff |= SFF_SAFEDIRPATH;
   2057 	i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
   2058 
   2059 	if (i != 0)
   2060 	{
   2061 		char *prob = "unsafe";
   2062 
   2063 		/* cannot open this map */
   2064 		if (i == ENOENT)
   2065 			prob = "missing";
   2066 		if (tTd(38, 2))
   2067 			sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
   2068 		errno = i;
   2069 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   2070 			syserr("%s map \"%s\": %s map file %s",
   2071 				mapclassname, map->map_mname, prob, buf);
   2072 		return false;
   2073 	}
   2074 	if (st.st_mode == ST_MODE_NOFILE)
   2075 		omode |= O_CREAT|O_EXCL;
   2076 
   2077 	map->map_lockfd = -1;
   2078 
   2079 # if LOCK_ON_OPEN
   2080 	if (mode == O_RDWR)
   2081 		omode |= O_TRUNC|O_EXLOCK;
   2082 	else
   2083 		omode |= O_SHLOCK;
   2084 # else /* LOCK_ON_OPEN */
   2085 	/*
   2086 	**  Pre-lock the file to avoid race conditions.  In particular,
   2087 	**  since dbopen returns NULL if the file is zero length, we
   2088 	**  must have a locked instance around the dbopen.
   2089 	*/
   2090 
   2091 	fd = open(buf, omode, DBMMODE);
   2092 	if (fd < 0)
   2093 	{
   2094 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   2095 			syserr("db_map_open: cannot pre-open database %s", buf);
   2096 		return false;
   2097 	}
   2098 
   2099 	/* make sure no baddies slipped in just before the open... */
   2100 	if (filechanged(buf, fd, &st))
   2101 	{
   2102 		save_errno = errno;
   2103 		(void) close(fd);
   2104 		errno = save_errno;
   2105 		syserr("db_map_open(%s): file changed after pre-open", buf);
   2106 		return false;
   2107 	}
   2108 
   2109 	/* if new file, get the "before" bits for later filechanged check */
   2110 	if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
   2111 	{
   2112 		save_errno = errno;
   2113 		(void) close(fd);
   2114 		errno = save_errno;
   2115 		syserr("db_map_open(%s): cannot fstat pre-opened file",
   2116 			buf);
   2117 		return false;
   2118 	}
   2119 
   2120 	/* actually lock the pre-opened file */
   2121 	if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
   2122 		syserr("db_map_open: cannot lock %s", buf);
   2123 
   2124 	/* set up mode bits for dbopen */
   2125 	if (mode == O_RDWR)
   2126 		omode |= O_TRUNC;
   2127 	omode &= ~(O_EXCL|O_CREAT);
   2128 # endif /* LOCK_ON_OPEN */
   2129 
   2130 # if DB_VERSION_MAJOR < 2
   2131 	db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
   2132 # else /* DB_VERSION_MAJOR < 2 */
   2133 	{
   2134 		int flags = 0;
   2135 #  if DB_VERSION_MAJOR > 2
   2136 		int ret;
   2137 #  endif /* DB_VERSION_MAJOR > 2 */
   2138 
   2139 		if (mode == O_RDONLY)
   2140 			flags |= DB_RDONLY;
   2141 		if (bitset(O_CREAT, omode))
   2142 			flags |= DB_CREATE;
   2143 		if (bitset(O_TRUNC, omode))
   2144 			flags |= DB_TRUNCATE;
   2145 		SM_DB_FLAG_ADD(flags);
   2146 
   2147 #  if DB_VERSION_MAJOR > 2
   2148 		ret = db_create(&db, NULL, 0);
   2149 #  ifdef DB_CACHE_SIZE
   2150 		if (ret == 0 && db != NULL)
   2151 		{
   2152 			ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
   2153 			if (ret != 0)
   2154 			{
   2155 				(void) db->close(db, 0);
   2156 				db = NULL;
   2157 			}
   2158 		}
   2159 #  endif /* DB_CACHE_SIZE */
   2160 #  ifdef DB_HASH_NELEM
   2161 		if (dbtype == DB_HASH && ret == 0 && db != NULL)
   2162 		{
   2163 			ret = db->set_h_nelem(db, DB_HASH_NELEM);
   2164 			if (ret != 0)
   2165 			{
   2166 				(void) db->close(db, 0);
   2167 				db = NULL;
   2168 			}
   2169 		}
   2170 #  endif /* DB_HASH_NELEM */
   2171 		if (ret == 0 && db != NULL)
   2172 		{
   2173 			ret = db->open(db,
   2174 					DBTXN	/* transaction for DB 4.1 */
   2175 					buf, NULL, dbtype, flags, DBMMODE);
   2176 			if (ret != 0)
   2177 			{
   2178 #ifdef DB_OLD_VERSION
   2179 				if (ret == DB_OLD_VERSION)
   2180 					ret = EINVAL;
   2181 #endif /* DB_OLD_VERSION */
   2182 				(void) db->close(db, 0);
   2183 				db = NULL;
   2184 			}
   2185 		}
   2186 		errno = ret;
   2187 #  else /* DB_VERSION_MAJOR > 2 */
   2188 		errno = db_open(buf, dbtype, flags, DBMMODE,
   2189 				NULL, openinfo, &db);
   2190 #  endif /* DB_VERSION_MAJOR > 2 */
   2191 	}
   2192 # endif /* DB_VERSION_MAJOR < 2 */
   2193 	save_errno = errno;
   2194 
   2195 # if !LOCK_ON_OPEN
   2196 	if (mode == O_RDWR)
   2197 		map->map_lockfd = fd;
   2198 	else
   2199 		(void) close(fd);
   2200 # endif /* !LOCK_ON_OPEN */
   2201 
   2202 	if (db == NULL)
   2203 	{
   2204 		if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
   2205 		    aliaswait(map, ".db", false))
   2206 			return true;
   2207 # if !LOCK_ON_OPEN
   2208 		if (map->map_lockfd >= 0)
   2209 			(void) close(map->map_lockfd);
   2210 # endif /* !LOCK_ON_OPEN */
   2211 		errno = save_errno;
   2212 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   2213 			syserr("Cannot open %s database %s",
   2214 				mapclassname, buf);
   2215 		return false;
   2216 	}
   2217 
   2218 # if DB_VERSION_MAJOR < 2
   2219 	fd = db->fd(db);
   2220 # else /* DB_VERSION_MAJOR < 2 */
   2221 	fd = -1;
   2222 	errno = db->fd(db, &fd);
   2223 # endif /* DB_VERSION_MAJOR < 2 */
   2224 	if (filechanged(buf, fd, &st))
   2225 	{
   2226 		save_errno = errno;
   2227 # if DB_VERSION_MAJOR < 2
   2228 		(void) db->close(db);
   2229 # else /* DB_VERSION_MAJOR < 2 */
   2230 		errno = db->close(db, 0);
   2231 # endif /* DB_VERSION_MAJOR < 2 */
   2232 # if !LOCK_ON_OPEN
   2233 		if (map->map_lockfd >= 0)
   2234 			(void) close(map->map_lockfd);
   2235 # endif /* !LOCK_ON_OPEN */
   2236 		errno = save_errno;
   2237 		syserr("db_map_open(%s): file changed after open", buf);
   2238 		return false;
   2239 	}
   2240 
   2241 	if (mode == O_RDWR)
   2242 		map->map_mflags |= MF_LOCKED;
   2243 # if LOCK_ON_OPEN
   2244 	if (fd >= 0 && mode == O_RDONLY)
   2245 	{
   2246 		(void) lockfile(fd, buf, NULL, LOCK_UN);
   2247 	}
   2248 # endif /* LOCK_ON_OPEN */
   2249 
   2250 	/* try to make sure that at least the database header is on disk */
   2251 	if (mode == O_RDWR)
   2252 	{
   2253 		(void) db->sync(db, 0);
   2254 		if (geteuid() == 0 && TrustedUid != 0)
   2255 		{
   2256 #  if HASFCHOWN
   2257 			if (fchown(fd, TrustedUid, -1) < 0)
   2258 			{
   2259 				int err = errno;
   2260 
   2261 				sm_syslog(LOG_ALERT, NOQID,
   2262 					  "ownership change on %s failed: %s",
   2263 					  buf, sm_errstring(err));
   2264 				message("050 ownership change on %s failed: %s",
   2265 					buf, sm_errstring(err));
   2266 			}
   2267 #  else /* HASFCHOWN */
   2268 			sm_syslog(LOG_ALERT, NOQID,
   2269 				  "no fchown(): cannot change ownership on %s",
   2270 				  map->map_file);
   2271 			message("050 no fchown(): cannot change ownership on %s",
   2272 				map->map_file);
   2273 #  endif /* HASFCHOWN */
   2274 		}
   2275 	}
   2276 
   2277 	map->map_db2 = (ARBPTR_T) db;
   2278 
   2279 	/*
   2280 	**  Need to set map_mtime before the call to aliaswait()
   2281 	**  as aliaswait() will call map_lookup() which requires
   2282 	**  map_mtime to be set
   2283 	*/
   2284 
   2285 	if (fd >= 0 && fstat(fd, &st) >= 0)
   2286 		map->map_mtime = st.st_mtime;
   2287 
   2288 	if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
   2289 	    !aliaswait(map, ".db", true))
   2290 		return false;
   2291 	return true;
   2292 }
   2293 
   2294 
   2295 /*
   2296 **  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
   2297 */
   2298 
   2299 char *
   2300 db_map_lookup(map, name, av, statp)
   2301 	MAP *map;
   2302 	char *name;
   2303 	char **av;
   2304 	int *statp;
   2305 {
   2306 	DBT key, val;
   2307 	register DB *db = (DB *) map->map_db2;
   2308 	int i;
   2309 	int st;
   2310 	int save_errno;
   2311 	int fd;
   2312 	struct stat stbuf;
   2313 	char keybuf[MAXNAME + 1];
   2314 	char buf[MAXPATHLEN];
   2315 
   2316 	memset(&key, '\0', sizeof(key));
   2317 	memset(&val, '\0', sizeof(val));
   2318 
   2319 	if (tTd(38, 20))
   2320 		sm_dprintf("db_map_lookup(%s, %s)\n",
   2321 			map->map_mname, name);
   2322 
   2323 	if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
   2324 	{
   2325 		errno = 0;
   2326 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   2327 			syserr("map \"%s\": map file %s name too long",
   2328 				map->map_mname, map->map_file);
   2329 		return NULL;
   2330 	}
   2331 	i = strlen(buf);
   2332 	if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
   2333 		buf[i - 3] = '\0';
   2334 
   2335 	key.size = strlen(name);
   2336 	if (key.size > sizeof(keybuf) - 1)
   2337 		key.size = sizeof(keybuf) - 1;
   2338 	key.data = keybuf;
   2339 	memmove(keybuf, name, key.size);
   2340 	keybuf[key.size] = '\0';
   2341 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
   2342 		makelower(keybuf);
   2343   lockdb:
   2344 # if DB_VERSION_MAJOR < 2
   2345 	fd = db->fd(db);
   2346 # else /* DB_VERSION_MAJOR < 2 */
   2347 	fd = -1;
   2348 	errno = db->fd(db, &fd);
   2349 # endif /* DB_VERSION_MAJOR < 2 */
   2350 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
   2351 		(void) lockfile(fd, buf, ".db", LOCK_SH);
   2352 	if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
   2353 	{
   2354 		/* Reopen the database to sync the cache */
   2355 		int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
   2356 								 : O_RDONLY;
   2357 
   2358 		if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
   2359 			(void) lockfile(fd, buf, ".db", LOCK_UN);
   2360 		map->map_mflags |= MF_CLOSING;
   2361 		map->map_class->map_close(map);
   2362 		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
   2363 		if (map->map_class->map_open(map, omode))
   2364 		{
   2365 			map->map_mflags |= MF_OPEN;
   2366 			map->map_pid = CurrentPid;
   2367 			if ((omode & O_ACCMODE) == O_RDWR)
   2368 				map->map_mflags |= MF_WRITABLE;
   2369 			db = (DB *) map->map_db2;
   2370 			goto lockdb;
   2371 		}
   2372 		else
   2373 		{
   2374 			if (!bitset(MF_OPTIONAL, map->map_mflags))
   2375 			{
   2376 				extern MAPCLASS BogusMapClass;
   2377 
   2378 				*statp = EX_TEMPFAIL;
   2379 				map->map_orgclass = map->map_class;
   2380 				map->map_class = &BogusMapClass;
   2381 				map->map_mflags |= MF_OPEN;
   2382 				map->map_pid = CurrentPid;
   2383 				syserr("Cannot reopen DB database %s",
   2384 					map->map_file);
   2385 			}
   2386 			return NULL;
   2387 		}
   2388 	}
   2389 
   2390 	st = 1;
   2391 	if (bitset(MF_TRY0NULL, map->map_mflags))
   2392 	{
   2393 # if DB_VERSION_MAJOR < 2
   2394 		st = db->get(db, &key, &val, 0);
   2395 # else /* DB_VERSION_MAJOR < 2 */
   2396 		errno = db->get(db, NULL, &key, &val, 0);
   2397 		switch (errno)
   2398 		{
   2399 		  case DB_NOTFOUND:
   2400 		  case DB_KEYEMPTY:
   2401 			st = 1;
   2402 			break;
   2403 
   2404 		  case 0:
   2405 			st = 0;
   2406 			break;
   2407 
   2408 		  default:
   2409 			st = -1;
   2410 			break;
   2411 		}
   2412 # endif /* DB_VERSION_MAJOR < 2 */
   2413 		if (st == 0)
   2414 			map->map_mflags &= ~MF_TRY1NULL;
   2415 	}
   2416 	if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
   2417 	{
   2418 		key.size++;
   2419 # if DB_VERSION_MAJOR < 2
   2420 		st = db->get(db, &key, &val, 0);
   2421 # else /* DB_VERSION_MAJOR < 2 */
   2422 		errno = db->get(db, NULL, &key, &val, 0);
   2423 		switch (errno)
   2424 		{
   2425 		  case DB_NOTFOUND:
   2426 		  case DB_KEYEMPTY:
   2427 			st = 1;
   2428 			break;
   2429 
   2430 		  case 0:
   2431 			st = 0;
   2432 			break;
   2433 
   2434 		  default:
   2435 			st = -1;
   2436 			break;
   2437 		}
   2438 # endif /* DB_VERSION_MAJOR < 2 */
   2439 		if (st == 0)
   2440 			map->map_mflags &= ~MF_TRY0NULL;
   2441 	}
   2442 	save_errno = errno;
   2443 	if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
   2444 		(void) lockfile(fd, buf, ".db", LOCK_UN);
   2445 	if (st != 0)
   2446 	{
   2447 		errno = save_errno;
   2448 		if (st < 0)
   2449 			syserr("db_map_lookup: get (%s)", name);
   2450 		return NULL;
   2451 	}
   2452 	if (bitset(MF_MATCHONLY, map->map_mflags))
   2453 		return map_rewrite(map, name, strlen(name), NULL);
   2454 	else
   2455 		return map_rewrite(map, val.data, val.size, av);
   2456 }
   2457 
   2458 
   2459 /*
   2460 **  DB_MAP_STORE -- store a datum in the NEWDB database
   2461 */
   2462 
   2463 void
   2464 db_map_store(map, lhs, rhs)
   2465 	register MAP *map;
   2466 	char *lhs;
   2467 	char *rhs;
   2468 {
   2469 	int status;
   2470 	DBT key;
   2471 	DBT data;
   2472 	register DB *db = map->map_db2;
   2473 	char keybuf[MAXNAME + 1];
   2474 
   2475 	memset(&key, '\0', sizeof(key));
   2476 	memset(&data, '\0', sizeof(data));
   2477 
   2478 	if (tTd(38, 12))
   2479 		sm_dprintf("db_map_store(%s, %s, %s)\n",
   2480 			map->map_mname, lhs, rhs);
   2481 
   2482 	key.size = strlen(lhs);
   2483 	key.data = lhs;
   2484 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
   2485 	{
   2486 		if (key.size > sizeof(keybuf) - 1)
   2487 			key.size = sizeof(keybuf) - 1;
   2488 		memmove(keybuf, key.data, key.size);
   2489 		keybuf[key.size] = '\0';
   2490 		makelower(keybuf);
   2491 		key.data = keybuf;
   2492 	}
   2493 
   2494 	data.size = strlen(rhs);
   2495 	data.data = rhs;
   2496 
   2497 	if (bitset(MF_INCLNULL, map->map_mflags))
   2498 	{
   2499 		key.size++;
   2500 		data.size++;
   2501 	}
   2502 
   2503 # if DB_VERSION_MAJOR < 2
   2504 	status = db->put(db, &key, &data, R_NOOVERWRITE);
   2505 # else /* DB_VERSION_MAJOR < 2 */
   2506 	errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
   2507 	switch (errno)
   2508 	{
   2509 	  case DB_KEYEXIST:
   2510 		status = 1;
   2511 		break;
   2512 
   2513 	  case 0:
   2514 		status = 0;
   2515 		break;
   2516 
   2517 	  default:
   2518 		status = -1;
   2519 		break;
   2520 	}
   2521 # endif /* DB_VERSION_MAJOR < 2 */
   2522 	if (status > 0)
   2523 	{
   2524 		if (!bitset(MF_APPEND, map->map_mflags))
   2525 			message("050 Warning: duplicate alias name %s", lhs);
   2526 		else
   2527 		{
   2528 			static char *buf = NULL;
   2529 			static int bufsiz = 0;
   2530 			DBT old;
   2531 
   2532 			memset(&old, '\0', sizeof(old));
   2533 
   2534 			old.data = db_map_lookup(map, key.data,
   2535 						 (char **) NULL, &status);
   2536 			if (old.data != NULL)
   2537 			{
   2538 				old.size = strlen(old.data);
   2539 				if (data.size + old.size + 2 > (size_t) bufsiz)
   2540 				{
   2541 					if (buf != NULL)
   2542 						sm_free(buf);
   2543 					bufsiz = data.size + old.size + 2;
   2544 					buf = sm_pmalloc_x(bufsiz);
   2545 				}
   2546 				(void) sm_strlcpyn(buf, bufsiz, 3,
   2547 					(char *) data.data, ",",
   2548 					(char *) old.data);
   2549 				data.size = data.size + old.size + 1;
   2550 				data.data = buf;
   2551 				if (tTd(38, 9))
   2552 					sm_dprintf("db_map_store append=%s\n",
   2553 						(char *) data.data);
   2554 			}
   2555 		}
   2556 # if DB_VERSION_MAJOR < 2
   2557 		status = db->put(db, &key, &data, 0);
   2558 # else /* DB_VERSION_MAJOR < 2 */
   2559 		status = errno = db->put(db, NULL, &key, &data, 0);
   2560 # endif /* DB_VERSION_MAJOR < 2 */
   2561 	}
   2562 	if (status != 0)
   2563 		syserr("readaliases: db put (%s)", lhs);
   2564 }
   2565 
   2566 
   2567 /*
   2568 **  DB_MAP_CLOSE -- add distinguished entries and close the database
   2569 */
   2570 
   2571 void
   2572 db_map_close(map)
   2573 	MAP *map;
   2574 {
   2575 	register DB *db = map->map_db2;
   2576 
   2577 	if (tTd(38, 9))
   2578 		sm_dprintf("db_map_close(%s, %s, %lx)\n",
   2579 			map->map_mname, map->map_file, map->map_mflags);
   2580 
   2581 	if (bitset(MF_WRITABLE, map->map_mflags))
   2582 	{
   2583 		/* write out the distinguished alias */
   2584 		db_map_store(map, "@", "@");
   2585 	}
   2586 
   2587 	(void) db->sync(db, 0);
   2588 
   2589 # if !LOCK_ON_OPEN
   2590 	if (map->map_lockfd >= 0)
   2591 		(void) close(map->map_lockfd);
   2592 # endif /* !LOCK_ON_OPEN */
   2593 
   2594 # if DB_VERSION_MAJOR < 2
   2595 	if (db->close(db) != 0)
   2596 # else /* DB_VERSION_MAJOR < 2 */
   2597 	/*
   2598 	**  Berkeley DB can use internal shared memory
   2599 	**  locking for its memory pool.  Closing a map
   2600 	**  opened by another process will interfere
   2601 	**  with the shared memory and locks of the parent
   2602 	**  process leaving things in a bad state.
   2603 	*/
   2604 
   2605 	/*
   2606 	**  If this map was not opened by the current
   2607 	**  process, do not close the map but recover
   2608 	**  the file descriptor.
   2609 	*/
   2610 
   2611 	if (map->map_pid != CurrentPid)
   2612 	{
   2613 		int fd = -1;
   2614 
   2615 		errno = db->fd(db, &fd);
   2616 		if (fd >= 0)
   2617 			(void) close(fd);
   2618 		return;
   2619 	}
   2620 
   2621 	if ((errno = db->close(db, 0)) != 0)
   2622 # endif /* DB_VERSION_MAJOR < 2 */
   2623 		syserr("db_map_close(%s, %s, %lx): db close failure",
   2624 			map->map_mname, map->map_file, map->map_mflags);
   2625 }
   2626 #endif /* NEWDB */
   2627 /*
   2628 **  NIS Modules
   2629 */
   2630 
   2631 #if NIS
   2632 
   2633 # ifndef YPERR_BUSY
   2634 #  define YPERR_BUSY	16
   2635 # endif /* ! YPERR_BUSY */
   2636 
   2637 /*
   2638 **  NIS_MAP_OPEN -- open DBM map
   2639 */
   2640 
   2641 bool
   2642 nis_map_open(map, mode)
   2643 	MAP *map;
   2644 	int mode;
   2645 {
   2646 	int yperr;
   2647 	register char *p;
   2648 	auto char *vp;
   2649 	auto int vsize;
   2650 
   2651 	if (tTd(38, 2))
   2652 		sm_dprintf("nis_map_open(%s, %s, %d)\n",
   2653 			map->map_mname, map->map_file, mode);
   2654 
   2655 	mode &= O_ACCMODE;
   2656 	if (mode != O_RDONLY)
   2657 	{
   2658 		/* issue a pseudo-error message */
   2659 		errno = SM_EMAPCANTWRITE;
   2660 		return false;
   2661 	}
   2662 
   2663 	p = strchr(map->map_file, '@');
   2664 	if (p != NULL)
   2665 	{
   2666 		*p++ = '\0';
   2667 		if (*p != '\0')
   2668 			map->map_domain = p;
   2669 	}
   2670 
   2671 	if (*map->map_file == '\0')
   2672 		map->map_file = "mail.aliases";
   2673 
   2674 	if (map->map_domain == NULL)
   2675 	{
   2676 		yperr = yp_get_default_domain(&map->map_domain);
   2677 		if (yperr != 0)
   2678 		{
   2679 			if (!bitset(MF_OPTIONAL, map->map_mflags))
   2680 				syserr("451 4.3.5 NIS map %s specified, but NIS not running",
   2681 				       map->map_file);
   2682 			return false;
   2683 		}
   2684 	}
   2685 
   2686 	/* check to see if this map actually exists */
   2687 	vp = NULL;
   2688 	yperr = yp_match(map->map_domain, map->map_file, "@", 1,
   2689 			&vp, &vsize);
   2690 	if (tTd(38, 10))
   2691 		sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
   2692 			map->map_domain, map->map_file, yperr_string(yperr));
   2693 	if (vp != NULL)
   2694 		sm_free(vp);
   2695 
   2696 	if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
   2697 	{
   2698 		/*
   2699 		**  We ought to be calling aliaswait() here if this is an
   2700 		**  alias file, but powerful HP-UX NIS servers  apparently
   2701 		**  don't insert the @:@ token into the alias map when it
   2702 		**  is rebuilt, so aliaswait() just hangs.  I hate HP-UX.
   2703 		*/
   2704 
   2705 # if 0
   2706 		if (!bitset(MF_ALIAS, map->map_mflags) ||
   2707 		    aliaswait(map, NULL, true))
   2708 # endif /* 0 */
   2709 			return true;
   2710 	}
   2711 
   2712 	if (!bitset(MF_OPTIONAL, map->map_mflags))
   2713 	{
   2714 		syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
   2715 			map->map_file, map->map_domain, yperr_string(yperr));
   2716 	}
   2717 
   2718 	return false;
   2719 }
   2720 
   2721 
   2722 /*
   2723 **  NIS_MAP_LOOKUP -- look up a datum in a NIS map
   2724 */
   2725 
   2726 /* ARGSUSED3 */
   2727 char *
   2728 nis_map_lookup(map, name, av, statp)
   2729 	MAP *map;
   2730 	char *name;
   2731 	char **av;
   2732 	int *statp;
   2733 {
   2734 	char *vp;
   2735 	auto int vsize;
   2736 	int buflen;
   2737 	int yperr;
   2738 	char keybuf[MAXNAME + 1];
   2739 	char *SM_NONVOLATILE result = NULL;
   2740 
   2741 	if (tTd(38, 20))
   2742 		sm_dprintf("nis_map_lookup(%s, %s)\n",
   2743 			map->map_mname, name);
   2744 
   2745 	buflen = strlen(name);
   2746 	if (buflen > sizeof(keybuf) - 1)
   2747 		buflen = sizeof(keybuf) - 1;
   2748 	memmove(keybuf, name, buflen);
   2749 	keybuf[buflen] = '\0';
   2750 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
   2751 		makelower(keybuf);
   2752 	yperr = YPERR_KEY;
   2753 	vp = NULL;
   2754 	if (bitset(MF_TRY0NULL, map->map_mflags))
   2755 	{
   2756 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
   2757 			     &vp, &vsize);
   2758 		if (yperr == 0)
   2759 			map->map_mflags &= ~MF_TRY1NULL;
   2760 	}
   2761 	if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
   2762 	{
   2763 		SM_FREE_CLR(vp);
   2764 		buflen++;
   2765 		yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
   2766 			     &vp, &vsize);
   2767 		if (yperr == 0)
   2768 			map->map_mflags &= ~MF_TRY0NULL;
   2769 	}
   2770 	if (yperr != 0)
   2771 	{
   2772 		if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
   2773 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
   2774 		if (vp != NULL)
   2775 			sm_free(vp);
   2776 		return NULL;
   2777 	}
   2778 	SM_TRY
   2779 		if (bitset(MF_MATCHONLY, map->map_mflags))
   2780 			result = map_rewrite(map, name, strlen(name), NULL);
   2781 		else
   2782 			result = map_rewrite(map, vp, vsize, av);
   2783 	SM_FINALLY
   2784 		if (vp != NULL)
   2785 			sm_free(vp);
   2786 	SM_END_TRY
   2787 	return result;
   2788 }
   2789 
   2790 
   2791 /*
   2792 **  NIS_GETCANONNAME -- look up canonical name in NIS
   2793 */
   2794 
   2795 static bool
   2796 nis_getcanonname(name, hbsize, statp)
   2797 	char *name;
   2798 	int hbsize;
   2799 	int *statp;
   2800 {
   2801 	char *vp;
   2802 	auto int vsize;
   2803 	int keylen;
   2804 	int yperr;
   2805 	static bool try0null = true;
   2806 	static bool try1null = true;
   2807 	static char *yp_domain = NULL;
   2808 	char host_record[MAXLINE];
   2809 	char cbuf[MAXNAME];
   2810 	char nbuf[MAXNAME + 1];
   2811 
   2812 	if (tTd(38, 20))
   2813 		sm_dprintf("nis_getcanonname(%s)\n", name);
   2814 
   2815 	if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
   2816 	{
   2817 		*statp = EX_UNAVAILABLE;
   2818 		return false;
   2819 	}
   2820 	(void) shorten_hostname(nbuf);
   2821 	keylen = strlen(nbuf);
   2822 
   2823 	if (yp_domain == NULL)
   2824 		(void) yp_get_default_domain(&yp_domain);
   2825 	makelower(nbuf);
   2826 	yperr = YPERR_KEY;
   2827 	vp = NULL;
   2828 	if (try0null)
   2829 	{
   2830 		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
   2831 			     &vp, &vsize);
   2832 		if (yperr == 0)
   2833 			try1null = false;
   2834 	}
   2835 	if (yperr == YPERR_KEY && try1null)
   2836 	{
   2837 		SM_FREE_CLR(vp);
   2838 		keylen++;
   2839 		yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
   2840 			     &vp, &vsize);
   2841 		if (yperr == 0)
   2842 			try0null = false;
   2843 	}
   2844 	if (yperr != 0)
   2845 	{
   2846 		if (yperr == YPERR_KEY)
   2847 			*statp = EX_NOHOST;
   2848 		else if (yperr == YPERR_BUSY)
   2849 			*statp = EX_TEMPFAIL;
   2850 		else
   2851 			*statp = EX_UNAVAILABLE;
   2852 		if (vp != NULL)
   2853 			sm_free(vp);
   2854 		return false;
   2855 	}
   2856 	(void) sm_strlcpy(host_record, vp, sizeof(host_record));
   2857 	sm_free(vp);
   2858 	if (tTd(38, 44))
   2859 		sm_dprintf("got record `%s'\n", host_record);
   2860 	vp = strpbrk(host_record, "#\n");
   2861 	if (vp != NULL)
   2862 		*vp = '\0';
   2863 	if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof(cbuf)))
   2864 	{
   2865 		/* this should not happen, but.... */
   2866 		*statp = EX_NOHOST;
   2867 		return false;
   2868 	}
   2869 	if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
   2870 	{
   2871 		*statp = EX_UNAVAILABLE;
   2872 		return false;
   2873 	}
   2874 	*statp = EX_OK;
   2875 	return true;
   2876 }
   2877 
   2878 #endif /* NIS */
   2879 /*
   2880 **  NISPLUS Modules
   2881 **
   2882 **	This code donated by Sun Microsystems.
   2883 */
   2884 
   2885 #if NISPLUS
   2886 
   2887 # undef NIS		/* symbol conflict in nis.h */
   2888 # undef T_UNSPEC	/* symbol conflict in nis.h -> ... -> sys/tiuser.h */
   2889 # include <rpcsvc/nis.h>
   2890 # include <rpcsvc/nislib.h>
   2891 
   2892 # define EN_col(col)	zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
   2893 # define COL_NAME(res,i)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
   2894 # define COL_MAX(res)	((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
   2895 # define PARTIAL_NAME(x)	((x)[strlen(x) - 1] != '.')
   2896 
   2897 /*
   2898 **  NISPLUS_MAP_OPEN -- open nisplus table
   2899 */
   2900 
   2901 bool
   2902 nisplus_map_open(map, mode)
   2903 	MAP *map;
   2904 	int mode;
   2905 {
   2906 	nis_result *res = NULL;
   2907 	int retry_cnt, max_col, i;
   2908 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
   2909 
   2910 	if (tTd(38, 2))
   2911 		sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
   2912 			map->map_mname, map->map_file, mode);
   2913 
   2914 	mode &= O_ACCMODE;
   2915 	if (mode != O_RDONLY)
   2916 	{
   2917 		errno = EPERM;
   2918 		return false;
   2919 	}
   2920 
   2921 	if (*map->map_file == '\0')
   2922 		map->map_file = "mail_aliases.org_dir";
   2923 
   2924 	if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
   2925 	{
   2926 		/* set default NISPLUS Domain to $m */
   2927 		map->map_domain = newstr(nisplus_default_domain());
   2928 		if (tTd(38, 2))
   2929 			sm_dprintf("nisplus_map_open(%s): using domain %s\n",
   2930 				map->map_file, map->map_domain);
   2931 	}
   2932 	if (!PARTIAL_NAME(map->map_file))
   2933 	{
   2934 		map->map_domain = newstr("");
   2935 		(void) sm_strlcpy(qbuf, map->map_file, sizeof(qbuf));
   2936 	}
   2937 	else
   2938 	{
   2939 		/* check to see if this map actually exists */
   2940 		(void) sm_strlcpyn(qbuf, sizeof(qbuf), 3,
   2941 				   map->map_file, ".", map->map_domain);
   2942 	}
   2943 
   2944 	retry_cnt = 0;
   2945 	while (res == NULL || res->status != NIS_SUCCESS)
   2946 	{
   2947 		res = nis_lookup(qbuf, FOLLOW_LINKS);
   2948 		switch (res->status)
   2949 		{
   2950 		  case NIS_SUCCESS:
   2951 			break;
   2952 
   2953 		  case NIS_TRYAGAIN:
   2954 		  case NIS_RPCERROR:
   2955 		  case NIS_NAMEUNREACHABLE:
   2956 			if (retry_cnt++ > 4)
   2957 			{
   2958 				errno = EAGAIN;
   2959 				return false;
   2960 			}
   2961 			/* try not to overwhelm hosed server */
   2962 			sleep(2);
   2963 			break;
   2964 
   2965 		  default:		/* all other nisplus errors */
   2966 # if 0
   2967 			if (!bitset(MF_OPTIONAL, map->map_mflags))
   2968 				syserr("451 4.3.5 Cannot find table %s.%s: %s",
   2969 					map->map_file, map->map_domain,
   2970 					nis_sperrno(res->status));
   2971 # endif /* 0 */
   2972 			errno = EAGAIN;
   2973 			return false;
   2974 		}
   2975 	}
   2976 
   2977 	if (NIS_RES_NUMOBJ(res) != 1 ||
   2978 	    (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
   2979 	{
   2980 		if (tTd(38, 10))
   2981 			sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
   2982 # if 0
   2983 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   2984 			syserr("451 4.3.5 %s.%s: %s is not a table",
   2985 				map->map_file, map->map_domain,
   2986 				nis_sperrno(res->status));
   2987 # endif /* 0 */
   2988 		errno = EBADF;
   2989 		return false;
   2990 	}
   2991 	/* default key column is column 0 */
   2992 	if (map->map_keycolnm == NULL)
   2993 		map->map_keycolnm = newstr(COL_NAME(res,0));
   2994 
   2995 	max_col = COL_MAX(res);
   2996 
   2997 	/* verify the key column exist */
   2998 	for (i = 0; i < max_col; i++)
   2999 	{
   3000 		if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
   3001 			break;
   3002 	}
   3003 	if (i == max_col)
   3004 	{
   3005 		if (tTd(38, 2))
   3006 			sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
   3007 				map->map_file, map->map_keycolnm);
   3008 		errno = ENOENT;
   3009 		return false;
   3010 	}
   3011 
   3012 	/* default value column is the last column */
   3013 	if (map->map_valcolnm == NULL)
   3014 	{
   3015 		map->map_valcolno = max_col - 1;
   3016 		return true;
   3017 	}
   3018 
   3019 	for (i = 0; i< max_col; i++)
   3020 	{
   3021 		if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
   3022 		{
   3023 			map->map_valcolno = i;
   3024 			return true;
   3025 		}
   3026 	}
   3027 
   3028 	if (tTd(38, 2))
   3029 		sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
   3030 			map->map_file, map->map_keycolnm);
   3031 	errno = ENOENT;
   3032 	return false;
   3033 }
   3034 
   3035 
   3036 /*
   3037 **  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
   3038 */
   3039 
   3040 char *
   3041 nisplus_map_lookup(map, name, av, statp)
   3042 	MAP *map;
   3043 	char *name;
   3044 	char **av;
   3045 	int *statp;
   3046 {
   3047 	char *p;
   3048 	auto int vsize;
   3049 	char *skp;
   3050 	int skleft;
   3051 	char search_key[MAXNAME + 4];
   3052 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
   3053 	nis_result *result;
   3054 
   3055 	if (tTd(38, 20))
   3056 		sm_dprintf("nisplus_map_lookup(%s, %s)\n",
   3057 			map->map_mname, name);
   3058 
   3059 	if (!bitset(MF_OPEN, map->map_mflags))
   3060 	{
   3061 		if (nisplus_map_open(map, O_RDONLY))
   3062 		{
   3063 			map->map_mflags |= MF_OPEN;
   3064 			map->map_pid = CurrentPid;
   3065 		}
   3066 		else
   3067 		{
   3068 			*statp = EX_UNAVAILABLE;
   3069 			return NULL;
   3070 		}
   3071 	}
   3072 
   3073 	/*
   3074 	**  Copy the name to the key buffer, escaping double quote characters
   3075 	**  by doubling them and quoting "]" and "," to avoid having the
   3076 	**  NIS+ parser choke on them.
   3077 	*/
   3078 
   3079 	skleft = sizeof(search_key) - 4;
   3080 	skp = search_key;
   3081 	for (p = name; *p != '\0' && skleft > 0; p++)
   3082 	{
   3083 		switch (*p)
   3084 		{
   3085 		  case ']':
   3086 		  case ',':
   3087 			/* quote the character */
   3088 			*skp++ = '"';
   3089 			*skp++ = *p;
   3090 			*skp++ = '"';
   3091 			skleft -= 3;
   3092 			break;
   3093 
   3094 		  case '"':
   3095 			/* double the quote */
   3096 			*skp++ = '"';
   3097 			skleft--;
   3098 			/* FALLTHROUGH */
   3099 
   3100 		  default:
   3101 			*skp++ = *p;
   3102 			skleft--;
   3103 			break;
   3104 		}
   3105 	}
   3106 	*skp = '\0';
   3107 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
   3108 		makelower(search_key);
   3109 
   3110 	/* construct the query */
   3111 	if (PARTIAL_NAME(map->map_file))
   3112 		(void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s.%s",
   3113 			map->map_keycolnm, search_key, map->map_file,
   3114 			map->map_domain);
   3115 	else
   3116 		(void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s",
   3117 			map->map_keycolnm, search_key, map->map_file);
   3118 
   3119 	if (tTd(38, 20))
   3120 		sm_dprintf("qbuf=%s\n", qbuf);
   3121 	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
   3122 	if (result->status == NIS_SUCCESS)
   3123 	{
   3124 		int count;
   3125 		char *str;
   3126 
   3127 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
   3128 		{
   3129 			if (LogLevel > 10)
   3130 				sm_syslog(LOG_WARNING, CurEnv->e_id,
   3131 					  "%s: lookup error, expected 1 entry, got %d",
   3132 					  map->map_file, count);
   3133 
   3134 			/* ignore second entry */
   3135 			if (tTd(38, 20))
   3136 				sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
   3137 					name, count);
   3138 		}
   3139 
   3140 		p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
   3141 		/* set the length of the result */
   3142 		if (p == NULL)
   3143 			p = "";
   3144 		vsize = strlen(p);
   3145 		if (tTd(38, 20))
   3146 			sm_dprintf("nisplus_map_lookup(%s), found %s\n",
   3147 				name, p);
   3148 		if (bitset(MF_MATCHONLY, map->map_mflags))
   3149 			str = map_rewrite(map, name, strlen(name), NULL);
   3150 		else
   3151 			str = map_rewrite(map, p, vsize, av);
   3152 		nis_freeresult(result);
   3153 		*statp = EX_OK;
   3154 		return str;
   3155 	}
   3156 	else
   3157 	{
   3158 		if (result->status == NIS_NOTFOUND)
   3159 			*statp = EX_NOTFOUND;
   3160 		else if (result->status == NIS_TRYAGAIN)
   3161 			*statp = EX_TEMPFAIL;
   3162 		else
   3163 		{
   3164 			*statp = EX_UNAVAILABLE;
   3165 			map->map_mflags &= ~(MF_VALID|MF_OPEN);
   3166 		}
   3167 	}
   3168 	if (tTd(38, 20))
   3169 		sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
   3170 	nis_freeresult(result);
   3171 	return NULL;
   3172 }
   3173 
   3174 
   3175 
   3176 /*
   3177 **  NISPLUS_GETCANONNAME -- look up canonical name in NIS+
   3178 */
   3179 
   3180 static bool
   3181 nisplus_getcanonname(name, hbsize, statp)
   3182 	char *name;
   3183 	int hbsize;
   3184 	int *statp;
   3185 {
   3186 	char *vp;
   3187 	auto int vsize;
   3188 	nis_result *result;
   3189 	char *p;
   3190 	char nbuf[MAXNAME + 1];
   3191 	char qbuf[MAXLINE + NIS_MAXNAMELEN];
   3192 
   3193 	if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
   3194 	{
   3195 		*statp = EX_UNAVAILABLE;
   3196 		return false;
   3197 	}
   3198 	(void) shorten_hostname(nbuf);
   3199 
   3200 	p = strchr(nbuf, '.');
   3201 	if (p == NULL)
   3202 	{
   3203 		/* single token */
   3204 		(void) sm_snprintf(qbuf, sizeof(qbuf),
   3205 			"[name=%s],hosts.org_dir", nbuf);
   3206 	}
   3207 	else if (p[1] != '\0')
   3208 	{
   3209 		/* multi token -- take only first token in nbuf */
   3210 		*p = '\0';
   3211 		(void) sm_snprintf(qbuf, sizeof(qbuf),
   3212 				   "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
   3213 	}
   3214 	else
   3215 	{
   3216 		*statp = EX_NOHOST;
   3217 		return false;
   3218 	}
   3219 
   3220 	if (tTd(38, 20))
   3221 		sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
   3222 			   name, qbuf);
   3223 
   3224 	result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
   3225 			  NULL, NULL);
   3226 
   3227 	if (result->status == NIS_SUCCESS)
   3228 	{
   3229 		int count;
   3230 		char *domain;
   3231 
   3232 		if ((count = NIS_RES_NUMOBJ(result)) != 1)
   3233 		{
   3234 			if (LogLevel > 10)
   3235 				sm_syslog(LOG_WARNING, CurEnv->e_id,
   3236 					  "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
   3237 					  count);
   3238 
   3239 			/* ignore second entry */
   3240 			if (tTd(38, 20))
   3241 				sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
   3242 					   name, count);
   3243 		}
   3244 
   3245 		if (tTd(38, 20))
   3246 			sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
   3247 				   name, (NIS_RES_OBJECT(result))->zo_domain);
   3248 
   3249 
   3250 		vp = ((NIS_RES_OBJECT(result))->EN_col(0));
   3251 		vsize = strlen(vp);
   3252 		if (tTd(38, 20))
   3253 			sm_dprintf("nisplus_getcanonname(%s), found %s\n",
   3254 				   name, vp);
   3255 		if (strchr(vp, '.') != NULL)
   3256 		{
   3257 			domain = "";
   3258 		}
   3259 		else
   3260 		{
   3261 			domain = macvalue('m', CurEnv);
   3262 			if (domain == NULL)
   3263 				domain = "";
   3264 		}
   3265 		if (hbsize > vsize + (int) strlen(domain) + 1)
   3266 		{
   3267 			if (domain[0] == '\0')
   3268 				(void) sm_strlcpy(name, vp, hbsize);
   3269 			else
   3270 				(void) sm_snprintf(name, hbsize,
   3271 						   "%s.%s", vp, domain);
   3272 			*statp = EX_OK;
   3273 		}
   3274 		else
   3275 			*statp = EX_NOHOST;
   3276 		nis_freeresult(result);
   3277 		return true;
   3278 	}
   3279 	else
   3280 	{
   3281 		if (result->status == NIS_NOTFOUND)
   3282 			*statp = EX_NOHOST;
   3283 		else if (result->status == NIS_TRYAGAIN)
   3284 			*statp = EX_TEMPFAIL;
   3285 		else
   3286 			*statp = EX_UNAVAILABLE;
   3287 	}
   3288 	if (tTd(38, 20))
   3289 		sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
   3290 			   name, result->status, *statp);
   3291 	nis_freeresult(result);
   3292 	return false;
   3293 }
   3294 
   3295 char *
   3296 nisplus_default_domain()
   3297 {
   3298 	static char default_domain[MAXNAME + 1] = "";
   3299 	char *p;
   3300 
   3301 	if (default_domain[0] != '\0')
   3302 		return default_domain;
   3303 
   3304 	p = nis_local_directory();
   3305 	(void) sm_strlcpy(default_domain, p, sizeof(default_domain));
   3306 	return default_domain;
   3307 }
   3308 
   3309 #endif /* NISPLUS */
   3310 /*
   3311 **  LDAP Modules
   3312 */
   3313 
   3314 /*
   3315 **  LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
   3316 */
   3317 
   3318 #if defined(LDAPMAP) || defined(PH_MAP)
   3319 
   3320 # if PH_MAP
   3321 #  define ph_map_dequote ldapmap_dequote
   3322 # endif /* PH_MAP */
   3323 
   3324 static char *ldapmap_dequote __P((char *));
   3325 
   3326 static char *
   3327 ldapmap_dequote(str)
   3328 	char *str;
   3329 {
   3330 	char *p;
   3331 	char *start;
   3332 
   3333 	if (str == NULL)
   3334 		return NULL;
   3335 
   3336 	p = str;
   3337 	if (*p == '"')
   3338 	{
   3339 		/* Should probably swallow initial whitespace here */
   3340 		start = ++p;
   3341 	}
   3342 	else
   3343 		return str;
   3344 	while (*p != '"' && *p != '\0')
   3345 		p++;
   3346 	if (*p != '\0')
   3347 		*p = '\0';
   3348 	return start;
   3349 }
   3350 #endif /* defined(LDAPMAP) || defined(PH_MAP) */
   3351 
   3352 #if LDAPMAP
   3353 
   3354 static SM_LDAP_STRUCT *LDAPDefaults = NULL;
   3355 
   3356 /*
   3357 **  LDAPMAP_OPEN -- open LDAP map
   3358 **
   3359 **	Connect to the LDAP server.  Re-use existing connections since a
   3360 **	single server connection to a host (with the same host, port,
   3361 **	bind DN, and secret) can answer queries for multiple maps.
   3362 */
   3363 
   3364 bool
   3365 ldapmap_open(map, mode)
   3366 	MAP *map;
   3367 	int mode;
   3368 {
   3369 	SM_LDAP_STRUCT *lmap;
   3370 	STAB *s;
   3371 	char *id;
   3372 
   3373 	if (tTd(38, 2))
   3374 		sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
   3375 
   3376 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
   3377     HASLDAPGETALIASBYNAME
   3378 	if (VendorCode == VENDOR_SUN &&
   3379 	    strcmp(map->map_mname, "aliases.ldap") == 0)
   3380 	{
   3381 		return true;
   3382 	}
   3383 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
   3384 
   3385 	mode &= O_ACCMODE;
   3386 
   3387 	/* sendmail doesn't have the ability to write to LDAP (yet) */
   3388 	if (mode != O_RDONLY)
   3389 	{
   3390 		/* issue a pseudo-error message */
   3391 		errno = SM_EMAPCANTWRITE;
   3392 		return false;
   3393 	}
   3394 
   3395 	lmap = (SM_LDAP_STRUCT *) map->map_db1;
   3396 
   3397 	s = ldapmap_findconn(lmap);
   3398 	if (s->s_lmap != NULL)
   3399 	{
   3400 		/* Already have a connection open to this LDAP server */
   3401 		lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
   3402 		lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
   3403 
   3404 		/* Add this map as head of linked list */
   3405 		lmap->ldap_next = s->s_lmap;
   3406 		s->s_lmap = map;
   3407 
   3408 		if (tTd(38, 2))
   3409 			sm_dprintf("using cached connection\n");
   3410 		return true;
   3411 	}
   3412 
   3413 	if (tTd(38, 2))
   3414 		sm_dprintf("opening new connection\n");
   3415 
   3416 	if (lmap->ldap_host != NULL)
   3417 		id = lmap->ldap_host;
   3418 	else if (lmap->ldap_uri != NULL)
   3419 		id = lmap->ldap_uri;
   3420 	else
   3421 		id = "localhost";
   3422 
   3423 	if (tTd(74, 104))
   3424 	{
   3425 		extern MAPCLASS NullMapClass;
   3426 
   3427 		/* debug mode: don't actually open an LDAP connection */
   3428 		map->map_orgclass = map->map_class;
   3429 		map->map_class = &NullMapClass;
   3430 		map->map_mflags |= MF_OPEN;
   3431 		map->map_pid = CurrentPid;
   3432 		return true;
   3433 	}
   3434 
   3435 	/* No connection yet, connect */
   3436 	if (!sm_ldap_start(map->map_mname, lmap))
   3437 	{
   3438 		if (errno == ETIMEDOUT)
   3439 		{
   3440 			if (LogLevel > 1)
   3441 				sm_syslog(LOG_NOTICE, CurEnv->e_id,
   3442 					  "timeout conning to LDAP server %.100s",
   3443 					  id);
   3444 		}
   3445 
   3446 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   3447 		{
   3448 			if (bitset(MF_NODEFER, map->map_mflags))
   3449 			{
   3450 				syserr("%s failed to %s in map %s",
   3451 # if USE_LDAP_INIT
   3452 				       "ldap_init/ldap_bind",
   3453 # else /* USE_LDAP_INIT */
   3454 				       "ldap_open",
   3455 # endif /* USE_LDAP_INIT */
   3456 				       id, map->map_mname);
   3457 			}
   3458 			else
   3459 			{
   3460 				syserr("451 4.3.5 %s failed to %s in map %s",
   3461 # if USE_LDAP_INIT
   3462 				       "ldap_init/ldap_bind",
   3463 # else /* USE_LDAP_INIT */
   3464 				       "ldap_open",
   3465 # endif /* USE_LDAP_INIT */
   3466 				       id, map->map_mname);
   3467 			}
   3468 		}
   3469 		return false;
   3470 	}
   3471 
   3472 	/* Save connection for reuse */
   3473 	s->s_lmap = map;
   3474 	return true;
   3475 }
   3476 
   3477 /*
   3478 **  LDAPMAP_CLOSE -- close ldap map
   3479 */
   3480 
   3481 void
   3482 ldapmap_close(map)
   3483 	MAP *map;
   3484 {
   3485 	SM_LDAP_STRUCT *lmap;
   3486 	STAB *s;
   3487 
   3488 	if (tTd(38, 2))
   3489 		sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
   3490 
   3491 	lmap = (SM_LDAP_STRUCT *) map->map_db1;
   3492 
   3493 	/* Check if already closed */
   3494 	if (lmap->ldap_ld == NULL)
   3495 		return;
   3496 
   3497 	/* Close the LDAP connection */
   3498 	sm_ldap_close(lmap);
   3499 
   3500 	/* Mark all the maps that share the connection as closed */
   3501 	s = ldapmap_findconn(lmap);
   3502 
   3503 	while (s->s_lmap != NULL)
   3504 	{
   3505 		MAP *smap = s->s_lmap;
   3506 
   3507 		if (tTd(38, 2) && smap != map)
   3508 			sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
   3509 				   map->map_mname, smap->map_mname);
   3510 		smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
   3511 		lmap = (SM_LDAP_STRUCT *) smap->map_db1;
   3512 		lmap->ldap_ld = NULL;
   3513 		s->s_lmap = lmap->ldap_next;
   3514 		lmap->ldap_next = NULL;
   3515 	}
   3516 }
   3517 
   3518 # ifdef SUNET_ID
   3519 /*
   3520 **  SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
   3521 **  This only makes sense at Stanford University.
   3522 */
   3523 
   3524 static char *
   3525 sunet_id_hash(str)
   3526 	char *str;
   3527 {
   3528 	char *p, *p_last;
   3529 
   3530 	p = str;
   3531 	p_last = p;
   3532 	while (*p != '\0')
   3533 	{
   3534 		if (isascii(*p) && (islower(*p) || isdigit(*p)))
   3535 		{
   3536 			*p_last = *p;
   3537 			p_last++;
   3538 		}
   3539 		else if (isascii(*p) && isupper(*p))
   3540 		{
   3541 			*p_last = tolower(*p);
   3542 			p_last++;
   3543 		}
   3544 		++p;
   3545 	}
   3546 	if (*p_last != '\0')
   3547 		*p_last = '\0';
   3548 	return str;
   3549 }
   3550 #  define SM_CONVERT_ID(str)	sunet_id_hash(str)
   3551 # else /* SUNET_ID */
   3552 #  define SM_CONVERT_ID(str)	makelower(str)
   3553 # endif /* SUNET_ID */
   3554 
   3555 /*
   3556 **  LDAPMAP_LOOKUP -- look up a datum in a LDAP map
   3557 */
   3558 
   3559 char *
   3560 ldapmap_lookup(map, name, av, statp)
   3561 	MAP *map;
   3562 	char *name;
   3563 	char **av;
   3564 	int *statp;
   3565 {
   3566 	int flags;
   3567 	int i;
   3568 	int plen = 0;
   3569 	int psize = 0;
   3570 	int msgid;
   3571 	int save_errno;
   3572 	char *vp, *p;
   3573 	char *result = NULL;
   3574 	SM_RPOOL_T *rpool;
   3575 	SM_LDAP_STRUCT *lmap = NULL;
   3576 	char *argv[SM_LDAP_ARGS];
   3577 	char keybuf[MAXKEY];
   3578 #if SM_LDAP_ARGS != MAX_MAP_ARGS
   3579 # ERROR _SM_LDAP_ARGS must be the same as _MAX_MAP_ARGS
   3580 #endif /* SM_LDAP_ARGS != MAX_MAP_ARGS */
   3581 
   3582 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
   3583     HASLDAPGETALIASBYNAME
   3584 	if (VendorCode == VENDOR_SUN &&
   3585 	    strcmp(map->map_mname, "aliases.ldap") == 0)
   3586 	{
   3587 		int rc;
   3588 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
   3589 		extern char *__getldapaliasbyname();
   3590 		char *answer;
   3591 
   3592 		answer = __getldapaliasbyname(name, &rc);
   3593 #else
   3594 		char answer[MAXNAME + 1];
   3595 
   3596 		rc = __getldapaliasbyname(name, answer, sizeof(answer));
   3597 #endif
   3598 		if (rc != 0)
   3599 		{
   3600 			if (tTd(38, 20))
   3601 				sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n",
   3602 					   name, errno);
   3603 			*statp = EX_NOTFOUND;
   3604 			return NULL;
   3605 		}
   3606 		*statp = EX_OK;
   3607 		if (tTd(38, 20))
   3608 			sm_dprintf("getldapaliasbyname(%.100s) => %s\n", name,
   3609 				   answer);
   3610 		if (bitset(MF_MATCHONLY, map->map_mflags))
   3611 			result = map_rewrite(map, name, strlen(name), NULL);
   3612 		else
   3613 			result = map_rewrite(map, answer, strlen(answer), av);
   3614 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
   3615 		free(answer);
   3616 #endif
   3617 		return result;
   3618 	}
   3619 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
   3620 
   3621 	/* Get ldap struct pointer from map */
   3622 	lmap = (SM_LDAP_STRUCT *) map->map_db1;
   3623 	sm_ldap_setopts(lmap->ldap_ld, lmap);
   3624 
   3625 	if (lmap->ldap_multi_args)
   3626 	{
   3627 		SM_REQUIRE(av != NULL);
   3628 		memset(argv, '\0', sizeof(argv));
   3629 		for (i = 0; i < SM_LDAP_ARGS && av[i] != NULL; i++)
   3630 		{
   3631 			argv[i] = sm_strdup(av[i]);
   3632 			if (argv[i] == NULL)
   3633 			{
   3634 				int save_errno, j;
   3635 
   3636 				save_errno = errno;
   3637 				for (j = 0; j < i && argv[j] != NULL; j++)
   3638 					SM_FREE(argv[j]);
   3639 				*statp = EX_TEMPFAIL;
   3640 				errno = save_errno;
   3641 				return NULL;
   3642 			}
   3643 
   3644 			if (!bitset(MF_NOFOLDCASE, map->map_mflags))
   3645 				SM_CONVERT_ID(av[i]);
   3646 		}
   3647 	}
   3648 	else
   3649 	{
   3650 		(void) sm_strlcpy(keybuf, name, sizeof(keybuf));
   3651 
   3652 		if (!bitset(MF_NOFOLDCASE, map->map_mflags))
   3653 			SM_CONVERT_ID(keybuf);
   3654 	}
   3655 
   3656 	if (tTd(38, 20))
   3657 	{
   3658 		if (lmap->ldap_multi_args)
   3659 		{
   3660 			sm_dprintf("ldapmap_lookup(%s, argv)\n",
   3661 				map->map_mname);
   3662 			for (i = 0; i < SM_LDAP_ARGS; i++)
   3663 			{
   3664 				sm_dprintf("   argv[%d] = %s\n", i,
   3665 					   argv[i] == NULL ? "NULL" : argv[i]);
   3666 			}
   3667 		}
   3668 		else
   3669 		{
   3670 			sm_dprintf("ldapmap_lookup(%s, %s)\n",
   3671 				   map->map_mname, name);
   3672 		}
   3673 	}
   3674 
   3675 	if (lmap->ldap_multi_args)
   3676 	{
   3677 		msgid = sm_ldap_search_m(lmap, argv);
   3678 
   3679 		/* free the argv array and its content, no longer needed */
   3680 		for (i = 0; i < SM_LDAP_ARGS && argv[i] != NULL; i++)
   3681 			SM_FREE(argv[i]);
   3682 	}
   3683 	else
   3684 		msgid = sm_ldap_search(lmap, keybuf);
   3685 	if (msgid == SM_LDAP_ERR)
   3686 	{
   3687 		errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
   3688 		save_errno = errno;
   3689 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   3690 		{
   3691 			/*
   3692 			**  Do not include keybuf as this error may be shown
   3693 			**  to outsiders.
   3694 			*/
   3695 
   3696 			if (bitset(MF_NODEFER, map->map_mflags))
   3697 				syserr("Error in ldap_search in map %s",
   3698 				       map->map_mname);
   3699 			else
   3700 				syserr("451 4.3.5 Error in ldap_search in map %s",
   3701 				       map->map_mname);
   3702 		}
   3703 		*statp = EX_TEMPFAIL;
   3704 		switch (save_errno - E_LDAPBASE)
   3705 		{
   3706 # ifdef LDAP_SERVER_DOWN
   3707 		  case LDAP_SERVER_DOWN:
   3708 # endif /* LDAP_SERVER_DOWN */
   3709 		  case LDAP_TIMEOUT:
   3710 		  case LDAP_UNAVAILABLE:
   3711 			/* server disappeared, try reopen on next search */
   3712 			ldapmap_close(map);
   3713 			break;
   3714 		}
   3715 		errno = save_errno;
   3716 		return NULL;
   3717 	}
   3718 #if SM_LDAP_ERROR_ON_MISSING_ARGS
   3719 	else if (msgid == SM_LDAP_ERR_ARG_MISS)
   3720 	{
   3721 		if (bitset(MF_NODEFER, map->map_mflags))
   3722 			syserr("Error in ldap_search in map %s, too few arguments",
   3723 			       map->map_mname);
   3724 		else
   3725 			syserr("554 5.3.5 Error in ldap_search in map %s, too few arguments",
   3726 			       map->map_mname);
   3727 		*statp = EX_CONFIG;
   3728 		return NULL;
   3729 	}
   3730 #endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
   3731 
   3732 	*statp = EX_NOTFOUND;
   3733 	vp = NULL;
   3734 
   3735 	flags = 0;
   3736 	if (bitset(MF_SINGLEMATCH, map->map_mflags))
   3737 		flags |= SM_LDAP_SINGLEMATCH;
   3738 	if (bitset(MF_MATCHONLY, map->map_mflags))
   3739 		flags |= SM_LDAP_MATCHONLY;
   3740 # if _FFR_LDAP_SINGLEDN
   3741 	if (bitset(MF_SINGLEDN, map->map_mflags))
   3742 		flags |= SM_LDAP_SINGLEDN;
   3743 # endif /* _FFR_LDAP_SINGLEDN */
   3744 
   3745 	/* Create an rpool for search related memory usage */
   3746 	rpool = sm_rpool_new_x(NULL);
   3747 
   3748 	p = NULL;
   3749 	*statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
   3750 				 rpool, &p, &plen, &psize, NULL);
   3751 	save_errno = errno;
   3752 
   3753 	/* Copy result so rpool can be freed */
   3754 	if (*statp == EX_OK && p != NULL)
   3755 		vp = newstr(p);
   3756 	sm_rpool_free(rpool);
   3757 
   3758 	/* need to restart LDAP connection? */
   3759 	if (*statp == EX_RESTART)
   3760 	{
   3761 		*statp = EX_TEMPFAIL;
   3762 		ldapmap_close(map);
   3763 	}
   3764 
   3765 	errno = save_errno;
   3766 	if (*statp != EX_OK && *statp != EX_NOTFOUND)
   3767 	{
   3768 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   3769 		{
   3770 			if (bitset(MF_NODEFER, map->map_mflags))
   3771 				syserr("Error getting LDAP results in map %s",
   3772 				       map->map_mname);
   3773 			else
   3774 				syserr("451 4.3.5 Error getting LDAP results in map %s",
   3775 				       map->map_mname);
   3776 		}
   3777 		errno = save_errno;
   3778 		return NULL;
   3779 	}
   3780 
   3781 	/* Did we match anything? */
   3782 	if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
   3783 		return NULL;
   3784 
   3785 	if (*statp == EX_OK)
   3786 	{
   3787 		if (LogLevel > 9)
   3788 			sm_syslog(LOG_INFO, CurEnv->e_id,
   3789 				  "ldap %.100s => %s", name,
   3790 				  vp == NULL ? "<NULL>" : vp);
   3791 		if (bitset(MF_MATCHONLY, map->map_mflags))
   3792 			result = map_rewrite(map, name, strlen(name), NULL);
   3793 		else
   3794 		{
   3795 			/* vp != NULL according to test above */
   3796 			result = map_rewrite(map, vp, strlen(vp), av);
   3797 		}
   3798 		if (vp != NULL)
   3799 			sm_free(vp); /* XXX */
   3800 	}
   3801 	return result;
   3802 }
   3803 
   3804 /*
   3805 **  LDAPMAP_FINDCONN -- find an LDAP connection to the server
   3806 **
   3807 **	Cache LDAP connections based on the host, port, bind DN,
   3808 **	secret, and PID so we don't have multiple connections open to
   3809 **	the same server for different maps.  Need a separate connection
   3810 **	per PID since a parent process may close the map before the
   3811 **	child is done with it.
   3812 **
   3813 **	Parameters:
   3814 **		lmap -- LDAP map information
   3815 **
   3816 **	Returns:
   3817 **		Symbol table entry for the LDAP connection.
   3818 */
   3819 
   3820 static STAB *
   3821 ldapmap_findconn(lmap)
   3822 	SM_LDAP_STRUCT *lmap;
   3823 {
   3824 	char *format;
   3825 	char *nbuf;
   3826 	char *id;
   3827 	STAB *SM_NONVOLATILE s = NULL;
   3828 
   3829 	if (lmap->ldap_host != NULL)
   3830 		id = lmap->ldap_host;
   3831 	else if (lmap->ldap_uri != NULL)
   3832 		id = lmap->ldap_uri;
   3833 	else
   3834 		id = "localhost";
   3835 
   3836 	format = "%s%c%d%c%d%c%s%c%s%d";
   3837 	nbuf = sm_stringf_x(format,
   3838 			    id,
   3839 			    CONDELSE,
   3840 			    lmap->ldap_port,
   3841 			    CONDELSE,
   3842 			    lmap->ldap_version,
   3843 			    CONDELSE,
   3844 			    (lmap->ldap_binddn == NULL ? ""
   3845 						       : lmap->ldap_binddn),
   3846 			    CONDELSE,
   3847 			    (lmap->ldap_secret == NULL ? ""
   3848 						       : lmap->ldap_secret),
   3849 			    (int) CurrentPid);
   3850 	SM_TRY
   3851 		s = stab(nbuf, ST_LMAP, ST_ENTER);
   3852 	SM_FINALLY
   3853 		sm_free(nbuf);
   3854 	SM_END_TRY
   3855 	return s;
   3856 }
   3857 /*
   3858 **  LDAPMAP_PARSEARGS -- parse ldap map definition args.
   3859 */
   3860 
   3861 static struct lamvalues LDAPAuthMethods[] =
   3862 {
   3863 	{	"none",		LDAP_AUTH_NONE		},
   3864 	{	"simple",	LDAP_AUTH_SIMPLE	},
   3865 # ifdef LDAP_AUTH_KRBV4
   3866 	{	"krbv4",	LDAP_AUTH_KRBV4		},
   3867 # endif /* LDAP_AUTH_KRBV4 */
   3868 	{	NULL,		0			}
   3869 };
   3870 
   3871 static struct ladvalues LDAPAliasDereference[] =
   3872 {
   3873 	{	"never",	LDAP_DEREF_NEVER	},
   3874 	{	"always",	LDAP_DEREF_ALWAYS	},
   3875 	{	"search",	LDAP_DEREF_SEARCHING	},
   3876 	{	"find",		LDAP_DEREF_FINDING	},
   3877 	{	NULL,		0			}
   3878 };
   3879 
   3880 static struct lssvalues LDAPSearchScope[] =
   3881 {
   3882 	{	"base",		LDAP_SCOPE_BASE		},
   3883 	{	"one",		LDAP_SCOPE_ONELEVEL	},
   3884 	{	"sub",		LDAP_SCOPE_SUBTREE	},
   3885 	{	NULL,		0			}
   3886 };
   3887 
   3888 bool
   3889 ldapmap_parseargs(map, args)
   3890 	MAP *map;
   3891 	char *args;
   3892 {
   3893 	bool secretread = true;
   3894 	bool attrssetup = false;
   3895 	int i;
   3896 	register char *p = args;
   3897 	SM_LDAP_STRUCT *lmap;
   3898 	struct lamvalues *lam;
   3899 	struct ladvalues *lad;
   3900 	struct lssvalues *lss;
   3901 	char ldapfilt[MAXLINE];
   3902 	char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
   3903 
   3904 	/* Get ldap struct pointer from map */
   3905 	lmap = (SM_LDAP_STRUCT *) map->map_db1;
   3906 
   3907 	/* Check if setting the initial LDAP defaults */
   3908 	if (lmap == NULL || lmap != LDAPDefaults)
   3909 	{
   3910 		/* We need to alloc an SM_LDAP_STRUCT struct */
   3911 		lmap = (SM_LDAP_STRUCT *) xalloc(sizeof(*lmap));
   3912 		if (LDAPDefaults == NULL)
   3913 			sm_ldap_clear(lmap);
   3914 		else
   3915 			STRUCTCOPY(*LDAPDefaults, *lmap);
   3916 	}
   3917 
   3918 	/* there is no check whether there is really an argument */
   3919 	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
   3920 	map->map_spacesub = SpaceSub;	/* default value */
   3921 
   3922 	/* Check if setting up an alias or file class LDAP map */
   3923 	if (bitset(MF_ALIAS, map->map_mflags))
   3924 	{
   3925 		/* Comma separate if used as an alias file */
   3926 		map->map_coldelim = ',';
   3927 		if (*args == '\0')
   3928 		{
   3929 			int n;
   3930 			char *lc;
   3931 			char jbuf[MAXHOSTNAMELEN];
   3932 			char lcbuf[MAXLINE];
   3933 
   3934 			/* Get $j */
   3935 			expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
   3936 			if (jbuf[0] == '\0')
   3937 			{
   3938 				(void) sm_strlcpy(jbuf, "localhost",
   3939 						  sizeof(jbuf));
   3940 			}
   3941 
   3942 			lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
   3943 			if (lc == NULL)
   3944 				lc = "";
   3945 			else
   3946 			{
   3947 				expand(lc, lcbuf, sizeof(lcbuf), CurEnv);
   3948 				lc = lcbuf;
   3949 			}
   3950 
   3951 			n = sm_snprintf(ldapfilt, sizeof(ldapfilt),
   3952 					"(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
   3953 					lc, jbuf);
   3954 			if (n >= sizeof(ldapfilt))
   3955 			{
   3956 				syserr("%s: Default LDAP string too long",
   3957 				       map->map_mname);
   3958 				return false;
   3959 			}
   3960 
   3961 			/* default args for an alias LDAP entry */
   3962 			lmap->ldap_filter = ldapfilt;
   3963 			lmap->ldap_attr[0] = "objectClass";
   3964 			lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
   3965 			lmap->ldap_attr_needobjclass[0] = NULL;
   3966 			lmap->ldap_attr[1] = "sendmailMTAAliasValue";
   3967 			lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
   3968 			lmap->ldap_attr_needobjclass[1] = NULL;
   3969 			lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
   3970 			lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
   3971 			lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
   3972 			lmap->ldap_attr[3] = "sendmailMTAAliasURL";
   3973 			lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
   3974 			lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
   3975 			lmap->ldap_attr[4] = NULL;
   3976 			lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
   3977 			lmap->ldap_attr_needobjclass[4] = NULL;
   3978 			attrssetup = true;
   3979 		}
   3980 	}
   3981 	else if (bitset(MF_FILECLASS, map->map_mflags))
   3982 	{
   3983 		/* Space separate if used as a file class file */
   3984 		map->map_coldelim = ' ';
   3985 	}
   3986 
   3987 # if _FFR_LDAP_NETWORK_TIMEOUT
   3988 	lmap->ldap_networktmo = 120;
   3989 # endif /* _FFR_LDAP_NETWORK_TIMEOUT */
   3990 
   3991 	for (;;)
   3992 	{
   3993 		while (isascii(*p) && isspace(*p))
   3994 			p++;
   3995 		if (*p != '-')
   3996 			break;
   3997 		switch (*++p)
   3998 		{
   3999 		  case 'A':
   4000 			map->map_mflags |= MF_APPEND;
   4001 			break;
   4002 
   4003 		  case 'a':
   4004 			map->map_app = ++p;
   4005 			break;
   4006 
   4007 		  case 'D':
   4008 			map->map_mflags |= MF_DEFER;
   4009 			break;
   4010 
   4011 		  case 'f':
   4012 			map->map_mflags |= MF_NOFOLDCASE;
   4013 			break;
   4014 
   4015 		  case 'm':
   4016 			map->map_mflags |= MF_MATCHONLY;
   4017 			break;
   4018 
   4019 		  case 'N':
   4020 			map->map_mflags |= MF_INCLNULL;
   4021 			map->map_mflags &= ~MF_TRY0NULL;
   4022 			break;
   4023 
   4024 		  case 'O':
   4025 			map->map_mflags &= ~MF_TRY1NULL;
   4026 			break;
   4027 
   4028 		  case 'o':
   4029 			map->map_mflags |= MF_OPTIONAL;
   4030 			break;
   4031 
   4032 		  case 'q':
   4033 			map->map_mflags |= MF_KEEPQUOTES;
   4034 			break;
   4035 
   4036 		  case 'S':
   4037 			map->map_spacesub = *++p;
   4038 			break;
   4039 
   4040 		  case 'T':
   4041 			map->map_tapp = ++p;
   4042 			break;
   4043 
   4044 		  case 't':
   4045 			map->map_mflags |= MF_NODEFER;
   4046 			break;
   4047 
   4048 		  case 'z':
   4049 			if (*++p != '\\')
   4050 				map->map_coldelim = *p;
   4051 			else
   4052 			{
   4053 				switch (*++p)
   4054 				{
   4055 				  case 'n':
   4056 					map->map_coldelim = '\n';
   4057 					break;
   4058 
   4059 				  case 't':
   4060 					map->map_coldelim = '\t';
   4061 					break;
   4062 
   4063 				  default:
   4064 					map->map_coldelim = '\\';
   4065 				}
   4066 			}
   4067 			break;
   4068 
   4069 			/* Start of ldapmap specific args */
   4070 		  case '1':
   4071 			map->map_mflags |= MF_SINGLEMATCH;
   4072 			break;
   4073 
   4074 # if _FFR_LDAP_SINGLEDN
   4075 		  case '2':
   4076 			map->map_mflags |= MF_SINGLEDN;
   4077 			break;
   4078 # endif /* _FFR_LDAP_SINGLEDN */
   4079 
   4080 		  case 'b':		/* search base */
   4081 			while (isascii(*++p) && isspace(*p))
   4082 				continue;
   4083 			lmap->ldap_base = p;
   4084 			break;
   4085 
   4086 # if _FFR_LDAP_NETWORK_TIMEOUT
   4087 		  case 'c':		/* network (connect) timeout */
   4088 			while (isascii(*++p) && isspace(*p))
   4089 				continue;
   4090 			lmap->ldap_networktmo = atoi(p);
   4091 			break;
   4092 # endif /* _FFR_LDAP_NETWORK_TIMEOUT */
   4093 
   4094 		  case 'd':		/* Dn to bind to server as */
   4095 			while (isascii(*++p) && isspace(*p))
   4096 				continue;
   4097 			lmap->ldap_binddn = p;
   4098 			break;
   4099 
   4100 		  case 'H':		/* Use LDAP URI */
   4101 #  if !USE_LDAP_INIT
   4102 			syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
   4103 			       map->map_mname);
   4104 			return false;
   4105 #   else /* !USE_LDAP_INIT */
   4106 			if (lmap->ldap_host != NULL)
   4107 			{
   4108 				syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
   4109 				       map->map_mname);
   4110 				return false;
   4111 			}
   4112 			while (isascii(*++p) && isspace(*p))
   4113 				continue;
   4114 			lmap->ldap_uri = p;
   4115 			break;
   4116 #  endif /* !USE_LDAP_INIT */
   4117 
   4118 		  case 'h':		/* ldap host */
   4119 			while (isascii(*++p) && isspace(*p))
   4120 				continue;
   4121 			if (lmap->ldap_uri != NULL)
   4122 			{
   4123 				syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
   4124 				       map->map_mname);
   4125 				return false;
   4126 			}
   4127 			lmap->ldap_host = p;
   4128 			break;
   4129 
   4130 		  case 'K':
   4131 			lmap->ldap_multi_args = true;
   4132 			break;
   4133 
   4134 		  case 'k':		/* search field */
   4135 			while (isascii(*++p) && isspace(*p))
   4136 				continue;
   4137 			lmap->ldap_filter = p;
   4138 			break;
   4139 
   4140 		  case 'l':		/* time limit */
   4141 			while (isascii(*++p) && isspace(*p))
   4142 				continue;
   4143 			lmap->ldap_timelimit = atoi(p);
   4144 			lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
   4145 			break;
   4146 
   4147 		  case 'M':		/* Method for binding */
   4148 			while (isascii(*++p) && isspace(*p))
   4149 				continue;
   4150 
   4151 			if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
   4152 				p += 10;
   4153 
   4154 			for (lam = LDAPAuthMethods;
   4155 			     lam != NULL && lam->lam_name != NULL; lam++)
   4156 			{
   4157 				if (sm_strncasecmp(p, lam->lam_name,
   4158 						   strlen(lam->lam_name)) == 0)
   4159 					break;
   4160 			}
   4161 			if (lam->lam_name != NULL)
   4162 				lmap->ldap_method = lam->lam_code;
   4163 			else
   4164 			{
   4165 				/* bad config line */
   4166 				if (!bitset(MCF_OPTFILE,
   4167 					    map->map_class->map_cflags))
   4168 				{
   4169 					char *ptr;
   4170 
   4171 					if ((ptr = strchr(p, ' ')) != NULL)
   4172 						*ptr = '\0';
   4173 					syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
   4174 						p, map->map_mname);
   4175 					if (ptr != NULL)
   4176 						*ptr = ' ';
   4177 					return false;
   4178 				}
   4179 			}
   4180 			break;
   4181 
   4182 		  case 'n':		/* retrieve attribute names only */
   4183 			lmap->ldap_attrsonly = LDAPMAP_TRUE;
   4184 			break;
   4185 
   4186 			/*
   4187 			**  This is a string that is dependent on the
   4188 			**  method used defined by 'M'.
   4189 			*/
   4190 
   4191 		  case 'P':		/* Secret password for binding */
   4192 			 while (isascii(*++p) && isspace(*p))
   4193 				continue;
   4194 			lmap->ldap_secret = p;
   4195 			secretread = false;
   4196 			break;
   4197 
   4198 		  case 'p':		/* ldap port */
   4199 			while (isascii(*++p) && isspace(*p))
   4200 				continue;
   4201 			lmap->ldap_port = atoi(p);
   4202 			break;
   4203 
   4204 			/* args stolen from ldapsearch.c */
   4205 		  case 'R':		/* don't auto chase referrals */
   4206 # ifdef LDAP_REFERRALS
   4207 			lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
   4208 # else /* LDAP_REFERRALS */
   4209 			syserr("compile with -DLDAP_REFERRALS for referral support");
   4210 # endif /* LDAP_REFERRALS */
   4211 			break;
   4212 
   4213 		  case 'r':		/* alias dereferencing */
   4214 			while (isascii(*++p) && isspace(*p))
   4215 				continue;
   4216 
   4217 			if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
   4218 				p += 11;
   4219 
   4220 			for (lad = LDAPAliasDereference;
   4221 			     lad != NULL && lad->lad_name != NULL; lad++)
   4222 			{
   4223 				if (sm_strncasecmp(p, lad->lad_name,
   4224 						   strlen(lad->lad_name)) == 0)
   4225 					break;
   4226 			}
   4227 			if (lad->lad_name != NULL)
   4228 				lmap->ldap_deref = lad->lad_code;
   4229 			else
   4230 			{
   4231 				/* bad config line */
   4232 				if (!bitset(MCF_OPTFILE,
   4233 					    map->map_class->map_cflags))
   4234 				{
   4235 					char *ptr;
   4236 
   4237 					if ((ptr = strchr(p, ' ')) != NULL)
   4238 						*ptr = '\0';
   4239 					syserr("Deref must be [never|always|search|find] (not %s) in map %s",
   4240 						p, map->map_mname);
   4241 					if (ptr != NULL)
   4242 						*ptr = ' ';
   4243 					return false;
   4244 				}
   4245 			}
   4246 			break;
   4247 
   4248 		  case 's':		/* search scope */
   4249 			while (isascii(*++p) && isspace(*p))
   4250 				continue;
   4251 
   4252 			if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
   4253 				p += 11;
   4254 
   4255 			for (lss = LDAPSearchScope;
   4256 			     lss != NULL && lss->lss_name != NULL; lss++)
   4257 			{
   4258 				if (sm_strncasecmp(p, lss->lss_name,
   4259 						   strlen(lss->lss_name)) == 0)
   4260 					break;
   4261 			}
   4262 			if (lss->lss_name != NULL)
   4263 				lmap->ldap_scope = lss->lss_code;
   4264 			else
   4265 			{
   4266 				/* bad config line */
   4267 				if (!bitset(MCF_OPTFILE,
   4268 					    map->map_class->map_cflags))
   4269 				{
   4270 					char *ptr;
   4271 
   4272 					if ((ptr = strchr(p, ' ')) != NULL)
   4273 						*ptr = '\0';
   4274 					syserr("Scope must be [base|one|sub] (not %s) in map %s",
   4275 						p, map->map_mname);
   4276 					if (ptr != NULL)
   4277 						*ptr = ' ';
   4278 					return false;
   4279 				}
   4280 			}
   4281 			break;
   4282 
   4283 		  case 'V':
   4284 			if (*++p != '\\')
   4285 				lmap->ldap_attrsep = *p;
   4286 			else
   4287 			{
   4288 				switch (*++p)
   4289 				{
   4290 				  case 'n':
   4291 					lmap->ldap_attrsep = '\n';
   4292 					break;
   4293 
   4294 				  case 't':
   4295 					lmap->ldap_attrsep = '\t';
   4296 					break;
   4297 
   4298 				  default:
   4299 					lmap->ldap_attrsep = '\\';
   4300 				}
   4301 			}
   4302 			break;
   4303 
   4304 		  case 'v':		/* attr to return */
   4305 			while (isascii(*++p) && isspace(*p))
   4306 				continue;
   4307 			lmap->ldap_attr[0] = p;
   4308 			lmap->ldap_attr[1] = NULL;
   4309 			break;
   4310 
   4311 		  case 'w':
   4312 			/* -w should be for passwd, -P should be for version */
   4313 			while (isascii(*++p) && isspace(*p))
   4314 				continue;
   4315 			lmap->ldap_version = atoi(p);
   4316 # ifdef LDAP_VERSION_MAX
   4317 			if (lmap->ldap_version > LDAP_VERSION_MAX)
   4318 			{
   4319 				syserr("LDAP version %d exceeds max of %d in map %s",
   4320 				       lmap->ldap_version, LDAP_VERSION_MAX,
   4321 				       map->map_mname);
   4322 				return false;
   4323 			}
   4324 # endif /* LDAP_VERSION_MAX */
   4325 # ifdef LDAP_VERSION_MIN
   4326 			if (lmap->ldap_version < LDAP_VERSION_MIN)
   4327 			{
   4328 				syserr("LDAP version %d is lower than min of %d in map %s",
   4329 				       lmap->ldap_version, LDAP_VERSION_MIN,
   4330 				       map->map_mname);
   4331 				return false;
   4332 			}
   4333 # endif /* LDAP_VERSION_MIN */
   4334 			break;
   4335 
   4336 		  case 'Z':
   4337 			while (isascii(*++p) && isspace(*p))
   4338 				continue;
   4339 			lmap->ldap_sizelimit = atoi(p);
   4340 			break;
   4341 
   4342 		  default:
   4343 			syserr("Illegal option %c map %s", *p, map->map_mname);
   4344 			break;
   4345 		}
   4346 
   4347 		/* need to account for quoted strings here */
   4348 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
   4349 		{
   4350 			if (*p == '"')
   4351 			{
   4352 				while (*++p != '"' && *p != '\0')
   4353 					continue;
   4354 				if (*p != '\0')
   4355 					p++;
   4356 			}
   4357 			else
   4358 				p++;
   4359 		}
   4360 
   4361 		if (*p != '\0')
   4362 			*p++ = '\0';
   4363 	}
   4364 
   4365 	if (map->map_app != NULL)
   4366 		map->map_app = newstr(ldapmap_dequote(map->map_app));
   4367 	if (map->map_tapp != NULL)
   4368 		map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
   4369 
   4370 	/*
   4371 	**  We need to swallow up all the stuff into a struct
   4372 	**  and dump it into map->map_dbptr1
   4373 	*/
   4374 
   4375 	if (lmap->ldap_host != NULL &&
   4376 	    (LDAPDefaults == NULL ||
   4377 	     LDAPDefaults == lmap ||
   4378 	     LDAPDefaults->ldap_host != lmap->ldap_host))
   4379 		lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
   4380 	map->map_domain = lmap->ldap_host;
   4381 
   4382 	if (lmap->ldap_uri != NULL &&
   4383 	    (LDAPDefaults == NULL ||
   4384 	     LDAPDefaults == lmap ||
   4385 	     LDAPDefaults->ldap_uri != lmap->ldap_uri))
   4386 		lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
   4387 	map->map_domain = lmap->ldap_uri;
   4388 
   4389 	if (lmap->ldap_binddn != NULL &&
   4390 	    (LDAPDefaults == NULL ||
   4391 	     LDAPDefaults == lmap ||
   4392 	     LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
   4393 		lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
   4394 
   4395 	if (lmap->ldap_secret != NULL &&
   4396 	    (LDAPDefaults == NULL ||
   4397 	     LDAPDefaults == lmap ||
   4398 	     LDAPDefaults->ldap_secret != lmap->ldap_secret))
   4399 	{
   4400 		SM_FILE_T *sfd;
   4401 		long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
   4402 
   4403 		if (DontLockReadFiles)
   4404 			sff |= SFF_NOLOCK;
   4405 
   4406 		/* need to use method to map secret to passwd string */
   4407 		switch (lmap->ldap_method)
   4408 		{
   4409 		  case LDAP_AUTH_NONE:
   4410 			/* Do nothing */
   4411 			break;
   4412 
   4413 		  case LDAP_AUTH_SIMPLE:
   4414 
   4415 			/*
   4416 			**  Secret is the name of a file with
   4417 			**  the first line as the password.
   4418 			*/
   4419 
   4420 			/* Already read in the secret? */
   4421 			if (secretread)
   4422 				break;
   4423 
   4424 			sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
   4425 					O_RDONLY, 0, sff);
   4426 			if (sfd == NULL)
   4427 			{
   4428 				syserr("LDAP map: cannot open secret %s",
   4429 				       ldapmap_dequote(lmap->ldap_secret));
   4430 				return false;
   4431 			}
   4432 			lmap->ldap_secret = sfgets(m_tmp, sizeof(m_tmp),
   4433 						   sfd, TimeOuts.to_fileopen,
   4434 						   "ldapmap_parseargs");
   4435 			(void) sm_io_close(sfd, SM_TIME_DEFAULT);
   4436 			if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
   4437 			{
   4438 				syserr("LDAP map: secret in %s too long",
   4439 				       ldapmap_dequote(lmap->ldap_secret));
   4440 				return false;
   4441 			}
   4442 			if (lmap->ldap_secret != NULL &&
   4443 			    strlen(m_tmp) > 0)
   4444 			{
   4445 				/* chomp newline */
   4446 				if (m_tmp[strlen(m_tmp) - 1] == '\n')
   4447 					m_tmp[strlen(m_tmp) - 1] = '\0';
   4448 
   4449 				lmap->ldap_secret = m_tmp;
   4450 			}
   4451 			break;
   4452 
   4453 # ifdef LDAP_AUTH_KRBV4
   4454 		  case LDAP_AUTH_KRBV4:
   4455 
   4456 			/*
   4457 			**  Secret is where the ticket file is
   4458 			**  stashed
   4459 			*/
   4460 
   4461 			(void) sm_snprintf(m_tmp, sizeof(m_tmp),
   4462 				"KRBTKFILE=%s",
   4463 				ldapmap_dequote(lmap->ldap_secret));
   4464 			lmap->ldap_secret = m_tmp;
   4465 			break;
   4466 # endif /* LDAP_AUTH_KRBV4 */
   4467 
   4468 		  default:	       /* Should NEVER get here */
   4469 			syserr("LDAP map: Illegal value in lmap method");
   4470 			return false;
   4471 			/* NOTREACHED */
   4472 			break;
   4473 		}
   4474 	}
   4475 
   4476 	if (lmap->ldap_secret != NULL &&
   4477 	    (LDAPDefaults == NULL ||
   4478 	     LDAPDefaults == lmap ||
   4479 	     LDAPDefaults->ldap_secret != lmap->ldap_secret))
   4480 		lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
   4481 
   4482 	if (lmap->ldap_base != NULL &&
   4483 	    (LDAPDefaults == NULL ||
   4484 	     LDAPDefaults == lmap ||
   4485 	     LDAPDefaults->ldap_base != lmap->ldap_base))
   4486 		lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
   4487 
   4488 	/*
   4489 	**  Save the server from extra work.  If request is for a single
   4490 	**  match, tell the server to only return enough records to
   4491 	**  determine if there is a single match or not.  This can not
   4492 	**  be one since the server would only return one and we wouldn't
   4493 	**  know if there were others available.
   4494 	*/
   4495 
   4496 	if (bitset(MF_SINGLEMATCH, map->map_mflags))
   4497 		lmap->ldap_sizelimit = 2;
   4498 
   4499 	/* If setting defaults, don't process ldap_filter and ldap_attr */
   4500 	if (lmap == LDAPDefaults)
   4501 		return true;
   4502 
   4503 	if (lmap->ldap_filter != NULL)
   4504 		lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
   4505 	else
   4506 	{
   4507 		if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
   4508 		{
   4509 			syserr("No filter given in map %s", map->map_mname);
   4510 			return false;
   4511 		}
   4512 	}
   4513 
   4514 	if (!attrssetup && lmap->ldap_attr[0] != NULL)
   4515 	{
   4516 		bool recurse = false;
   4517 		bool normalseen = false;
   4518 
   4519 		i = 0;
   4520 		p = ldapmap_dequote(lmap->ldap_attr[0]);
   4521 		lmap->ldap_attr[0] = NULL;
   4522 
   4523 		/* Prime the attr list with the objectClass attribute */
   4524 		lmap->ldap_attr[i] = "objectClass";
   4525 		lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
   4526 		lmap->ldap_attr_needobjclass[i] = NULL;
   4527 		i++;
   4528 
   4529 		while (p != NULL)
   4530 		{
   4531 			char *v;
   4532 
   4533 			while (isascii(*p) && isspace(*p))
   4534 				p++;
   4535 			if (*p == '\0')
   4536 				break;
   4537 			v = p;
   4538 			p = strchr(v, ',');
   4539 			if (p != NULL)
   4540 				*p++ = '\0';
   4541 
   4542 			if (i >= LDAPMAP_MAX_ATTR)
   4543 			{
   4544 				syserr("Too many return attributes in %s (max %d)",
   4545 				       map->map_mname, LDAPMAP_MAX_ATTR);
   4546 				return false;
   4547 			}
   4548 			if (*v != '\0')
   4549 			{
   4550 				int j;
   4551 				int use;
   4552 				char *type;
   4553 				char *needobjclass;
   4554 
   4555 				type = strchr(v, ':');
   4556 				if (type != NULL)
   4557 				{
   4558 					*type++ = '\0';
   4559 					needobjclass = strchr(type, ':');
   4560 					if (needobjclass != NULL)
   4561 						*needobjclass++ = '\0';
   4562 				}
   4563 				else
   4564 				{
   4565 					needobjclass = NULL;
   4566 				}
   4567 
   4568 				use = i;
   4569 
   4570 				/* allow override on "objectClass" type */
   4571 				if (sm_strcasecmp(v, "objectClass") == 0 &&
   4572 				    lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
   4573 				{
   4574 					use = 0;
   4575 				}
   4576 				else
   4577 				{
   4578 					/*
   4579 					**  Don't add something to attribute
   4580 					**  list twice.
   4581 					*/
   4582 
   4583 					for (j = 1; j < i; j++)
   4584 					{
   4585 						if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
   4586 						{
   4587 							syserr("Duplicate attribute (%s) in %s",
   4588 							       v, map->map_mname);
   4589 							return false;
   4590 						}
   4591 					}
   4592 
   4593 					lmap->ldap_attr[use] = newstr(v);
   4594 					if (needobjclass != NULL &&
   4595 					    *needobjclass != '\0' &&
   4596 					    *needobjclass != '*')
   4597 					{
   4598 						lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
   4599 					}
   4600 					else
   4601 					{
   4602 						lmap->ldap_attr_needobjclass[use] = NULL;
   4603 					}
   4604 
   4605 				}
   4606 
   4607 				if (type != NULL && *type != '\0')
   4608 				{
   4609 					if (sm_strcasecmp(type, "dn") == 0)
   4610 					{
   4611 						recurse = true;
   4612 						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
   4613 					}
   4614 					else if (sm_strcasecmp(type, "filter") == 0)
   4615 					{
   4616 						recurse = true;
   4617 						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
   4618 					}
   4619 					else if (sm_strcasecmp(type, "url") == 0)
   4620 					{
   4621 						recurse = true;
   4622 						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
   4623 					}
   4624 					else if (sm_strcasecmp(type, "normal") == 0)
   4625 					{
   4626 						lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
   4627 						normalseen = true;
   4628 					}
   4629 					else
   4630 					{
   4631 						syserr("Unknown attribute type (%s) in %s",
   4632 						       type, map->map_mname);
   4633 						return false;
   4634 					}
   4635 				}
   4636 				else
   4637 				{
   4638 					lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
   4639 					normalseen = true;
   4640 				}
   4641 				i++;
   4642 			}
   4643 		}
   4644 		lmap->ldap_attr[i] = NULL;
   4645 
   4646 		/* Set in case needed in future code */
   4647 		attrssetup = true;
   4648 
   4649 		if (recurse && !normalseen)
   4650 		{
   4651 			syserr("LDAP recursion requested in %s but no returnable attribute given",
   4652 			       map->map_mname);
   4653 			return false;
   4654 		}
   4655 		if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
   4656 		{
   4657 			syserr("LDAP recursion requested in %s can not be used with -n",
   4658 			       map->map_mname);
   4659 			return false;
   4660 		}
   4661 	}
   4662 	map->map_db1 = (ARBPTR_T) lmap;
   4663 	return true;
   4664 }
   4665 
   4666 /*
   4667 **  LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
   4668 **
   4669 **	Parameters:
   4670 **		spec -- map argument string from LDAPDefaults option
   4671 **
   4672 **	Returns:
   4673 **		None.
   4674 */
   4675 
   4676 void
   4677 ldapmap_set_defaults(spec)
   4678 	char *spec;
   4679 {
   4680 	STAB *class;
   4681 	MAP map;
   4682 
   4683 	/* Allocate and set the default values */
   4684 	if (LDAPDefaults == NULL)
   4685 		LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof(*LDAPDefaults));
   4686 	sm_ldap_clear(LDAPDefaults);
   4687 
   4688 	memset(&map, '\0', sizeof(map));
   4689 
   4690 	/* look up the class */
   4691 	class = stab("ldap", ST_MAPCLASS, ST_FIND);
   4692 	if (class == NULL)
   4693 	{
   4694 		syserr("readcf: LDAPDefaultSpec: class ldap not available");
   4695 		return;
   4696 	}
   4697 	map.map_class = &class->s_mapclass;
   4698 	map.map_db1 = (ARBPTR_T) LDAPDefaults;
   4699 	map.map_mname = "O LDAPDefaultSpec";
   4700 
   4701 	(void) ldapmap_parseargs(&map, spec);
   4702 
   4703 	/* These should never be set in LDAPDefaults */
   4704 	if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
   4705 	    map.map_spacesub != SpaceSub ||
   4706 	    map.map_app != NULL ||
   4707 	    map.map_tapp != NULL)
   4708 	{
   4709 		syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
   4710 		SM_FREE_CLR(map.map_app);
   4711 		SM_FREE_CLR(map.map_tapp);
   4712 	}
   4713 
   4714 	if (LDAPDefaults->ldap_filter != NULL)
   4715 	{
   4716 		syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
   4717 
   4718 		/* don't free, it isn't malloc'ed in parseargs */
   4719 		LDAPDefaults->ldap_filter = NULL;
   4720 	}
   4721 
   4722 	if (LDAPDefaults->ldap_attr[0] != NULL)
   4723 	{
   4724 		syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
   4725 		/* don't free, they aren't malloc'ed in parseargs */
   4726 		LDAPDefaults->ldap_attr[0] = NULL;
   4727 	}
   4728 }
   4729 #endif /* LDAPMAP */
   4730 /*
   4731 **  PH map
   4732 */
   4733 
   4734 #if PH_MAP
   4735 
   4736 /*
   4737 **  Support for the CCSO Nameserver (ph/qi).
   4738 **  This code is intended to replace the so-called "ph mailer".
   4739 **  Contributed by Mark D. Roth.  Contact him for support.
   4740 */
   4741 
   4742 /* what version of the ph map code we're running */
   4743 static char phmap_id[128];
   4744 
   4745 /* sendmail version for phmap id string */
   4746 extern const char Version[];
   4747 
   4748 /* assume we're using nph-1.2.x if not specified */
   4749 # ifndef NPH_VERSION
   4750 #  define NPH_VERSION		10200
   4751 # endif
   4752 
   4753 /* compatibility for versions older than nph-1.2.0 */
   4754 # if NPH_VERSION < 10200
   4755 #  define PH_OPEN_ROUNDROBIN	PH_ROUNDROBIN
   4756 #  define PH_OPEN_DONTID	PH_DONTID
   4757 #  define PH_CLOSE_FAST		PH_FASTCLOSE
   4758 #  define PH_ERR_DATAERR	PH_DATAERR
   4759 #  define PH_ERR_NOMATCH	PH_NOMATCH
   4760 # endif /* NPH_VERSION < 10200 */
   4761 
   4762 /*
   4763 **  PH_MAP_PARSEARGS -- parse ph map definition args.
   4764 */
   4765 
   4766 bool
   4767 ph_map_parseargs(map, args)
   4768 	MAP *map;
   4769 	char *args;
   4770 {
   4771 	register bool done;
   4772 	register char *p = args;
   4773 	PH_MAP_STRUCT *pmap = NULL;
   4774 
   4775 	/* initialize version string */
   4776 	(void) sm_snprintf(phmap_id, sizeof(phmap_id),
   4777 			   "sendmail-%s phmap-20010529 libphclient-%s",
   4778 			   Version, libphclient_version);
   4779 
   4780 	pmap = (PH_MAP_STRUCT *) xalloc(sizeof(*pmap));
   4781 
   4782 	/* defaults */
   4783 	pmap->ph_servers = NULL;
   4784 	pmap->ph_field_list = NULL;
   4785 	pmap->ph = NULL;
   4786 	pmap->ph_timeout = 0;
   4787 	pmap->ph_fastclose = 0;
   4788 
   4789 	map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
   4790 	for (;;)
   4791 	{
   4792 		while (isascii(*p) && isspace(*p))
   4793 			p++;
   4794 		if (*p != '-')
   4795 			break;
   4796 		switch (*++p)
   4797 		{
   4798 		  case 'N':
   4799 			map->map_mflags |= MF_INCLNULL;
   4800 			map->map_mflags &= ~MF_TRY0NULL;
   4801 			break;
   4802 
   4803 		  case 'O':
   4804 			map->map_mflags &= ~MF_TRY1NULL;
   4805 			break;
   4806 
   4807 		  case 'o':
   4808 			map->map_mflags |= MF_OPTIONAL;
   4809 			break;
   4810 
   4811 		  case 'f':
   4812 			map->map_mflags |= MF_NOFOLDCASE;
   4813 			break;
   4814 
   4815 		  case 'm':
   4816 			map->map_mflags |= MF_MATCHONLY;
   4817 			break;
   4818 
   4819 		  case 'A':
   4820 			map->map_mflags |= MF_APPEND;
   4821 			break;
   4822 
   4823 		  case 'q':
   4824 			map->map_mflags |= MF_KEEPQUOTES;
   4825 			break;
   4826 
   4827 		  case 't':
   4828 			map->map_mflags |= MF_NODEFER;
   4829 			break;
   4830 
   4831 		  case 'a':
   4832 			map->map_app = ++p;
   4833 			break;
   4834 
   4835 		  case 'T':
   4836 			map->map_tapp = ++p;
   4837 			break;
   4838 
   4839 		  case 'l':
   4840 			while (isascii(*++p) && isspace(*p))
   4841 				continue;
   4842 			pmap->ph_timeout = atoi(p);
   4843 			break;
   4844 
   4845 		  case 'S':
   4846 			map->map_spacesub = *++p;
   4847 			break;
   4848 
   4849 		  case 'D':
   4850 			map->map_mflags |= MF_DEFER;
   4851 			break;
   4852 
   4853 		  case 'h':		/* PH server list */
   4854 			while (isascii(*++p) && isspace(*p))
   4855 				continue;
   4856 			pmap->ph_servers = p;
   4857 			break;
   4858 
   4859 		  case 'k':		/* fields to search for */
   4860 			while (isascii(*++p) && isspace(*p))
   4861 				continue;
   4862 			pmap->ph_field_list = p;
   4863 			break;
   4864 
   4865 		  default:
   4866 			syserr("ph_map_parseargs: unknown option -%c", *p);
   4867 		}
   4868 
   4869 		/* try to account for quoted strings */
   4870 		done = isascii(*p) && isspace(*p);
   4871 		while (*p != '\0' && !done)
   4872 		{
   4873 			if (*p == '"')
   4874 			{
   4875 				while (*++p != '"' && *p != '\0')
   4876 					continue;
   4877 				if (*p != '\0')
   4878 					p++;
   4879 			}
   4880 			else
   4881 				p++;
   4882 			done = isascii(*p) && isspace(*p);
   4883 		}
   4884 
   4885 		if (*p != '\0')
   4886 			*p++ = '\0';
   4887 	}
   4888 
   4889 	if (map->map_app != NULL)
   4890 		map->map_app = newstr(ph_map_dequote(map->map_app));
   4891 	if (map->map_tapp != NULL)
   4892 		map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
   4893 
   4894 	if (pmap->ph_field_list != NULL)
   4895 		pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
   4896 
   4897 	if (pmap->ph_servers != NULL)
   4898 		pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
   4899 	else
   4900 	{
   4901 		syserr("ph_map_parseargs: -h flag is required");
   4902 		return false;
   4903 	}
   4904 
   4905 	map->map_db1 = (ARBPTR_T) pmap;
   4906 	return true;
   4907 }
   4908 
   4909 /*
   4910 **  PH_MAP_CLOSE -- close the connection to the ph server
   4911 */
   4912 
   4913 void
   4914 ph_map_close(map)
   4915 	MAP *map;
   4916 {
   4917 	PH_MAP_STRUCT *pmap;
   4918 
   4919 	pmap = (PH_MAP_STRUCT *)map->map_db1;
   4920 	if (tTd(38, 9))
   4921 		sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
   4922 			   map->map_mname, pmap->ph_fastclose);
   4923 
   4924 
   4925 	if (pmap->ph != NULL)
   4926 	{
   4927 		ph_set_sendhook(pmap->ph, NULL);
   4928 		ph_set_recvhook(pmap->ph, NULL);
   4929 		ph_close(pmap->ph, pmap->ph_fastclose);
   4930 	}
   4931 
   4932 	map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
   4933 }
   4934 
   4935 static jmp_buf  PHTimeout;
   4936 
   4937 /* ARGSUSED */
   4938 static void
   4939 ph_timeout(unused)
   4940 	int unused;
   4941 {
   4942 	/*
   4943 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
   4944 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
   4945 	**	DOING.
   4946 	*/
   4947 
   4948 	errno = ETIMEDOUT;
   4949 	longjmp(PHTimeout, 1);
   4950 }
   4951 
   4952 static void
   4953 #if NPH_VERSION >= 10200
   4954 ph_map_send_debug(appdata, text)
   4955 	void *appdata;
   4956 #else
   4957 ph_map_send_debug(text)
   4958 #endif
   4959 	char *text;
   4960 {
   4961 	if (LogLevel > 9)
   4962 		sm_syslog(LOG_NOTICE, CurEnv->e_id,
   4963 			  "ph_map_send_debug: ==> %s", text);
   4964 	if (tTd(38, 20))
   4965 		sm_dprintf("ph_map_send_debug: ==> %s\n", text);
   4966 }
   4967 
   4968 static void
   4969 #if NPH_VERSION >= 10200
   4970 ph_map_recv_debug(appdata, text)
   4971 	void *appdata;
   4972 #else
   4973 ph_map_recv_debug(text)
   4974 #endif
   4975 	char *text;
   4976 {
   4977 	if (LogLevel > 10)
   4978 		sm_syslog(LOG_NOTICE, CurEnv->e_id,
   4979 			  "ph_map_recv_debug: <== %s", text);
   4980 	if (tTd(38, 21))
   4981 		sm_dprintf("ph_map_recv_debug: <== %s\n", text);
   4982 }
   4983 
   4984 /*
   4985 **  PH_MAP_OPEN -- sub for opening PH map
   4986 */
   4987 bool
   4988 ph_map_open(map, mode)
   4989 	MAP *map;
   4990 	int mode;
   4991 {
   4992 	PH_MAP_STRUCT *pmap;
   4993 	register SM_EVENT *ev = NULL;
   4994 	int save_errno = 0;
   4995 	char *hostlist, *host;
   4996 
   4997 	if (tTd(38, 2))
   4998 		sm_dprintf("ph_map_open(%s)\n", map->map_mname);
   4999 
   5000 	mode &= O_ACCMODE;
   5001 	if (mode != O_RDONLY)
   5002 	{
   5003 		/* issue a pseudo-error message */
   5004 		errno = SM_EMAPCANTWRITE;
   5005 		return false;
   5006 	}
   5007 
   5008 	if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
   5009 	    bitset(MF_DEFER, map->map_mflags))
   5010 	{
   5011 		if (tTd(9, 1))
   5012 			sm_dprintf("ph_map_open(%s) => DEFERRED\n",
   5013 				   map->map_mname);
   5014 
   5015 		/*
   5016 		**  Unset MF_DEFER here so that map_lookup() returns
   5017 		**  a temporary failure using the bogus map and
   5018 		**  map->map_tapp instead of the default permanent error.
   5019 		*/
   5020 
   5021 		map->map_mflags &= ~MF_DEFER;
   5022 		return false;
   5023 	}
   5024 
   5025 	pmap = (PH_MAP_STRUCT *)map->map_db1;
   5026 	pmap->ph_fastclose = 0;		/* refresh field for reopen */
   5027 
   5028 	/* try each host in the list */
   5029 	hostlist = newstr(pmap->ph_servers);
   5030 	for (host = strtok(hostlist, " ");
   5031 	     host != NULL;
   5032 	     host = strtok(NULL, " "))
   5033 	{
   5034 		/* set timeout */
   5035 		if (pmap->ph_timeout != 0)
   5036 		{
   5037 			if (setjmp(PHTimeout) != 0)
   5038 			{
   5039 				ev = NULL;
   5040 				if (LogLevel > 1)
   5041 					sm_syslog(LOG_NOTICE, CurEnv->e_id,
   5042 						  "timeout connecting to PH server %.100s",
   5043 						  host);
   5044 				errno = ETIMEDOUT;
   5045 				goto ph_map_open_abort;
   5046 			}
   5047 			ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
   5048 		}
   5049 
   5050 		/* open connection to server */
   5051 		if (ph_open(&(pmap->ph), host,
   5052 			    PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
   5053 			    ph_map_send_debug, ph_map_recv_debug
   5054 #if NPH_VERSION >= 10200
   5055 			    , NULL
   5056 #endif
   5057 			    ) == 0
   5058 		    && ph_id(pmap->ph, phmap_id) == 0)
   5059 		{
   5060 			if (ev != NULL)
   5061 				sm_clrevent(ev);
   5062 			sm_free(hostlist); /* XXX */
   5063 			return true;
   5064 		}
   5065 
   5066   ph_map_open_abort:
   5067 		save_errno = errno;
   5068 		if (ev != NULL)
   5069 			sm_clrevent(ev);
   5070 		pmap->ph_fastclose = PH_CLOSE_FAST;
   5071 		ph_map_close(map);
   5072 		errno = save_errno;
   5073 	}
   5074 
   5075 	if (bitset(MF_NODEFER, map->map_mflags))
   5076 	{
   5077 		if (errno == 0)
   5078 			errno = EAGAIN;
   5079 		syserr("ph_map_open: %s: cannot connect to PH server",
   5080 		       map->map_mname);
   5081 	}
   5082 	else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
   5083 		sm_syslog(LOG_NOTICE, CurEnv->e_id,
   5084 			  "ph_map_open: %s: cannot connect to PH server",
   5085 			  map->map_mname);
   5086 	sm_free(hostlist); /* XXX */
   5087 	return false;
   5088 }
   5089 
   5090 /*
   5091 **  PH_MAP_LOOKUP -- look up key from ph server
   5092 */
   5093 
   5094 char *
   5095 ph_map_lookup(map, key, args, pstat)
   5096 	MAP *map;
   5097 	char *key;
   5098 	char **args;
   5099 	int *pstat;
   5100 {
   5101 	int i, save_errno = 0;
   5102 	register SM_EVENT *ev = NULL;
   5103 	PH_MAP_STRUCT *pmap;
   5104 	char *value = NULL;
   5105 
   5106 	pmap = (PH_MAP_STRUCT *)map->map_db1;
   5107 
   5108 	*pstat = EX_OK;
   5109 
   5110 	/* set timeout */
   5111 	if (pmap->ph_timeout != 0)
   5112 	{
   5113 		if (setjmp(PHTimeout) != 0)
   5114 		{
   5115 			ev = NULL;
   5116 			if (LogLevel > 1)
   5117 				sm_syslog(LOG_NOTICE, CurEnv->e_id,
   5118 					  "timeout during PH lookup of %.100s",
   5119 					  key);
   5120 			errno = ETIMEDOUT;
   5121 			*pstat = EX_TEMPFAIL;
   5122 			goto ph_map_lookup_abort;
   5123 		}
   5124 		ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
   5125 	}
   5126 
   5127 	/* perform lookup */
   5128 	i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
   5129 	if (i == -1)
   5130 		*pstat = EX_TEMPFAIL;
   5131 	else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
   5132 		*pstat = EX_UNAVAILABLE;
   5133 
   5134   ph_map_lookup_abort:
   5135 	if (ev != NULL)
   5136 		sm_clrevent(ev);
   5137 
   5138 	/*
   5139 	**  Close the connection if the timer popped
   5140 	**  or we got a temporary PH error
   5141 	*/
   5142 
   5143 	if (*pstat == EX_TEMPFAIL)
   5144 	{
   5145 		save_errno = errno;
   5146 		pmap->ph_fastclose = PH_CLOSE_FAST;
   5147 		ph_map_close(map);
   5148 		errno = save_errno;
   5149 	}
   5150 
   5151 	if (*pstat == EX_OK)
   5152 	{
   5153 		if (tTd(38,20))
   5154 			sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
   5155 
   5156 		if (bitset(MF_MATCHONLY, map->map_mflags))
   5157 			return map_rewrite(map, key, strlen(key), NULL);
   5158 		else
   5159 			return map_rewrite(map, value, strlen(value), args);
   5160 	}
   5161 
   5162 	return NULL;
   5163 }
   5164 #endif /* PH_MAP */
   5165 
   5166 /*
   5167 **  syslog map
   5168 */
   5169 
   5170 #define map_prio	map_lockfd	/* overload field */
   5171 
   5172 /*
   5173 **  SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
   5174 */
   5175 
   5176 bool
   5177 syslog_map_parseargs(map, args)
   5178 	MAP *map;
   5179 	char *args;
   5180 {
   5181 	char *p = args;
   5182 	char *priority = NULL;
   5183 
   5184 	/* there is no check whether there is really an argument */
   5185 	while (*p != '\0')
   5186 	{
   5187 		while (isascii(*p) && isspace(*p))
   5188 			p++;
   5189 		if (*p != '-')
   5190 			break;
   5191 		++p;
   5192 		if (*p == 'D')
   5193 		{
   5194 			map->map_mflags |= MF_DEFER;
   5195 			++p;
   5196 		}
   5197 		else if (*p == 'S')
   5198 		{
   5199 			map->map_spacesub = *++p;
   5200 			if (*p != '\0')
   5201 				p++;
   5202 		}
   5203 		else if (*p == 'L')
   5204 		{
   5205 			while (*++p != '\0' && isascii(*p) && isspace(*p))
   5206 				continue;
   5207 			if (*p == '\0')
   5208 				break;
   5209 			priority = p;
   5210 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
   5211 				p++;
   5212 			if (*p != '\0')
   5213 				*p++ = '\0';
   5214 		}
   5215 		else
   5216 		{
   5217 			syserr("Illegal option %c map syslog", *p);
   5218 			++p;
   5219 		}
   5220 	}
   5221 
   5222 	if (priority == NULL)
   5223 		map->map_prio = LOG_INFO;
   5224 	else
   5225 	{
   5226 		if (sm_strncasecmp("LOG_", priority, 4) == 0)
   5227 			priority += 4;
   5228 
   5229 #ifdef LOG_EMERG
   5230 		if (sm_strcasecmp("EMERG", priority) == 0)
   5231 			map->map_prio = LOG_EMERG;
   5232 		else
   5233 #endif /* LOG_EMERG */
   5234 #ifdef LOG_ALERT
   5235 		if (sm_strcasecmp("ALERT", priority) == 0)
   5236 			map->map_prio = LOG_ALERT;
   5237 		else
   5238 #endif /* LOG_ALERT */
   5239 #ifdef LOG_CRIT
   5240 		if (sm_strcasecmp("CRIT", priority) == 0)
   5241 			map->map_prio = LOG_CRIT;
   5242 		else
   5243 #endif /* LOG_CRIT */
   5244 #ifdef LOG_ERR
   5245 		if (sm_strcasecmp("ERR", priority) == 0)
   5246 			map->map_prio = LOG_ERR;
   5247 		else
   5248 #endif /* LOG_ERR */
   5249 #ifdef LOG_WARNING
   5250 		if (sm_strcasecmp("WARNING", priority) == 0)
   5251 			map->map_prio = LOG_WARNING;
   5252 		else
   5253 #endif /* LOG_WARNING */
   5254 #ifdef LOG_NOTICE
   5255 		if (sm_strcasecmp("NOTICE", priority) == 0)
   5256 			map->map_prio = LOG_NOTICE;
   5257 		else
   5258 #endif /* LOG_NOTICE */
   5259 #ifdef LOG_INFO
   5260 		if (sm_strcasecmp("INFO", priority) == 0)
   5261 			map->map_prio = LOG_INFO;
   5262 		else
   5263 #endif /* LOG_INFO */
   5264 #ifdef LOG_DEBUG
   5265 		if (sm_strcasecmp("DEBUG", priority) == 0)
   5266 			map->map_prio = LOG_DEBUG;
   5267 		else
   5268 #endif /* LOG_DEBUG */
   5269 		{
   5270 			syserr("syslog_map_parseargs: Unknown priority %s",
   5271 			       priority);
   5272 			return false;
   5273 		}
   5274 	}
   5275 	return true;
   5276 }
   5277 
   5278 /*
   5279 **  SYSLOG_MAP_LOOKUP -- rewrite and syslog message.  Always return empty string
   5280 */
   5281 
   5282 char *
   5283 syslog_map_lookup(map, string, args, statp)
   5284 	MAP *map;
   5285 	char *string;
   5286 	char **args;
   5287 	int *statp;
   5288 {
   5289 	char *ptr = map_rewrite(map, string, strlen(string), args);
   5290 
   5291 	if (ptr != NULL)
   5292 	{
   5293 		if (tTd(38, 20))
   5294 			sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
   5295 				map->map_mname, map->map_prio, ptr);
   5296 
   5297 		sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
   5298 	}
   5299 
   5300 	*statp = EX_OK;
   5301 	return "";
   5302 }
   5303 
   5304 #if _FFR_DPRINTF_MAP
   5305 /*
   5306 **  dprintf map
   5307 */
   5308 
   5309 #define map_dbg_level	map_lockfd	/* overload field */
   5310 
   5311 /*
   5312 **  DPRINTF_MAP_PARSEARGS -- check for priority level to dprintf messages.
   5313 */
   5314 
   5315 bool
   5316 dprintf_map_parseargs(map, args)
   5317 	MAP *map;
   5318 	char *args;
   5319 {
   5320 	char *p = args;
   5321 	char *dbg_level = NULL;
   5322 
   5323 	/* there is no check whether there is really an argument */
   5324 	while (*p != '\0')
   5325 	{
   5326 		while (isascii(*p) && isspace(*p))
   5327 			p++;
   5328 		if (*p != '-')
   5329 			break;
   5330 		++p;
   5331 		if (*p == 'D')
   5332 		{
   5333 			map->map_mflags |= MF_DEFER;
   5334 			++p;
   5335 		}
   5336 		else if (*p == 'S')
   5337 		{
   5338 			map->map_spacesub = *++p;
   5339 			if (*p != '\0')
   5340 				p++;
   5341 		}
   5342 		else if (*p == 'd')
   5343 		{
   5344 			while (*++p != '\0' && isascii(*p) && isspace(*p))
   5345 				continue;
   5346 			if (*p == '\0')
   5347 				break;
   5348 			dbg_level = p;
   5349 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
   5350 				p++;
   5351 			if (*p != '\0')
   5352 				*p++ = '\0';
   5353 		}
   5354 		else
   5355 		{
   5356 			syserr("Illegal option %c map dprintf", *p);
   5357 			++p;
   5358 		}
   5359 	}
   5360 
   5361 	if (dbg_level == NULL)
   5362 		map->map_dbg_level = 0;
   5363 	else
   5364 	{
   5365 		if (!(isascii(*dbg_level) && isdigit(*dbg_level)))
   5366 		{
   5367 			syserr("dprintf map \"%s\", file %s: -d should specify a number, not %s",
   5368 				map->map_mname, map->map_file,
   5369 				dbg_level);
   5370 			return false;
   5371 		}
   5372 		map->map_dbg_level = atoi(dbg_level);
   5373 	}
   5374 	return true;
   5375 }
   5376 
   5377 /*
   5378 **  DPRINTF_MAP_LOOKUP -- rewrite and print message.  Always return empty string
   5379 */
   5380 
   5381 char *
   5382 dprintf_map_lookup(map, string, args, statp)
   5383 	MAP *map;
   5384 	char *string;
   5385 	char **args;
   5386 	int *statp;
   5387 {
   5388 	char *ptr = map_rewrite(map, string, strlen(string), args);
   5389 
   5390 	if (ptr != NULL && tTd(85, map->map_dbg_level))
   5391 		sm_dprintf("%s\n", ptr);
   5392 	*statp = EX_OK;
   5393 	return "";
   5394 }
   5395 #endif /* _FFR_DPRINTF_MAP */
   5396 
   5397 /*
   5398 **  HESIOD Modules
   5399 */
   5400 
   5401 #if HESIOD
   5402 
   5403 bool
   5404 hes_map_open(map, mode)
   5405 	MAP *map;
   5406 	int mode;
   5407 {
   5408 	if (tTd(38, 2))
   5409 		sm_dprintf("hes_map_open(%s, %s, %d)\n",
   5410 			map->map_mname, map->map_file, mode);
   5411 
   5412 	if (mode != O_RDONLY)
   5413 	{
   5414 		/* issue a pseudo-error message */
   5415 		errno = SM_EMAPCANTWRITE;
   5416 		return false;
   5417 	}
   5418 
   5419 # ifdef HESIOD_INIT
   5420 	if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
   5421 		return true;
   5422 
   5423 	if (!bitset(MF_OPTIONAL, map->map_mflags))
   5424 		syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
   5425 			sm_errstring(errno));
   5426 	return false;
   5427 # else /* HESIOD_INIT */
   5428 	if (hes_error() == HES_ER_UNINIT)
   5429 		hes_init();
   5430 	switch (hes_error())
   5431 	{
   5432 	  case HES_ER_OK:
   5433 	  case HES_ER_NOTFOUND:
   5434 		return true;
   5435 	}
   5436 
   5437 	if (!bitset(MF_OPTIONAL, map->map_mflags))
   5438 		syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
   5439 
   5440 	return false;
   5441 # endif /* HESIOD_INIT */
   5442 }
   5443 
   5444 char *
   5445 hes_map_lookup(map, name, av, statp)
   5446 	MAP *map;
   5447 	char *name;
   5448 	char **av;
   5449 	int *statp;
   5450 {
   5451 	char **hp;
   5452 
   5453 	if (tTd(38, 20))
   5454 		sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
   5455 
   5456 	if (name[0] == '\\')
   5457 	{
   5458 		char *np;
   5459 		int nl;
   5460 		int save_errno;
   5461 		char nbuf[MAXNAME];
   5462 
   5463 		nl = strlen(name);
   5464 		if (nl < sizeof(nbuf) - 1)
   5465 			np = nbuf;
   5466 		else
   5467 			np = xalloc(strlen(name) + 2);
   5468 		np[0] = '\\';
   5469 		(void) sm_strlcpy(&np[1], name, (sizeof(nbuf)) - 1);
   5470 # ifdef HESIOD_INIT
   5471 		hp = hesiod_resolve(HesiodContext, np, map->map_file);
   5472 # else /* HESIOD_INIT */
   5473 		hp = hes_resolve(np, map->map_file);
   5474 # endif /* HESIOD_INIT */
   5475 		save_errno = errno;
   5476 		if (np != nbuf)
   5477 			sm_free(np); /* XXX */
   5478 		errno = save_errno;
   5479 	}
   5480 	else
   5481 	{
   5482 # ifdef HESIOD_INIT
   5483 		hp = hesiod_resolve(HesiodContext, name, map->map_file);
   5484 # else /* HESIOD_INIT */
   5485 		hp = hes_resolve(name, map->map_file);
   5486 # endif /* HESIOD_INIT */
   5487 	}
   5488 # ifdef HESIOD_INIT
   5489 	if (hp == NULL || *hp == NULL)
   5490 	{
   5491 		switch (errno)
   5492 		{
   5493 		  case ENOENT:
   5494 			  *statp = EX_NOTFOUND;
   5495 			  break;
   5496 		  case ECONNREFUSED:
   5497 			  *statp = EX_TEMPFAIL;
   5498 			  break;
   5499 		  case EMSGSIZE:
   5500 		  case ENOMEM:
   5501 		  default:
   5502 			  *statp = EX_UNAVAILABLE;
   5503 			  break;
   5504 		}
   5505 		if (hp != NULL)
   5506 			hesiod_free_list(HesiodContext, hp);
   5507 		return NULL;
   5508 	}
   5509 # else /* HESIOD_INIT */
   5510 	if (hp == NULL || hp[0] == NULL)
   5511 	{
   5512 		switch (hes_error())
   5513 		{
   5514 		  case HES_ER_OK:
   5515 			*statp = EX_OK;
   5516 			break;
   5517 
   5518 		  case HES_ER_NOTFOUND:
   5519 			*statp = EX_NOTFOUND;
   5520 			break;
   5521 
   5522 		  case HES_ER_CONFIG:
   5523 			*statp = EX_UNAVAILABLE;
   5524 			break;
   5525 
   5526 		  case HES_ER_NET:
   5527 			*statp = EX_TEMPFAIL;
   5528 			break;
   5529 		}
   5530 		return NULL;
   5531 	}
   5532 # endif /* HESIOD_INIT */
   5533 
   5534 	if (bitset(MF_MATCHONLY, map->map_mflags))
   5535 		return map_rewrite(map, name, strlen(name), NULL);
   5536 	else
   5537 		return map_rewrite(map, hp[0], strlen(hp[0]), av);
   5538 }
   5539 
   5540 /*
   5541 **  HES_MAP_CLOSE -- free the Hesiod context
   5542 */
   5543 
   5544 void
   5545 hes_map_close(map)
   5546 	MAP *map;
   5547 {
   5548 	if (tTd(38, 20))
   5549 		sm_dprintf("hes_map_close(%s)\n", map->map_file);
   5550 
   5551 # ifdef HESIOD_INIT
   5552 	/* Free the hesiod context */
   5553 	if (HesiodContext != NULL)
   5554 	{
   5555 		hesiod_end(HesiodContext);
   5556 		HesiodContext = NULL;
   5557 	}
   5558 # endif /* HESIOD_INIT */
   5559 }
   5560 
   5561 #endif /* HESIOD */
   5562 /*
   5563 **  NeXT NETINFO Modules
   5564 */
   5565 
   5566 #if NETINFO
   5567 
   5568 # define NETINFO_DEFAULT_DIR		"/aliases"
   5569 # define NETINFO_DEFAULT_PROPERTY	"members"
   5570 
   5571 /*
   5572 **  NI_MAP_OPEN -- open NetInfo Aliases
   5573 */
   5574 
   5575 bool
   5576 ni_map_open(map, mode)
   5577 	MAP *map;
   5578 	int mode;
   5579 {
   5580 	if (tTd(38, 2))
   5581 		sm_dprintf("ni_map_open(%s, %s, %d)\n",
   5582 			map->map_mname, map->map_file, mode);
   5583 	mode &= O_ACCMODE;
   5584 
   5585 	if (*map->map_file == '\0')
   5586 		map->map_file = NETINFO_DEFAULT_DIR;
   5587 
   5588 	if (map->map_valcolnm == NULL)
   5589 		map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
   5590 
   5591 	if (map->map_coldelim == '\0')
   5592 	{
   5593 		if (bitset(MF_ALIAS, map->map_mflags))
   5594 			map->map_coldelim = ',';
   5595 		else if (bitset(MF_FILECLASS, map->map_mflags))
   5596 			map->map_coldelim = ' ';
   5597 	}
   5598 	return true;
   5599 }
   5600 
   5601 
   5602 /*
   5603 **  NI_MAP_LOOKUP -- look up a datum in NetInfo
   5604 */
   5605 
   5606 char *
   5607 ni_map_lookup(map, name, av, statp)
   5608 	MAP *map;
   5609 	char *name;
   5610 	char **av;
   5611 	int *statp;
   5612 {
   5613 	char *res;
   5614 	char *propval;
   5615 
   5616 	if (tTd(38, 20))
   5617 		sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
   5618 
   5619 	propval = ni_propval(map->map_file, map->map_keycolnm, name,
   5620 			     map->map_valcolnm, map->map_coldelim);
   5621 
   5622 	if (propval == NULL)
   5623 		return NULL;
   5624 
   5625 	SM_TRY
   5626 		if (bitset(MF_MATCHONLY, map->map_mflags))
   5627 			res = map_rewrite(map, name, strlen(name), NULL);
   5628 		else
   5629 			res = map_rewrite(map, propval, strlen(propval), av);
   5630 	SM_FINALLY
   5631 		sm_free(propval);
   5632 	SM_END_TRY
   5633 	return res;
   5634 }
   5635 
   5636 
   5637 static bool
   5638 ni_getcanonname(name, hbsize, statp)
   5639 	char *name;
   5640 	int hbsize;
   5641 	int *statp;
   5642 {
   5643 	char *vptr;
   5644 	char *ptr;
   5645 	char nbuf[MAXNAME + 1];
   5646 
   5647 	if (tTd(38, 20))
   5648 		sm_dprintf("ni_getcanonname(%s)\n", name);
   5649 
   5650 	if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
   5651 	{
   5652 		*statp = EX_UNAVAILABLE;
   5653 		return false;
   5654 	}
   5655 	(void) shorten_hostname(nbuf);
   5656 
   5657 	/* we only accept single token search key */
   5658 	if (strchr(nbuf, '.'))
   5659 	{
   5660 		*statp = EX_NOHOST;
   5661 		return false;
   5662 	}
   5663 
   5664 	/* Do the search */
   5665 	vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
   5666 
   5667 	if (vptr == NULL)
   5668 	{
   5669 		*statp = EX_NOHOST;
   5670 		return false;
   5671 	}
   5672 
   5673 	/* Only want the first machine name */
   5674 	if ((ptr = strchr(vptr, '\n')) != NULL)
   5675 		*ptr = '\0';
   5676 
   5677 	if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
   5678 	{
   5679 		sm_free(vptr);
   5680 		*statp = EX_UNAVAILABLE;
   5681 		return true;
   5682 	}
   5683 	sm_free(vptr);
   5684 	*statp = EX_OK;
   5685 	return false;
   5686 }
   5687 #endif /* NETINFO */
   5688 /*
   5689 **  TEXT (unindexed text file) Modules
   5690 **
   5691 **	This code donated by Sun Microsystems.
   5692 */
   5693 
   5694 #define map_sff		map_lockfd	/* overload field */
   5695 
   5696 
   5697 /*
   5698 **  TEXT_MAP_OPEN -- open text table
   5699 */
   5700 
   5701 bool
   5702 text_map_open(map, mode)
   5703 	MAP *map;
   5704 	int mode;
   5705 {
   5706 	long sff;
   5707 	int i;
   5708 
   5709 	if (tTd(38, 2))
   5710 		sm_dprintf("text_map_open(%s, %s, %d)\n",
   5711 			map->map_mname, map->map_file, mode);
   5712 
   5713 	mode &= O_ACCMODE;
   5714 	if (mode != O_RDONLY)
   5715 	{
   5716 		errno = EPERM;
   5717 		return false;
   5718 	}
   5719 
   5720 	if (*map->map_file == '\0')
   5721 	{
   5722 		syserr("text map \"%s\": file name required",
   5723 			map->map_mname);
   5724 		return false;
   5725 	}
   5726 
   5727 	if (map->map_file[0] != '/')
   5728 	{
   5729 		syserr("text map \"%s\": file name must be fully qualified",
   5730 			map->map_mname);
   5731 		return false;
   5732 	}
   5733 
   5734 	sff = SFF_ROOTOK|SFF_REGONLY;
   5735 	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
   5736 		sff |= SFF_NOWLINK;
   5737 	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
   5738 		sff |= SFF_SAFEDIRPATH;
   5739 	if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
   5740 			  sff, S_IRUSR, NULL)) != 0)
   5741 	{
   5742 		int save_errno = errno;
   5743 
   5744 		/* cannot open this map */
   5745 		if (tTd(38, 2))
   5746 			sm_dprintf("\tunsafe map file: %d\n", i);
   5747 		errno = save_errno;
   5748 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   5749 			syserr("text map \"%s\": unsafe map file %s",
   5750 				map->map_mname, map->map_file);
   5751 		return false;
   5752 	}
   5753 
   5754 	if (map->map_keycolnm == NULL)
   5755 		map->map_keycolno = 0;
   5756 	else
   5757 	{
   5758 		if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
   5759 		{
   5760 			syserr("text map \"%s\", file %s: -k should specify a number, not %s",
   5761 				map->map_mname, map->map_file,
   5762 				map->map_keycolnm);
   5763 			return false;
   5764 		}
   5765 		map->map_keycolno = atoi(map->map_keycolnm);
   5766 	}
   5767 
   5768 	if (map->map_valcolnm == NULL)
   5769 		map->map_valcolno = 0;
   5770 	else
   5771 	{
   5772 		if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
   5773 		{
   5774 			syserr("text map \"%s\", file %s: -v should specify a number, not %s",
   5775 					map->map_mname, map->map_file,
   5776 					map->map_valcolnm);
   5777 			return false;
   5778 		}
   5779 		map->map_valcolno = atoi(map->map_valcolnm);
   5780 	}
   5781 
   5782 	if (tTd(38, 2))
   5783 	{
   5784 		sm_dprintf("text_map_open(%s, %s): delimiter = ",
   5785 			map->map_mname, map->map_file);
   5786 		if (map->map_coldelim == '\0')
   5787 			sm_dprintf("(white space)\n");
   5788 		else
   5789 			sm_dprintf("%c\n", map->map_coldelim);
   5790 	}
   5791 
   5792 	map->map_sff = sff;
   5793 	return true;
   5794 }
   5795 
   5796 
   5797 /*
   5798 **  TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
   5799 */
   5800 
   5801 char *
   5802 text_map_lookup(map, name, av, statp)
   5803 	MAP *map;
   5804 	char *name;
   5805 	char **av;
   5806 	int *statp;
   5807 {
   5808 	char *vp;
   5809 	auto int vsize;
   5810 	int buflen;
   5811 	SM_FILE_T *f;
   5812 	char delim;
   5813 	int key_idx;
   5814 	bool found_it;
   5815 	long sff = map->map_sff;
   5816 	char search_key[MAXNAME + 1];
   5817 	char linebuf[MAXLINE];
   5818 	char buf[MAXNAME + 1];
   5819 
   5820 	found_it = false;
   5821 	if (tTd(38, 20))
   5822 		sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname,  name);
   5823 
   5824 	buflen = strlen(name);
   5825 	if (buflen > sizeof(search_key) - 1)
   5826 		buflen = sizeof(search_key) - 1;	/* XXX just cut if off? */
   5827 	memmove(search_key, name, buflen);
   5828 	search_key[buflen] = '\0';
   5829 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
   5830 		makelower(search_key);
   5831 
   5832 	f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
   5833 	if (f == NULL)
   5834 	{
   5835 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
   5836 		*statp = EX_UNAVAILABLE;
   5837 		return NULL;
   5838 	}
   5839 	key_idx = map->map_keycolno;
   5840 	delim = map->map_coldelim;
   5841 	while (sm_io_fgets(f, SM_TIME_DEFAULT,
   5842 			   linebuf, sizeof(linebuf)) != NULL)
   5843 	{
   5844 		char *p;
   5845 
   5846 		/* skip comment line */
   5847 		if (linebuf[0] == '#')
   5848 			continue;
   5849 		p = strchr(linebuf, '\n');
   5850 		if (p != NULL)
   5851 			*p = '\0';
   5852 		p = get_column(linebuf, key_idx, delim, buf, sizeof(buf));
   5853 		if (p != NULL && sm_strcasecmp(search_key, p) == 0)
   5854 		{
   5855 			found_it = true;
   5856 			break;
   5857 		}
   5858 	}
   5859 	(void) sm_io_close(f, SM_TIME_DEFAULT);
   5860 	if (!found_it)
   5861 	{
   5862 		*statp = EX_NOTFOUND;
   5863 		return NULL;
   5864 	}
   5865 	vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof(buf));
   5866 	if (vp == NULL)
   5867 	{
   5868 		*statp = EX_NOTFOUND;
   5869 		return NULL;
   5870 	}
   5871 	vsize = strlen(vp);
   5872 	*statp = EX_OK;
   5873 	if (bitset(MF_MATCHONLY, map->map_mflags))
   5874 		return map_rewrite(map, name, strlen(name), NULL);
   5875 	else
   5876 		return map_rewrite(map, vp, vsize, av);
   5877 }
   5878 
   5879 /*
   5880 **  TEXT_GETCANONNAME -- look up canonical name in hosts file
   5881 */
   5882 
   5883 static bool
   5884 text_getcanonname(name, hbsize, statp)
   5885 	char *name;
   5886 	int hbsize;
   5887 	int *statp;
   5888 {
   5889 	bool found;
   5890 	char *dot;
   5891 	SM_FILE_T *f;
   5892 	char linebuf[MAXLINE];
   5893 	char cbuf[MAXNAME + 1];
   5894 	char nbuf[MAXNAME + 1];
   5895 
   5896 	if (tTd(38, 20))
   5897 		sm_dprintf("text_getcanonname(%s)\n", name);
   5898 
   5899 	if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
   5900 	{
   5901 		*statp = EX_UNAVAILABLE;
   5902 		return false;
   5903 	}
   5904 	dot = shorten_hostname(nbuf);
   5905 
   5906 	f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
   5907 		       NULL);
   5908 	if (f == NULL)
   5909 	{
   5910 		*statp = EX_UNAVAILABLE;
   5911 		return false;
   5912 	}
   5913 	found = false;
   5914 	while (!found &&
   5915 		sm_io_fgets(f, SM_TIME_DEFAULT,
   5916 			    linebuf, sizeof(linebuf)) != NULL)
   5917 	{
   5918 		char *p = strpbrk(linebuf, "#\n");
   5919 
   5920 		if (p != NULL)
   5921 			*p = '\0';
   5922 		if (linebuf[0] != '\0')
   5923 			found = extract_canonname(nbuf, dot, linebuf,
   5924 						  cbuf, sizeof(cbuf));
   5925 	}
   5926 	(void) sm_io_close(f, SM_TIME_DEFAULT);
   5927 	if (!found)
   5928 	{
   5929 		*statp = EX_NOHOST;
   5930 		return false;
   5931 	}
   5932 
   5933 	if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
   5934 	{
   5935 		*statp = EX_UNAVAILABLE;
   5936 		return false;
   5937 	}
   5938 	*statp = EX_OK;
   5939 	return true;
   5940 }
   5941 /*
   5942 **  STAB (Symbol Table) Modules
   5943 */
   5944 
   5945 
   5946 /*
   5947 **  STAB_MAP_LOOKUP -- look up alias in symbol table
   5948 */
   5949 
   5950 /* ARGSUSED2 */
   5951 char *
   5952 stab_map_lookup(map, name, av, pstat)
   5953 	register MAP *map;
   5954 	char *name;
   5955 	char **av;
   5956 	int *pstat;
   5957 {
   5958 	register STAB *s;
   5959 
   5960 	if (tTd(38, 20))
   5961 		sm_dprintf("stab_lookup(%s, %s)\n",
   5962 			map->map_mname, name);
   5963 
   5964 	s = stab(name, ST_ALIAS, ST_FIND);
   5965 	if (s == NULL)
   5966 		return NULL;
   5967 	if (bitset(MF_MATCHONLY, map->map_mflags))
   5968 		return map_rewrite(map, name, strlen(name), NULL);
   5969 	else
   5970 		return map_rewrite(map, s->s_alias, strlen(s->s_alias), av);
   5971 }
   5972 
   5973 /*
   5974 **  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
   5975 */
   5976 
   5977 void
   5978 stab_map_store(map, lhs, rhs)
   5979 	register MAP *map;
   5980 	char *lhs;
   5981 	char *rhs;
   5982 {
   5983 	register STAB *s;
   5984 
   5985 	s = stab(lhs, ST_ALIAS, ST_ENTER);
   5986 	s->s_alias = newstr(rhs);
   5987 }
   5988 
   5989 
   5990 /*
   5991 **  STAB_MAP_OPEN -- initialize (reads data file)
   5992 **
   5993 **	This is a wierd case -- it is only intended as a fallback for
   5994 **	aliases.  For this reason, opens for write (only during a
   5995 **	"newaliases") always fails, and opens for read open the
   5996 **	actual underlying text file instead of the database.
   5997 */
   5998 
   5999 bool
   6000 stab_map_open(map, mode)
   6001 	register MAP *map;
   6002 	int mode;
   6003 {
   6004 	SM_FILE_T *af;
   6005 	long sff;
   6006 	struct stat st;
   6007 
   6008 	if (tTd(38, 2))
   6009 		sm_dprintf("stab_map_open(%s, %s, %d)\n",
   6010 			map->map_mname, map->map_file, mode);
   6011 
   6012 	mode &= O_ACCMODE;
   6013 	if (mode != O_RDONLY)
   6014 	{
   6015 		errno = EPERM;
   6016 		return false;
   6017 	}
   6018 
   6019 	sff = SFF_ROOTOK|SFF_REGONLY;
   6020 	if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
   6021 		sff |= SFF_NOWLINK;
   6022 	if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
   6023 		sff |= SFF_SAFEDIRPATH;
   6024 	af = safefopen(map->map_file, O_RDONLY, 0444, sff);
   6025 	if (af == NULL)
   6026 		return false;
   6027 	readaliases(map, af, false, false);
   6028 
   6029 	if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
   6030 		map->map_mtime = st.st_mtime;
   6031 	(void) sm_io_close(af, SM_TIME_DEFAULT);
   6032 
   6033 	return true;
   6034 }
   6035 /*
   6036 **  Implicit Modules
   6037 **
   6038 **	Tries several types.  For back compatibility of aliases.
   6039 */
   6040 
   6041 
   6042 /*
   6043 **  IMPL_MAP_LOOKUP -- lookup in best open database
   6044 */
   6045 
   6046 char *
   6047 impl_map_lookup(map, name, av, pstat)
   6048 	MAP *map;
   6049 	char *name;
   6050 	char **av;
   6051 	int *pstat;
   6052 {
   6053 	if (tTd(38, 20))
   6054 		sm_dprintf("impl_map_lookup(%s, %s)\n",
   6055 			map->map_mname, name);
   6056 
   6057 #if NEWDB
   6058 	if (bitset(MF_IMPL_HASH, map->map_mflags))
   6059 		return db_map_lookup(map, name, av, pstat);
   6060 #endif /* NEWDB */
   6061 #if NDBM
   6062 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
   6063 		return ndbm_map_lookup(map, name, av, pstat);
   6064 #endif /* NDBM */
   6065 	return stab_map_lookup(map, name, av, pstat);
   6066 }
   6067 
   6068 /*
   6069 **  IMPL_MAP_STORE -- store in open databases
   6070 */
   6071 
   6072 void
   6073 impl_map_store(map, lhs, rhs)
   6074 	MAP *map;
   6075 	char *lhs;
   6076 	char *rhs;
   6077 {
   6078 	if (tTd(38, 12))
   6079 		sm_dprintf("impl_map_store(%s, %s, %s)\n",
   6080 			map->map_mname, lhs, rhs);
   6081 #if NEWDB
   6082 	if (bitset(MF_IMPL_HASH, map->map_mflags))
   6083 		db_map_store(map, lhs, rhs);
   6084 #endif /* NEWDB */
   6085 #if NDBM
   6086 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
   6087 		ndbm_map_store(map, lhs, rhs);
   6088 #endif /* NDBM */
   6089 	stab_map_store(map, lhs, rhs);
   6090 }
   6091 
   6092 /*
   6093 **  IMPL_MAP_OPEN -- implicit database open
   6094 */
   6095 
   6096 bool
   6097 impl_map_open(map, mode)
   6098 	MAP *map;
   6099 	int mode;
   6100 {
   6101 	if (tTd(38, 2))
   6102 		sm_dprintf("impl_map_open(%s, %s, %d)\n",
   6103 			map->map_mname, map->map_file, mode);
   6104 
   6105 	mode &= O_ACCMODE;
   6106 #if NEWDB
   6107 	map->map_mflags |= MF_IMPL_HASH;
   6108 	if (hash_map_open(map, mode))
   6109 	{
   6110 # ifdef NDBM_YP_COMPAT
   6111 		if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
   6112 # endif /* NDBM_YP_COMPAT */
   6113 			return true;
   6114 	}
   6115 	else
   6116 		map->map_mflags &= ~MF_IMPL_HASH;
   6117 #endif /* NEWDB */
   6118 #if NDBM
   6119 	map->map_mflags |= MF_IMPL_NDBM;
   6120 	if (ndbm_map_open(map, mode))
   6121 	{
   6122 		return true;
   6123 	}
   6124 	else
   6125 		map->map_mflags &= ~MF_IMPL_NDBM;
   6126 #endif /* NDBM */
   6127 
   6128 #if defined(NEWDB) || defined(NDBM)
   6129 	if (Verbose)
   6130 		message("WARNING: cannot open alias database %s%s",
   6131 			map->map_file,
   6132 			mode == O_RDONLY ? "; reading text version" : "");
   6133 #else /* defined(NEWDB) || defined(NDBM) */
   6134 	if (mode != O_RDONLY)
   6135 		usrerr("Cannot rebuild aliases: no database format defined");
   6136 #endif /* defined(NEWDB) || defined(NDBM) */
   6137 
   6138 	if (mode == O_RDONLY)
   6139 		return stab_map_open(map, mode);
   6140 	else
   6141 		return false;
   6142 }
   6143 
   6144 
   6145 /*
   6146 **  IMPL_MAP_CLOSE -- close any open database(s)
   6147 */
   6148 
   6149 void
   6150 impl_map_close(map)
   6151 	MAP *map;
   6152 {
   6153 	if (tTd(38, 9))
   6154 		sm_dprintf("impl_map_close(%s, %s, %lx)\n",
   6155 			map->map_mname, map->map_file, map->map_mflags);
   6156 #if NEWDB
   6157 	if (bitset(MF_IMPL_HASH, map->map_mflags))
   6158 	{
   6159 		db_map_close(map);
   6160 		map->map_mflags &= ~MF_IMPL_HASH;
   6161 	}
   6162 #endif /* NEWDB */
   6163 
   6164 #if NDBM
   6165 	if (bitset(MF_IMPL_NDBM, map->map_mflags))
   6166 	{
   6167 		ndbm_map_close(map);
   6168 		map->map_mflags &= ~MF_IMPL_NDBM;
   6169 	}
   6170 #endif /* NDBM */
   6171 }
   6172 /*
   6173 **  User map class.
   6174 **
   6175 **	Provides access to the system password file.
   6176 */
   6177 
   6178 /*
   6179 **  USER_MAP_OPEN -- open user map
   6180 **
   6181 **	Really just binds field names to field numbers.
   6182 */
   6183 
   6184 bool
   6185 user_map_open(map, mode)
   6186 	MAP *map;
   6187 	int mode;
   6188 {
   6189 	if (tTd(38, 2))
   6190 		sm_dprintf("user_map_open(%s, %d)\n",
   6191 			map->map_mname, mode);
   6192 
   6193 	mode &= O_ACCMODE;
   6194 	if (mode != O_RDONLY)
   6195 	{
   6196 		/* issue a pseudo-error message */
   6197 		errno = SM_EMAPCANTWRITE;
   6198 		return false;
   6199 	}
   6200 	if (map->map_valcolnm == NULL)
   6201 		/* EMPTY */
   6202 		/* nothing */ ;
   6203 	else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
   6204 		map->map_valcolno = 1;
   6205 	else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
   6206 		map->map_valcolno = 2;
   6207 	else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
   6208 		map->map_valcolno = 3;
   6209 	else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
   6210 		map->map_valcolno = 4;
   6211 	else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
   6212 		map->map_valcolno = 5;
   6213 	else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
   6214 		map->map_valcolno = 6;
   6215 	else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
   6216 		map->map_valcolno = 7;
   6217 	else
   6218 	{
   6219 		syserr("User map %s: unknown column name %s",
   6220 			map->map_mname, map->map_valcolnm);
   6221 		return false;
   6222 	}
   6223 	return true;
   6224 }
   6225 
   6226 
   6227 /*
   6228 **  USER_MAP_LOOKUP -- look up a user in the passwd file.
   6229 */
   6230 
   6231 /* ARGSUSED3 */
   6232 char *
   6233 user_map_lookup(map, key, av, statp)
   6234 	MAP *map;
   6235 	char *key;
   6236 	char **av;
   6237 	int *statp;
   6238 {
   6239 	auto bool fuzzy;
   6240 	SM_MBDB_T user;
   6241 
   6242 	if (tTd(38, 20))
   6243 		sm_dprintf("user_map_lookup(%s, %s)\n",
   6244 			map->map_mname, key);
   6245 
   6246 	*statp = finduser(key, &fuzzy, &user);
   6247 	if (*statp != EX_OK)
   6248 		return NULL;
   6249 	if (bitset(MF_MATCHONLY, map->map_mflags))
   6250 		return map_rewrite(map, key, strlen(key), NULL);
   6251 	else
   6252 	{
   6253 		char *rwval = NULL;
   6254 		char buf[30];
   6255 
   6256 		switch (map->map_valcolno)
   6257 		{
   6258 		  case 0:
   6259 		  case 1:
   6260 			rwval = user.mbdb_name;
   6261 			break;
   6262 
   6263 		  case 2:
   6264 			rwval = "x";	/* passwd no longer supported */
   6265 			break;
   6266 
   6267 		  case 3:
   6268 			(void) sm_snprintf(buf, sizeof(buf), "%d",
   6269 					   (int) user.mbdb_uid);
   6270 			rwval = buf;
   6271 			break;
   6272 
   6273 		  case 4:
   6274 			(void) sm_snprintf(buf, sizeof(buf), "%d",
   6275 					   (int) user.mbdb_gid);
   6276 			rwval = buf;
   6277 			break;
   6278 
   6279 		  case 5:
   6280 			rwval = user.mbdb_fullname;
   6281 			break;
   6282 
   6283 		  case 6:
   6284 			rwval = user.mbdb_homedir;
   6285 			break;
   6286 
   6287 		  case 7:
   6288 			rwval = user.mbdb_shell;
   6289 			break;
   6290 		  default:
   6291 			syserr("user_map %s: bogus field %d",
   6292 				map->map_mname, map->map_valcolno);
   6293 			return NULL;
   6294 		}
   6295 		return map_rewrite(map, rwval, strlen(rwval), av);
   6296 	}
   6297 }
   6298 /*
   6299 **  Program map type.
   6300 **
   6301 **	This provides access to arbitrary programs.  It should be used
   6302 **	only very sparingly, since there is no way to bound the cost
   6303 **	of invoking an arbitrary program.
   6304 */
   6305 
   6306 char *
   6307 prog_map_lookup(map, name, av, statp)
   6308 	MAP *map;
   6309 	char *name;
   6310 	char **av;
   6311 	int *statp;
   6312 {
   6313 	int i;
   6314 	int save_errno;
   6315 	int fd;
   6316 	int status;
   6317 	auto pid_t pid;
   6318 	register char *p;
   6319 	char *rval;
   6320 	char *argv[MAXPV + 1];
   6321 	char buf[MAXLINE];
   6322 
   6323 	if (tTd(38, 20))
   6324 		sm_dprintf("prog_map_lookup(%s, %s) %s\n",
   6325 			map->map_mname, name, map->map_file);
   6326 
   6327 	i = 0;
   6328 	argv[i++] = map->map_file;
   6329 	if (map->map_rebuild != NULL)
   6330 	{
   6331 		(void) sm_strlcpy(buf, map->map_rebuild, sizeof(buf));
   6332 		for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
   6333 		{
   6334 			if (i >= MAXPV - 1)
   6335 				break;
   6336 			argv[i++] = p;
   6337 		}
   6338 	}
   6339 	argv[i++] = name;
   6340 	argv[i] = NULL;
   6341 	if (tTd(38, 21))
   6342 	{
   6343 		sm_dprintf("prog_open:");
   6344 		for (i = 0; argv[i] != NULL; i++)
   6345 			sm_dprintf(" %s", argv[i]);
   6346 		sm_dprintf("\n");
   6347 	}
   6348 	(void) sm_blocksignal(SIGCHLD);
   6349 	pid = prog_open(argv, &fd, CurEnv);
   6350 	if (pid < 0)
   6351 	{
   6352 		if (!bitset(MF_OPTIONAL, map->map_mflags))
   6353 			syserr("prog_map_lookup(%s) failed (%s) -- closing",
   6354 			       map->map_mname, sm_errstring(errno));
   6355 		else if (tTd(38, 9))
   6356 			sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
   6357 				   map->map_mname, sm_errstring(errno));
   6358 		map->map_mflags &= ~(MF_VALID|MF_OPEN);
   6359 		*statp = EX_OSFILE;
   6360 		return NULL;
   6361 	}
   6362 	i = read(fd, buf, sizeof(buf) - 1);
   6363 	if (i < 0)
   6364 	{
   6365 		syserr("prog_map_lookup(%s): read error %s",
   6366 		       map->map_mname, sm_errstring(errno));
   6367 		rval = NULL;
   6368 	}
   6369 	else if (i == 0)
   6370 	{
   6371 		if (tTd(38, 20))
   6372 			sm_dprintf("prog_map_lookup(%s): empty answer\n",
   6373 				   map->map_mname);
   6374 		rval = NULL;
   6375 	}
   6376 	else
   6377 	{
   6378 		buf[i] = '\0';
   6379 		p = strchr(buf, '\n');
   6380 		if (p != NULL)
   6381 			*p = '\0';
   6382 
   6383 		/* collect the return value */
   6384 		if (bitset(MF_MATCHONLY, map->map_mflags))
   6385 			rval = map_rewrite(map, name, strlen(name), NULL);
   6386 		else
   6387 			rval = map_rewrite(map, buf, strlen(buf), av);
   6388 
   6389 		/* now flush any additional output */
   6390 		while ((i = read(fd, buf, sizeof(buf))) > 0)
   6391 			continue;
   6392 	}
   6393 
   6394 	/* wait for the process to terminate */
   6395 	(void) close(fd);
   6396 	status = waitfor(pid);
   6397 	save_errno = errno;
   6398 	(void) sm_releasesignal(SIGCHLD);
   6399 	errno = save_errno;
   6400 
   6401 	if (status == -1)
   6402 	{
   6403 		syserr("prog_map_lookup(%s): wait error %s",
   6404 		       map->map_mname, sm_errstring(errno));
   6405 		*statp = EX_SOFTWARE;
   6406 		rval = NULL;
   6407 	}
   6408 	else if (WIFEXITED(status))
   6409 	{
   6410 		if ((*statp = WEXITSTATUS(status)) != EX_OK)
   6411 			rval = NULL;
   6412 	}
   6413 	else
   6414 	{
   6415 		syserr("prog_map_lookup(%s): child died on signal %d",
   6416 		       map->map_mname, status);
   6417 		*statp = EX_UNAVAILABLE;
   6418 		rval = NULL;
   6419 	}
   6420 	return rval;
   6421 }
   6422 /*
   6423 **  Sequenced map type.
   6424 **
   6425 **	Tries each map in order until something matches, much like
   6426 **	implicit.  Stores go to the first map in the list that can
   6427 **	support storing.
   6428 **
   6429 **	This is slightly unusual in that there are two interfaces.
   6430 **	The "sequence" interface lets you stack maps arbitrarily.
   6431 **	The "switch" interface builds a sequence map by looking
   6432 **	at a system-dependent configuration file such as
   6433 **	/etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
   6434 **
   6435 **	We don't need an explicit open, since all maps are
   6436 **	opened on demand.
   6437 */
   6438 
   6439 /*
   6440 **  SEQ_MAP_PARSE -- Sequenced map parsing
   6441 */
   6442 
   6443 bool
   6444 seq_map_parse(map, ap)
   6445 	MAP *map;
   6446 	char *ap;
   6447 {
   6448 	int maxmap;
   6449 
   6450 	if (tTd(38, 2))
   6451 		sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
   6452 	maxmap = 0;
   6453 	while (*ap != '\0')
   6454 	{
   6455 		register char *p;
   6456 		STAB *s;
   6457 
   6458 		/* find beginning of map name */
   6459 		while (isascii(*ap) && isspace(*ap))
   6460 			ap++;
   6461 		for (p = ap;
   6462 		     (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
   6463 		     p++)
   6464 			continue;
   6465 		if (*p != '\0')
   6466 			*p++ = '\0';
   6467 		while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
   6468 			p++;
   6469 		if (*ap == '\0')
   6470 		{
   6471 			ap = p;
   6472 			continue;
   6473 		}
   6474 		s = stab(ap, ST_MAP, ST_FIND);
   6475 		if (s == NULL)
   6476 		{
   6477 			syserr("Sequence map %s: unknown member map %s",
   6478 				map->map_mname, ap);
   6479 		}
   6480 		else if (maxmap >= MAXMAPSTACK)
   6481 		{
   6482 			syserr("Sequence map %s: too many member maps (%d max)",
   6483 				map->map_mname, MAXMAPSTACK);
   6484 			maxmap++;
   6485 		}
   6486 		else if (maxmap < MAXMAPSTACK)
   6487 		{
   6488 			map->map_stack[maxmap++] = &s->s_map;
   6489 		}
   6490 		ap = p;
   6491 	}
   6492 	return true;
   6493 }
   6494 
   6495 /*
   6496 **  SWITCH_MAP_OPEN -- open a switched map
   6497 **
   6498 **	This looks at the system-dependent configuration and builds
   6499 **	a sequence map that does the same thing.
   6500 **
   6501 **	Every system must define a switch_map_find routine in conf.c
   6502 **	that will return the list of service types associated with a
   6503 **	given service class.
   6504 */
   6505 
   6506 bool
   6507 switch_map_open(map, mode)
   6508 	MAP *map;
   6509 	int mode;
   6510 {
   6511 	int mapno;
   6512 	int nmaps;
   6513 	char *maptype[MAXMAPSTACK];
   6514 
   6515 	if (tTd(38, 2))
   6516 		sm_dprintf("switch_map_open(%s, %s, %d)\n",
   6517 			map->map_mname, map->map_file, mode);
   6518 
   6519 	mode &= O_ACCMODE;
   6520 	nmaps = switch_map_find(map->map_file, maptype, map->map_return);
   6521 	if (tTd(38, 19))
   6522 	{
   6523 		sm_dprintf("\tswitch_map_find => %d\n", nmaps);
   6524 		for (mapno = 0; mapno < nmaps; mapno++)
   6525 			sm_dprintf("\t\t%s\n", maptype[mapno]);
   6526 	}
   6527 	if (nmaps <= 0 || nmaps > MAXMAPSTACK)
   6528 		return false;
   6529 
   6530 	for (mapno = 0; mapno < nmaps; mapno++)
   6531 	{
   6532 		register STAB *s;
   6533 		char nbuf[MAXNAME + 1];
   6534 
   6535 		if (maptype[mapno] == NULL)
   6536 			continue;
   6537 		(void) sm_strlcpyn(nbuf, sizeof(nbuf), 3,
   6538 				   map->map_mname, ".", maptype[mapno]);
   6539 		s = stab(nbuf, ST_MAP, ST_FIND);
   6540 		if (s == NULL)
   6541 		{
   6542 			syserr("Switch map %s: unknown member map %s",
   6543 				map->map_mname, nbuf);
   6544 		}
   6545 		else
   6546 		{
   6547 			map->map_stack[mapno] = &s->s_map;
   6548 			if (tTd(38, 4))
   6549 				sm_dprintf("\tmap_stack[%d] = %s:%s\n",
   6550 					   mapno,
   6551 					   s->s_map.map_class->map_cname,
   6552 					   nbuf);
   6553 		}
   6554 	}
   6555 	return true;
   6556 }
   6557 
   6558 #if 0
   6559 /*
   6560 **  SEQ_MAP_CLOSE -- close all underlying maps
   6561 */
   6562 
   6563 void
   6564 seq_map_close(map)
   6565 	MAP *map;
   6566 {
   6567 	int mapno;
   6568 
   6569 	if (tTd(38, 9))
   6570 		sm_dprintf("seq_map_close(%s)\n", map->map_mname);
   6571 
   6572 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
   6573 	{
   6574 		MAP *mm = map->map_stack[mapno];
   6575 
   6576 		if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
   6577 			continue;
   6578 		mm->map_mflags |= MF_CLOSING;
   6579 		mm->map_class->map_close(mm);
   6580 		mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
   6581 	}
   6582 }
   6583 #endif /* 0 */
   6584 
   6585 /*
   6586 **  SEQ_MAP_LOOKUP -- sequenced map lookup
   6587 */
   6588 
   6589 char *
   6590 seq_map_lookup(map, key, args, pstat)
   6591 	MAP *map;
   6592 	char *key;
   6593 	char **args;
   6594 	int *pstat;
   6595 {
   6596 	int mapno;
   6597 	int mapbit = 0x01;
   6598 	bool tempfail = false;
   6599 
   6600 	if (tTd(38, 20))
   6601 		sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
   6602 
   6603 	for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
   6604 	{
   6605 		MAP *mm = map->map_stack[mapno];
   6606 		char *rv;
   6607 
   6608 		if (mm == NULL)
   6609 			continue;
   6610 		if (!bitset(MF_OPEN, mm->map_mflags) &&
   6611 		    !openmap(mm))
   6612 		{
   6613 			if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
   6614 			{
   6615 				*pstat = EX_UNAVAILABLE;
   6616 				return NULL;
   6617 			}
   6618 			continue;
   6619 		}
   6620 		*pstat = EX_OK;
   6621 		rv = mm->map_class->map_lookup(mm, key, args, pstat);
   6622 		if (rv != NULL)
   6623 			return rv;
   6624 		if (*pstat == EX_TEMPFAIL)
   6625 		{
   6626 			if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
   6627 				return NULL;
   6628 			tempfail = true;
   6629 		}
   6630 		else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
   6631 			break;
   6632 	}
   6633 	if (tempfail)
   6634 		*pstat = EX_TEMPFAIL;
   6635 	else if (*pstat == EX_OK)
   6636 		*pstat = EX_NOTFOUND;
   6637 	return NULL;
   6638 }
   6639 
   6640 /*
   6641 **  SEQ_MAP_STORE -- sequenced map store
   6642 */
   6643 
   6644 void
   6645 seq_map_store(map, key, val)
   6646 	MAP *map;
   6647 	char *key;
   6648 	char *val;
   6649 {
   6650 	int mapno;
   6651 
   6652 	if (tTd(38, 12))
   6653 		sm_dprintf("seq_map_store(%s, %s, %s)\n",
   6654 			map->map_mname, key, val);
   6655 
   6656 	for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
   6657 	{
   6658 		MAP *mm = map->map_stack[mapno];
   6659 
   6660 		if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
   6661 			continue;
   6662 
   6663 		mm->map_class->map_store(mm, key, val);
   6664 		return;
   6665 	}
   6666 	syserr("seq_map_store(%s, %s, %s): no writable map",
   6667 		map->map_mname, key, val);
   6668 }
   6669 /*
   6670 **  NULL stubs
   6671 */
   6672 
   6673 /* ARGSUSED */
   6674 bool
   6675 null_map_open(map, mode)
   6676 	MAP *map;
   6677 	int mode;
   6678 {
   6679 	return true;
   6680 }
   6681 
   6682 /* ARGSUSED */
   6683 void
   6684 null_map_close(map)
   6685 	MAP *map;
   6686 {
   6687 	return;
   6688 }
   6689 
   6690 char *
   6691 null_map_lookup(map, key, args, pstat)
   6692 	MAP *map;
   6693 	char *key;
   6694 	char **args;
   6695 	int *pstat;
   6696 {
   6697 	*pstat = EX_NOTFOUND;
   6698 	return NULL;
   6699 }
   6700 
   6701 /* ARGSUSED */
   6702 void
   6703 null_map_store(map, key, val)
   6704 	MAP *map;
   6705 	char *key;
   6706 	char *val;
   6707 {
   6708 	return;
   6709 }
   6710 
   6711 MAPCLASS	NullMapClass =
   6712 {
   6713 	"null-map",		NULL,			0,
   6714 	NULL,			null_map_lookup,	null_map_store,
   6715 	null_map_open,		null_map_close,
   6716 };
   6717 
   6718 /*
   6719 **  BOGUS stubs
   6720 */
   6721 
   6722 char *
   6723 bogus_map_lookup(map, key, args, pstat)
   6724 	MAP *map;
   6725 	char *key;
   6726 	char **args;
   6727 	int *pstat;
   6728 {
   6729 	*pstat = EX_TEMPFAIL;
   6730 	return NULL;
   6731 }
   6732 
   6733 MAPCLASS	BogusMapClass =
   6734 {
   6735 	"bogus-map",		NULL,			0,
   6736 	NULL,			bogus_map_lookup,	null_map_store,
   6737 	null_map_open,		null_map_close,
   6738 };
   6739 /*
   6740 **  MACRO modules
   6741 */
   6742 
   6743 char *
   6744 macro_map_lookup(map, name, av, statp)
   6745 	MAP *map;
   6746 	char *name;
   6747 	char **av;
   6748 	int *statp;
   6749 {
   6750 	int mid;
   6751 
   6752 	if (tTd(38, 20))
   6753 		sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
   6754 			name == NULL ? "NULL" : name);
   6755 
   6756 	if (name == NULL ||
   6757 	    *name == '\0' ||
   6758 	    (mid = macid(name)) == 0)
   6759 	{
   6760 		*statp = EX_CONFIG;
   6761 		return NULL;
   6762 	}
   6763 
   6764 	if (av[1] == NULL)
   6765 		macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
   6766 	else
   6767 		macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
   6768 
   6769 	*statp = EX_OK;
   6770 	return "";
   6771 }
   6772 /*
   6773 **  REGEX modules
   6774 */
   6775 
   6776 #if MAP_REGEX
   6777 
   6778 # include <regex.h>
   6779 
   6780 # define DEFAULT_DELIM	CONDELSE
   6781 # define END_OF_FIELDS	-1
   6782 # define ERRBUF_SIZE	80
   6783 # define MAX_MATCH	32
   6784 
   6785 # define xnalloc(s)	memset(xalloc(s), '\0', s);
   6786 
   6787 struct regex_map
   6788 {
   6789 	regex_t	*regex_pattern_buf;	/* xalloc it */
   6790 	int	*regex_subfields;	/* move to type MAP */
   6791 	char	*regex_delim;		/* move to type MAP */
   6792 };
   6793 
   6794 static int	parse_fields __P((char *, int *, int, int));
   6795 static char	*regex_map_rewrite __P((MAP *, const char*, size_t, char **));
   6796 
   6797 static int
   6798 parse_fields(s, ibuf, blen, nr_substrings)
   6799 	char *s;
   6800 	int *ibuf;		/* array */
   6801 	int blen;		/* number of elements in ibuf */
   6802 	int nr_substrings;	/* number of substrings in the pattern */
   6803 {
   6804 	register char *cp;
   6805 	int i = 0;
   6806 	bool lastone = false;
   6807 
   6808 	blen--;		/* for terminating END_OF_FIELDS */
   6809 	cp = s;
   6810 	do
   6811 	{
   6812 		for (;; cp++)
   6813 		{
   6814 			if (*cp == ',')
   6815 			{
   6816 				*cp = '\0';
   6817 				break;
   6818 			}
   6819 			if (*cp == '\0')
   6820 			{
   6821 				lastone = true;
   6822 				break;
   6823 			}
   6824 		}
   6825 		if (i < blen)
   6826 		{
   6827 			int val = atoi(s);
   6828 
   6829 			if (val < 0 || val >= nr_substrings)
   6830 			{
   6831 				syserr("field (%d) out of range, only %d substrings in pattern",
   6832 				       val, nr_substrings);
   6833 				return -1;
   6834 			}
   6835 			ibuf[i++] = val;
   6836 		}
   6837 		else
   6838 		{
   6839 			syserr("too many fields, %d max", blen);
   6840 			return -1;
   6841 		}
   6842 		s = ++cp;
   6843 	} while (!lastone);
   6844 	ibuf[i] = END_OF_FIELDS;
   6845 	return i;
   6846 }
   6847 
   6848 bool
   6849 regex_map_init(map, ap)
   6850 	MAP *map;
   6851 	char *ap;
   6852 {
   6853 	int regerr;
   6854 	struct regex_map *map_p;
   6855 	register char *p;
   6856 	char *sub_param = NULL;
   6857 	int pflags;
   6858 	static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
   6859 
   6860 	if (tTd(38, 2))
   6861 		sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
   6862 			map->map_mname, ap);
   6863 
   6864 	pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
   6865 	p = ap;
   6866 	map_p = (struct regex_map *) xnalloc(sizeof(*map_p));
   6867 	map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
   6868 
   6869 	for (;;)
   6870 	{
   6871 		while (isascii(*p) && isspace(*p))
   6872 			p++;
   6873 		if (*p != '-')
   6874 			break;
   6875 		switch (*++p)
   6876 		{
   6877 		  case 'n':	/* not */
   6878 			map->map_mflags |= MF_REGEX_NOT;
   6879 			break;
   6880 
   6881 		  case 'f':	/* case sensitive */
   6882 			map->map_mflags |= MF_NOFOLDCASE;
   6883 			pflags &= ~REG_ICASE;
   6884 			break;
   6885 
   6886 		  case 'b':	/* basic regular expressions */
   6887 			pflags &= ~REG_EXTENDED;
   6888 			break;
   6889 
   6890 		  case 's':	/* substring match () syntax */
   6891 			sub_param = ++p;
   6892 			pflags &= ~REG_NOSUB;
   6893 			break;
   6894 
   6895 		  case 'd':	/* delimiter */
   6896 			map_p->regex_delim = ++p;
   6897 			break;
   6898 
   6899 		  case 'a':	/* map append */
   6900 			map->map_app = ++p;
   6901 			break;
   6902 
   6903 		  case 'm':	/* matchonly */
   6904 			map->map_mflags |= MF_MATCHONLY;
   6905 			break;
   6906 
   6907 		  case 'q':
   6908 			map->map_mflags |= MF_KEEPQUOTES;
   6909 			break;
   6910 
   6911 		  case 'S':
   6912 			map->map_spacesub = *++p;
   6913 			break;
   6914 
   6915 		  case 'D':
   6916 			map->map_mflags |= MF_DEFER;
   6917 			break;
   6918 
   6919 		}
   6920 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
   6921 			p++;
   6922 		if (*p != '\0')
   6923 			*p++ = '\0';
   6924 	}
   6925 	if (tTd(38, 3))
   6926 		sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
   6927 
   6928 	if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
   6929 	{
   6930 		/* Errorhandling */
   6931 		char errbuf[ERRBUF_SIZE];
   6932 
   6933 		(void) regerror(regerr, map_p->regex_pattern_buf,
   6934 			 errbuf, sizeof(errbuf));
   6935 		syserr("pattern-compile-error: %s", errbuf);
   6936 		sm_free(map_p->regex_pattern_buf); /* XXX */
   6937 		sm_free(map_p); /* XXX */
   6938 		return false;
   6939 	}
   6940 
   6941 	if (map->map_app != NULL)
   6942 		map->map_app = newstr(map->map_app);
   6943 	if (map_p->regex_delim != NULL)
   6944 		map_p->regex_delim = newstr(map_p->regex_delim);
   6945 	else
   6946 		map_p->regex_delim = defdstr;
   6947 
   6948 	if (!bitset(REG_NOSUB, pflags))
   6949 	{
   6950 		/* substring matching */
   6951 		int substrings;
   6952 		int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
   6953 
   6954 		substrings = map_p->regex_pattern_buf->re_nsub + 1;
   6955 
   6956 		if (tTd(38, 3))
   6957 			sm_dprintf("regex_map_init: nr of substrings %d\n",
   6958 				substrings);
   6959 
   6960 		if (substrings >= MAX_MATCH)
   6961 		{
   6962 			syserr("too many substrings, %d max", MAX_MATCH);
   6963 			sm_free(map_p->regex_pattern_buf); /* XXX */
   6964 			sm_free(map_p); /* XXX */
   6965 			return false;
   6966 		}
   6967 		if (sub_param != NULL && sub_param[0] != '\0')
   6968 		{
   6969 			/* optional parameter -sfields */
   6970 			if (parse_fields(sub_param, fields,
   6971 					 MAX_MATCH + 1, substrings) == -1)
   6972 				return false;
   6973 		}
   6974 		else
   6975 		{
   6976 			int i;
   6977 
   6978 			/* set default fields */
   6979 			for (i = 0; i < substrings; i++)
   6980 				fields[i] = i;
   6981 			fields[i] = END_OF_FIELDS;
   6982 		}
   6983 		map_p->regex_subfields = fields;
   6984 		if (tTd(38, 3))
   6985 		{
   6986 			int *ip;
   6987 
   6988 			sm_dprintf("regex_map_init: subfields");
   6989 			for (ip = fields; *ip != END_OF_FIELDS; ip++)
   6990 				sm_dprintf(" %d", *ip);
   6991 			sm_dprintf("\n");
   6992 		}
   6993 	}
   6994 	map->map_db1 = (ARBPTR_T) map_p;	/* dirty hack */
   6995 	return true;
   6996 }
   6997 
   6998 static char *
   6999 regex_map_rewrite(map, s, slen, av)
   7000 	MAP *map;
   7001 	const char *s;
   7002 	size_t slen;
   7003 	char **av;
   7004 {
   7005 	if (bitset(MF_MATCHONLY, map->map_mflags))
   7006 		return map_rewrite(map, av[0], strlen(av[0]), NULL);
   7007 	else
   7008 		return map_rewrite(map, s, slen, av);
   7009 }
   7010 
   7011 char *
   7012 regex_map_lookup(map, name, av, statp)
   7013 	MAP *map;
   7014 	char *name;
   7015 	char **av;
   7016 	int *statp;
   7017 {
   7018 	int reg_res;
   7019 	struct regex_map *map_p;
   7020 	regmatch_t pmatch[MAX_MATCH];
   7021 
   7022 	if (tTd(38, 20))
   7023 	{
   7024 		char **cpp;
   7025 
   7026 		sm_dprintf("regex_map_lookup: key '%s'\n", name);
   7027 		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
   7028 			sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
   7029 	}
   7030 
   7031 	map_p = (struct regex_map *)(map->map_db1);
   7032 	reg_res = regexec(map_p->regex_pattern_buf,
   7033 			  name, MAX_MATCH, pmatch, 0);
   7034 
   7035 	if (bitset(MF_REGEX_NOT, map->map_mflags))
   7036 	{
   7037 		/* option -n */
   7038 		if (reg_res == REG_NOMATCH)
   7039 			return regex_map_rewrite(map, "", (size_t) 0, av);
   7040 		else
   7041 			return NULL;
   7042 	}
   7043 	if (reg_res == REG_NOMATCH)
   7044 		return NULL;
   7045 
   7046 	if (map_p->regex_subfields != NULL)
   7047 	{
   7048 		/* option -s */
   7049 		static char retbuf[MAXNAME];
   7050 		int fields[MAX_MATCH + 1];
   7051 		bool first = true;
   7052 		int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
   7053 		bool quotemode = false, bslashmode = false;
   7054 		register char *dp, *sp;
   7055 		char *endp, *ldp;
   7056 		int *ip;
   7057 
   7058 		dp = retbuf;
   7059 		ldp = retbuf + sizeof(retbuf) - 1;
   7060 
   7061 		if (av[1] != NULL)
   7062 		{
   7063 			if (parse_fields(av[1], fields, MAX_MATCH + 1,
   7064 					 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
   7065 			{
   7066 				*statp = EX_CONFIG;
   7067 				return NULL;
   7068 			}
   7069 			ip = fields;
   7070 		}
   7071 		else
   7072 			ip = map_p->regex_subfields;
   7073 
   7074 		for ( ; *ip != END_OF_FIELDS; ip++)
   7075 		{
   7076 			if (!first)
   7077 			{
   7078 				for (sp = map_p->regex_delim; *sp; sp++)
   7079 				{
   7080 					if (dp < ldp)
   7081 						*dp++ = *sp;
   7082 				}
   7083 			}
   7084 			else
   7085 				first = false;
   7086 
   7087 			if (*ip >= MAX_MATCH ||
   7088 			    pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
   7089 				continue;
   7090 
   7091 			sp = name + pmatch[*ip].rm_so;
   7092 			endp = name + pmatch[*ip].rm_eo;
   7093 			for (; endp > sp; sp++)
   7094 			{
   7095 				if (dp < ldp)
   7096 				{
   7097 					if (bslashmode)
   7098 					{
   7099 						*dp++ = *sp;
   7100 						bslashmode = false;
   7101 					}
   7102 					else if (quotemode && *sp != '"' &&
   7103 						*sp != '\\')
   7104 					{
   7105 						*dp++ = *sp;
   7106 					}
   7107 					else switch (*dp++ = *sp)
   7108 					{
   7109 					  case '\\':
   7110 						bslashmode = true;
   7111 						break;
   7112 
   7113 					  case '(':
   7114 						cmntcnt++;
   7115 						break;
   7116 
   7117 					  case ')':
   7118 						cmntcnt--;
   7119 						break;
   7120 
   7121 					  case '<':
   7122 						anglecnt++;
   7123 						break;
   7124 
   7125 					  case '>':
   7126 						anglecnt--;
   7127 						break;
   7128 
   7129 					  case ' ':
   7130 						spacecnt++;
   7131 						break;
   7132 
   7133 					  case '"':
   7134 						quotemode = !quotemode;
   7135 						break;
   7136 					}
   7137 				}
   7138 			}
   7139 		}
   7140 		if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
   7141 		    bslashmode || spacecnt != 0)
   7142 		{
   7143 			sm_syslog(LOG_WARNING, NOQID,
   7144 				  "Warning: regex may cause prescan() failure map=%s lookup=%s",
   7145 				  map->map_mname, name);
   7146 			return NULL;
   7147 		}
   7148 
   7149 		*dp = '\0';
   7150 
   7151 		return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
   7152 	}
   7153 	return regex_map_rewrite(map, "", (size_t)0, av);
   7154 }
   7155 #endif /* MAP_REGEX */
   7156 /*
   7157 **  NSD modules
   7158 */
   7159 #if MAP_NSD
   7160 
   7161 # include <ndbm.h>
   7162 # define _DATUM_DEFINED
   7163 # include <ns_api.h>
   7164 
   7165 typedef struct ns_map_list
   7166 {
   7167 	ns_map_t		*map;		/* XXX ns_ ? */
   7168 	char			*mapname;
   7169 	struct ns_map_list	*next;
   7170 } ns_map_list_t;
   7171 
   7172 static ns_map_t *
   7173 ns_map_t_find(mapname)
   7174 	char *mapname;
   7175 {
   7176 	static ns_map_list_t *ns_maps = NULL;
   7177 	ns_map_list_t *ns_map;
   7178 
   7179 	/* walk the list of maps looking for the correctly named map */
   7180 	for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
   7181 	{
   7182 		if (strcmp(ns_map->mapname, mapname) == 0)
   7183 			break;
   7184 	}
   7185 
   7186 	/* if we are looking at a NULL ns_map_list_t, then create a new one */
   7187 	if (ns_map == NULL)
   7188 	{
   7189 		ns_map = (ns_map_list_t *) xalloc(sizeof(*ns_map));
   7190 		ns_map->mapname = newstr(mapname);
   7191 		ns_map->map = (ns_map_t *) xalloc(sizeof(*ns_map->map));
   7192 		memset(ns_map->map, '\0', sizeof(*ns_map->map));
   7193 		ns_map->next = ns_maps;
   7194 		ns_maps = ns_map;
   7195 	}
   7196 	return ns_map->map;
   7197 }
   7198 
   7199 char *
   7200 nsd_map_lookup(map, name, av, statp)
   7201 	MAP *map;
   7202 	char *name;
   7203 	char **av;
   7204 	int *statp;
   7205 {
   7206 	int buflen, r;
   7207 	char *p;
   7208 	ns_map_t *ns_map;
   7209 	char keybuf[MAXNAME + 1];
   7210 	char buf[MAXLINE];
   7211 
   7212 	if (tTd(38, 20))
   7213 		sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
   7214 
   7215 	buflen = strlen(name);
   7216 	if (buflen > sizeof(keybuf) - 1)
   7217 		buflen = sizeof(keybuf) - 1;	/* XXX simply cut off? */
   7218 	memmove(keybuf, name, buflen);
   7219 	keybuf[buflen] = '\0';
   7220 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
   7221 		makelower(keybuf);
   7222 
   7223 	ns_map = ns_map_t_find(map->map_file);
   7224 	if (ns_map == NULL)
   7225 	{
   7226 		if (tTd(38, 20))
   7227 			sm_dprintf("nsd_map_t_find failed\n");
   7228 		*statp = EX_UNAVAILABLE;
   7229 		return NULL;
   7230 	}
   7231 	r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
   7232 		      buf, sizeof(buf));
   7233 	if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
   7234 	{
   7235 		*statp = EX_TEMPFAIL;
   7236 		return NULL;
   7237 	}
   7238 	if (r == NS_BADREQ
   7239 # ifdef NS_NOPERM
   7240 	    || r == NS_NOPERM
   7241 # endif /* NS_NOPERM */
   7242 	    )
   7243 	{
   7244 		*statp = EX_CONFIG;
   7245 		return NULL;
   7246 	}
   7247 	if (r != NS_SUCCESS)
   7248 	{
   7249 		*statp = EX_NOTFOUND;
   7250 		return NULL;
   7251 	}
   7252 
   7253 	*statp = EX_OK;
   7254 
   7255 	/* Null out trailing \n */
   7256 	if ((p = strchr(buf, '\n')) != NULL)
   7257 		*p = '\0';
   7258 
   7259 	return map_rewrite(map, buf, strlen(buf), av);
   7260 }
   7261 #endif /* MAP_NSD */
   7262 
   7263 char *
   7264 arith_map_lookup(map, name, av, statp)
   7265 	MAP *map;
   7266 	char *name;
   7267 	char **av;
   7268 	int *statp;
   7269 {
   7270 	long r;
   7271 	long v[2];
   7272 	bool res = false;
   7273 	bool boolres;
   7274 	static char result[16];
   7275 	char **cpp;
   7276 
   7277 	if (tTd(38, 2))
   7278 	{
   7279 		sm_dprintf("arith_map_lookup: key '%s'\n", name);
   7280 		for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
   7281 			sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
   7282 	}
   7283 	r = 0;
   7284 	boolres = false;
   7285 	cpp = av;
   7286 	*statp = EX_OK;
   7287 
   7288 	/*
   7289 	**  read arguments for arith map
   7290 	**  - no check is made whether they are really numbers
   7291 	**  - just ignores args after the second
   7292 	*/
   7293 
   7294 	for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
   7295 		v[r++] = strtol(*cpp, NULL, 0);
   7296 
   7297 	/* operator and (at least) two operands given? */
   7298 	if (name != NULL && r == 2)
   7299 	{
   7300 		switch (*name)
   7301 		{
   7302 		  case '|':
   7303 			r = v[0] | v[1];
   7304 			break;
   7305 
   7306 		  case '&':
   7307 			r = v[0] & v[1];
   7308 			break;
   7309 
   7310 		  case '%':
   7311 			if (v[1] == 0)
   7312 				return NULL;
   7313 			r = v[0] % v[1];
   7314 			break;
   7315 		  case '+':
   7316 			r = v[0] + v[1];
   7317 			break;
   7318 
   7319 		  case '-':
   7320 			r = v[0] - v[1];
   7321 			break;
   7322 
   7323 		  case '*':
   7324 			r = v[0] * v[1];
   7325 			break;
   7326 
   7327 		  case '/':
   7328 			if (v[1] == 0)
   7329 				return NULL;
   7330 			r = v[0] / v[1];
   7331 			break;
   7332 
   7333 		  case 'l':
   7334 			res = v[0] < v[1];
   7335 			boolres = true;
   7336 			break;
   7337 
   7338 		  case '=':
   7339 			res = v[0] == v[1];
   7340 			boolres = true;
   7341 			break;
   7342 
   7343 		  case 'r':
   7344 			r = v[1] - v[0] + 1;
   7345 			if (r <= 0)
   7346 				return NULL;
   7347 			r = get_random() % r + v[0];
   7348 			break;
   7349 
   7350 		  default:
   7351 			/* XXX */
   7352 			*statp = EX_CONFIG;
   7353 			if (LogLevel > 10)
   7354 				sm_syslog(LOG_WARNING, NOQID,
   7355 					  "arith_map: unknown operator %c",
   7356 					  (isascii(*name) && isprint(*name)) ?
   7357 					  *name : '?');
   7358 			return NULL;
   7359 		}
   7360 		if (boolres)
   7361 			(void) sm_snprintf(result, sizeof(result),
   7362 				res ? "TRUE" : "FALSE");
   7363 		else
   7364 			(void) sm_snprintf(result, sizeof(result), "%ld", r);
   7365 		return result;
   7366 	}
   7367 	*statp = EX_CONFIG;
   7368 	return NULL;
   7369 }
   7370 
   7371 #if SOCKETMAP
   7372 
   7373 # if NETINET || NETINET6
   7374 #  include <arpa/inet.h>
   7375 # endif /* NETINET || NETINET6 */
   7376 
   7377 # define socket_map_next map_stack[0]
   7378 
   7379 /*
   7380 **  SOCKET_MAP_OPEN -- open socket table
   7381 */
   7382 
   7383 bool
   7384 socket_map_open(map, mode)
   7385 	MAP *map;
   7386 	int mode;
   7387 {
   7388 	STAB *s;
   7389 	int sock = 0;
   7390 	SOCKADDR_LEN_T addrlen = 0;
   7391 	int addrno = 0;
   7392 	int save_errno;
   7393 	char *p;
   7394 	char *colon;
   7395 	char *at;
   7396 	struct hostent *hp = NULL;
   7397 	SOCKADDR addr;
   7398 
   7399 	if (tTd(38, 2))
   7400 		sm_dprintf("socket_map_open(%s, %s, %d)\n",
   7401 			map->map_mname, map->map_file, mode);
   7402 
   7403 	mode &= O_ACCMODE;
   7404 
   7405 	/* sendmail doesn't have the ability to write to SOCKET (yet) */
   7406 	if (mode != O_RDONLY)
   7407 	{
   7408 		/* issue a pseudo-error message */
   7409 		errno = SM_EMAPCANTWRITE;
   7410 		return false;
   7411 	}
   7412 
   7413 	if (*map->map_file == '\0')
   7414 	{
   7415 		syserr("socket map \"%s\": empty or missing socket information",
   7416 			map->map_mname);
   7417 		return false;
   7418 	}
   7419 
   7420 	s = socket_map_findconn(map->map_file);
   7421 	if (s->s_socketmap != NULL)
   7422 	{
   7423 		/* Copy open connection */
   7424 		map->map_db1 = s->s_socketmap->map_db1;
   7425 
   7426 		/* Add this map as head of linked list */
   7427 		map->socket_map_next = s->s_socketmap;
   7428 		s->s_socketmap = map;
   7429 
   7430 		if (tTd(38, 2))
   7431 			sm_dprintf("using cached connection\n");
   7432 		return true;
   7433 	}
   7434 
   7435 	if (tTd(38, 2))
   7436 		sm_dprintf("opening new connection\n");
   7437 
   7438 	/* following code is ripped from milter.c */
   7439 	/* XXX It should be put in a library... */
   7440 
   7441 	/* protocol:filename or protocol:port@host */
   7442 	memset(&addr, '\0', sizeof(addr));
   7443 	p = map->map_file;
   7444 	colon = strchr(p, ':');
   7445 	if (colon != NULL)
   7446 	{
   7447 		*colon = '\0';
   7448 
   7449 		if (*p == '\0')
   7450 		{
   7451 # if NETUNIX
   7452 			/* default to AF_UNIX */
   7453 			addr.sa.sa_family = AF_UNIX;
   7454 # else /* NETUNIX */
   7455 #  if NETINET
   7456 			/* default to AF_INET */
   7457 			addr.sa.sa_family = AF_INET;
   7458 #  else /* NETINET */
   7459 #   if NETINET6
   7460 			/* default to AF_INET6 */
   7461 			addr.sa.sa_family = AF_INET6;
   7462 #   else /* NETINET6 */
   7463 			/* no protocols available */
   7464 			syserr("socket map \"%s\": no valid socket protocols available",
   7465 			map->map_mname);
   7466 			return false;
   7467 #   endif /* NETINET6 */
   7468 #  endif /* NETINET */
   7469 # endif /* NETUNIX */
   7470 		}
   7471 # if NETUNIX
   7472 		else if (sm_strcasecmp(p, "unix") == 0 ||
   7473 			 sm_strcasecmp(p, "local") == 0)
   7474 			addr.sa.sa_family = AF_UNIX;
   7475 # endif /* NETUNIX */
   7476 # if NETINET
   7477 		else if (sm_strcasecmp(p, "inet") == 0)
   7478 			addr.sa.sa_family = AF_INET;
   7479 # endif /* NETINET */
   7480 # if NETINET6
   7481 		else if (sm_strcasecmp(p, "inet6") == 0)
   7482 			addr.sa.sa_family = AF_INET6;
   7483 # endif /* NETINET6 */
   7484 		else
   7485 		{
   7486 # ifdef EPROTONOSUPPORT
   7487 			errno = EPROTONOSUPPORT;
   7488 # else /* EPROTONOSUPPORT */
   7489 			errno = EINVAL;
   7490 # endif /* EPROTONOSUPPORT */
   7491 			syserr("socket map \"%s\": unknown socket type %s",
   7492 			       map->map_mname, p);
   7493 			return false;
   7494 		}
   7495 		*colon++ = ':';
   7496 	}
   7497 	else
   7498 	{
   7499 		colon = p;
   7500 #if NETUNIX
   7501 		/* default to AF_UNIX */
   7502 		addr.sa.sa_family = AF_UNIX;
   7503 #else /* NETUNIX */
   7504 # if NETINET
   7505 		/* default to AF_INET */
   7506 		addr.sa.sa_family = AF_INET;
   7507 # else /* NETINET */
   7508 #  if NETINET6
   7509 		/* default to AF_INET6 */
   7510 		addr.sa.sa_family = AF_INET6;
   7511 #  else /* NETINET6 */
   7512 		syserr("socket map \"%s\": unknown socket type %s",
   7513 		       map->map_mname, p);
   7514 		return false;
   7515 #  endif /* NETINET6 */
   7516 # endif /* NETINET */
   7517 #endif /* NETUNIX */
   7518 	}
   7519 
   7520 # if NETUNIX
   7521 	if (addr.sa.sa_family == AF_UNIX)
   7522 	{
   7523 		long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
   7524 
   7525 		at = colon;
   7526 		if (strlen(colon) >= sizeof(addr.sunix.sun_path))
   7527 		{
   7528 			syserr("socket map \"%s\": local socket name %s too long",
   7529 			       map->map_mname, colon);
   7530 			return false;
   7531 		}
   7532 		errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
   7533 				 S_IRUSR|S_IWUSR, NULL);
   7534 
   7535 		if (errno != 0)
   7536 		{
   7537 			/* if not safe, don't create */
   7538 				syserr("socket map \"%s\": local socket name %s unsafe",
   7539 			       map->map_mname, colon);
   7540 			return false;
   7541 		}
   7542 
   7543 		(void) sm_strlcpy(addr.sunix.sun_path, colon,
   7544 			       sizeof(addr.sunix.sun_path));
   7545 		addrlen = sizeof(struct sockaddr_un);
   7546 	}
   7547 	else
   7548 # endif /* NETUNIX */
   7549 # if NETINET || NETINET6
   7550 	if (false
   7551 #  if NETINET
   7552 		 || addr.sa.sa_family == AF_INET
   7553 #  endif /* NETINET */
   7554 #  if NETINET6
   7555 		 || addr.sa.sa_family == AF_INET6
   7556 #  endif /* NETINET6 */
   7557 		 )
   7558 	{
   7559 		unsigned short port;
   7560 
   7561 		/* Parse port@host */
   7562 		at = strchr(colon, '@');
   7563 		if (at == NULL)
   7564 		{
   7565 			syserr("socket map \"%s\": bad address %s (expected port@host)",
   7566 				       map->map_mname, colon);
   7567 			return false;
   7568 		}
   7569 		*at = '\0';
   7570 		if (isascii(*colon) && isdigit(*colon))
   7571 			port = htons((unsigned short) atoi(colon));
   7572 		else
   7573 		{
   7574 #  ifdef NO_GETSERVBYNAME
   7575 			syserr("socket map \"%s\": invalid port number %s",
   7576 				       map->map_mname, colon);
   7577 			return false;
   7578 #  else /* NO_GETSERVBYNAME */
   7579 			register struct servent *sp;
   7580 
   7581 			sp = getservbyname(colon, "tcp");
   7582 			if (sp == NULL)
   7583 			{
   7584 				syserr("socket map \"%s\": unknown port name %s",
   7585 					       map->map_mname, colon);
   7586 				return false;
   7587 			}
   7588 			port = sp->s_port;
   7589 #  endif /* NO_GETSERVBYNAME */
   7590 		}
   7591 		*at++ = '@';
   7592 		if (*at == '[')
   7593 		{
   7594 			char *end;
   7595 
   7596 			end = strchr(at, ']');
   7597 			if (end != NULL)
   7598 			{
   7599 				bool found = false;
   7600 #  if NETINET
   7601 				unsigned long hid = INADDR_NONE;
   7602 #  endif /* NETINET */
   7603 #  if NETINET6
   7604 				struct sockaddr_in6 hid6;
   7605 #  endif /* NETINET6 */
   7606 
   7607 				*end = '\0';
   7608 #  if NETINET
   7609 				if (addr.sa.sa_family == AF_INET &&
   7610 				    (hid = inet_addr(&at[1])) != INADDR_NONE)
   7611 				{
   7612 					addr.sin.sin_addr.s_addr = hid;
   7613 					addr.sin.sin_port = port;
   7614 					found = true;
   7615 				}
   7616 #  endif /* NETINET */
   7617 #  if NETINET6
   7618 				(void) memset(&hid6, '\0', sizeof(hid6));
   7619 				if (addr.sa.sa_family == AF_INET6 &&
   7620 				    anynet_pton(AF_INET6, &at[1],
   7621 						&hid6.sin6_addr) == 1)
   7622 				{
   7623 					addr.sin6.sin6_addr = hid6.sin6_addr;
   7624 					addr.sin6.sin6_port = port;
   7625 					found = true;
   7626 				}
   7627 #  endif /* NETINET6 */
   7628 				*end = ']';
   7629 				if (!found)
   7630 				{
   7631 					syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
   7632 					       map->map_mname, at);
   7633 					return false;
   7634 				}
   7635 			}
   7636 			else
   7637 			{
   7638 				syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
   7639 				       map->map_mname, at);
   7640 				return false;
   7641 			}
   7642 		}
   7643 		else
   7644 		{
   7645 			hp = sm_gethostbyname(at, addr.sa.sa_family);
   7646 			if (hp == NULL)
   7647 			{
   7648 				syserr("socket map \"%s\": Unknown host name %s",
   7649 					map->map_mname, at);
   7650 				return false;
   7651 			}
   7652 			addr.sa.sa_family = hp->h_addrtype;
   7653 			switch (hp->h_addrtype)
   7654 			{
   7655 #  if NETINET
   7656 			  case AF_INET:
   7657 				memmove(&addr.sin.sin_addr,
   7658 					hp->h_addr, INADDRSZ);
   7659 				addr.sin.sin_port = port;
   7660 				addrlen = sizeof(struct sockaddr_in);
   7661 				addrno = 1;
   7662 				break;
   7663 #  endif /* NETINET */
   7664 
   7665 #  if NETINET6
   7666 			  case AF_INET6:
   7667 				memmove(&addr.sin6.sin6_addr,
   7668 					hp->h_addr, IN6ADDRSZ);
   7669 				addr.sin6.sin6_port = port;
   7670 				addrlen = sizeof(struct sockaddr_in6);
   7671 				addrno = 1;
   7672 				break;
   7673 #  endif /* NETINET6 */
   7674 
   7675 			  default:
   7676 				syserr("socket map \"%s\": Unknown protocol for %s (%d)",
   7677 					map->map_mname, at, hp->h_addrtype);
   7678 #  if NETINET6
   7679 				freehostent(hp);
   7680 #  endif /* NETINET6 */
   7681 				return false;
   7682 			}
   7683 		}
   7684 	}
   7685 	else
   7686 # endif /* NETINET || NETINET6 */
   7687 	{
   7688 		syserr("socket map \"%s\": unknown socket protocol",
   7689 			map->map_mname);
   7690 		return false;
   7691 	}
   7692 
   7693 	/* nope, actually connecting */
   7694 	for (;;)
   7695 	{
   7696 		sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
   7697 		if (sock < 0)
   7698 		{
   7699 			save_errno = errno;
   7700 			if (tTd(38, 5))
   7701 				sm_dprintf("socket map \"%s\": error creating socket: %s\n",
   7702 					   map->map_mname,
   7703 					   sm_errstring(save_errno));
   7704 # if NETINET6
   7705 			if (hp != NULL)
   7706 				freehostent(hp);
   7707 # endif /* NETINET6 */
   7708 			return false;
   7709 		}
   7710 
   7711 		if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
   7712 			break;
   7713 
   7714 		/* couldn't connect.... try next address */
   7715 		save_errno = errno;
   7716 		p = CurHostName;
   7717 		CurHostName = at;
   7718 		if (tTd(38, 5))
   7719 			sm_dprintf("socket_open (%s): open %s failed: %s\n",
   7720 				map->map_mname, at, sm_errstring(save_errno));
   7721 		CurHostName = p;
   7722 		(void) close(sock);
   7723 
   7724 		/* try next address */
   7725 		if (hp != NULL && hp->h_addr_list[addrno] != NULL)
   7726 		{
   7727 			switch (addr.sa.sa_family)
   7728 			{
   7729 # if NETINET
   7730 			  case AF_INET:
   7731 				memmove(&addr.sin.sin_addr,
   7732 					hp->h_addr_list[addrno++],
   7733 					INADDRSZ);
   7734 				break;
   7735 # endif /* NETINET */
   7736 
   7737 # if NETINET6
   7738 			  case AF_INET6:
   7739 				memmove(&addr.sin6.sin6_addr,
   7740 					hp->h_addr_list[addrno++],
   7741 					IN6ADDRSZ);
   7742 				break;
   7743 # endif /* NETINET6 */
   7744 
   7745 			  default:
   7746 				if (tTd(38, 5))
   7747 					sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
   7748 						   map->map_mname, at,
   7749 						   hp->h_addrtype);
   7750 # if NETINET6
   7751 				freehostent(hp);
   7752 # endif /* NETINET6 */
   7753 				return false;
   7754 			}
   7755 			continue;
   7756 		}
   7757 		p = CurHostName;
   7758 		CurHostName = at;
   7759 		if (tTd(38, 5))
   7760 			sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
   7761 				   map->map_mname, sm_errstring(save_errno));
   7762 		CurHostName = p;
   7763 # if NETINET6
   7764 		if (hp != NULL)
   7765 			freehostent(hp);
   7766 # endif /* NETINET6 */
   7767 		return false;
   7768 	}
   7769 # if NETINET6
   7770 	if (hp != NULL)
   7771 	{
   7772 		freehostent(hp);
   7773 		hp = NULL;
   7774 	}
   7775 # endif /* NETINET6 */
   7776 	if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
   7777 						  SM_TIME_DEFAULT,
   7778 						  (void *) &sock,
   7779 						  SM_IO_RDWR,
   7780 						  NULL)) == NULL)
   7781 	{
   7782 		close(sock);
   7783 		if (tTd(38, 2))
   7784 		    sm_dprintf("socket_open (%s): failed to create stream: %s\n",
   7785 			       map->map_mname, sm_errstring(errno));
   7786 		return false;
   7787 	}
   7788 
   7789 	/* Save connection for reuse */
   7790 	s->s_socketmap = map;
   7791 	return true;
   7792 }
   7793 
   7794 /*
   7795 **  SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
   7796 **
   7797 **	Cache SOCKET connections based on the connection specifier
   7798 **	and PID so we don't have multiple connections open to
   7799 **	the same server for different maps.  Need a separate connection
   7800 **	per PID since a parent process may close the map before the
   7801 **	child is done with it.
   7802 **
   7803 **	Parameters:
   7804 **		conn -- SOCKET map connection specifier
   7805 **
   7806 **	Returns:
   7807 **		Symbol table entry for the SOCKET connection.
   7808 */
   7809 
   7810 static STAB *
   7811 socket_map_findconn(conn)
   7812 	const char *conn;
   7813 {
   7814 	char *nbuf;
   7815 	STAB *SM_NONVOLATILE s = NULL;
   7816 
   7817 	nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
   7818 	SM_TRY
   7819 		s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
   7820 	SM_FINALLY
   7821 		sm_free(nbuf);
   7822 	SM_END_TRY
   7823 	return s;
   7824 }
   7825 
   7826 /*
   7827 **  SOCKET_MAP_CLOSE -- close the socket
   7828 */
   7829 
   7830 void
   7831 socket_map_close(map)
   7832 	MAP *map;
   7833 {
   7834 	STAB *s;
   7835 	MAP *smap;
   7836 
   7837 	if (tTd(38, 20))
   7838 		sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
   7839 			(long) CurrentPid);
   7840 
   7841 	/* Check if already closed */
   7842 	if (map->map_db1 == NULL)
   7843 	{
   7844 		if (tTd(38, 20))
   7845 			sm_dprintf("socket_map_close(%s) already closed\n",
   7846 				map->map_file);
   7847 		return;
   7848 	}
   7849 	sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
   7850 
   7851 	/* Mark all the maps that share the connection as closed */
   7852 	s = socket_map_findconn(map->map_file);
   7853 	smap = s->s_socketmap;
   7854 	while (smap != NULL)
   7855 	{
   7856 		MAP *next;
   7857 
   7858 		if (tTd(38, 2) && smap != map)
   7859 			sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
   7860 				map->map_mname, smap->map_mname);
   7861 
   7862 		smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
   7863 		smap->map_db1 = NULL;
   7864 		next = smap->socket_map_next;
   7865 		smap->socket_map_next = NULL;
   7866 		smap = next;
   7867 	}
   7868 	s->s_socketmap = NULL;
   7869 }
   7870 
   7871 /*
   7872 ** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
   7873 */
   7874 
   7875 char *
   7876 socket_map_lookup(map, name, av, statp)
   7877 	MAP *map;
   7878 	char *name;
   7879 	char **av;
   7880 	int *statp;
   7881 {
   7882 	unsigned int nettolen, replylen, recvlen;
   7883 	char *replybuf, *rval, *value, *status, *key;
   7884 	SM_FILE_T *f;
   7885 	char keybuf[MAXNAME + 1];
   7886 
   7887 	replybuf = NULL;
   7888 	rval = NULL;
   7889 	f = (SM_FILE_T *)map->map_db1;
   7890 	if (tTd(38, 20))
   7891 		sm_dprintf("socket_map_lookup(%s, %s) %s\n",
   7892 			map->map_mname, name, map->map_file);
   7893 
   7894 	if (!bitset(MF_NOFOLDCASE, map->map_mflags))
   7895 	{
   7896 		nettolen = strlen(name);
   7897 		if (nettolen > sizeof(keybuf) - 1)
   7898 			nettolen = sizeof(keybuf) - 1;
   7899 		memmove(keybuf, name, nettolen);
   7900 		keybuf[nettolen] = '\0';
   7901 		makelower(keybuf);
   7902 		key = keybuf;
   7903 	}
   7904 	else
   7905 		key = name;
   7906 
   7907 	nettolen = strlen(map->map_mname) + 1 + strlen(key);
   7908 	SM_ASSERT(nettolen > strlen(map->map_mname));
   7909 	SM_ASSERT(nettolen > strlen(key));
   7910 	if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
   7911 			   nettolen, map->map_mname, key) == SM_IO_EOF) ||
   7912 	    (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
   7913 	    (sm_io_error(f)))
   7914 	{
   7915 		syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
   7916 			map->map_mname);
   7917 		*statp = EX_TEMPFAIL;
   7918 		goto errcl;
   7919 	}
   7920 
   7921 	if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
   7922 	{
   7923 		syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
   7924 			map->map_mname);
   7925 		*statp = EX_TEMPFAIL;
   7926 		goto errcl;
   7927 	}
   7928 	if (replylen > SOCKETMAP_MAXL)
   7929 	{
   7930 		syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
   7931 			   map->map_mname, replylen);
   7932 		*statp = EX_TEMPFAIL;
   7933 		goto errcl;
   7934 	}
   7935 	if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
   7936 	{
   7937 		syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
   7938 			map->map_mname);
   7939 		*statp = EX_TEMPFAIL;
   7940 		goto error;
   7941 	}
   7942 
   7943 	replybuf = (char *) sm_malloc(replylen + 1);
   7944 	if (replybuf == NULL)
   7945 	{
   7946 		syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
   7947 			map->map_mname, replylen + 1);
   7948 		*statp = EX_OSERR;
   7949 		goto error;
   7950 	}
   7951 
   7952 	recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
   7953 	if (recvlen < replylen)
   7954 	{
   7955 		syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
   7956 			   map->map_mname, recvlen, replylen);
   7957 		*statp = EX_TEMPFAIL;
   7958 		goto errcl;
   7959 	}
   7960 	if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
   7961 	{
   7962 		syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
   7963 			map->map_mname);
   7964 		*statp = EX_TEMPFAIL;
   7965 		goto errcl;
   7966 	}
   7967 	status = replybuf;
   7968 	replybuf[recvlen] = '\0';
   7969 	value = strchr(replybuf, ' ');
   7970 	if (value != NULL)
   7971 	{
   7972 		*value = '\0';
   7973 		value++;
   7974 	}
   7975 	if (strcmp(status, "OK") == 0)
   7976 	{
   7977 		*statp = EX_OK;
   7978 
   7979 		/* collect the return value */
   7980 		if (bitset(MF_MATCHONLY, map->map_mflags))
   7981 			rval = map_rewrite(map, key, strlen(key), NULL);
   7982 		else
   7983 			rval = map_rewrite(map, value, strlen(value), av);
   7984 	}
   7985 	else if (strcmp(status, "NOTFOUND") == 0)
   7986 	{
   7987 		*statp = EX_NOTFOUND;
   7988 		if (tTd(38, 20))
   7989 			sm_dprintf("socket_map_lookup(%s): %s not found\n",
   7990 				map->map_mname, key);
   7991 	}
   7992 	else
   7993 	{
   7994 		if (tTd(38, 5))
   7995 			sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
   7996 				map->map_mname, key, status,
   7997 				value ? value : "");
   7998 		if ((strcmp(status, "TEMP") == 0) ||
   7999 		    (strcmp(status, "TIMEOUT") == 0))
   8000 			*statp = EX_TEMPFAIL;
   8001 		else if(strcmp(status, "PERM") == 0)
   8002 			*statp = EX_UNAVAILABLE;
   8003 		else
   8004 			*statp = EX_PROTOCOL;
   8005 	}
   8006 
   8007 	if (replybuf != NULL)
   8008 		sm_free(replybuf);
   8009 	return rval;
   8010 
   8011   errcl:
   8012 	socket_map_close(map);
   8013   error:
   8014 	if (replybuf != NULL)
   8015 		sm_free(replybuf);
   8016 	return rval;
   8017 }
   8018 #endif /* SOCKETMAP */
   8019