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