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