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