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