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