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