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