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