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