dma: bump man page date
[dragonfly.git] / contrib / sendmail-8.14 / sendmail / map.c
blob4248fd90f519018c5510df98373b78166bdd4b9e
1 /*
2 * Copyright (c) 1998-2007 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.
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.
14 #include <sendmail.h>
16 SM_RCSID("@(#)$Id: map.c,v 8.699 2007/10/10 00:06:45 ca Exp $")
18 #if LDAPMAP
19 # include <sm/ldap.h>
20 #endif /* LDAPMAP */
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 */
42 #include "map.h"
44 #if NEWDB
45 # if DB_VERSION_MAJOR < 2
46 static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
47 # endif /* DB_VERSION_MAJOR < 2 */
48 # if DB_VERSION_MAJOR == 2
49 static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
50 # endif /* DB_VERSION_MAJOR == 2 */
51 # if DB_VERSION_MAJOR > 2
52 static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **));
53 # endif /* DB_VERSION_MAJOR > 2 */
54 #endif /* NEWDB */
55 static bool extract_canonname __P((char *, char *, char *, char[], int));
56 static void map_close __P((STAB *, int));
57 static void map_init __P((STAB *, int));
58 #ifdef LDAPMAP
59 static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *));
60 #endif /* LDAPMAP */
61 #if NISPLUS
62 static bool nisplus_getcanonname __P((char *, int, int *));
63 #endif /* NISPLUS */
64 #if NIS
65 static bool nis_getcanonname __P((char *, int, int *));
66 #endif /* NIS */
67 #if NETINFO
68 static bool ni_getcanonname __P((char *, int, int *));
69 #endif /* NETINFO */
70 static bool text_getcanonname __P((char *, int, int *));
71 #if SOCKETMAP
72 static STAB *socket_map_findconn __P((const char*));
74 /* XXX arbitrary limit for sanity */
75 # define SOCKETMAP_MAXL 1000000
76 #endif /* SOCKETMAP */
78 /* default error message for trying to open a map in write mode */
79 #ifdef ENOSYS
80 # define SM_EMAPCANTWRITE ENOSYS
81 #else /* ENOSYS */
82 # ifdef EFTYPE
83 # define SM_EMAPCANTWRITE EFTYPE
84 # else /* EFTYPE */
85 # define SM_EMAPCANTWRITE ENXIO
86 # endif /* EFTYPE */
87 #endif /* ENOSYS */
90 ** MAP.C -- implementations for various map classes.
92 ** Each map class implements a series of functions:
94 ** bool map_parse(MAP *map, char *args)
95 ** Parse the arguments from the config file. Return true
96 ** if they were ok, false otherwise. Fill in map with the
97 ** values.
99 ** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
100 ** Look up the key in the given map. If found, do any
101 ** rewriting the map wants (including "args" if desired)
102 ** and return the value. Set *pstat to the appropriate status
103 ** on error and return NULL. Args will be NULL if called
104 ** from the alias routines, although this should probably
105 ** not be relied upon. It is suggested you call map_rewrite
106 ** to return the results -- it takes care of null termination
107 ** and uses a dynamically expanded buffer as needed.
109 ** void map_store(MAP *map, char *key, char *value)
110 ** Store the key:value pair in the map.
112 ** bool map_open(MAP *map, int mode)
113 ** Open the map for the indicated mode. Mode should
114 ** be either O_RDONLY or O_RDWR. Return true if it
115 ** was opened successfully, false otherwise. If the open
116 ** failed and the MF_OPTIONAL flag is not set, it should
117 ** also print an error. If the MF_ALIAS bit is set
118 ** and this map class understands the @:@ convention, it
119 ** should call aliaswait() before returning.
121 ** void map_close(MAP *map)
122 ** Close the map.
124 ** This file also includes the implementation for getcanonname.
125 ** It is currently implemented in a pretty ad-hoc manner; it ought
126 ** to be more properly integrated into the map structure.
129 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
130 # define LOCK_ON_OPEN 1 /* we can open/create a locked file */
131 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
132 # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
133 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
136 ** MAP_PARSEARGS -- parse config line arguments for database lookup
138 ** This is a generic version of the map_parse method.
140 ** Parameters:
141 ** map -- the map being initialized.
142 ** ap -- a pointer to the args on the config line.
144 ** Returns:
145 ** true -- if everything parsed OK.
146 ** false -- otherwise.
148 ** Side Effects:
149 ** null terminates the filename; stores it in map
152 bool
153 map_parseargs(map, ap)
154 MAP *map;
155 char *ap;
157 register char *p = ap;
160 ** There is no check whether there is really an argument,
161 ** but that's not important enough to warrant extra code.
164 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
165 map->map_spacesub = SpaceSub; /* default value */
166 for (;;)
168 while (isascii(*p) && isspace(*p))
169 p++;
170 if (*p != '-')
171 break;
172 switch (*++p)
174 case 'N':
175 map->map_mflags |= MF_INCLNULL;
176 map->map_mflags &= ~MF_TRY0NULL;
177 break;
179 case 'O':
180 map->map_mflags &= ~MF_TRY1NULL;
181 break;
183 case 'o':
184 map->map_mflags |= MF_OPTIONAL;
185 break;
187 case 'f':
188 map->map_mflags |= MF_NOFOLDCASE;
189 break;
191 case 'm':
192 map->map_mflags |= MF_MATCHONLY;
193 break;
195 case 'A':
196 map->map_mflags |= MF_APPEND;
197 break;
199 case 'q':
200 map->map_mflags |= MF_KEEPQUOTES;
201 break;
203 case 'a':
204 map->map_app = ++p;
205 break;
207 case 'T':
208 map->map_tapp = ++p;
209 break;
211 case 'k':
212 while (isascii(*++p) && isspace(*p))
213 continue;
214 map->map_keycolnm = p;
215 break;
217 case 'v':
218 while (isascii(*++p) && isspace(*p))
219 continue;
220 map->map_valcolnm = p;
221 break;
223 case 'z':
224 if (*++p != '\\')
225 map->map_coldelim = *p;
226 else
228 switch (*++p)
230 case 'n':
231 map->map_coldelim = '\n';
232 break;
234 case 't':
235 map->map_coldelim = '\t';
236 break;
238 default:
239 map->map_coldelim = '\\';
242 break;
244 case 't':
245 map->map_mflags |= MF_NODEFER;
246 break;
249 case 'S':
250 map->map_spacesub = *++p;
251 break;
253 case 'D':
254 map->map_mflags |= MF_DEFER;
255 break;
257 default:
258 syserr("Illegal option %c map %s", *p, map->map_mname);
259 break;
261 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
262 p++;
263 if (*p != '\0')
264 *p++ = '\0';
266 if (map->map_app != NULL)
267 map->map_app = newstr(map->map_app);
268 if (map->map_tapp != NULL)
269 map->map_tapp = newstr(map->map_tapp);
270 if (map->map_keycolnm != NULL)
271 map->map_keycolnm = newstr(map->map_keycolnm);
272 if (map->map_valcolnm != NULL)
273 map->map_valcolnm = newstr(map->map_valcolnm);
275 if (*p != '\0')
277 map->map_file = p;
278 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
279 p++;
280 if (*p != '\0')
281 *p++ = '\0';
282 map->map_file = newstr(map->map_file);
285 while (*p != '\0' && isascii(*p) && isspace(*p))
286 p++;
287 if (*p != '\0')
288 map->map_rebuild = newstr(p);
290 if (map->map_file == NULL &&
291 !bitset(MCF_OPTFILE, map->map_class->map_cflags))
293 syserr("No file name for %s map %s",
294 map->map_class->map_cname, map->map_mname);
295 return false;
297 return true;
300 ** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
302 ** It also adds the map_app string. It can be used as a utility
303 ** in the map_lookup method.
305 ** Parameters:
306 ** map -- the map that causes this.
307 ** s -- the string to rewrite, NOT necessarily null terminated.
308 ** slen -- the length of s.
309 ** av -- arguments to interpolate into buf.
311 ** Returns:
312 ** Pointer to rewritten result. This is static data that
313 ** should be copied if it is to be saved!
316 char *
317 map_rewrite(map, s, slen, av)
318 register MAP *map;
319 register const char *s;
320 size_t slen;
321 char **av;
323 register char *bp;
324 register char c;
325 char **avp;
326 register char *ap;
327 size_t l;
328 size_t len;
329 static size_t buflen = 0;
330 static char *buf = NULL;
332 if (tTd(39, 1))
334 sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
335 if (av == NULL)
336 sm_dprintf(" (nullv)");
337 else
339 for (avp = av; *avp != NULL; avp++)
340 sm_dprintf("\n\t%s", *avp);
342 sm_dprintf("\n");
345 /* count expected size of output (can safely overestimate) */
346 l = len = slen;
347 if (av != NULL)
349 const char *sp = s;
351 while (l-- > 0 && (c = *sp++) != '\0')
353 if (c != '%')
354 continue;
355 if (l-- <= 0)
356 break;
357 c = *sp++;
358 if (!(isascii(c) && isdigit(c)))
359 continue;
360 for (avp = av; --c >= '0' && *avp != NULL; avp++)
361 continue;
362 if (*avp == NULL)
363 continue;
364 len += strlen(*avp);
367 if (map->map_app != NULL)
368 len += strlen(map->map_app);
369 if (buflen < ++len)
371 /* need to malloc additional space */
372 buflen = len;
373 if (buf != NULL)
374 sm_free(buf);
375 buf = sm_pmalloc_x(buflen);
378 bp = buf;
379 if (av == NULL)
381 memmove(bp, s, slen);
382 bp += slen;
384 /* assert(len > slen); */
385 len -= slen;
387 else
389 while (slen-- > 0 && (c = *s++) != '\0')
391 if (c != '%')
393 pushc:
394 if (len-- <= 1)
395 break;
396 *bp++ = c;
397 continue;
399 if (slen-- <= 0 || (c = *s++) == '\0')
400 c = '%';
401 if (c == '%')
402 goto pushc;
403 if (!(isascii(c) && isdigit(c)))
405 if (len-- <= 1)
406 break;
407 *bp++ = '%';
408 goto pushc;
410 for (avp = av; --c >= '0' && *avp != NULL; avp++)
411 continue;
412 if (*avp == NULL)
413 continue;
415 /* transliterate argument into output string */
416 for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
417 *bp++ = c;
420 if (map->map_app != NULL && len > 0)
421 (void) sm_strlcpy(bp, map->map_app, len);
422 else
423 *bp = '\0';
424 if (tTd(39, 1))
425 sm_dprintf("map_rewrite => %s\n", buf);
426 return buf;
429 ** INITMAPS -- rebuild alias maps
431 ** Parameters:
432 ** none.
434 ** Returns:
435 ** none.
438 void
439 initmaps()
441 #if XDEBUG
442 checkfd012("entering initmaps");
443 #endif /* XDEBUG */
444 stabapply(map_init, 0);
445 #if XDEBUG
446 checkfd012("exiting initmaps");
447 #endif /* XDEBUG */
450 ** MAP_INIT -- rebuild a map
452 ** Parameters:
453 ** s -- STAB entry: if map: try to rebuild
454 ** unused -- unused variable
456 ** Returns:
457 ** none.
459 ** Side Effects:
460 ** will close already open rebuildable map.
463 /* ARGSUSED1 */
464 static void
465 map_init(s, unused)
466 register STAB *s;
467 int unused;
469 register MAP *map;
471 /* has to be a map */
472 if (s->s_symtype != ST_MAP)
473 return;
475 map = &s->s_map;
476 if (!bitset(MF_VALID, map->map_mflags))
477 return;
479 if (tTd(38, 2))
480 sm_dprintf("map_init(%s:%s, %s)\n",
481 map->map_class->map_cname == NULL ? "NULL" :
482 map->map_class->map_cname,
483 map->map_mname == NULL ? "NULL" : map->map_mname,
484 map->map_file == NULL ? "NULL" : map->map_file);
486 if (!bitset(MF_ALIAS, map->map_mflags) ||
487 !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
489 if (tTd(38, 3))
490 sm_dprintf("\tnot rebuildable\n");
491 return;
494 /* if already open, close it (for nested open) */
495 if (bitset(MF_OPEN, map->map_mflags))
497 map->map_mflags |= MF_CLOSING;
498 map->map_class->map_close(map);
499 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
502 (void) rebuildaliases(map, false);
503 return;
506 ** OPENMAP -- open a map
508 ** Parameters:
509 ** map -- map to open (it must not be open).
511 ** Returns:
512 ** whether open succeeded.
515 bool
516 openmap(map)
517 MAP *map;
519 bool restore = false;
520 bool savehold = HoldErrs;
521 bool savequick = QuickAbort;
522 int saveerrors = Errors;
524 if (!bitset(MF_VALID, map->map_mflags))
525 return false;
527 /* better safe than sorry... */
528 if (bitset(MF_OPEN, map->map_mflags))
529 return true;
531 /* Don't send a map open error out via SMTP */
532 if ((OnlyOneError || QuickAbort) &&
533 (OpMode == MD_SMTP || OpMode == MD_DAEMON))
535 restore = true;
536 HoldErrs = true;
537 QuickAbort = false;
540 errno = 0;
541 if (map->map_class->map_open(map, O_RDONLY))
543 if (tTd(38, 4))
544 sm_dprintf("openmap()\t%s:%s %s: valid\n",
545 map->map_class->map_cname == NULL ? "NULL" :
546 map->map_class->map_cname,
547 map->map_mname == NULL ? "NULL" :
548 map->map_mname,
549 map->map_file == NULL ? "NULL" :
550 map->map_file);
551 map->map_mflags |= MF_OPEN;
552 map->map_pid = CurrentPid;
554 else
556 if (tTd(38, 4))
557 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
558 map->map_class->map_cname == NULL ? "NULL" :
559 map->map_class->map_cname,
560 map->map_mname == NULL ? "NULL" :
561 map->map_mname,
562 map->map_file == NULL ? "NULL" :
563 map->map_file,
564 errno == 0 ? "" : ": ",
565 errno == 0 ? "" : sm_errstring(errno));
566 if (!bitset(MF_OPTIONAL, map->map_mflags))
568 extern MAPCLASS BogusMapClass;
570 map->map_orgclass = map->map_class;
571 map->map_class = &BogusMapClass;
572 map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
573 map->map_pid = CurrentPid;
575 else
577 /* don't try again */
578 map->map_mflags &= ~MF_VALID;
582 if (restore)
584 Errors = saveerrors;
585 HoldErrs = savehold;
586 QuickAbort = savequick;
589 return bitset(MF_OPEN, map->map_mflags);
592 ** CLOSEMAPS -- close all open maps opened by the current pid.
594 ** Parameters:
595 ** bogus -- only close bogus maps.
597 ** Returns:
598 ** none.
601 void
602 closemaps(bogus)
603 bool bogus;
605 stabapply(map_close, bogus);
608 ** MAP_CLOSE -- close a map opened by the current pid.
610 ** Parameters:
611 ** s -- STAB entry: if map: try to close
612 ** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
614 ** Returns:
615 ** none.
618 /* ARGSUSED1 */
619 static void
620 map_close(s, bogus)
621 register STAB *s;
622 int bogus; /* int because of stabapply(), used as bool */
624 MAP *map;
625 extern MAPCLASS BogusMapClass;
627 if (s->s_symtype != ST_MAP)
628 return;
630 map = &s->s_map;
633 ** close the map iff:
634 ** it is valid and open and opened by this process
635 ** and (!bogus or it's a bogus map or it is not persistent)
636 ** negate this: return iff
637 ** it is not valid or it is not open or not opened by this process
638 ** or (bogus and it's not a bogus map and it's not not-persistent)
641 if (!bitset(MF_VALID, map->map_mflags) ||
642 !bitset(MF_OPEN, map->map_mflags) ||
643 bitset(MF_CLOSING, map->map_mflags) ||
644 map->map_pid != CurrentPid ||
645 (bogus && map->map_class != &BogusMapClass &&
646 !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
647 return;
649 if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
650 map->map_orgclass != &BogusMapClass)
651 map->map_class = map->map_orgclass;
652 if (tTd(38, 5))
653 sm_dprintf("closemaps: closing %s (%s)\n",
654 map->map_mname == NULL ? "NULL" : map->map_mname,
655 map->map_file == NULL ? "NULL" : map->map_file);
657 if (!bitset(MF_OPENBOGUS, map->map_mflags))
659 map->map_mflags |= MF_CLOSING;
660 map->map_class->map_close(map);
662 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
665 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
666 extern int getdomainname();
668 /* this is mainly for backward compatibility in Sun environment */
669 static char *
670 sun_init_domain()
673 ** Get the domain name from the kernel.
674 ** If it does not start with a leading dot, then remove
675 ** the first component. Since leading dots are funny Unix
676 ** files, we treat a leading "+" the same as a leading dot.
677 ** Finally, force there to be at least one dot in the domain name
678 ** (i.e. top-level domains are not allowed, like "com", must be
679 ** something like "sun.com").
682 char buf[MAXNAME];
683 char *period, *autodomain;
685 if (getdomainname(buf, sizeof buf) < 0)
686 return NULL;
688 if (buf[0] == '\0')
689 return NULL;
691 if (tTd(0, 20))
692 printf("domainname = %s\n", buf);
694 if (buf[0] == '+')
695 buf[0] = '.';
696 period = strchr(buf, '.');
697 if (period == NULL)
698 autodomain = buf;
699 else
700 autodomain = period + 1;
701 if (strchr(autodomain, '.') == NULL)
702 return newstr(buf);
703 else
704 return newstr(autodomain);
706 #endif /* SUN_EXTENSIONS && SUN_INIT_DOMAIN */
709 ** GETCANONNAME -- look up name using service switch
711 ** Parameters:
712 ** host -- the host name to look up.
713 ** hbsize -- the size of the host buffer.
714 ** trymx -- if set, try MX records.
715 ** pttl -- pointer to return TTL (can be NULL).
717 ** Returns:
718 ** true -- if the host was found.
719 ** false -- otherwise.
722 bool
723 getcanonname(host, hbsize, trymx, pttl)
724 char *host;
725 int hbsize;
726 bool trymx;
727 int *pttl;
729 int nmaps;
730 int mapno;
731 bool found = false;
732 bool got_tempfail = false;
733 auto int status;
734 char *maptype[MAXMAPSTACK];
735 short mapreturn[MAXMAPACTIONS];
736 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
737 bool should_try_nis_domain = false;
738 static char *nis_domain = NULL;
739 #endif
741 nmaps = switch_map_find("hosts", maptype, mapreturn);
742 if (pttl != 0)
743 *pttl = SM_DEFAULT_TTL;
744 for (mapno = 0; mapno < nmaps; mapno++)
746 int i;
748 if (tTd(38, 20))
749 sm_dprintf("getcanonname(%s), trying %s\n",
750 host, maptype[mapno]);
751 if (strcmp("files", maptype[mapno]) == 0)
753 found = text_getcanonname(host, hbsize, &status);
755 #if NIS
756 else if (strcmp("nis", maptype[mapno]) == 0)
758 found = nis_getcanonname(host, hbsize, &status);
759 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
760 if (nis_domain == NULL)
761 nis_domain = sun_init_domain();
762 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
764 #endif /* NIS */
765 #if NISPLUS
766 else if (strcmp("nisplus", maptype[mapno]) == 0)
768 found = nisplus_getcanonname(host, hbsize, &status);
769 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
770 if (nis_domain == NULL)
771 nis_domain = sun_init_domain();
772 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
774 #endif /* NISPLUS */
775 #if NAMED_BIND
776 else if (strcmp("dns", maptype[mapno]) == 0)
778 found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
780 #endif /* NAMED_BIND */
781 #if NETINFO
782 else if (strcmp("netinfo", maptype[mapno]) == 0)
784 found = ni_getcanonname(host, hbsize, &status);
786 #endif /* NETINFO */
787 else
789 found = false;
790 status = EX_UNAVAILABLE;
794 ** Heuristic: if $m is not set, we are running during system
795 ** startup. In this case, when a name is apparently found
796 ** but has no dot, treat is as not found. This avoids
797 ** problems if /etc/hosts has no FQDN but is listed first
798 ** in the service switch.
801 if (found &&
802 (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
803 break;
805 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
806 if (found)
807 should_try_nis_domain = true;
808 /* but don't break, as we need to try all methods first */
809 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
811 /* see if we should continue */
812 if (status == EX_TEMPFAIL)
814 i = MA_TRYAGAIN;
815 got_tempfail = true;
817 else if (status == EX_NOTFOUND)
818 i = MA_NOTFOUND;
819 else
820 i = MA_UNAVAIL;
821 if (bitset(1 << mapno, mapreturn[i]))
822 break;
825 if (found)
827 char *d;
829 if (tTd(38, 20))
830 sm_dprintf("getcanonname(%s), found\n", host);
833 ** If returned name is still single token, compensate
834 ** by tagging on $m. This is because some sites set
835 ** up their DNS or NIS databases wrong.
838 if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
840 d = macvalue('m', CurEnv);
841 if (d != NULL &&
842 hbsize > (int) (strlen(host) + strlen(d) + 1))
844 if (host[strlen(host) - 1] != '.')
845 (void) sm_strlcat2(host, ".", d,
846 hbsize);
847 else
848 (void) sm_strlcat(host, d, hbsize);
850 else
852 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
853 if (VendorCode == VENDOR_SUN &&
854 should_try_nis_domain)
856 goto try_nis_domain;
858 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
859 return false;
862 return true;
865 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
866 if (VendorCode == VENDOR_SUN && should_try_nis_domain)
868 try_nis_domain:
869 if (nis_domain != NULL &&
870 strlen(nis_domain) + strlen(host) + 1 < hbsize)
872 (void) sm_strlcat2(host, ".", nis_domain, hbsize);
873 return true;
876 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
878 if (tTd(38, 20))
879 sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
880 status);
882 if (got_tempfail)
883 SM_SET_H_ERRNO(TRY_AGAIN);
884 else
885 SM_SET_H_ERRNO(HOST_NOT_FOUND);
887 return false;
890 ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
892 ** Parameters:
893 ** name -- the name against which to match.
894 ** dot -- where to reinsert '.' to get FQDN
895 ** line -- the /etc/hosts line.
896 ** cbuf -- the location to store the result.
897 ** cbuflen -- the size of cbuf.
899 ** Returns:
900 ** true -- if the line matched the desired name.
901 ** false -- otherwise.
904 static bool
905 extract_canonname(name, dot, line, cbuf, cbuflen)
906 char *name;
907 char *dot;
908 char *line;
909 char cbuf[];
910 int cbuflen;
912 int i;
913 char *p;
914 bool found = false;
916 cbuf[0] = '\0';
917 if (line[0] == '#')
918 return false;
920 for (i = 1; ; i++)
922 char nbuf[MAXNAME + 1];
924 p = get_column(line, i, '\0', nbuf, sizeof(nbuf));
925 if (p == NULL)
926 break;
927 if (*p == '\0')
928 continue;
929 if (cbuf[0] == '\0' ||
930 (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
932 (void) sm_strlcpy(cbuf, p, cbuflen);
934 if (sm_strcasecmp(name, p) == 0)
935 found = true;
936 else if (dot != NULL)
938 /* try looking for the FQDN as well */
939 *dot = '.';
940 if (sm_strcasecmp(name, p) == 0)
941 found = true;
942 *dot = '\0';
945 if (found && strchr(cbuf, '.') == NULL)
947 /* try to add a domain on the end of the name */
948 char *domain = macvalue('m', CurEnv);
950 if (domain != NULL &&
951 strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
953 p = &cbuf[i];
954 *p++ = '.';
955 (void) sm_strlcpy(p, domain, cbuflen - i - 1);
958 return found;
962 ** DNS modules
965 #if NAMED_BIND
966 # if DNSMAP
968 # include "sm_resolve.h"
969 # if NETINET || NETINET6
970 # include <arpa/inet.h>
971 # endif /* NETINET || NETINET6 */
974 ** DNS_MAP_OPEN -- stub to check proper value for dns map type
977 bool
978 dns_map_open(map, mode)
979 MAP *map;
980 int mode;
982 if (tTd(38,2))
983 sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
985 mode &= O_ACCMODE;
986 if (mode != O_RDONLY)
988 /* issue a pseudo-error message */
989 errno = SM_EMAPCANTWRITE;
990 return false;
992 return true;
996 ** DNS_MAP_PARSEARGS -- parse dns map definition args.
998 ** Parameters:
999 ** map -- pointer to MAP
1000 ** args -- pointer to the args on the config line.
1002 ** Returns:
1003 ** true -- if everything parsed OK.
1004 ** false -- otherwise.
1007 #define map_sizelimit map_lockfd /* overload field */
1009 struct dns_map
1011 int dns_m_type;
1014 bool
1015 dns_map_parseargs(map,args)
1016 MAP *map;
1017 char *args;
1019 register char *p = args;
1020 struct dns_map *map_p;
1022 map_p = (struct dns_map *) xalloc(sizeof(*map_p));
1023 map_p->dns_m_type = -1;
1024 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
1026 for (;;)
1028 while (isascii(*p) && isspace(*p))
1029 p++;
1030 if (*p != '-')
1031 break;
1032 switch (*++p)
1034 case 'N':
1035 map->map_mflags |= MF_INCLNULL;
1036 map->map_mflags &= ~MF_TRY0NULL;
1037 break;
1039 case 'O':
1040 map->map_mflags &= ~MF_TRY1NULL;
1041 break;
1043 case 'o':
1044 map->map_mflags |= MF_OPTIONAL;
1045 break;
1047 case 'f':
1048 map->map_mflags |= MF_NOFOLDCASE;
1049 break;
1051 case 'm':
1052 map->map_mflags |= MF_MATCHONLY;
1053 break;
1055 case 'A':
1056 map->map_mflags |= MF_APPEND;
1057 break;
1059 case 'q':
1060 map->map_mflags |= MF_KEEPQUOTES;
1061 break;
1063 case 't':
1064 map->map_mflags |= MF_NODEFER;
1065 break;
1067 case 'a':
1068 map->map_app = ++p;
1069 break;
1071 case 'T':
1072 map->map_tapp = ++p;
1073 break;
1075 case 'd':
1077 char *h;
1079 ++p;
1080 h = strchr(p, ' ');
1081 if (h != NULL)
1082 *h = '\0';
1083 map->map_timeout = convtime(p, 's');
1084 if (h != NULL)
1085 *h = ' ';
1087 break;
1089 case 'r':
1090 while (isascii(*++p) && isspace(*p))
1091 continue;
1092 map->map_retry = atoi(p);
1093 break;
1095 case 'z':
1096 if (*++p != '\\')
1097 map->map_coldelim = *p;
1098 else
1100 switch (*++p)
1102 case 'n':
1103 map->map_coldelim = '\n';
1104 break;
1106 case 't':
1107 map->map_coldelim = '\t';
1108 break;
1110 default:
1111 map->map_coldelim = '\\';
1114 break;
1116 case 'Z':
1117 while (isascii(*++p) && isspace(*p))
1118 continue;
1119 map->map_sizelimit = atoi(p);
1120 break;
1122 /* Start of dns_map specific args */
1123 case 'R': /* search field */
1125 char *h;
1127 while (isascii(*++p) && isspace(*p))
1128 continue;
1129 h = strchr(p, ' ');
1130 if (h != NULL)
1131 *h = '\0';
1132 map_p->dns_m_type = dns_string_to_type(p);
1133 if (h != NULL)
1134 *h = ' ';
1135 if (map_p->dns_m_type < 0)
1136 syserr("dns map %s: wrong type %s",
1137 map->map_mname, p);
1139 break;
1141 case 'B': /* base domain */
1143 char *h;
1145 while (isascii(*++p) && isspace(*p))
1146 continue;
1147 h = strchr(p, ' ');
1148 if (h != NULL)
1149 *h = '\0';
1152 ** slight abuse of map->map_file; it isn't
1153 ** used otherwise in this map type.
1156 map->map_file = newstr(p);
1157 if (h != NULL)
1158 *h = ' ';
1160 break;
1162 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1163 p++;
1164 if (*p != '\0')
1165 *p++ = '\0';
1167 if (map_p->dns_m_type < 0)
1168 syserr("dns map %s: missing -R type", map->map_mname);
1169 if (map->map_app != NULL)
1170 map->map_app = newstr(map->map_app);
1171 if (map->map_tapp != NULL)
1172 map->map_tapp = newstr(map->map_tapp);
1175 ** Assumption: assert(sizeof(int) <= sizeof(ARBPTR_T));
1176 ** Even if this assumption is wrong, we use only one byte,
1177 ** so it doesn't really matter.
1180 map->map_db1 = (ARBPTR_T) map_p;
1181 return true;
1185 ** DNS_MAP_LOOKUP -- perform dns map lookup.
1187 ** Parameters:
1188 ** map -- pointer to MAP
1189 ** name -- name to lookup
1190 ** av -- arguments to interpolate into buf.
1191 ** statp -- pointer to status (EX_)
1193 ** Returns:
1194 ** result of lookup if succeeded.
1195 ** NULL -- otherwise.
1198 char *
1199 dns_map_lookup(map, name, av, statp)
1200 MAP *map;
1201 char *name;
1202 char **av;
1203 int *statp;
1205 int resnum = 0;
1206 char *vp = NULL, *result = NULL;
1207 size_t vsize;
1208 struct dns_map *map_p;
1209 RESOURCE_RECORD_T *rr = NULL;
1210 DNS_REPLY_T *r = NULL;
1211 # if NETINET6
1212 static char buf6[INET6_ADDRSTRLEN];
1213 # endif /* NETINET6 */
1215 if (tTd(38, 20))
1216 sm_dprintf("dns_map_lookup(%s, %s)\n",
1217 map->map_mname, name);
1219 map_p = (struct dns_map *)(map->map_db1);
1220 if (map->map_file != NULL && *map->map_file != '\0')
1222 size_t len;
1223 char *appdomain;
1225 len = strlen(map->map_file) + strlen(name) + 2;
1226 appdomain = (char *) sm_malloc(len);
1227 if (appdomain == NULL)
1229 *statp = EX_UNAVAILABLE;
1230 return NULL;
1232 (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
1233 r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
1234 map->map_timeout, map->map_retry);
1235 sm_free(appdomain);
1237 else
1239 r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
1240 map->map_timeout, map->map_retry);
1243 if (r == NULL)
1245 result = NULL;
1246 if (h_errno == TRY_AGAIN || transienterror(errno))
1247 *statp = EX_TEMPFAIL;
1248 else
1249 *statp = EX_NOTFOUND;
1250 goto cleanup;
1252 *statp = EX_OK;
1253 for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
1255 char *type = NULL;
1256 char *value = NULL;
1258 switch (rr->rr_type)
1260 case T_NS:
1261 type = "T_NS";
1262 value = rr->rr_u.rr_txt;
1263 break;
1264 case T_CNAME:
1265 type = "T_CNAME";
1266 value = rr->rr_u.rr_txt;
1267 break;
1268 case T_AFSDB:
1269 type = "T_AFSDB";
1270 value = rr->rr_u.rr_mx->mx_r_domain;
1271 break;
1272 case T_SRV:
1273 type = "T_SRV";
1274 value = rr->rr_u.rr_srv->srv_r_target;
1275 break;
1276 case T_PTR:
1277 type = "T_PTR";
1278 value = rr->rr_u.rr_txt;
1279 break;
1280 case T_TXT:
1281 type = "T_TXT";
1282 value = rr->rr_u.rr_txt;
1283 break;
1284 case T_MX:
1285 type = "T_MX";
1286 value = rr->rr_u.rr_mx->mx_r_domain;
1287 break;
1288 # if NETINET
1289 case T_A:
1290 type = "T_A";
1291 value = inet_ntoa(*(rr->rr_u.rr_a));
1292 break;
1293 # endif /* NETINET */
1294 # if NETINET6
1295 case T_AAAA:
1296 type = "T_AAAA";
1297 value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
1298 sizeof(buf6));
1299 break;
1300 # endif /* NETINET6 */
1303 (void) strreplnonprt(value, 'X');
1304 if (map_p->dns_m_type != rr->rr_type)
1306 if (tTd(38, 40))
1307 sm_dprintf("\tskipping type %s (%d) value %s\n",
1308 type != NULL ? type : "<UNKNOWN>",
1309 rr->rr_type,
1310 value != NULL ? value : "<NO VALUE>");
1311 continue;
1314 # if NETINET6
1315 if (rr->rr_type == T_AAAA && value == NULL)
1317 result = NULL;
1318 *statp = EX_DATAERR;
1319 if (tTd(38, 40))
1320 sm_dprintf("\tbad T_AAAA conversion\n");
1321 goto cleanup;
1323 # endif /* NETINET6 */
1324 if (tTd(38, 40))
1325 sm_dprintf("\tfound type %s (%d) value %s\n",
1326 type != NULL ? type : "<UNKNOWN>",
1327 rr->rr_type,
1328 value != NULL ? value : "<NO VALUE>");
1329 if (value != NULL &&
1330 (map->map_coldelim == '\0' ||
1331 map->map_sizelimit == 1 ||
1332 bitset(MF_MATCHONLY, map->map_mflags)))
1334 /* Only care about the first match */
1335 vp = newstr(value);
1336 break;
1338 else if (vp == NULL)
1340 /* First result */
1341 vp = newstr(value);
1343 else
1345 /* concatenate the results */
1346 int sz;
1347 char *new;
1349 sz = strlen(vp) + strlen(value) + 2;
1350 new = xalloc(sz);
1351 (void) sm_snprintf(new, sz, "%s%c%s",
1352 vp, map->map_coldelim, value);
1353 sm_free(vp);
1354 vp = new;
1355 if (map->map_sizelimit > 0 &&
1356 ++resnum >= map->map_sizelimit)
1357 break;
1360 if (vp == NULL)
1362 result = NULL;
1363 *statp = EX_NOTFOUND;
1364 if (tTd(38, 40))
1365 sm_dprintf("\tno match found\n");
1366 goto cleanup;
1369 /* Cleanly truncate for rulesets */
1370 truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
1372 vsize = strlen(vp);
1374 if (LogLevel > 9)
1375 sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
1376 name, vp);
1377 if (bitset(MF_MATCHONLY, map->map_mflags))
1378 result = map_rewrite(map, name, strlen(name), NULL);
1379 else
1380 result = map_rewrite(map, vp, vsize, av);
1382 cleanup:
1383 if (vp != NULL)
1384 sm_free(vp);
1385 if (r != NULL)
1386 dns_free_data(r);
1387 return result;
1389 # endif /* DNSMAP */
1390 #endif /* NAMED_BIND */
1393 ** NDBM modules
1396 #if NDBM
1399 ** NDBM_MAP_OPEN -- DBM-style map open
1402 bool
1403 ndbm_map_open(map, mode)
1404 MAP *map;
1405 int mode;
1407 register DBM *dbm;
1408 int save_errno;
1409 int dfd;
1410 int pfd;
1411 long sff;
1412 int ret;
1413 int smode = S_IREAD;
1414 char dirfile[MAXPATHLEN];
1415 char pagfile[MAXPATHLEN];
1416 struct stat st;
1417 struct stat std, stp;
1419 if (tTd(38, 2))
1420 sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1421 map->map_mname, map->map_file, mode);
1422 map->map_lockfd = -1;
1423 mode &= O_ACCMODE;
1425 /* do initial file and directory checks */
1426 if (sm_strlcpyn(dirfile, sizeof(dirfile), 2,
1427 map->map_file, ".dir") >= sizeof(dirfile) ||
1428 sm_strlcpyn(pagfile, sizeof(pagfile), 2,
1429 map->map_file, ".pag") >= sizeof(pagfile))
1431 errno = 0;
1432 if (!bitset(MF_OPTIONAL, map->map_mflags))
1433 syserr("dbm map \"%s\": map file %s name too long",
1434 map->map_mname, map->map_file);
1435 return false;
1437 sff = SFF_ROOTOK|SFF_REGONLY;
1438 if (mode == O_RDWR)
1440 sff |= SFF_CREAT;
1441 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1442 sff |= SFF_NOSLINK;
1443 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1444 sff |= SFF_NOHLINK;
1445 smode = S_IWRITE;
1447 else
1449 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1450 sff |= SFF_NOWLINK;
1452 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1453 sff |= SFF_SAFEDIRPATH;
1454 ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
1455 sff, smode, &std);
1456 if (ret == 0)
1457 ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
1458 sff, smode, &stp);
1460 if (ret != 0)
1462 char *prob = "unsafe";
1464 /* cannot open this map */
1465 if (ret == ENOENT)
1466 prob = "missing";
1467 if (tTd(38, 2))
1468 sm_dprintf("\t%s map file: %d\n", prob, ret);
1469 if (!bitset(MF_OPTIONAL, map->map_mflags))
1470 syserr("dbm map \"%s\": %s map file %s",
1471 map->map_mname, prob, map->map_file);
1472 return false;
1474 if (std.st_mode == ST_MODE_NOFILE)
1475 mode |= O_CREAT|O_EXCL;
1477 # if LOCK_ON_OPEN
1478 if (mode == O_RDONLY)
1479 mode |= O_SHLOCK;
1480 else
1481 mode |= O_TRUNC|O_EXLOCK;
1482 # else /* LOCK_ON_OPEN */
1483 if ((mode & O_ACCMODE) == O_RDWR)
1485 # if NOFTRUNCATE
1487 ** Warning: race condition. Try to lock the file as
1488 ** quickly as possible after opening it.
1489 ** This may also have security problems on some systems,
1490 ** but there isn't anything we can do about it.
1493 mode |= O_TRUNC;
1494 # else /* NOFTRUNCATE */
1496 ** This ugly code opens the map without truncating it,
1497 ** locks the file, then truncates it. Necessary to
1498 ** avoid race conditions.
1501 int dirfd;
1502 int pagfd;
1503 long sff = SFF_CREAT|SFF_OPENASROOT;
1505 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1506 sff |= SFF_NOSLINK;
1507 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1508 sff |= SFF_NOHLINK;
1510 dirfd = safeopen(dirfile, mode, DBMMODE, sff);
1511 pagfd = safeopen(pagfile, mode, DBMMODE, sff);
1513 if (dirfd < 0 || pagfd < 0)
1515 save_errno = errno;
1516 if (dirfd >= 0)
1517 (void) close(dirfd);
1518 if (pagfd >= 0)
1519 (void) close(pagfd);
1520 errno = save_errno;
1521 syserr("ndbm_map_open: cannot create database %s",
1522 map->map_file);
1523 return false;
1525 if (ftruncate(dirfd, (off_t) 0) < 0 ||
1526 ftruncate(pagfd, (off_t) 0) < 0)
1528 save_errno = errno;
1529 (void) close(dirfd);
1530 (void) close(pagfd);
1531 errno = save_errno;
1532 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1533 map->map_file);
1534 return false;
1537 /* if new file, get "before" bits for later filechanged check */
1538 if (std.st_mode == ST_MODE_NOFILE &&
1539 (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
1541 save_errno = errno;
1542 (void) close(dirfd);
1543 (void) close(pagfd);
1544 errno = save_errno;
1545 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1546 map->map_file);
1547 return false;
1550 /* have to save the lock for the duration (bletch) */
1551 map->map_lockfd = dirfd;
1552 (void) close(pagfd);
1554 /* twiddle bits for dbm_open */
1555 mode &= ~(O_CREAT|O_EXCL);
1556 # endif /* NOFTRUNCATE */
1558 # endif /* LOCK_ON_OPEN */
1560 /* open the database */
1561 dbm = dbm_open(map->map_file, mode, DBMMODE);
1562 if (dbm == NULL)
1564 save_errno = errno;
1565 if (bitset(MF_ALIAS, map->map_mflags) &&
1566 aliaswait(map, ".pag", false))
1567 return true;
1568 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1569 if (map->map_lockfd >= 0)
1570 (void) close(map->map_lockfd);
1571 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1572 errno = save_errno;
1573 if (!bitset(MF_OPTIONAL, map->map_mflags))
1574 syserr("Cannot open DBM database %s", map->map_file);
1575 return false;
1577 dfd = dbm_dirfno(dbm);
1578 pfd = dbm_pagfno(dbm);
1579 if (dfd == pfd)
1581 /* heuristic: if files are linked, this is actually gdbm */
1582 dbm_close(dbm);
1583 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1584 if (map->map_lockfd >= 0)
1585 (void) close(map->map_lockfd);
1586 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1587 errno = 0;
1588 syserr("dbm map \"%s\": cannot support GDBM",
1589 map->map_mname);
1590 return false;
1593 if (filechanged(dirfile, dfd, &std) ||
1594 filechanged(pagfile, pfd, &stp))
1596 save_errno = errno;
1597 dbm_close(dbm);
1598 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1599 if (map->map_lockfd >= 0)
1600 (void) close(map->map_lockfd);
1601 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1602 errno = save_errno;
1603 syserr("ndbm_map_open(%s): file changed after open",
1604 map->map_file);
1605 return false;
1608 map->map_db1 = (ARBPTR_T) dbm;
1611 ** Need to set map_mtime before the call to aliaswait()
1612 ** as aliaswait() will call map_lookup() which requires
1613 ** map_mtime to be set
1616 if (fstat(pfd, &st) >= 0)
1617 map->map_mtime = st.st_mtime;
1619 if (mode == O_RDONLY)
1621 # if LOCK_ON_OPEN
1622 if (dfd >= 0)
1623 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1624 if (pfd >= 0)
1625 (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
1626 # endif /* LOCK_ON_OPEN */
1627 if (bitset(MF_ALIAS, map->map_mflags) &&
1628 !aliaswait(map, ".pag", true))
1629 return false;
1631 else
1633 map->map_mflags |= MF_LOCKED;
1634 if (geteuid() == 0 && TrustedUid != 0)
1636 # if HASFCHOWN
1637 if (fchown(dfd, TrustedUid, -1) < 0 ||
1638 fchown(pfd, TrustedUid, -1) < 0)
1640 int err = errno;
1642 sm_syslog(LOG_ALERT, NOQID,
1643 "ownership change on %s failed: %s",
1644 map->map_file, sm_errstring(err));
1645 message("050 ownership change on %s failed: %s",
1646 map->map_file, sm_errstring(err));
1648 # else /* HASFCHOWN */
1649 sm_syslog(LOG_ALERT, NOQID,
1650 "no fchown(): cannot change ownership on %s",
1651 map->map_file);
1652 message("050 no fchown(): cannot change ownership on %s",
1653 map->map_file);
1654 # endif /* HASFCHOWN */
1657 return true;
1662 ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1665 char *
1666 ndbm_map_lookup(map, name, av, statp)
1667 MAP *map;
1668 char *name;
1669 char **av;
1670 int *statp;
1672 datum key, val;
1673 int dfd, pfd;
1674 char keybuf[MAXNAME + 1];
1675 struct stat stbuf;
1677 if (tTd(38, 20))
1678 sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1679 map->map_mname, name);
1681 key.dptr = name;
1682 key.dsize = strlen(name);
1683 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1685 if (key.dsize > sizeof(keybuf) - 1)
1686 key.dsize = sizeof(keybuf) - 1;
1687 memmove(keybuf, key.dptr, key.dsize);
1688 keybuf[key.dsize] = '\0';
1689 makelower(keybuf);
1690 key.dptr = keybuf;
1692 lockdbm:
1693 dfd = dbm_dirfno((DBM *) map->map_db1);
1694 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1695 (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
1696 pfd = dbm_pagfno((DBM *) map->map_db1);
1697 if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
1698 stbuf.st_mtime > map->map_mtime)
1700 /* Reopen the database to sync the cache */
1701 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1702 : O_RDONLY;
1704 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1705 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1706 map->map_mflags |= MF_CLOSING;
1707 map->map_class->map_close(map);
1708 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1709 if (map->map_class->map_open(map, omode))
1711 map->map_mflags |= MF_OPEN;
1712 map->map_pid = CurrentPid;
1713 if ((omode && O_ACCMODE) == O_RDWR)
1714 map->map_mflags |= MF_WRITABLE;
1715 goto lockdbm;
1717 else
1719 if (!bitset(MF_OPTIONAL, map->map_mflags))
1721 extern MAPCLASS BogusMapClass;
1723 *statp = EX_TEMPFAIL;
1724 map->map_orgclass = map->map_class;
1725 map->map_class = &BogusMapClass;
1726 map->map_mflags |= MF_OPEN;
1727 map->map_pid = CurrentPid;
1728 syserr("Cannot reopen NDBM database %s",
1729 map->map_file);
1731 return NULL;
1734 val.dptr = NULL;
1735 if (bitset(MF_TRY0NULL, map->map_mflags))
1737 val = dbm_fetch((DBM *) map->map_db1, key);
1738 if (val.dptr != NULL)
1739 map->map_mflags &= ~MF_TRY1NULL;
1741 if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1743 key.dsize++;
1744 val = dbm_fetch((DBM *) map->map_db1, key);
1745 if (val.dptr != NULL)
1746 map->map_mflags &= ~MF_TRY0NULL;
1748 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1749 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1750 if (val.dptr == NULL)
1751 return NULL;
1752 if (bitset(MF_MATCHONLY, map->map_mflags))
1753 return map_rewrite(map, name, strlen(name), NULL);
1754 else
1755 return map_rewrite(map, val.dptr, val.dsize, av);
1760 ** NDBM_MAP_STORE -- store a datum in the database
1763 void
1764 ndbm_map_store(map, lhs, rhs)
1765 register MAP *map;
1766 char *lhs;
1767 char *rhs;
1769 datum key;
1770 datum data;
1771 int status;
1772 char keybuf[MAXNAME + 1];
1774 if (tTd(38, 12))
1775 sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1776 map->map_mname, lhs, rhs);
1778 key.dsize = strlen(lhs);
1779 key.dptr = lhs;
1780 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1782 if (key.dsize > sizeof(keybuf) - 1)
1783 key.dsize = sizeof(keybuf) - 1;
1784 memmove(keybuf, key.dptr, key.dsize);
1785 keybuf[key.dsize] = '\0';
1786 makelower(keybuf);
1787 key.dptr = keybuf;
1790 data.dsize = strlen(rhs);
1791 data.dptr = rhs;
1793 if (bitset(MF_INCLNULL, map->map_mflags))
1795 key.dsize++;
1796 data.dsize++;
1799 status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1800 if (status > 0)
1802 if (!bitset(MF_APPEND, map->map_mflags))
1803 message("050 Warning: duplicate alias name %s", lhs);
1804 else
1806 static char *buf = NULL;
1807 static int bufsiz = 0;
1808 auto int xstat;
1809 datum old;
1811 old.dptr = ndbm_map_lookup(map, key.dptr,
1812 (char **) NULL, &xstat);
1813 if (old.dptr != NULL && *(char *) old.dptr != '\0')
1815 old.dsize = strlen(old.dptr);
1816 if (data.dsize + old.dsize + 2 > bufsiz)
1818 if (buf != NULL)
1819 (void) sm_free(buf);
1820 bufsiz = data.dsize + old.dsize + 2;
1821 buf = sm_pmalloc_x(bufsiz);
1823 (void) sm_strlcpyn(buf, bufsiz, 3,
1824 data.dptr, ",", old.dptr);
1825 data.dsize = data.dsize + old.dsize + 1;
1826 data.dptr = buf;
1827 if (tTd(38, 9))
1828 sm_dprintf("ndbm_map_store append=%s\n",
1829 data.dptr);
1832 status = dbm_store((DBM *) map->map_db1,
1833 key, data, DBM_REPLACE);
1835 if (status != 0)
1836 syserr("readaliases: dbm put (%s): %d", lhs, status);
1841 ** NDBM_MAP_CLOSE -- close the database
1844 void
1845 ndbm_map_close(map)
1846 register MAP *map;
1848 if (tTd(38, 9))
1849 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1850 map->map_mname, map->map_file, map->map_mflags);
1852 if (bitset(MF_WRITABLE, map->map_mflags))
1854 # ifdef NDBM_YP_COMPAT
1855 bool inclnull;
1856 char buf[MAXHOSTNAMELEN];
1858 inclnull = bitset(MF_INCLNULL, map->map_mflags);
1859 map->map_mflags &= ~MF_INCLNULL;
1861 if (strstr(map->map_file, "/yp/") != NULL)
1863 long save_mflags = map->map_mflags;
1865 map->map_mflags |= MF_NOFOLDCASE;
1867 (void) sm_snprintf(buf, sizeof(buf), "%010ld", curtime());
1868 ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1870 (void) gethostname(buf, sizeof(buf));
1871 ndbm_map_store(map, "YP_MASTER_NAME", buf);
1873 map->map_mflags = save_mflags;
1876 if (inclnull)
1877 map->map_mflags |= MF_INCLNULL;
1878 # endif /* NDBM_YP_COMPAT */
1880 /* write out the distinguished alias */
1881 ndbm_map_store(map, "@", "@");
1883 dbm_close((DBM *) map->map_db1);
1885 /* release lock (if needed) */
1886 # if !LOCK_ON_OPEN
1887 if (map->map_lockfd >= 0)
1888 (void) close(map->map_lockfd);
1889 # endif /* !LOCK_ON_OPEN */
1892 #endif /* NDBM */
1894 ** NEWDB (Hash and BTree) Modules
1897 #if NEWDB
1900 ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1902 ** These do rather bizarre locking. If you can lock on open,
1903 ** do that to avoid the condition of opening a database that
1904 ** is being rebuilt. If you don't, we'll try to fake it, but
1905 ** there will be a race condition. If opening for read-only,
1906 ** we immediately release the lock to avoid freezing things up.
1907 ** We really ought to hold the lock, but guarantee that we won't
1908 ** be pokey about it. That's hard to do.
1911 /* these should be K line arguments */
1912 # if DB_VERSION_MAJOR < 2
1913 # define db_cachesize cachesize
1914 # define h_nelem nelem
1915 # ifndef DB_CACHE_SIZE
1916 # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
1917 # endif /* ! DB_CACHE_SIZE */
1918 # ifndef DB_HASH_NELEM
1919 # define DB_HASH_NELEM 4096 /* (starting) size of hash table */
1920 # endif /* ! DB_HASH_NELEM */
1921 # endif /* DB_VERSION_MAJOR < 2 */
1923 bool
1924 bt_map_open(map, mode)
1925 MAP *map;
1926 int mode;
1928 # if DB_VERSION_MAJOR < 2
1929 BTREEINFO btinfo;
1930 # endif /* DB_VERSION_MAJOR < 2 */
1931 # if DB_VERSION_MAJOR == 2
1932 DB_INFO btinfo;
1933 # endif /* DB_VERSION_MAJOR == 2 */
1934 # if DB_VERSION_MAJOR > 2
1935 void *btinfo = NULL;
1936 # endif /* DB_VERSION_MAJOR > 2 */
1938 if (tTd(38, 2))
1939 sm_dprintf("bt_map_open(%s, %s, %d)\n",
1940 map->map_mname, map->map_file, mode);
1942 # if DB_VERSION_MAJOR < 3
1943 memset(&btinfo, '\0', sizeof(btinfo));
1944 # ifdef DB_CACHE_SIZE
1945 btinfo.db_cachesize = DB_CACHE_SIZE;
1946 # endif /* DB_CACHE_SIZE */
1947 # endif /* DB_VERSION_MAJOR < 3 */
1949 return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1952 bool
1953 hash_map_open(map, mode)
1954 MAP *map;
1955 int mode;
1957 # if DB_VERSION_MAJOR < 2
1958 HASHINFO hinfo;
1959 # endif /* DB_VERSION_MAJOR < 2 */
1960 # if DB_VERSION_MAJOR == 2
1961 DB_INFO hinfo;
1962 # endif /* DB_VERSION_MAJOR == 2 */
1963 # if DB_VERSION_MAJOR > 2
1964 void *hinfo = NULL;
1965 # endif /* DB_VERSION_MAJOR > 2 */
1967 if (tTd(38, 2))
1968 sm_dprintf("hash_map_open(%s, %s, %d)\n",
1969 map->map_mname, map->map_file, mode);
1971 # if DB_VERSION_MAJOR < 3
1972 memset(&hinfo, '\0', sizeof(hinfo));
1973 # ifdef DB_HASH_NELEM
1974 hinfo.h_nelem = DB_HASH_NELEM;
1975 # endif /* DB_HASH_NELEM */
1976 # ifdef DB_CACHE_SIZE
1977 hinfo.db_cachesize = DB_CACHE_SIZE;
1978 # endif /* DB_CACHE_SIZE */
1979 # endif /* DB_VERSION_MAJOR < 3 */
1981 return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1984 static bool
1985 db_map_open(map, mode, mapclassname, dbtype, openinfo)
1986 MAP *map;
1987 int mode;
1988 char *mapclassname;
1989 DBTYPE dbtype;
1990 # if DB_VERSION_MAJOR < 2
1991 const void *openinfo;
1992 # endif /* DB_VERSION_MAJOR < 2 */
1993 # if DB_VERSION_MAJOR == 2
1994 DB_INFO *openinfo;
1995 # endif /* DB_VERSION_MAJOR == 2 */
1996 # if DB_VERSION_MAJOR > 2
1997 void **openinfo;
1998 # endif /* DB_VERSION_MAJOR > 2 */
2000 DB *db = NULL;
2001 int i;
2002 int omode;
2003 int smode = S_IREAD;
2004 int fd;
2005 long sff;
2006 int save_errno;
2007 struct stat st;
2008 char buf[MAXPATHLEN];
2010 /* do initial file and directory checks */
2011 if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
2013 errno = 0;
2014 if (!bitset(MF_OPTIONAL, map->map_mflags))
2015 syserr("map \"%s\": map file %s name too long",
2016 map->map_mname, map->map_file);
2017 return false;
2019 i = strlen(buf);
2020 if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
2022 if (sm_strlcat(buf, ".db", sizeof(buf)) >= sizeof(buf))
2024 errno = 0;
2025 if (!bitset(MF_OPTIONAL, map->map_mflags))
2026 syserr("map \"%s\": map file %s name too long",
2027 map->map_mname, map->map_file);
2028 return false;
2032 mode &= O_ACCMODE;
2033 omode = mode;
2035 sff = SFF_ROOTOK|SFF_REGONLY;
2036 if (mode == O_RDWR)
2038 sff |= SFF_CREAT;
2039 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
2040 sff |= SFF_NOSLINK;
2041 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
2042 sff |= SFF_NOHLINK;
2043 smode = S_IWRITE;
2045 else
2047 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
2048 sff |= SFF_NOWLINK;
2050 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
2051 sff |= SFF_SAFEDIRPATH;
2052 i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
2054 if (i != 0)
2056 char *prob = "unsafe";
2058 /* cannot open this map */
2059 if (i == ENOENT)
2060 prob = "missing";
2061 if (tTd(38, 2))
2062 sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
2063 errno = i;
2064 if (!bitset(MF_OPTIONAL, map->map_mflags))
2065 syserr("%s map \"%s\": %s map file %s",
2066 mapclassname, map->map_mname, prob, buf);
2067 return false;
2069 if (st.st_mode == ST_MODE_NOFILE)
2070 omode |= O_CREAT|O_EXCL;
2072 map->map_lockfd = -1;
2074 # if LOCK_ON_OPEN
2075 if (mode == O_RDWR)
2076 omode |= O_TRUNC|O_EXLOCK;
2077 else
2078 omode |= O_SHLOCK;
2079 # else /* LOCK_ON_OPEN */
2081 ** Pre-lock the file to avoid race conditions. In particular,
2082 ** since dbopen returns NULL if the file is zero length, we
2083 ** must have a locked instance around the dbopen.
2086 fd = open(buf, omode, DBMMODE);
2087 if (fd < 0)
2089 if (!bitset(MF_OPTIONAL, map->map_mflags))
2090 syserr("db_map_open: cannot pre-open database %s", buf);
2091 return false;
2094 /* make sure no baddies slipped in just before the open... */
2095 if (filechanged(buf, fd, &st))
2097 save_errno = errno;
2098 (void) close(fd);
2099 errno = save_errno;
2100 syserr("db_map_open(%s): file changed after pre-open", buf);
2101 return false;
2104 /* if new file, get the "before" bits for later filechanged check */
2105 if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
2107 save_errno = errno;
2108 (void) close(fd);
2109 errno = save_errno;
2110 syserr("db_map_open(%s): cannot fstat pre-opened file",
2111 buf);
2112 return false;
2115 /* actually lock the pre-opened file */
2116 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
2117 syserr("db_map_open: cannot lock %s", buf);
2119 /* set up mode bits for dbopen */
2120 if (mode == O_RDWR)
2121 omode |= O_TRUNC;
2122 omode &= ~(O_EXCL|O_CREAT);
2123 # endif /* LOCK_ON_OPEN */
2125 # if DB_VERSION_MAJOR < 2
2126 db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
2127 # else /* DB_VERSION_MAJOR < 2 */
2129 int flags = 0;
2130 # if DB_VERSION_MAJOR > 2
2131 int ret;
2132 # endif /* DB_VERSION_MAJOR > 2 */
2134 if (mode == O_RDONLY)
2135 flags |= DB_RDONLY;
2136 if (bitset(O_CREAT, omode))
2137 flags |= DB_CREATE;
2138 if (bitset(O_TRUNC, omode))
2139 flags |= DB_TRUNCATE;
2140 SM_DB_FLAG_ADD(flags);
2142 # if DB_VERSION_MAJOR > 2
2143 ret = db_create(&db, NULL, 0);
2144 # ifdef DB_CACHE_SIZE
2145 if (ret == 0 && db != NULL)
2147 ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
2148 if (ret != 0)
2150 (void) db->close(db, 0);
2151 db = NULL;
2154 # endif /* DB_CACHE_SIZE */
2155 # ifdef DB_HASH_NELEM
2156 if (dbtype == DB_HASH && ret == 0 && db != NULL)
2158 ret = db->set_h_nelem(db, DB_HASH_NELEM);
2159 if (ret != 0)
2161 (void) db->close(db, 0);
2162 db = NULL;
2165 # endif /* DB_HASH_NELEM */
2166 if (ret == 0 && db != NULL)
2168 ret = db->open(db,
2169 DBTXN /* transaction for DB 4.1 */
2170 buf, NULL, dbtype, flags, DBMMODE);
2171 if (ret != 0)
2173 #ifdef DB_OLD_VERSION
2174 if (ret == DB_OLD_VERSION)
2175 ret = EINVAL;
2176 #endif /* DB_OLD_VERSION */
2177 (void) db->close(db, 0);
2178 db = NULL;
2181 errno = ret;
2182 # else /* DB_VERSION_MAJOR > 2 */
2183 errno = db_open(buf, dbtype, flags, DBMMODE,
2184 NULL, openinfo, &db);
2185 # endif /* DB_VERSION_MAJOR > 2 */
2187 # endif /* DB_VERSION_MAJOR < 2 */
2188 save_errno = errno;
2190 # if !LOCK_ON_OPEN
2191 if (mode == O_RDWR)
2192 map->map_lockfd = fd;
2193 else
2194 (void) close(fd);
2195 # endif /* !LOCK_ON_OPEN */
2197 if (db == NULL)
2199 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2200 aliaswait(map, ".db", false))
2201 return true;
2202 # if !LOCK_ON_OPEN
2203 if (map->map_lockfd >= 0)
2204 (void) close(map->map_lockfd);
2205 # endif /* !LOCK_ON_OPEN */
2206 errno = save_errno;
2207 if (!bitset(MF_OPTIONAL, map->map_mflags))
2208 syserr("Cannot open %s database %s",
2209 mapclassname, buf);
2210 return false;
2213 # if DB_VERSION_MAJOR < 2
2214 fd = db->fd(db);
2215 # else /* DB_VERSION_MAJOR < 2 */
2216 fd = -1;
2217 errno = db->fd(db, &fd);
2218 # endif /* DB_VERSION_MAJOR < 2 */
2219 if (filechanged(buf, fd, &st))
2221 save_errno = errno;
2222 # if DB_VERSION_MAJOR < 2
2223 (void) db->close(db);
2224 # else /* DB_VERSION_MAJOR < 2 */
2225 errno = db->close(db, 0);
2226 # endif /* DB_VERSION_MAJOR < 2 */
2227 # if !LOCK_ON_OPEN
2228 if (map->map_lockfd >= 0)
2229 (void) close(map->map_lockfd);
2230 # endif /* !LOCK_ON_OPEN */
2231 errno = save_errno;
2232 syserr("db_map_open(%s): file changed after open", buf);
2233 return false;
2236 if (mode == O_RDWR)
2237 map->map_mflags |= MF_LOCKED;
2238 # if LOCK_ON_OPEN
2239 if (fd >= 0 && mode == O_RDONLY)
2241 (void) lockfile(fd, buf, NULL, LOCK_UN);
2243 # endif /* LOCK_ON_OPEN */
2245 /* try to make sure that at least the database header is on disk */
2246 if (mode == O_RDWR)
2248 (void) db->sync(db, 0);
2249 if (geteuid() == 0 && TrustedUid != 0)
2251 # if HASFCHOWN
2252 if (fchown(fd, TrustedUid, -1) < 0)
2254 int err = errno;
2256 sm_syslog(LOG_ALERT, NOQID,
2257 "ownership change on %s failed: %s",
2258 buf, sm_errstring(err));
2259 message("050 ownership change on %s failed: %s",
2260 buf, sm_errstring(err));
2262 # else /* HASFCHOWN */
2263 sm_syslog(LOG_ALERT, NOQID,
2264 "no fchown(): cannot change ownership on %s",
2265 map->map_file);
2266 message("050 no fchown(): cannot change ownership on %s",
2267 map->map_file);
2268 # endif /* HASFCHOWN */
2272 map->map_db2 = (ARBPTR_T) db;
2275 ** Need to set map_mtime before the call to aliaswait()
2276 ** as aliaswait() will call map_lookup() which requires
2277 ** map_mtime to be set
2280 if (fd >= 0 && fstat(fd, &st) >= 0)
2281 map->map_mtime = st.st_mtime;
2283 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2284 !aliaswait(map, ".db", true))
2285 return false;
2286 return true;
2291 ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2294 char *
2295 db_map_lookup(map, name, av, statp)
2296 MAP *map;
2297 char *name;
2298 char **av;
2299 int *statp;
2301 DBT key, val;
2302 register DB *db = (DB *) map->map_db2;
2303 int i;
2304 int st;
2305 int save_errno;
2306 int fd;
2307 struct stat stbuf;
2308 char keybuf[MAXNAME + 1];
2309 char buf[MAXPATHLEN];
2311 memset(&key, '\0', sizeof(key));
2312 memset(&val, '\0', sizeof(val));
2314 if (tTd(38, 20))
2315 sm_dprintf("db_map_lookup(%s, %s)\n",
2316 map->map_mname, name);
2318 if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
2320 errno = 0;
2321 if (!bitset(MF_OPTIONAL, map->map_mflags))
2322 syserr("map \"%s\": map file %s name too long",
2323 map->map_mname, map->map_file);
2324 return NULL;
2326 i = strlen(buf);
2327 if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
2328 buf[i - 3] = '\0';
2330 key.size = strlen(name);
2331 if (key.size > sizeof(keybuf) - 1)
2332 key.size = sizeof(keybuf) - 1;
2333 key.data = keybuf;
2334 memmove(keybuf, name, key.size);
2335 keybuf[key.size] = '\0';
2336 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2337 makelower(keybuf);
2338 lockdb:
2339 # if DB_VERSION_MAJOR < 2
2340 fd = db->fd(db);
2341 # else /* DB_VERSION_MAJOR < 2 */
2342 fd = -1;
2343 errno = db->fd(db, &fd);
2344 # endif /* DB_VERSION_MAJOR < 2 */
2345 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2346 (void) lockfile(fd, buf, ".db", LOCK_SH);
2347 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
2349 /* Reopen the database to sync the cache */
2350 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
2351 : O_RDONLY;
2353 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2354 (void) lockfile(fd, buf, ".db", LOCK_UN);
2355 map->map_mflags |= MF_CLOSING;
2356 map->map_class->map_close(map);
2357 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
2358 if (map->map_class->map_open(map, omode))
2360 map->map_mflags |= MF_OPEN;
2361 map->map_pid = CurrentPid;
2362 if ((omode && O_ACCMODE) == O_RDWR)
2363 map->map_mflags |= MF_WRITABLE;
2364 db = (DB *) map->map_db2;
2365 goto lockdb;
2367 else
2369 if (!bitset(MF_OPTIONAL, map->map_mflags))
2371 extern MAPCLASS BogusMapClass;
2373 *statp = EX_TEMPFAIL;
2374 map->map_orgclass = map->map_class;
2375 map->map_class = &BogusMapClass;
2376 map->map_mflags |= MF_OPEN;
2377 map->map_pid = CurrentPid;
2378 syserr("Cannot reopen DB database %s",
2379 map->map_file);
2381 return NULL;
2385 st = 1;
2386 if (bitset(MF_TRY0NULL, map->map_mflags))
2388 # if DB_VERSION_MAJOR < 2
2389 st = db->get(db, &key, &val, 0);
2390 # else /* DB_VERSION_MAJOR < 2 */
2391 errno = db->get(db, NULL, &key, &val, 0);
2392 switch (errno)
2394 case DB_NOTFOUND:
2395 case DB_KEYEMPTY:
2396 st = 1;
2397 break;
2399 case 0:
2400 st = 0;
2401 break;
2403 default:
2404 st = -1;
2405 break;
2407 # endif /* DB_VERSION_MAJOR < 2 */
2408 if (st == 0)
2409 map->map_mflags &= ~MF_TRY1NULL;
2411 if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
2413 key.size++;
2414 # if DB_VERSION_MAJOR < 2
2415 st = db->get(db, &key, &val, 0);
2416 # else /* DB_VERSION_MAJOR < 2 */
2417 errno = db->get(db, NULL, &key, &val, 0);
2418 switch (errno)
2420 case DB_NOTFOUND:
2421 case DB_KEYEMPTY:
2422 st = 1;
2423 break;
2425 case 0:
2426 st = 0;
2427 break;
2429 default:
2430 st = -1;
2431 break;
2433 # endif /* DB_VERSION_MAJOR < 2 */
2434 if (st == 0)
2435 map->map_mflags &= ~MF_TRY0NULL;
2437 save_errno = errno;
2438 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2439 (void) lockfile(fd, buf, ".db", LOCK_UN);
2440 if (st != 0)
2442 errno = save_errno;
2443 if (st < 0)
2444 syserr("db_map_lookup: get (%s)", name);
2445 return NULL;
2447 if (bitset(MF_MATCHONLY, map->map_mflags))
2448 return map_rewrite(map, name, strlen(name), NULL);
2449 else
2450 return map_rewrite(map, val.data, val.size, av);
2455 ** DB_MAP_STORE -- store a datum in the NEWDB database
2458 void
2459 db_map_store(map, lhs, rhs)
2460 register MAP *map;
2461 char *lhs;
2462 char *rhs;
2464 int status;
2465 DBT key;
2466 DBT data;
2467 register DB *db = map->map_db2;
2468 char keybuf[MAXNAME + 1];
2470 memset(&key, '\0', sizeof(key));
2471 memset(&data, '\0', sizeof(data));
2473 if (tTd(38, 12))
2474 sm_dprintf("db_map_store(%s, %s, %s)\n",
2475 map->map_mname, lhs, rhs);
2477 key.size = strlen(lhs);
2478 key.data = lhs;
2479 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2481 if (key.size > sizeof(keybuf) - 1)
2482 key.size = sizeof(keybuf) - 1;
2483 memmove(keybuf, key.data, key.size);
2484 keybuf[key.size] = '\0';
2485 makelower(keybuf);
2486 key.data = keybuf;
2489 data.size = strlen(rhs);
2490 data.data = rhs;
2492 if (bitset(MF_INCLNULL, map->map_mflags))
2494 key.size++;
2495 data.size++;
2498 # if DB_VERSION_MAJOR < 2
2499 status = db->put(db, &key, &data, R_NOOVERWRITE);
2500 # else /* DB_VERSION_MAJOR < 2 */
2501 errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
2502 switch (errno)
2504 case DB_KEYEXIST:
2505 status = 1;
2506 break;
2508 case 0:
2509 status = 0;
2510 break;
2512 default:
2513 status = -1;
2514 break;
2516 # endif /* DB_VERSION_MAJOR < 2 */
2517 if (status > 0)
2519 if (!bitset(MF_APPEND, map->map_mflags))
2520 message("050 Warning: duplicate alias name %s", lhs);
2521 else
2523 static char *buf = NULL;
2524 static int bufsiz = 0;
2525 DBT old;
2527 memset(&old, '\0', sizeof(old));
2529 old.data = db_map_lookup(map, key.data,
2530 (char **) NULL, &status);
2531 if (old.data != NULL)
2533 old.size = strlen(old.data);
2534 if (data.size + old.size + 2 > (size_t) bufsiz)
2536 if (buf != NULL)
2537 sm_free(buf);
2538 bufsiz = data.size + old.size + 2;
2539 buf = sm_pmalloc_x(bufsiz);
2541 (void) sm_strlcpyn(buf, bufsiz, 3,
2542 (char *) data.data, ",",
2543 (char *) old.data);
2544 data.size = data.size + old.size + 1;
2545 data.data = buf;
2546 if (tTd(38, 9))
2547 sm_dprintf("db_map_store append=%s\n",
2548 (char *) data.data);
2551 # if DB_VERSION_MAJOR < 2
2552 status = db->put(db, &key, &data, 0);
2553 # else /* DB_VERSION_MAJOR < 2 */
2554 status = errno = db->put(db, NULL, &key, &data, 0);
2555 # endif /* DB_VERSION_MAJOR < 2 */
2557 if (status != 0)
2558 syserr("readaliases: db put (%s)", lhs);
2563 ** DB_MAP_CLOSE -- add distinguished entries and close the database
2566 void
2567 db_map_close(map)
2568 MAP *map;
2570 register DB *db = map->map_db2;
2572 if (tTd(38, 9))
2573 sm_dprintf("db_map_close(%s, %s, %lx)\n",
2574 map->map_mname, map->map_file, map->map_mflags);
2576 if (bitset(MF_WRITABLE, map->map_mflags))
2578 /* write out the distinguished alias */
2579 db_map_store(map, "@", "@");
2582 (void) db->sync(db, 0);
2584 # if !LOCK_ON_OPEN
2585 if (map->map_lockfd >= 0)
2586 (void) close(map->map_lockfd);
2587 # endif /* !LOCK_ON_OPEN */
2589 # if DB_VERSION_MAJOR < 2
2590 if (db->close(db) != 0)
2591 # else /* DB_VERSION_MAJOR < 2 */
2593 ** Berkeley DB can use internal shared memory
2594 ** locking for its memory pool. Closing a map
2595 ** opened by another process will interfere
2596 ** with the shared memory and locks of the parent
2597 ** process leaving things in a bad state.
2601 ** If this map was not opened by the current
2602 ** process, do not close the map but recover
2603 ** the file descriptor.
2606 if (map->map_pid != CurrentPid)
2608 int fd = -1;
2610 errno = db->fd(db, &fd);
2611 if (fd >= 0)
2612 (void) close(fd);
2613 return;
2616 if ((errno = db->close(db, 0)) != 0)
2617 # endif /* DB_VERSION_MAJOR < 2 */
2618 syserr("db_map_close(%s, %s, %lx): db close failure",
2619 map->map_mname, map->map_file, map->map_mflags);
2621 #endif /* NEWDB */
2623 ** NIS Modules
2626 #if NIS
2628 # ifndef YPERR_BUSY
2629 # define YPERR_BUSY 16
2630 # endif /* ! YPERR_BUSY */
2633 ** NIS_MAP_OPEN -- open DBM map
2636 bool
2637 nis_map_open(map, mode)
2638 MAP *map;
2639 int mode;
2641 int yperr;
2642 register char *p;
2643 auto char *vp;
2644 auto int vsize;
2646 if (tTd(38, 2))
2647 sm_dprintf("nis_map_open(%s, %s, %d)\n",
2648 map->map_mname, map->map_file, mode);
2650 mode &= O_ACCMODE;
2651 if (mode != O_RDONLY)
2653 /* issue a pseudo-error message */
2654 errno = SM_EMAPCANTWRITE;
2655 return false;
2658 p = strchr(map->map_file, '@');
2659 if (p != NULL)
2661 *p++ = '\0';
2662 if (*p != '\0')
2663 map->map_domain = p;
2666 if (*map->map_file == '\0')
2667 map->map_file = "mail.aliases";
2669 if (map->map_domain == NULL)
2671 yperr = yp_get_default_domain(&map->map_domain);
2672 if (yperr != 0)
2674 if (!bitset(MF_OPTIONAL, map->map_mflags))
2675 syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2676 map->map_file);
2677 return false;
2681 /* check to see if this map actually exists */
2682 vp = NULL;
2683 yperr = yp_match(map->map_domain, map->map_file, "@", 1,
2684 &vp, &vsize);
2685 if (tTd(38, 10))
2686 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2687 map->map_domain, map->map_file, yperr_string(yperr));
2688 if (vp != NULL)
2689 sm_free(vp);
2691 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
2694 ** We ought to be calling aliaswait() here if this is an
2695 ** alias file, but powerful HP-UX NIS servers apparently
2696 ** don't insert the @:@ token into the alias map when it
2697 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
2700 # if 0
2701 if (!bitset(MF_ALIAS, map->map_mflags) ||
2702 aliaswait(map, NULL, true))
2703 # endif /* 0 */
2704 return true;
2707 if (!bitset(MF_OPTIONAL, map->map_mflags))
2709 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2710 map->map_file, map->map_domain, yperr_string(yperr));
2713 return false;
2718 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map
2721 /* ARGSUSED3 */
2722 char *
2723 nis_map_lookup(map, name, av, statp)
2724 MAP *map;
2725 char *name;
2726 char **av;
2727 int *statp;
2729 char *vp;
2730 auto int vsize;
2731 int buflen;
2732 int yperr;
2733 char keybuf[MAXNAME + 1];
2734 char *SM_NONVOLATILE result = NULL;
2736 if (tTd(38, 20))
2737 sm_dprintf("nis_map_lookup(%s, %s)\n",
2738 map->map_mname, name);
2740 buflen = strlen(name);
2741 if (buflen > sizeof(keybuf) - 1)
2742 buflen = sizeof(keybuf) - 1;
2743 memmove(keybuf, name, buflen);
2744 keybuf[buflen] = '\0';
2745 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2746 makelower(keybuf);
2747 yperr = YPERR_KEY;
2748 vp = NULL;
2749 if (bitset(MF_TRY0NULL, map->map_mflags))
2751 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2752 &vp, &vsize);
2753 if (yperr == 0)
2754 map->map_mflags &= ~MF_TRY1NULL;
2756 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2758 SM_FREE_CLR(vp);
2759 buflen++;
2760 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2761 &vp, &vsize);
2762 if (yperr == 0)
2763 map->map_mflags &= ~MF_TRY0NULL;
2765 if (yperr != 0)
2767 if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2768 map->map_mflags &= ~(MF_VALID|MF_OPEN);
2769 if (vp != NULL)
2770 sm_free(vp);
2771 return NULL;
2773 SM_TRY
2774 if (bitset(MF_MATCHONLY, map->map_mflags))
2775 result = map_rewrite(map, name, strlen(name), NULL);
2776 else
2777 result = map_rewrite(map, vp, vsize, av);
2778 SM_FINALLY
2779 if (vp != NULL)
2780 sm_free(vp);
2781 SM_END_TRY
2782 return result;
2787 ** NIS_GETCANONNAME -- look up canonical name in NIS
2790 static bool
2791 nis_getcanonname(name, hbsize, statp)
2792 char *name;
2793 int hbsize;
2794 int *statp;
2796 char *vp;
2797 auto int vsize;
2798 int keylen;
2799 int yperr;
2800 static bool try0null = true;
2801 static bool try1null = true;
2802 static char *yp_domain = NULL;
2803 char host_record[MAXLINE];
2804 char cbuf[MAXNAME];
2805 char nbuf[MAXNAME + 1];
2807 if (tTd(38, 20))
2808 sm_dprintf("nis_getcanonname(%s)\n", name);
2810 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
2812 *statp = EX_UNAVAILABLE;
2813 return false;
2815 (void) shorten_hostname(nbuf);
2816 keylen = strlen(nbuf);
2818 if (yp_domain == NULL)
2819 (void) yp_get_default_domain(&yp_domain);
2820 makelower(nbuf);
2821 yperr = YPERR_KEY;
2822 vp = NULL;
2823 if (try0null)
2825 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2826 &vp, &vsize);
2827 if (yperr == 0)
2828 try1null = false;
2830 if (yperr == YPERR_KEY && try1null)
2832 SM_FREE_CLR(vp);
2833 keylen++;
2834 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2835 &vp, &vsize);
2836 if (yperr == 0)
2837 try0null = false;
2839 if (yperr != 0)
2841 if (yperr == YPERR_KEY)
2842 *statp = EX_NOHOST;
2843 else if (yperr == YPERR_BUSY)
2844 *statp = EX_TEMPFAIL;
2845 else
2846 *statp = EX_UNAVAILABLE;
2847 if (vp != NULL)
2848 sm_free(vp);
2849 return false;
2851 (void) sm_strlcpy(host_record, vp, sizeof(host_record));
2852 sm_free(vp);
2853 if (tTd(38, 44))
2854 sm_dprintf("got record `%s'\n", host_record);
2855 vp = strpbrk(host_record, "#\n");
2856 if (vp != NULL)
2857 *vp = '\0';
2858 if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof(cbuf)))
2860 /* this should not happen, but.... */
2861 *statp = EX_NOHOST;
2862 return false;
2864 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
2866 *statp = EX_UNAVAILABLE;
2867 return false;
2869 *statp = EX_OK;
2870 return true;
2873 #endif /* NIS */
2875 ** NISPLUS Modules
2877 ** This code donated by Sun Microsystems.
2880 #if NISPLUS
2882 # undef NIS /* symbol conflict in nis.h */
2883 # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2884 # include <rpcsvc/nis.h>
2885 # include <rpcsvc/nislib.h>
2887 # define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2888 # define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2889 # define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2890 # define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
2893 ** NISPLUS_MAP_OPEN -- open nisplus table
2896 bool
2897 nisplus_map_open(map, mode)
2898 MAP *map;
2899 int mode;
2901 nis_result *res = NULL;
2902 int retry_cnt, max_col, i;
2903 char qbuf[MAXLINE + NIS_MAXNAMELEN];
2905 if (tTd(38, 2))
2906 sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2907 map->map_mname, map->map_file, mode);
2909 mode &= O_ACCMODE;
2910 if (mode != O_RDONLY)
2912 errno = EPERM;
2913 return false;
2916 if (*map->map_file == '\0')
2917 map->map_file = "mail_aliases.org_dir";
2919 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2921 /* set default NISPLUS Domain to $m */
2922 map->map_domain = newstr(nisplus_default_domain());
2923 if (tTd(38, 2))
2924 sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2925 map->map_file, map->map_domain);
2927 if (!PARTIAL_NAME(map->map_file))
2929 map->map_domain = newstr("");
2930 (void) sm_strlcpy(qbuf, map->map_file, sizeof(qbuf));
2932 else
2934 /* check to see if this map actually exists */
2935 (void) sm_strlcpyn(qbuf, sizeof(qbuf), 3,
2936 map->map_file, ".", map->map_domain);
2939 retry_cnt = 0;
2940 while (res == NULL || res->status != NIS_SUCCESS)
2942 res = nis_lookup(qbuf, FOLLOW_LINKS);
2943 switch (res->status)
2945 case NIS_SUCCESS:
2946 break;
2948 case NIS_TRYAGAIN:
2949 case NIS_RPCERROR:
2950 case NIS_NAMEUNREACHABLE:
2951 if (retry_cnt++ > 4)
2953 errno = EAGAIN;
2954 return false;
2956 /* try not to overwhelm hosed server */
2957 sleep(2);
2958 break;
2960 default: /* all other nisplus errors */
2961 # if 0
2962 if (!bitset(MF_OPTIONAL, map->map_mflags))
2963 syserr("451 4.3.5 Cannot find table %s.%s: %s",
2964 map->map_file, map->map_domain,
2965 nis_sperrno(res->status));
2966 # endif /* 0 */
2967 errno = EAGAIN;
2968 return false;
2972 if (NIS_RES_NUMOBJ(res) != 1 ||
2973 (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
2975 if (tTd(38, 10))
2976 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
2977 # if 0
2978 if (!bitset(MF_OPTIONAL, map->map_mflags))
2979 syserr("451 4.3.5 %s.%s: %s is not a table",
2980 map->map_file, map->map_domain,
2981 nis_sperrno(res->status));
2982 # endif /* 0 */
2983 errno = EBADF;
2984 return false;
2986 /* default key column is column 0 */
2987 if (map->map_keycolnm == NULL)
2988 map->map_keycolnm = newstr(COL_NAME(res,0));
2990 max_col = COL_MAX(res);
2992 /* verify the key column exist */
2993 for (i = 0; i < max_col; i++)
2995 if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
2996 break;
2998 if (i == max_col)
3000 if (tTd(38, 2))
3001 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
3002 map->map_file, map->map_keycolnm);
3003 errno = ENOENT;
3004 return false;
3007 /* default value column is the last column */
3008 if (map->map_valcolnm == NULL)
3010 map->map_valcolno = max_col - 1;
3011 return true;
3014 for (i = 0; i< max_col; i++)
3016 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
3018 map->map_valcolno = i;
3019 return true;
3023 if (tTd(38, 2))
3024 sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
3025 map->map_file, map->map_keycolnm);
3026 errno = ENOENT;
3027 return false;
3032 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
3035 char *
3036 nisplus_map_lookup(map, name, av, statp)
3037 MAP *map;
3038 char *name;
3039 char **av;
3040 int *statp;
3042 char *p;
3043 auto int vsize;
3044 char *skp;
3045 int skleft;
3046 char search_key[MAXNAME + 4];
3047 char qbuf[MAXLINE + NIS_MAXNAMELEN];
3048 nis_result *result;
3050 if (tTd(38, 20))
3051 sm_dprintf("nisplus_map_lookup(%s, %s)\n",
3052 map->map_mname, name);
3054 if (!bitset(MF_OPEN, map->map_mflags))
3056 if (nisplus_map_open(map, O_RDONLY))
3058 map->map_mflags |= MF_OPEN;
3059 map->map_pid = CurrentPid;
3061 else
3063 *statp = EX_UNAVAILABLE;
3064 return NULL;
3069 ** Copy the name to the key buffer, escaping double quote characters
3070 ** by doubling them and quoting "]" and "," to avoid having the
3071 ** NIS+ parser choke on them.
3074 skleft = sizeof(search_key) - 4;
3075 skp = search_key;
3076 for (p = name; *p != '\0' && skleft > 0; p++)
3078 switch (*p)
3080 case ']':
3081 case ',':
3082 /* quote the character */
3083 *skp++ = '"';
3084 *skp++ = *p;
3085 *skp++ = '"';
3086 skleft -= 3;
3087 break;
3089 case '"':
3090 /* double the quote */
3091 *skp++ = '"';
3092 skleft--;
3093 /* FALLTHROUGH */
3095 default:
3096 *skp++ = *p;
3097 skleft--;
3098 break;
3101 *skp = '\0';
3102 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3103 makelower(search_key);
3105 /* construct the query */
3106 if (PARTIAL_NAME(map->map_file))
3107 (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s.%s",
3108 map->map_keycolnm, search_key, map->map_file,
3109 map->map_domain);
3110 else
3111 (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s",
3112 map->map_keycolnm, search_key, map->map_file);
3114 if (tTd(38, 20))
3115 sm_dprintf("qbuf=%s\n", qbuf);
3116 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
3117 if (result->status == NIS_SUCCESS)
3119 int count;
3120 char *str;
3122 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3124 if (LogLevel > 10)
3125 sm_syslog(LOG_WARNING, CurEnv->e_id,
3126 "%s: lookup error, expected 1 entry, got %d",
3127 map->map_file, count);
3129 /* ignore second entry */
3130 if (tTd(38, 20))
3131 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3132 name, count);
3135 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
3136 /* set the length of the result */
3137 if (p == NULL)
3138 p = "";
3139 vsize = strlen(p);
3140 if (tTd(38, 20))
3141 sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3142 name, p);
3143 if (bitset(MF_MATCHONLY, map->map_mflags))
3144 str = map_rewrite(map, name, strlen(name), NULL);
3145 else
3146 str = map_rewrite(map, p, vsize, av);
3147 nis_freeresult(result);
3148 *statp = EX_OK;
3149 return str;
3151 else
3153 if (result->status == NIS_NOTFOUND)
3154 *statp = EX_NOTFOUND;
3155 else if (result->status == NIS_TRYAGAIN)
3156 *statp = EX_TEMPFAIL;
3157 else
3159 *statp = EX_UNAVAILABLE;
3160 map->map_mflags &= ~(MF_VALID|MF_OPEN);
3163 if (tTd(38, 20))
3164 sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
3165 nis_freeresult(result);
3166 return NULL;
3172 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3175 static bool
3176 nisplus_getcanonname(name, hbsize, statp)
3177 char *name;
3178 int hbsize;
3179 int *statp;
3181 char *vp;
3182 auto int vsize;
3183 nis_result *result;
3184 char *p;
3185 char nbuf[MAXNAME + 1];
3186 char qbuf[MAXLINE + NIS_MAXNAMELEN];
3188 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
3190 *statp = EX_UNAVAILABLE;
3191 return false;
3193 (void) shorten_hostname(nbuf);
3195 p = strchr(nbuf, '.');
3196 if (p == NULL)
3198 /* single token */
3199 (void) sm_snprintf(qbuf, sizeof(qbuf),
3200 "[name=%s],hosts.org_dir", nbuf);
3202 else if (p[1] != '\0')
3204 /* multi token -- take only first token in nbuf */
3205 *p = '\0';
3206 (void) sm_snprintf(qbuf, sizeof(qbuf),
3207 "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
3209 else
3211 *statp = EX_NOHOST;
3212 return false;
3215 if (tTd(38, 20))
3216 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3217 name, qbuf);
3219 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
3220 NULL, NULL);
3222 if (result->status == NIS_SUCCESS)
3224 int count;
3225 char *domain;
3227 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3229 if (LogLevel > 10)
3230 sm_syslog(LOG_WARNING, CurEnv->e_id,
3231 "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3232 count);
3234 /* ignore second entry */
3235 if (tTd(38, 20))
3236 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3237 name, count);
3240 if (tTd(38, 20))
3241 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3242 name, (NIS_RES_OBJECT(result))->zo_domain);
3245 vp = ((NIS_RES_OBJECT(result))->EN_col(0));
3246 vsize = strlen(vp);
3247 if (tTd(38, 20))
3248 sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3249 name, vp);
3250 if (strchr(vp, '.') != NULL)
3252 domain = "";
3254 else
3256 domain = macvalue('m', CurEnv);
3257 if (domain == NULL)
3258 domain = "";
3260 if (hbsize > vsize + (int) strlen(domain) + 1)
3262 if (domain[0] == '\0')
3263 (void) sm_strlcpy(name, vp, hbsize);
3264 else
3265 (void) sm_snprintf(name, hbsize,
3266 "%s.%s", vp, domain);
3267 *statp = EX_OK;
3269 else
3270 *statp = EX_NOHOST;
3271 nis_freeresult(result);
3272 return true;
3274 else
3276 if (result->status == NIS_NOTFOUND)
3277 *statp = EX_NOHOST;
3278 else if (result->status == NIS_TRYAGAIN)
3279 *statp = EX_TEMPFAIL;
3280 else
3281 *statp = EX_UNAVAILABLE;
3283 if (tTd(38, 20))
3284 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3285 name, result->status, *statp);
3286 nis_freeresult(result);
3287 return false;
3290 char *
3291 nisplus_default_domain()
3293 static char default_domain[MAXNAME + 1] = "";
3294 char *p;
3296 if (default_domain[0] != '\0')
3297 return default_domain;
3299 p = nis_local_directory();
3300 (void) sm_strlcpy(default_domain, p, sizeof(default_domain));
3301 return default_domain;
3304 #endif /* NISPLUS */
3306 ** LDAP Modules
3310 ** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3313 #if defined(LDAPMAP) || defined(PH_MAP)
3315 # if PH_MAP
3316 # define ph_map_dequote ldapmap_dequote
3317 # endif /* PH_MAP */
3319 static char *ldapmap_dequote __P((char *));
3321 static char *
3322 ldapmap_dequote(str)
3323 char *str;
3325 char *p;
3326 char *start;
3328 if (str == NULL)
3329 return NULL;
3331 p = str;
3332 if (*p == '"')
3334 /* Should probably swallow initial whitespace here */
3335 start = ++p;
3337 else
3338 return str;
3339 while (*p != '"' && *p != '\0')
3340 p++;
3341 if (*p != '\0')
3342 *p = '\0';
3343 return start;
3345 #endif /* defined(LDAPMAP) || defined(PH_MAP) */
3347 #if LDAPMAP
3349 static SM_LDAP_STRUCT *LDAPDefaults = NULL;
3352 ** LDAPMAP_OPEN -- open LDAP map
3354 ** Connect to the LDAP server. Re-use existing connections since a
3355 ** single server connection to a host (with the same host, port,
3356 ** bind DN, and secret) can answer queries for multiple maps.
3359 bool
3360 ldapmap_open(map, mode)
3361 MAP *map;
3362 int mode;
3364 SM_LDAP_STRUCT *lmap;
3365 STAB *s;
3366 char *id;
3368 if (tTd(38, 2))
3369 sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
3371 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
3372 HASLDAPGETALIASBYNAME
3373 if (VendorCode == VENDOR_SUN &&
3374 strcmp(map->map_mname, "aliases.ldap") == 0)
3376 return true;
3378 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
3380 mode &= O_ACCMODE;
3382 /* sendmail doesn't have the ability to write to LDAP (yet) */
3383 if (mode != O_RDONLY)
3385 /* issue a pseudo-error message */
3386 errno = SM_EMAPCANTWRITE;
3387 return false;
3390 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3392 s = ldapmap_findconn(lmap);
3393 if (s->s_lmap != NULL)
3395 /* Already have a connection open to this LDAP server */
3396 lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
3397 lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
3399 /* Add this map as head of linked list */
3400 lmap->ldap_next = s->s_lmap;
3401 s->s_lmap = map;
3403 if (tTd(38, 2))
3404 sm_dprintf("using cached connection\n");
3405 return true;
3408 if (tTd(38, 2))
3409 sm_dprintf("opening new connection\n");
3411 if (lmap->ldap_host != NULL)
3412 id = lmap->ldap_host;
3413 else if (lmap->ldap_uri != NULL)
3414 id = lmap->ldap_uri;
3415 else
3416 id = "localhost";
3418 /* No connection yet, connect */
3419 if (!sm_ldap_start(map->map_mname, lmap))
3421 if (errno == ETIMEDOUT)
3423 if (LogLevel > 1)
3424 sm_syslog(LOG_NOTICE, CurEnv->e_id,
3425 "timeout conning to LDAP server %.100s",
3426 id);
3429 if (!bitset(MF_OPTIONAL, map->map_mflags))
3431 if (bitset(MF_NODEFER, map->map_mflags))
3433 syserr("%s failed to %s in map %s",
3434 # if USE_LDAP_INIT
3435 "ldap_init/ldap_bind",
3436 # else /* USE_LDAP_INIT */
3437 "ldap_open",
3438 # endif /* USE_LDAP_INIT */
3439 id, map->map_mname);
3441 else
3443 syserr("451 4.3.5 %s failed to %s in map %s",
3444 # if USE_LDAP_INIT
3445 "ldap_init/ldap_bind",
3446 # else /* USE_LDAP_INIT */
3447 "ldap_open",
3448 # endif /* USE_LDAP_INIT */
3449 id, map->map_mname);
3452 return false;
3455 /* Save connection for reuse */
3456 s->s_lmap = map;
3457 return true;
3461 ** LDAPMAP_CLOSE -- close ldap map
3464 void
3465 ldapmap_close(map)
3466 MAP *map;
3468 SM_LDAP_STRUCT *lmap;
3469 STAB *s;
3471 if (tTd(38, 2))
3472 sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
3474 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3476 /* Check if already closed */
3477 if (lmap->ldap_ld == NULL)
3478 return;
3480 /* Close the LDAP connection */
3481 sm_ldap_close(lmap);
3483 /* Mark all the maps that share the connection as closed */
3484 s = ldapmap_findconn(lmap);
3486 while (s->s_lmap != NULL)
3488 MAP *smap = s->s_lmap;
3490 if (tTd(38, 2) && smap != map)
3491 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3492 map->map_mname, smap->map_mname);
3493 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
3494 lmap = (SM_LDAP_STRUCT *) smap->map_db1;
3495 lmap->ldap_ld = NULL;
3496 s->s_lmap = lmap->ldap_next;
3497 lmap->ldap_next = NULL;
3501 # ifdef SUNET_ID
3503 ** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3504 ** This only makes sense at Stanford University.
3507 static char *
3508 sunet_id_hash(str)
3509 char *str;
3511 char *p, *p_last;
3513 p = str;
3514 p_last = p;
3515 while (*p != '\0')
3517 if (islower(*p) || isdigit(*p))
3519 *p_last = *p;
3520 p_last++;
3522 else if (isupper(*p))
3524 *p_last = tolower(*p);
3525 p_last++;
3527 ++p;
3529 if (*p_last != '\0')
3530 *p_last = '\0';
3531 return str;
3533 # define SM_CONVERT_ID(str) sunet_id_hash(str)
3534 # else /* SUNET_ID */
3535 # define SM_CONVERT_ID(str) makelower(str)
3536 # endif /* SUNET_ID */
3539 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3542 char *
3543 ldapmap_lookup(map, name, av, statp)
3544 MAP *map;
3545 char *name;
3546 char **av;
3547 int *statp;
3549 int flags;
3550 int i;
3551 int plen = 0;
3552 int psize = 0;
3553 int msgid;
3554 int save_errno;
3555 char *vp, *p;
3556 char *result = NULL;
3557 SM_RPOOL_T *rpool;
3558 SM_LDAP_STRUCT *lmap = NULL;
3559 char *argv[SM_LDAP_ARGS];
3560 char keybuf[MAXKEY];
3561 #if SM_LDAP_ARGS != MAX_MAP_ARGS
3562 # ERROR _SM_LDAP_ARGS must be the same as _MAX_MAP_ARGS
3563 #endif /* SM_LDAP_ARGS != MAX_MAP_ARGS */
3565 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
3566 HASLDAPGETALIASBYNAME
3567 if (VendorCode == VENDOR_SUN &&
3568 strcmp(map->map_mname, "aliases.ldap") == 0)
3570 int rc;
3571 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
3572 extern char *__getldapaliasbyname();
3573 char *answer;
3575 answer = __getldapaliasbyname(name, &rc);
3576 #else
3577 char answer[MAXNAME + 1];
3579 rc = __getldapaliasbyname(name, answer, sizeof(answer));
3580 #endif
3581 if (rc != 0)
3583 if (tTd(38, 20))
3584 sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n",
3585 name, errno);
3586 *statp = EX_NOTFOUND;
3587 return NULL;
3589 *statp = EX_OK;
3590 if (tTd(38, 20))
3591 sm_dprintf("getldapaliasbyname(%.100s) => %s\n", name,
3592 answer);
3593 if (bitset(MF_MATCHONLY, map->map_mflags))
3594 result = map_rewrite(map, name, strlen(name), NULL);
3595 else
3596 result = map_rewrite(map, answer, strlen(answer), av);
3597 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
3598 free(answer);
3599 #endif
3600 return result;
3602 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
3604 /* Get ldap struct pointer from map */
3605 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3606 sm_ldap_setopts(lmap->ldap_ld, lmap);
3608 if (lmap->ldap_multi_args)
3610 SM_REQUIRE(av != NULL);
3611 memset(argv, '\0', sizeof(argv));
3612 for (i = 0; i < SM_LDAP_ARGS && av[i] != NULL; i++)
3614 argv[i] = sm_strdup(av[i]);
3615 if (argv[i] == NULL)
3617 int save_errno, j;
3619 save_errno = errno;
3620 for (j = 0; j < i && argv[j] != NULL; j++)
3621 SM_FREE(argv[j]);
3622 *statp = EX_TEMPFAIL;
3623 errno = save_errno;
3624 return NULL;
3627 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3628 SM_CONVERT_ID(av[i]);
3631 else
3633 (void) sm_strlcpy(keybuf, name, sizeof(keybuf));
3635 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3636 SM_CONVERT_ID(keybuf);
3639 if (tTd(38, 20))
3641 if (lmap->ldap_multi_args)
3643 sm_dprintf("ldapmap_lookup(%s, argv)\n",
3644 map->map_mname);
3645 for (i = 0; i < SM_LDAP_ARGS; i++)
3647 sm_dprintf(" argv[%d] = %s\n", i,
3648 argv[i] == NULL ? "NULL" : argv[i]);
3651 else
3653 sm_dprintf("ldapmap_lookup(%s, %s)\n",
3654 map->map_mname, name);
3658 if (lmap->ldap_multi_args)
3660 msgid = sm_ldap_search_m(lmap, argv);
3662 /* free the argv array and its content, no longer needed */
3663 for (i = 0; i < SM_LDAP_ARGS && argv[i] != NULL; i++)
3664 SM_FREE(argv[i]);
3666 else
3667 msgid = sm_ldap_search(lmap, keybuf);
3668 if (msgid == SM_LDAP_ERR)
3670 errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
3671 save_errno = errno;
3672 if (!bitset(MF_OPTIONAL, map->map_mflags))
3675 ** Do not include keybuf as this error may be shown
3676 ** to outsiders.
3679 if (bitset(MF_NODEFER, map->map_mflags))
3680 syserr("Error in ldap_search in map %s",
3681 map->map_mname);
3682 else
3683 syserr("451 4.3.5 Error in ldap_search in map %s",
3684 map->map_mname);
3686 *statp = EX_TEMPFAIL;
3687 switch (save_errno - E_LDAPBASE)
3689 # ifdef LDAP_SERVER_DOWN
3690 case LDAP_SERVER_DOWN:
3691 # endif /* LDAP_SERVER_DOWN */
3692 case LDAP_TIMEOUT:
3693 case LDAP_UNAVAILABLE:
3694 /* server disappeared, try reopen on next search */
3695 ldapmap_close(map);
3696 break;
3698 errno = save_errno;
3699 return NULL;
3701 #if SM_LDAP_ERROR_ON_MISSING_ARGS
3702 else if (msgid == SM_LDAP_ERR_ARG_MISS)
3704 if (bitset(MF_NODEFER, map->map_mflags))
3705 syserr("Error in ldap_search in map %s, too few arguments",
3706 map->map_mname);
3707 else
3708 syserr("554 5.3.5 Error in ldap_search in map %s, too few arguments",
3709 map->map_mname);
3710 *statp = EX_CONFIG;
3711 return NULL;
3713 #endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
3715 *statp = EX_NOTFOUND;
3716 vp = NULL;
3718 flags = 0;
3719 if (bitset(MF_SINGLEMATCH, map->map_mflags))
3720 flags |= SM_LDAP_SINGLEMATCH;
3721 if (bitset(MF_MATCHONLY, map->map_mflags))
3722 flags |= SM_LDAP_MATCHONLY;
3723 # if _FFR_LDAP_SINGLEDN
3724 if (bitset(MF_SINGLEDN, map->map_mflags))
3725 flags |= SM_LDAP_SINGLEDN;
3726 # endif /* _FFR_LDAP_SINGLEDN */
3728 /* Create an rpool for search related memory usage */
3729 rpool = sm_rpool_new_x(NULL);
3731 p = NULL;
3732 *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
3733 rpool, &p, &plen, &psize, NULL);
3734 save_errno = errno;
3736 /* Copy result so rpool can be freed */
3737 if (*statp == EX_OK && p != NULL)
3738 vp = newstr(p);
3739 sm_rpool_free(rpool);
3741 /* need to restart LDAP connection? */
3742 if (*statp == EX_RESTART)
3744 *statp = EX_TEMPFAIL;
3745 ldapmap_close(map);
3748 errno = save_errno;
3749 if (*statp != EX_OK && *statp != EX_NOTFOUND)
3751 if (!bitset(MF_OPTIONAL, map->map_mflags))
3753 if (bitset(MF_NODEFER, map->map_mflags))
3754 syserr("Error getting LDAP results in map %s",
3755 map->map_mname);
3756 else
3757 syserr("451 4.3.5 Error getting LDAP results in map %s",
3758 map->map_mname);
3760 errno = save_errno;
3761 return NULL;
3764 /* Did we match anything? */
3765 if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
3766 return NULL;
3768 if (*statp == EX_OK)
3770 if (LogLevel > 9)
3771 sm_syslog(LOG_INFO, CurEnv->e_id,
3772 "ldap %.100s => %s", name,
3773 vp == NULL ? "<NULL>" : vp);
3774 if (bitset(MF_MATCHONLY, map->map_mflags))
3775 result = map_rewrite(map, name, strlen(name), NULL);
3776 else
3778 /* vp != NULL according to test above */
3779 result = map_rewrite(map, vp, strlen(vp), av);
3781 if (vp != NULL)
3782 sm_free(vp); /* XXX */
3784 return result;
3788 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server
3790 ** Cache LDAP connections based on the host, port, bind DN,
3791 ** secret, and PID so we don't have multiple connections open to
3792 ** the same server for different maps. Need a separate connection
3793 ** per PID since a parent process may close the map before the
3794 ** child is done with it.
3796 ** Parameters:
3797 ** lmap -- LDAP map information
3799 ** Returns:
3800 ** Symbol table entry for the LDAP connection.
3803 static STAB *
3804 ldapmap_findconn(lmap)
3805 SM_LDAP_STRUCT *lmap;
3807 char *format;
3808 char *nbuf;
3809 char *id;
3810 STAB *SM_NONVOLATILE s = NULL;
3812 if (lmap->ldap_host != NULL)
3813 id = lmap->ldap_host;
3814 else if (lmap->ldap_uri != NULL)
3815 id = lmap->ldap_uri;
3816 else
3817 id = "localhost";
3819 format = "%s%c%d%c%d%c%s%c%s%d";
3820 nbuf = sm_stringf_x(format,
3822 CONDELSE,
3823 lmap->ldap_port,
3824 CONDELSE,
3825 lmap->ldap_version,
3826 CONDELSE,
3827 (lmap->ldap_binddn == NULL ? ""
3828 : lmap->ldap_binddn),
3829 CONDELSE,
3830 (lmap->ldap_secret == NULL ? ""
3831 : lmap->ldap_secret),
3832 (int) CurrentPid);
3833 SM_TRY
3834 s = stab(nbuf, ST_LMAP, ST_ENTER);
3835 SM_FINALLY
3836 sm_free(nbuf);
3837 SM_END_TRY
3838 return s;
3841 ** LDAPMAP_PARSEARGS -- parse ldap map definition args.
3844 static struct lamvalues LDAPAuthMethods[] =
3846 { "none", LDAP_AUTH_NONE },
3847 { "simple", LDAP_AUTH_SIMPLE },
3848 # ifdef LDAP_AUTH_KRBV4
3849 { "krbv4", LDAP_AUTH_KRBV4 },
3850 # endif /* LDAP_AUTH_KRBV4 */
3851 { NULL, 0 }
3854 static struct ladvalues LDAPAliasDereference[] =
3856 { "never", LDAP_DEREF_NEVER },
3857 { "always", LDAP_DEREF_ALWAYS },
3858 { "search", LDAP_DEREF_SEARCHING },
3859 { "find", LDAP_DEREF_FINDING },
3860 { NULL, 0 }
3863 static struct lssvalues LDAPSearchScope[] =
3865 { "base", LDAP_SCOPE_BASE },
3866 { "one", LDAP_SCOPE_ONELEVEL },
3867 { "sub", LDAP_SCOPE_SUBTREE },
3868 { NULL, 0 }
3871 bool
3872 ldapmap_parseargs(map, args)
3873 MAP *map;
3874 char *args;
3876 bool secretread = true;
3877 bool attrssetup = false;
3878 int i;
3879 register char *p = args;
3880 SM_LDAP_STRUCT *lmap;
3881 struct lamvalues *lam;
3882 struct ladvalues *lad;
3883 struct lssvalues *lss;
3884 char ldapfilt[MAXLINE];
3885 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
3887 /* Get ldap struct pointer from map */
3888 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3890 /* Check if setting the initial LDAP defaults */
3891 if (lmap == NULL || lmap != LDAPDefaults)
3893 /* We need to alloc an SM_LDAP_STRUCT struct */
3894 lmap = (SM_LDAP_STRUCT *) xalloc(sizeof(*lmap));
3895 if (LDAPDefaults == NULL)
3896 sm_ldap_clear(lmap);
3897 else
3898 STRUCTCOPY(*LDAPDefaults, *lmap);
3901 /* there is no check whether there is really an argument */
3902 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
3903 map->map_spacesub = SpaceSub; /* default value */
3905 /* Check if setting up an alias or file class LDAP map */
3906 if (bitset(MF_ALIAS, map->map_mflags))
3908 /* Comma separate if used as an alias file */
3909 map->map_coldelim = ',';
3910 if (*args == '\0')
3912 int n;
3913 char *lc;
3914 char jbuf[MAXHOSTNAMELEN];
3915 char lcbuf[MAXLINE];
3917 /* Get $j */
3918 expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
3919 if (jbuf[0] == '\0')
3921 (void) sm_strlcpy(jbuf, "localhost",
3922 sizeof(jbuf));
3925 lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
3926 if (lc == NULL)
3927 lc = "";
3928 else
3930 expand(lc, lcbuf, sizeof(lcbuf), CurEnv);
3931 lc = lcbuf;
3934 n = sm_snprintf(ldapfilt, sizeof(ldapfilt),
3935 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
3936 lc, jbuf);
3937 if (n >= sizeof(ldapfilt))
3939 syserr("%s: Default LDAP string too long",
3940 map->map_mname);
3941 return false;
3944 /* default args for an alias LDAP entry */
3945 lmap->ldap_filter = ldapfilt;
3946 lmap->ldap_attr[0] = "objectClass";
3947 lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
3948 lmap->ldap_attr_needobjclass[0] = NULL;
3949 lmap->ldap_attr[1] = "sendmailMTAAliasValue";
3950 lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
3951 lmap->ldap_attr_needobjclass[1] = NULL;
3952 lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
3953 lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
3954 lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
3955 lmap->ldap_attr[3] = "sendmailMTAAliasURL";
3956 lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
3957 lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
3958 lmap->ldap_attr[4] = NULL;
3959 lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
3960 lmap->ldap_attr_needobjclass[4] = NULL;
3961 attrssetup = true;
3964 else if (bitset(MF_FILECLASS, map->map_mflags))
3966 /* Space separate if used as a file class file */
3967 map->map_coldelim = ' ';
3970 for (;;)
3972 while (isascii(*p) && isspace(*p))
3973 p++;
3974 if (*p != '-')
3975 break;
3976 switch (*++p)
3978 case 'A':
3979 map->map_mflags |= MF_APPEND;
3980 break;
3982 case 'a':
3983 map->map_app = ++p;
3984 break;
3986 case 'D':
3987 map->map_mflags |= MF_DEFER;
3988 break;
3990 case 'f':
3991 map->map_mflags |= MF_NOFOLDCASE;
3992 break;
3994 case 'm':
3995 map->map_mflags |= MF_MATCHONLY;
3996 break;
3998 case 'N':
3999 map->map_mflags |= MF_INCLNULL;
4000 map->map_mflags &= ~MF_TRY0NULL;
4001 break;
4003 case 'O':
4004 map->map_mflags &= ~MF_TRY1NULL;
4005 break;
4007 case 'o':
4008 map->map_mflags |= MF_OPTIONAL;
4009 break;
4011 case 'q':
4012 map->map_mflags |= MF_KEEPQUOTES;
4013 break;
4015 case 'S':
4016 map->map_spacesub = *++p;
4017 break;
4019 case 'T':
4020 map->map_tapp = ++p;
4021 break;
4023 case 't':
4024 map->map_mflags |= MF_NODEFER;
4025 break;
4027 case 'z':
4028 if (*++p != '\\')
4029 map->map_coldelim = *p;
4030 else
4032 switch (*++p)
4034 case 'n':
4035 map->map_coldelim = '\n';
4036 break;
4038 case 't':
4039 map->map_coldelim = '\t';
4040 break;
4042 default:
4043 map->map_coldelim = '\\';
4046 break;
4048 /* Start of ldapmap specific args */
4049 case '1':
4050 map->map_mflags |= MF_SINGLEMATCH;
4051 break;
4053 # if _FFR_LDAP_SINGLEDN
4054 case '2':
4055 map->map_mflags |= MF_SINGLEDN;
4056 break;
4057 # endif /* _FFR_LDAP_SINGLEDN */
4059 case 'b': /* search base */
4060 while (isascii(*++p) && isspace(*p))
4061 continue;
4062 lmap->ldap_base = p;
4063 break;
4065 # if _FFR_LDAP_NETWORK_TIMEOUT
4066 case 'c': /* network (connect) timeout */
4067 while (isascii(*++p) && isspace(*p))
4068 continue;
4069 lmap->ldap_networktmo.tv_sec = atoi(p);
4070 break;
4071 # endif /* _FFR_LDAP_NETWORK_TIMEOUT */
4073 case 'd': /* Dn to bind to server as */
4074 while (isascii(*++p) && isspace(*p))
4075 continue;
4076 lmap->ldap_binddn = p;
4077 break;
4079 case 'H': /* Use LDAP URI */
4080 # if !USE_LDAP_INIT
4081 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4082 map->map_mname);
4083 return false;
4084 # else /* !USE_LDAP_INIT */
4085 if (lmap->ldap_host != NULL)
4087 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4088 map->map_mname);
4089 return false;
4091 while (isascii(*++p) && isspace(*p))
4092 continue;
4093 lmap->ldap_uri = p;
4094 break;
4095 # endif /* !USE_LDAP_INIT */
4097 case 'h': /* ldap host */
4098 while (isascii(*++p) && isspace(*p))
4099 continue;
4100 if (lmap->ldap_uri != NULL)
4102 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4103 map->map_mname);
4104 return false;
4106 lmap->ldap_host = p;
4107 break;
4109 case 'K':
4110 lmap->ldap_multi_args = true;
4111 break;
4113 case 'k': /* search field */
4114 while (isascii(*++p) && isspace(*p))
4115 continue;
4116 lmap->ldap_filter = p;
4117 break;
4119 case 'l': /* time limit */
4120 while (isascii(*++p) && isspace(*p))
4121 continue;
4122 lmap->ldap_timelimit = atoi(p);
4123 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
4124 break;
4126 case 'M': /* Method for binding */
4127 while (isascii(*++p) && isspace(*p))
4128 continue;
4130 if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
4131 p += 10;
4133 for (lam = LDAPAuthMethods;
4134 lam != NULL && lam->lam_name != NULL; lam++)
4136 if (sm_strncasecmp(p, lam->lam_name,
4137 strlen(lam->lam_name)) == 0)
4138 break;
4140 if (lam->lam_name != NULL)
4141 lmap->ldap_method = lam->lam_code;
4142 else
4144 /* bad config line */
4145 if (!bitset(MCF_OPTFILE,
4146 map->map_class->map_cflags))
4148 char *ptr;
4150 if ((ptr = strchr(p, ' ')) != NULL)
4151 *ptr = '\0';
4152 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4153 p, map->map_mname);
4154 if (ptr != NULL)
4155 *ptr = ' ';
4156 return false;
4159 break;
4161 case 'n': /* retrieve attribute names only */
4162 lmap->ldap_attrsonly = LDAPMAP_TRUE;
4163 break;
4166 ** This is a string that is dependent on the
4167 ** method used defined by 'M'.
4170 case 'P': /* Secret password for binding */
4171 while (isascii(*++p) && isspace(*p))
4172 continue;
4173 lmap->ldap_secret = p;
4174 secretread = false;
4175 break;
4177 case 'p': /* ldap port */
4178 while (isascii(*++p) && isspace(*p))
4179 continue;
4180 lmap->ldap_port = atoi(p);
4181 break;
4183 /* args stolen from ldapsearch.c */
4184 case 'R': /* don't auto chase referrals */
4185 # ifdef LDAP_REFERRALS
4186 lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
4187 # else /* LDAP_REFERRALS */
4188 syserr("compile with -DLDAP_REFERRALS for referral support");
4189 # endif /* LDAP_REFERRALS */
4190 break;
4192 case 'r': /* alias dereferencing */
4193 while (isascii(*++p) && isspace(*p))
4194 continue;
4196 if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
4197 p += 11;
4199 for (lad = LDAPAliasDereference;
4200 lad != NULL && lad->lad_name != NULL; lad++)
4202 if (sm_strncasecmp(p, lad->lad_name,
4203 strlen(lad->lad_name)) == 0)
4204 break;
4206 if (lad->lad_name != NULL)
4207 lmap->ldap_deref = lad->lad_code;
4208 else
4210 /* bad config line */
4211 if (!bitset(MCF_OPTFILE,
4212 map->map_class->map_cflags))
4214 char *ptr;
4216 if ((ptr = strchr(p, ' ')) != NULL)
4217 *ptr = '\0';
4218 syserr("Deref must be [never|always|search|find] (not %s) in map %s",
4219 p, map->map_mname);
4220 if (ptr != NULL)
4221 *ptr = ' ';
4222 return false;
4225 break;
4227 case 's': /* search scope */
4228 while (isascii(*++p) && isspace(*p))
4229 continue;
4231 if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
4232 p += 11;
4234 for (lss = LDAPSearchScope;
4235 lss != NULL && lss->lss_name != NULL; lss++)
4237 if (sm_strncasecmp(p, lss->lss_name,
4238 strlen(lss->lss_name)) == 0)
4239 break;
4241 if (lss->lss_name != NULL)
4242 lmap->ldap_scope = lss->lss_code;
4243 else
4245 /* bad config line */
4246 if (!bitset(MCF_OPTFILE,
4247 map->map_class->map_cflags))
4249 char *ptr;
4251 if ((ptr = strchr(p, ' ')) != NULL)
4252 *ptr = '\0';
4253 syserr("Scope must be [base|one|sub] (not %s) in map %s",
4254 p, map->map_mname);
4255 if (ptr != NULL)
4256 *ptr = ' ';
4257 return false;
4260 break;
4262 case 'V':
4263 if (*++p != '\\')
4264 lmap->ldap_attrsep = *p;
4265 else
4267 switch (*++p)
4269 case 'n':
4270 lmap->ldap_attrsep = '\n';
4271 break;
4273 case 't':
4274 lmap->ldap_attrsep = '\t';
4275 break;
4277 default:
4278 lmap->ldap_attrsep = '\\';
4281 break;
4283 case 'v': /* attr to return */
4284 while (isascii(*++p) && isspace(*p))
4285 continue;
4286 lmap->ldap_attr[0] = p;
4287 lmap->ldap_attr[1] = NULL;
4288 break;
4290 case 'w':
4291 /* -w should be for passwd, -P should be for version */
4292 while (isascii(*++p) && isspace(*p))
4293 continue;
4294 lmap->ldap_version = atoi(p);
4295 # ifdef LDAP_VERSION_MAX
4296 if (lmap->ldap_version > LDAP_VERSION_MAX)
4298 syserr("LDAP version %d exceeds max of %d in map %s",
4299 lmap->ldap_version, LDAP_VERSION_MAX,
4300 map->map_mname);
4301 return false;
4303 # endif /* LDAP_VERSION_MAX */
4304 # ifdef LDAP_VERSION_MIN
4305 if (lmap->ldap_version < LDAP_VERSION_MIN)
4307 syserr("LDAP version %d is lower than min of %d in map %s",
4308 lmap->ldap_version, LDAP_VERSION_MIN,
4309 map->map_mname);
4310 return false;
4312 # endif /* LDAP_VERSION_MIN */
4313 break;
4315 case 'Z':
4316 while (isascii(*++p) && isspace(*p))
4317 continue;
4318 lmap->ldap_sizelimit = atoi(p);
4319 break;
4321 default:
4322 syserr("Illegal option %c map %s", *p, map->map_mname);
4323 break;
4326 /* need to account for quoted strings here */
4327 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4329 if (*p == '"')
4331 while (*++p != '"' && *p != '\0')
4332 continue;
4333 if (*p != '\0')
4334 p++;
4336 else
4337 p++;
4340 if (*p != '\0')
4341 *p++ = '\0';
4344 if (map->map_app != NULL)
4345 map->map_app = newstr(ldapmap_dequote(map->map_app));
4346 if (map->map_tapp != NULL)
4347 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
4350 ** We need to swallow up all the stuff into a struct
4351 ** and dump it into map->map_dbptr1
4354 if (lmap->ldap_host != NULL &&
4355 (LDAPDefaults == NULL ||
4356 LDAPDefaults == lmap ||
4357 LDAPDefaults->ldap_host != lmap->ldap_host))
4358 lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
4359 map->map_domain = lmap->ldap_host;
4361 if (lmap->ldap_uri != NULL &&
4362 (LDAPDefaults == NULL ||
4363 LDAPDefaults == lmap ||
4364 LDAPDefaults->ldap_uri != lmap->ldap_uri))
4365 lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
4366 map->map_domain = lmap->ldap_uri;
4368 if (lmap->ldap_binddn != NULL &&
4369 (LDAPDefaults == NULL ||
4370 LDAPDefaults == lmap ||
4371 LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
4372 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
4374 if (lmap->ldap_secret != NULL &&
4375 (LDAPDefaults == NULL ||
4376 LDAPDefaults == lmap ||
4377 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4379 SM_FILE_T *sfd;
4380 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
4382 if (DontLockReadFiles)
4383 sff |= SFF_NOLOCK;
4385 /* need to use method to map secret to passwd string */
4386 switch (lmap->ldap_method)
4388 case LDAP_AUTH_NONE:
4389 /* Do nothing */
4390 break;
4392 case LDAP_AUTH_SIMPLE:
4395 ** Secret is the name of a file with
4396 ** the first line as the password.
4399 /* Already read in the secret? */
4400 if (secretread)
4401 break;
4403 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
4404 O_RDONLY, 0, sff);
4405 if (sfd == NULL)
4407 syserr("LDAP map: cannot open secret %s",
4408 ldapmap_dequote(lmap->ldap_secret));
4409 return false;
4411 lmap->ldap_secret = sfgets(m_tmp, sizeof(m_tmp),
4412 sfd, TimeOuts.to_fileopen,
4413 "ldapmap_parseargs");
4414 (void) sm_io_close(sfd, SM_TIME_DEFAULT);
4415 if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
4417 syserr("LDAP map: secret in %s too long",
4418 ldapmap_dequote(lmap->ldap_secret));
4419 return false;
4421 if (lmap->ldap_secret != NULL &&
4422 strlen(m_tmp) > 0)
4424 /* chomp newline */
4425 if (m_tmp[strlen(m_tmp) - 1] == '\n')
4426 m_tmp[strlen(m_tmp) - 1] = '\0';
4428 lmap->ldap_secret = m_tmp;
4430 break;
4432 # ifdef LDAP_AUTH_KRBV4
4433 case LDAP_AUTH_KRBV4:
4436 ** Secret is where the ticket file is
4437 ** stashed
4440 (void) sm_snprintf(m_tmp, sizeof(m_tmp),
4441 "KRBTKFILE=%s",
4442 ldapmap_dequote(lmap->ldap_secret));
4443 lmap->ldap_secret = m_tmp;
4444 break;
4445 # endif /* LDAP_AUTH_KRBV4 */
4447 default: /* Should NEVER get here */
4448 syserr("LDAP map: Illegal value in lmap method");
4449 return false;
4450 /* NOTREACHED */
4451 break;
4455 if (lmap->ldap_secret != NULL &&
4456 (LDAPDefaults == NULL ||
4457 LDAPDefaults == lmap ||
4458 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4459 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
4461 if (lmap->ldap_base != NULL &&
4462 (LDAPDefaults == NULL ||
4463 LDAPDefaults == lmap ||
4464 LDAPDefaults->ldap_base != lmap->ldap_base))
4465 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
4468 ** Save the server from extra work. If request is for a single
4469 ** match, tell the server to only return enough records to
4470 ** determine if there is a single match or not. This can not
4471 ** be one since the server would only return one and we wouldn't
4472 ** know if there were others available.
4475 if (bitset(MF_SINGLEMATCH, map->map_mflags))
4476 lmap->ldap_sizelimit = 2;
4478 /* If setting defaults, don't process ldap_filter and ldap_attr */
4479 if (lmap == LDAPDefaults)
4480 return true;
4482 if (lmap->ldap_filter != NULL)
4483 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
4484 else
4486 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
4488 syserr("No filter given in map %s", map->map_mname);
4489 return false;
4493 if (!attrssetup && lmap->ldap_attr[0] != NULL)
4495 bool recurse = false;
4496 bool normalseen = false;
4498 i = 0;
4499 p = ldapmap_dequote(lmap->ldap_attr[0]);
4500 lmap->ldap_attr[0] = NULL;
4502 /* Prime the attr list with the objectClass attribute */
4503 lmap->ldap_attr[i] = "objectClass";
4504 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
4505 lmap->ldap_attr_needobjclass[i] = NULL;
4506 i++;
4508 while (p != NULL)
4510 char *v;
4512 while (isascii(*p) && isspace(*p))
4513 p++;
4514 if (*p == '\0')
4515 break;
4516 v = p;
4517 p = strchr(v, ',');
4518 if (p != NULL)
4519 *p++ = '\0';
4521 if (i >= LDAPMAP_MAX_ATTR)
4523 syserr("Too many return attributes in %s (max %d)",
4524 map->map_mname, LDAPMAP_MAX_ATTR);
4525 return false;
4527 if (*v != '\0')
4529 int j;
4530 int use;
4531 char *type;
4532 char *needobjclass;
4534 type = strchr(v, ':');
4535 if (type != NULL)
4537 *type++ = '\0';
4538 needobjclass = strchr(type, ':');
4539 if (needobjclass != NULL)
4540 *needobjclass++ = '\0';
4542 else
4544 needobjclass = NULL;
4547 use = i;
4549 /* allow override on "objectClass" type */
4550 if (sm_strcasecmp(v, "objectClass") == 0 &&
4551 lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
4553 use = 0;
4555 else
4558 ** Don't add something to attribute
4559 ** list twice.
4562 for (j = 1; j < i; j++)
4564 if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
4566 syserr("Duplicate attribute (%s) in %s",
4567 v, map->map_mname);
4568 return false;
4572 lmap->ldap_attr[use] = newstr(v);
4573 if (needobjclass != NULL &&
4574 *needobjclass != '\0' &&
4575 *needobjclass != '*')
4577 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
4579 else
4581 lmap->ldap_attr_needobjclass[use] = NULL;
4586 if (type != NULL && *type != '\0')
4588 if (sm_strcasecmp(type, "dn") == 0)
4590 recurse = true;
4591 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
4593 else if (sm_strcasecmp(type, "filter") == 0)
4595 recurse = true;
4596 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
4598 else if (sm_strcasecmp(type, "url") == 0)
4600 recurse = true;
4601 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
4603 else if (sm_strcasecmp(type, "normal") == 0)
4605 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4606 normalseen = true;
4608 else
4610 syserr("Unknown attribute type (%s) in %s",
4611 type, map->map_mname);
4612 return false;
4615 else
4617 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4618 normalseen = true;
4620 i++;
4623 lmap->ldap_attr[i] = NULL;
4625 /* Set in case needed in future code */
4626 attrssetup = true;
4628 if (recurse && !normalseen)
4630 syserr("LDAP recursion requested in %s but no returnable attribute given",
4631 map->map_mname);
4632 return false;
4634 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
4636 syserr("LDAP recursion requested in %s can not be used with -n",
4637 map->map_mname);
4638 return false;
4641 map->map_db1 = (ARBPTR_T) lmap;
4642 return true;
4646 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4648 ** Parameters:
4649 ** spec -- map argument string from LDAPDefaults option
4651 ** Returns:
4652 ** None.
4655 void
4656 ldapmap_set_defaults(spec)
4657 char *spec;
4659 STAB *class;
4660 MAP map;
4662 /* Allocate and set the default values */
4663 if (LDAPDefaults == NULL)
4664 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof(*LDAPDefaults));
4665 sm_ldap_clear(LDAPDefaults);
4667 memset(&map, '\0', sizeof(map));
4669 /* look up the class */
4670 class = stab("ldap", ST_MAPCLASS, ST_FIND);
4671 if (class == NULL)
4673 syserr("readcf: LDAPDefaultSpec: class ldap not available");
4674 return;
4676 map.map_class = &class->s_mapclass;
4677 map.map_db1 = (ARBPTR_T) LDAPDefaults;
4678 map.map_mname = "O LDAPDefaultSpec";
4680 (void) ldapmap_parseargs(&map, spec);
4682 /* These should never be set in LDAPDefaults */
4683 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
4684 map.map_spacesub != SpaceSub ||
4685 map.map_app != NULL ||
4686 map.map_tapp != NULL)
4688 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4689 SM_FREE_CLR(map.map_app);
4690 SM_FREE_CLR(map.map_tapp);
4693 if (LDAPDefaults->ldap_filter != NULL)
4695 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4697 /* don't free, it isn't malloc'ed in parseargs */
4698 LDAPDefaults->ldap_filter = NULL;
4701 if (LDAPDefaults->ldap_attr[0] != NULL)
4703 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4704 /* don't free, they aren't malloc'ed in parseargs */
4705 LDAPDefaults->ldap_attr[0] = NULL;
4708 #endif /* LDAPMAP */
4710 ** PH map
4713 #if PH_MAP
4716 ** Support for the CCSO Nameserver (ph/qi).
4717 ** This code is intended to replace the so-called "ph mailer".
4718 ** Contributed by Mark D. Roth. Contact him for support.
4721 /* what version of the ph map code we're running */
4722 static char phmap_id[128];
4724 /* sendmail version for phmap id string */
4725 extern const char Version[];
4727 /* assume we're using nph-1.2.x if not specified */
4728 # ifndef NPH_VERSION
4729 # define NPH_VERSION 10200
4730 # endif
4732 /* compatibility for versions older than nph-1.2.0 */
4733 # if NPH_VERSION < 10200
4734 # define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
4735 # define PH_OPEN_DONTID PH_DONTID
4736 # define PH_CLOSE_FAST PH_FASTCLOSE
4737 # define PH_ERR_DATAERR PH_DATAERR
4738 # define PH_ERR_NOMATCH PH_NOMATCH
4739 # endif /* NPH_VERSION < 10200 */
4742 ** PH_MAP_PARSEARGS -- parse ph map definition args.
4745 bool
4746 ph_map_parseargs(map, args)
4747 MAP *map;
4748 char *args;
4750 register bool done;
4751 register char *p = args;
4752 PH_MAP_STRUCT *pmap = NULL;
4754 /* initialize version string */
4755 (void) sm_snprintf(phmap_id, sizeof(phmap_id),
4756 "sendmail-%s phmap-20010529 libphclient-%s",
4757 Version, libphclient_version);
4759 pmap = (PH_MAP_STRUCT *) xalloc(sizeof(*pmap));
4761 /* defaults */
4762 pmap->ph_servers = NULL;
4763 pmap->ph_field_list = NULL;
4764 pmap->ph = NULL;
4765 pmap->ph_timeout = 0;
4766 pmap->ph_fastclose = 0;
4768 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4769 for (;;)
4771 while (isascii(*p) && isspace(*p))
4772 p++;
4773 if (*p != '-')
4774 break;
4775 switch (*++p)
4777 case 'N':
4778 map->map_mflags |= MF_INCLNULL;
4779 map->map_mflags &= ~MF_TRY0NULL;
4780 break;
4782 case 'O':
4783 map->map_mflags &= ~MF_TRY1NULL;
4784 break;
4786 case 'o':
4787 map->map_mflags |= MF_OPTIONAL;
4788 break;
4790 case 'f':
4791 map->map_mflags |= MF_NOFOLDCASE;
4792 break;
4794 case 'm':
4795 map->map_mflags |= MF_MATCHONLY;
4796 break;
4798 case 'A':
4799 map->map_mflags |= MF_APPEND;
4800 break;
4802 case 'q':
4803 map->map_mflags |= MF_KEEPQUOTES;
4804 break;
4806 case 't':
4807 map->map_mflags |= MF_NODEFER;
4808 break;
4810 case 'a':
4811 map->map_app = ++p;
4812 break;
4814 case 'T':
4815 map->map_tapp = ++p;
4816 break;
4818 case 'l':
4819 while (isascii(*++p) && isspace(*p))
4820 continue;
4821 pmap->ph_timeout = atoi(p);
4822 break;
4824 case 'S':
4825 map->map_spacesub = *++p;
4826 break;
4828 case 'D':
4829 map->map_mflags |= MF_DEFER;
4830 break;
4832 case 'h': /* PH server list */
4833 while (isascii(*++p) && isspace(*p))
4834 continue;
4835 pmap->ph_servers = p;
4836 break;
4838 case 'k': /* fields to search for */
4839 while (isascii(*++p) && isspace(*p))
4840 continue;
4841 pmap->ph_field_list = p;
4842 break;
4844 default:
4845 syserr("ph_map_parseargs: unknown option -%c", *p);
4848 /* try to account for quoted strings */
4849 done = isascii(*p) && isspace(*p);
4850 while (*p != '\0' && !done)
4852 if (*p == '"')
4854 while (*++p != '"' && *p != '\0')
4855 continue;
4856 if (*p != '\0')
4857 p++;
4859 else
4860 p++;
4861 done = isascii(*p) && isspace(*p);
4864 if (*p != '\0')
4865 *p++ = '\0';
4868 if (map->map_app != NULL)
4869 map->map_app = newstr(ph_map_dequote(map->map_app));
4870 if (map->map_tapp != NULL)
4871 map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
4873 if (pmap->ph_field_list != NULL)
4874 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
4876 if (pmap->ph_servers != NULL)
4877 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
4878 else
4880 syserr("ph_map_parseargs: -h flag is required");
4881 return false;
4884 map->map_db1 = (ARBPTR_T) pmap;
4885 return true;
4889 ** PH_MAP_CLOSE -- close the connection to the ph server
4892 void
4893 ph_map_close(map)
4894 MAP *map;
4896 PH_MAP_STRUCT *pmap;
4898 pmap = (PH_MAP_STRUCT *)map->map_db1;
4899 if (tTd(38, 9))
4900 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
4901 map->map_mname, pmap->ph_fastclose);
4904 if (pmap->ph != NULL)
4906 ph_set_sendhook(pmap->ph, NULL);
4907 ph_set_recvhook(pmap->ph, NULL);
4908 ph_close(pmap->ph, pmap->ph_fastclose);
4911 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4914 static jmp_buf PHTimeout;
4916 /* ARGSUSED */
4917 static void
4918 ph_timeout(unused)
4919 int unused;
4922 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
4923 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
4924 ** DOING.
4927 errno = ETIMEDOUT;
4928 longjmp(PHTimeout, 1);
4931 static void
4932 #if NPH_VERSION >= 10200
4933 ph_map_send_debug(appdata, text)
4934 void *appdata;
4935 #else
4936 ph_map_send_debug(text)
4937 #endif
4938 char *text;
4940 if (LogLevel > 9)
4941 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4942 "ph_map_send_debug: ==> %s", text);
4943 if (tTd(38, 20))
4944 sm_dprintf("ph_map_send_debug: ==> %s\n", text);
4947 static void
4948 #if NPH_VERSION >= 10200
4949 ph_map_recv_debug(appdata, text)
4950 void *appdata;
4951 #else
4952 ph_map_recv_debug(text)
4953 #endif
4954 char *text;
4956 if (LogLevel > 10)
4957 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4958 "ph_map_recv_debug: <== %s", text);
4959 if (tTd(38, 21))
4960 sm_dprintf("ph_map_recv_debug: <== %s\n", text);
4964 ** PH_MAP_OPEN -- sub for opening PH map
4966 bool
4967 ph_map_open(map, mode)
4968 MAP *map;
4969 int mode;
4971 PH_MAP_STRUCT *pmap;
4972 register SM_EVENT *ev = NULL;
4973 int save_errno = 0;
4974 char *hostlist, *host;
4976 if (tTd(38, 2))
4977 sm_dprintf("ph_map_open(%s)\n", map->map_mname);
4979 mode &= O_ACCMODE;
4980 if (mode != O_RDONLY)
4982 /* issue a pseudo-error message */
4983 errno = SM_EMAPCANTWRITE;
4984 return false;
4987 if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
4988 bitset(MF_DEFER, map->map_mflags))
4990 if (tTd(9, 1))
4991 sm_dprintf("ph_map_open(%s) => DEFERRED\n",
4992 map->map_mname);
4995 ** Unset MF_DEFER here so that map_lookup() returns
4996 ** a temporary failure using the bogus map and
4997 ** map->map_tapp instead of the default permanent error.
5000 map->map_mflags &= ~MF_DEFER;
5001 return false;
5004 pmap = (PH_MAP_STRUCT *)map->map_db1;
5005 pmap->ph_fastclose = 0; /* refresh field for reopen */
5007 /* try each host in the list */
5008 hostlist = newstr(pmap->ph_servers);
5009 for (host = strtok(hostlist, " ");
5010 host != NULL;
5011 host = strtok(NULL, " "))
5013 /* set timeout */
5014 if (pmap->ph_timeout != 0)
5016 if (setjmp(PHTimeout) != 0)
5018 ev = NULL;
5019 if (LogLevel > 1)
5020 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5021 "timeout connecting to PH server %.100s",
5022 host);
5023 errno = ETIMEDOUT;
5024 goto ph_map_open_abort;
5026 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
5029 /* open connection to server */
5030 if (ph_open(&(pmap->ph), host,
5031 PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
5032 ph_map_send_debug, ph_map_recv_debug
5033 #if NPH_VERSION >= 10200
5034 , NULL
5035 #endif
5036 ) == 0
5037 && ph_id(pmap->ph, phmap_id) == 0)
5039 if (ev != NULL)
5040 sm_clrevent(ev);
5041 sm_free(hostlist); /* XXX */
5042 return true;
5045 ph_map_open_abort:
5046 save_errno = errno;
5047 if (ev != NULL)
5048 sm_clrevent(ev);
5049 pmap->ph_fastclose = PH_CLOSE_FAST;
5050 ph_map_close(map);
5051 errno = save_errno;
5054 if (bitset(MF_NODEFER, map->map_mflags))
5056 if (errno == 0)
5057 errno = EAGAIN;
5058 syserr("ph_map_open: %s: cannot connect to PH server",
5059 map->map_mname);
5061 else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
5062 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5063 "ph_map_open: %s: cannot connect to PH server",
5064 map->map_mname);
5065 sm_free(hostlist); /* XXX */
5066 return false;
5070 ** PH_MAP_LOOKUP -- look up key from ph server
5073 char *
5074 ph_map_lookup(map, key, args, pstat)
5075 MAP *map;
5076 char *key;
5077 char **args;
5078 int *pstat;
5080 int i, save_errno = 0;
5081 register SM_EVENT *ev = NULL;
5082 PH_MAP_STRUCT *pmap;
5083 char *value = NULL;
5085 pmap = (PH_MAP_STRUCT *)map->map_db1;
5087 *pstat = EX_OK;
5089 /* set timeout */
5090 if (pmap->ph_timeout != 0)
5092 if (setjmp(PHTimeout) != 0)
5094 ev = NULL;
5095 if (LogLevel > 1)
5096 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5097 "timeout during PH lookup of %.100s",
5098 key);
5099 errno = ETIMEDOUT;
5100 *pstat = EX_TEMPFAIL;
5101 goto ph_map_lookup_abort;
5103 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
5106 /* perform lookup */
5107 i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
5108 if (i == -1)
5109 *pstat = EX_TEMPFAIL;
5110 else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
5111 *pstat = EX_UNAVAILABLE;
5113 ph_map_lookup_abort:
5114 if (ev != NULL)
5115 sm_clrevent(ev);
5118 ** Close the connection if the timer popped
5119 ** or we got a temporary PH error
5122 if (*pstat == EX_TEMPFAIL)
5124 save_errno = errno;
5125 pmap->ph_fastclose = PH_CLOSE_FAST;
5126 ph_map_close(map);
5127 errno = save_errno;
5130 if (*pstat == EX_OK)
5132 if (tTd(38,20))
5133 sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
5135 if (bitset(MF_MATCHONLY, map->map_mflags))
5136 return map_rewrite(map, key, strlen(key), NULL);
5137 else
5138 return map_rewrite(map, value, strlen(value), args);
5141 return NULL;
5143 #endif /* PH_MAP */
5146 ** syslog map
5149 #define map_prio map_lockfd /* overload field */
5152 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
5155 bool
5156 syslog_map_parseargs(map, args)
5157 MAP *map;
5158 char *args;
5160 char *p = args;
5161 char *priority = NULL;
5163 /* there is no check whether there is really an argument */
5164 while (*p != '\0')
5166 while (isascii(*p) && isspace(*p))
5167 p++;
5168 if (*p != '-')
5169 break;
5170 ++p;
5171 if (*p == 'D')
5173 map->map_mflags |= MF_DEFER;
5174 ++p;
5176 else if (*p == 'S')
5178 map->map_spacesub = *++p;
5179 if (*p != '\0')
5180 p++;
5182 else if (*p == 'L')
5184 while (*++p != '\0' && isascii(*p) && isspace(*p))
5185 continue;
5186 if (*p == '\0')
5187 break;
5188 priority = p;
5189 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5190 p++;
5191 if (*p != '\0')
5192 *p++ = '\0';
5194 else
5196 syserr("Illegal option %c map syslog", *p);
5197 ++p;
5201 if (priority == NULL)
5202 map->map_prio = LOG_INFO;
5203 else
5205 if (sm_strncasecmp("LOG_", priority, 4) == 0)
5206 priority += 4;
5208 #ifdef LOG_EMERG
5209 if (sm_strcasecmp("EMERG", priority) == 0)
5210 map->map_prio = LOG_EMERG;
5211 else
5212 #endif /* LOG_EMERG */
5213 #ifdef LOG_ALERT
5214 if (sm_strcasecmp("ALERT", priority) == 0)
5215 map->map_prio = LOG_ALERT;
5216 else
5217 #endif /* LOG_ALERT */
5218 #ifdef LOG_CRIT
5219 if (sm_strcasecmp("CRIT", priority) == 0)
5220 map->map_prio = LOG_CRIT;
5221 else
5222 #endif /* LOG_CRIT */
5223 #ifdef LOG_ERR
5224 if (sm_strcasecmp("ERR", priority) == 0)
5225 map->map_prio = LOG_ERR;
5226 else
5227 #endif /* LOG_ERR */
5228 #ifdef LOG_WARNING
5229 if (sm_strcasecmp("WARNING", priority) == 0)
5230 map->map_prio = LOG_WARNING;
5231 else
5232 #endif /* LOG_WARNING */
5233 #ifdef LOG_NOTICE
5234 if (sm_strcasecmp("NOTICE", priority) == 0)
5235 map->map_prio = LOG_NOTICE;
5236 else
5237 #endif /* LOG_NOTICE */
5238 #ifdef LOG_INFO
5239 if (sm_strcasecmp("INFO", priority) == 0)
5240 map->map_prio = LOG_INFO;
5241 else
5242 #endif /* LOG_INFO */
5243 #ifdef LOG_DEBUG
5244 if (sm_strcasecmp("DEBUG", priority) == 0)
5245 map->map_prio = LOG_DEBUG;
5246 else
5247 #endif /* LOG_DEBUG */
5249 syserr("syslog_map_parseargs: Unknown priority %s",
5250 priority);
5251 return false;
5254 return true;
5258 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
5261 char *
5262 syslog_map_lookup(map, string, args, statp)
5263 MAP *map;
5264 char *string;
5265 char **args;
5266 int *statp;
5268 char *ptr = map_rewrite(map, string, strlen(string), args);
5270 if (ptr != NULL)
5272 if (tTd(38, 20))
5273 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5274 map->map_mname, map->map_prio, ptr);
5276 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
5279 *statp = EX_OK;
5280 return "";
5283 #if _FFR_DPRINTF_MAP
5285 ** dprintf map
5288 #define map_dbg_level map_lockfd /* overload field */
5291 ** DPRINTF_MAP_PARSEARGS -- check for priority level to dprintf messages.
5294 bool
5295 dprintf_map_parseargs(map, args)
5296 MAP *map;
5297 char *args;
5299 char *p = args;
5300 char *dbg_level = NULL;
5302 /* there is no check whether there is really an argument */
5303 while (*p != '\0')
5305 while (isascii(*p) && isspace(*p))
5306 p++;
5307 if (*p != '-')
5308 break;
5309 ++p;
5310 if (*p == 'D')
5312 map->map_mflags |= MF_DEFER;
5313 ++p;
5315 else if (*p == 'S')
5317 map->map_spacesub = *++p;
5318 if (*p != '\0')
5319 p++;
5321 else if (*p == 'd')
5323 while (*++p != '\0' && isascii(*p) && isspace(*p))
5324 continue;
5325 if (*p == '\0')
5326 break;
5327 dbg_level = p;
5328 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5329 p++;
5330 if (*p != '\0')
5331 *p++ = '\0';
5333 else
5335 syserr("Illegal option %c map dprintf", *p);
5336 ++p;
5340 if (dbg_level == NULL)
5341 map->map_dbg_level = 0;
5342 else
5344 if (!(isascii(*dbg_level) && isdigit(*dbg_level)))
5346 syserr("dprintf map \"%s\", file %s: -d should specify a number, not %s",
5347 map->map_mname, map->map_file,
5348 dbg_level);
5349 return false;
5351 map->map_dbg_level = atoi(dbg_level);
5353 return true;
5357 ** DPRINTF_MAP_LOOKUP -- rewrite and print message. Always return empty string
5360 char *
5361 dprintf_map_lookup(map, string, args, statp)
5362 MAP *map;
5363 char *string;
5364 char **args;
5365 int *statp;
5367 char *ptr = map_rewrite(map, string, strlen(string), args);
5369 if (ptr != NULL && tTd(85, map->map_dbg_level))
5370 sm_dprintf("%s\n", ptr);
5371 *statp = EX_OK;
5372 return "";
5374 #endif /* _FFR_DPRINTF_MAP */
5377 ** HESIOD Modules
5380 #if HESIOD
5382 bool
5383 hes_map_open(map, mode)
5384 MAP *map;
5385 int mode;
5387 if (tTd(38, 2))
5388 sm_dprintf("hes_map_open(%s, %s, %d)\n",
5389 map->map_mname, map->map_file, mode);
5391 if (mode != O_RDONLY)
5393 /* issue a pseudo-error message */
5394 errno = SM_EMAPCANTWRITE;
5395 return false;
5398 # ifdef HESIOD_INIT
5399 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
5400 return true;
5402 if (!bitset(MF_OPTIONAL, map->map_mflags))
5403 syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5404 sm_errstring(errno));
5405 return false;
5406 # else /* HESIOD_INIT */
5407 if (hes_error() == HES_ER_UNINIT)
5408 hes_init();
5409 switch (hes_error())
5411 case HES_ER_OK:
5412 case HES_ER_NOTFOUND:
5413 return true;
5416 if (!bitset(MF_OPTIONAL, map->map_mflags))
5417 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5419 return false;
5420 # endif /* HESIOD_INIT */
5423 char *
5424 hes_map_lookup(map, name, av, statp)
5425 MAP *map;
5426 char *name;
5427 char **av;
5428 int *statp;
5430 char **hp;
5432 if (tTd(38, 20))
5433 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
5435 if (name[0] == '\\')
5437 char *np;
5438 int nl;
5439 int save_errno;
5440 char nbuf[MAXNAME];
5442 nl = strlen(name);
5443 if (nl < sizeof(nbuf) - 1)
5444 np = nbuf;
5445 else
5446 np = xalloc(strlen(name) + 2);
5447 np[0] = '\\';
5448 (void) sm_strlcpy(&np[1], name, (sizeof(nbuf)) - 1);
5449 # ifdef HESIOD_INIT
5450 hp = hesiod_resolve(HesiodContext, np, map->map_file);
5451 # else /* HESIOD_INIT */
5452 hp = hes_resolve(np, map->map_file);
5453 # endif /* HESIOD_INIT */
5454 save_errno = errno;
5455 if (np != nbuf)
5456 sm_free(np); /* XXX */
5457 errno = save_errno;
5459 else
5461 # ifdef HESIOD_INIT
5462 hp = hesiod_resolve(HesiodContext, name, map->map_file);
5463 # else /* HESIOD_INIT */
5464 hp = hes_resolve(name, map->map_file);
5465 # endif /* HESIOD_INIT */
5467 # ifdef HESIOD_INIT
5468 if (hp == NULL || *hp == NULL)
5470 switch (errno)
5472 case ENOENT:
5473 *statp = EX_NOTFOUND;
5474 break;
5475 case ECONNREFUSED:
5476 *statp = EX_TEMPFAIL;
5477 break;
5478 case EMSGSIZE:
5479 case ENOMEM:
5480 default:
5481 *statp = EX_UNAVAILABLE;
5482 break;
5484 if (hp != NULL)
5485 hesiod_free_list(HesiodContext, hp);
5486 return NULL;
5488 # else /* HESIOD_INIT */
5489 if (hp == NULL || hp[0] == NULL)
5491 switch (hes_error())
5493 case HES_ER_OK:
5494 *statp = EX_OK;
5495 break;
5497 case HES_ER_NOTFOUND:
5498 *statp = EX_NOTFOUND;
5499 break;
5501 case HES_ER_CONFIG:
5502 *statp = EX_UNAVAILABLE;
5503 break;
5505 case HES_ER_NET:
5506 *statp = EX_TEMPFAIL;
5507 break;
5509 return NULL;
5511 # endif /* HESIOD_INIT */
5513 if (bitset(MF_MATCHONLY, map->map_mflags))
5514 return map_rewrite(map, name, strlen(name), NULL);
5515 else
5516 return map_rewrite(map, hp[0], strlen(hp[0]), av);
5520 ** HES_MAP_CLOSE -- free the Hesiod context
5523 void
5524 hes_map_close(map)
5525 MAP *map;
5527 if (tTd(38, 20))
5528 sm_dprintf("hes_map_close(%s)\n", map->map_file);
5530 # ifdef HESIOD_INIT
5531 /* Free the hesiod context */
5532 if (HesiodContext != NULL)
5534 hesiod_end(HesiodContext);
5535 HesiodContext = NULL;
5537 # endif /* HESIOD_INIT */
5540 #endif /* HESIOD */
5542 ** NeXT NETINFO Modules
5545 #if NETINFO
5547 # define NETINFO_DEFAULT_DIR "/aliases"
5548 # define NETINFO_DEFAULT_PROPERTY "members"
5551 ** NI_MAP_OPEN -- open NetInfo Aliases
5554 bool
5555 ni_map_open(map, mode)
5556 MAP *map;
5557 int mode;
5559 if (tTd(38, 2))
5560 sm_dprintf("ni_map_open(%s, %s, %d)\n",
5561 map->map_mname, map->map_file, mode);
5562 mode &= O_ACCMODE;
5564 if (*map->map_file == '\0')
5565 map->map_file = NETINFO_DEFAULT_DIR;
5567 if (map->map_valcolnm == NULL)
5568 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
5570 if (map->map_coldelim == '\0')
5572 if (bitset(MF_ALIAS, map->map_mflags))
5573 map->map_coldelim = ',';
5574 else if (bitset(MF_FILECLASS, map->map_mflags))
5575 map->map_coldelim = ' ';
5577 return true;
5582 ** NI_MAP_LOOKUP -- look up a datum in NetInfo
5585 char *
5586 ni_map_lookup(map, name, av, statp)
5587 MAP *map;
5588 char *name;
5589 char **av;
5590 int *statp;
5592 char *res;
5593 char *propval;
5595 if (tTd(38, 20))
5596 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
5598 propval = ni_propval(map->map_file, map->map_keycolnm, name,
5599 map->map_valcolnm, map->map_coldelim);
5601 if (propval == NULL)
5602 return NULL;
5604 SM_TRY
5605 if (bitset(MF_MATCHONLY, map->map_mflags))
5606 res = map_rewrite(map, name, strlen(name), NULL);
5607 else
5608 res = map_rewrite(map, propval, strlen(propval), av);
5609 SM_FINALLY
5610 sm_free(propval);
5611 SM_END_TRY
5612 return res;
5616 static bool
5617 ni_getcanonname(name, hbsize, statp)
5618 char *name;
5619 int hbsize;
5620 int *statp;
5622 char *vptr;
5623 char *ptr;
5624 char nbuf[MAXNAME + 1];
5626 if (tTd(38, 20))
5627 sm_dprintf("ni_getcanonname(%s)\n", name);
5629 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
5631 *statp = EX_UNAVAILABLE;
5632 return false;
5634 (void) shorten_hostname(nbuf);
5636 /* we only accept single token search key */
5637 if (strchr(nbuf, '.'))
5639 *statp = EX_NOHOST;
5640 return false;
5643 /* Do the search */
5644 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
5646 if (vptr == NULL)
5648 *statp = EX_NOHOST;
5649 return false;
5652 /* Only want the first machine name */
5653 if ((ptr = strchr(vptr, '\n')) != NULL)
5654 *ptr = '\0';
5656 if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
5658 sm_free(vptr);
5659 *statp = EX_UNAVAILABLE;
5660 return true;
5662 sm_free(vptr);
5663 *statp = EX_OK;
5664 return false;
5666 #endif /* NETINFO */
5668 ** TEXT (unindexed text file) Modules
5670 ** This code donated by Sun Microsystems.
5673 #define map_sff map_lockfd /* overload field */
5677 ** TEXT_MAP_OPEN -- open text table
5680 bool
5681 text_map_open(map, mode)
5682 MAP *map;
5683 int mode;
5685 long sff;
5686 int i;
5688 if (tTd(38, 2))
5689 sm_dprintf("text_map_open(%s, %s, %d)\n",
5690 map->map_mname, map->map_file, mode);
5692 mode &= O_ACCMODE;
5693 if (mode != O_RDONLY)
5695 errno = EPERM;
5696 return false;
5699 if (*map->map_file == '\0')
5701 syserr("text map \"%s\": file name required",
5702 map->map_mname);
5703 return false;
5706 if (map->map_file[0] != '/')
5708 syserr("text map \"%s\": file name must be fully qualified",
5709 map->map_mname);
5710 return false;
5713 sff = SFF_ROOTOK|SFF_REGONLY;
5714 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5715 sff |= SFF_NOWLINK;
5716 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5717 sff |= SFF_SAFEDIRPATH;
5718 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
5719 sff, S_IRUSR, NULL)) != 0)
5721 int save_errno = errno;
5723 /* cannot open this map */
5724 if (tTd(38, 2))
5725 sm_dprintf("\tunsafe map file: %d\n", i);
5726 errno = save_errno;
5727 if (!bitset(MF_OPTIONAL, map->map_mflags))
5728 syserr("text map \"%s\": unsafe map file %s",
5729 map->map_mname, map->map_file);
5730 return false;
5733 if (map->map_keycolnm == NULL)
5734 map->map_keycolno = 0;
5735 else
5737 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
5739 syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5740 map->map_mname, map->map_file,
5741 map->map_keycolnm);
5742 return false;
5744 map->map_keycolno = atoi(map->map_keycolnm);
5747 if (map->map_valcolnm == NULL)
5748 map->map_valcolno = 0;
5749 else
5751 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
5753 syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5754 map->map_mname, map->map_file,
5755 map->map_valcolnm);
5756 return false;
5758 map->map_valcolno = atoi(map->map_valcolnm);
5761 if (tTd(38, 2))
5763 sm_dprintf("text_map_open(%s, %s): delimiter = ",
5764 map->map_mname, map->map_file);
5765 if (map->map_coldelim == '\0')
5766 sm_dprintf("(white space)\n");
5767 else
5768 sm_dprintf("%c\n", map->map_coldelim);
5771 map->map_sff = sff;
5772 return true;
5777 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5780 char *
5781 text_map_lookup(map, name, av, statp)
5782 MAP *map;
5783 char *name;
5784 char **av;
5785 int *statp;
5787 char *vp;
5788 auto int vsize;
5789 int buflen;
5790 SM_FILE_T *f;
5791 char delim;
5792 int key_idx;
5793 bool found_it;
5794 long sff = map->map_sff;
5795 char search_key[MAXNAME + 1];
5796 char linebuf[MAXLINE];
5797 char buf[MAXNAME + 1];
5799 found_it = false;
5800 if (tTd(38, 20))
5801 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
5803 buflen = strlen(name);
5804 if (buflen > sizeof(search_key) - 1)
5805 buflen = sizeof(search_key) - 1; /* XXX just cut if off? */
5806 memmove(search_key, name, buflen);
5807 search_key[buflen] = '\0';
5808 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
5809 makelower(search_key);
5811 f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
5812 if (f == NULL)
5814 map->map_mflags &= ~(MF_VALID|MF_OPEN);
5815 *statp = EX_UNAVAILABLE;
5816 return NULL;
5818 key_idx = map->map_keycolno;
5819 delim = map->map_coldelim;
5820 while (sm_io_fgets(f, SM_TIME_DEFAULT,
5821 linebuf, sizeof(linebuf)) != NULL)
5823 char *p;
5825 /* skip comment line */
5826 if (linebuf[0] == '#')
5827 continue;
5828 p = strchr(linebuf, '\n');
5829 if (p != NULL)
5830 *p = '\0';
5831 p = get_column(linebuf, key_idx, delim, buf, sizeof(buf));
5832 if (p != NULL && sm_strcasecmp(search_key, p) == 0)
5834 found_it = true;
5835 break;
5838 (void) sm_io_close(f, SM_TIME_DEFAULT);
5839 if (!found_it)
5841 *statp = EX_NOTFOUND;
5842 return NULL;
5844 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof(buf));
5845 if (vp == NULL)
5847 *statp = EX_NOTFOUND;
5848 return NULL;
5850 vsize = strlen(vp);
5851 *statp = EX_OK;
5852 if (bitset(MF_MATCHONLY, map->map_mflags))
5853 return map_rewrite(map, name, strlen(name), NULL);
5854 else
5855 return map_rewrite(map, vp, vsize, av);
5859 ** TEXT_GETCANONNAME -- look up canonical name in hosts file
5862 static bool
5863 text_getcanonname(name, hbsize, statp)
5864 char *name;
5865 int hbsize;
5866 int *statp;
5868 bool found;
5869 char *dot;
5870 SM_FILE_T *f;
5871 char linebuf[MAXLINE];
5872 char cbuf[MAXNAME + 1];
5873 char nbuf[MAXNAME + 1];
5875 if (tTd(38, 20))
5876 sm_dprintf("text_getcanonname(%s)\n", name);
5878 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
5880 *statp = EX_UNAVAILABLE;
5881 return false;
5883 dot = shorten_hostname(nbuf);
5885 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
5886 NULL);
5887 if (f == NULL)
5889 *statp = EX_UNAVAILABLE;
5890 return false;
5892 found = false;
5893 while (!found &&
5894 sm_io_fgets(f, SM_TIME_DEFAULT,
5895 linebuf, sizeof(linebuf)) != NULL)
5897 char *p = strpbrk(linebuf, "#\n");
5899 if (p != NULL)
5900 *p = '\0';
5901 if (linebuf[0] != '\0')
5902 found = extract_canonname(nbuf, dot, linebuf,
5903 cbuf, sizeof(cbuf));
5905 (void) sm_io_close(f, SM_TIME_DEFAULT);
5906 if (!found)
5908 *statp = EX_NOHOST;
5909 return false;
5912 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
5914 *statp = EX_UNAVAILABLE;
5915 return false;
5917 *statp = EX_OK;
5918 return true;
5921 ** STAB (Symbol Table) Modules
5926 ** STAB_MAP_LOOKUP -- look up alias in symbol table
5929 /* ARGSUSED2 */
5930 char *
5931 stab_map_lookup(map, name, av, pstat)
5932 register MAP *map;
5933 char *name;
5934 char **av;
5935 int *pstat;
5937 register STAB *s;
5939 if (tTd(38, 20))
5940 sm_dprintf("stab_lookup(%s, %s)\n",
5941 map->map_mname, name);
5943 s = stab(name, ST_ALIAS, ST_FIND);
5944 if (s == NULL)
5945 return NULL;
5946 if (bitset(MF_MATCHONLY, map->map_mflags))
5947 return map_rewrite(map, name, strlen(name), NULL);
5948 else
5949 return map_rewrite(map, s->s_alias, strlen(s->s_alias), av);
5953 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
5956 void
5957 stab_map_store(map, lhs, rhs)
5958 register MAP *map;
5959 char *lhs;
5960 char *rhs;
5962 register STAB *s;
5964 s = stab(lhs, ST_ALIAS, ST_ENTER);
5965 s->s_alias = newstr(rhs);
5970 ** STAB_MAP_OPEN -- initialize (reads data file)
5972 ** This is a wierd case -- it is only intended as a fallback for
5973 ** aliases. For this reason, opens for write (only during a
5974 ** "newaliases") always fails, and opens for read open the
5975 ** actual underlying text file instead of the database.
5978 bool
5979 stab_map_open(map, mode)
5980 register MAP *map;
5981 int mode;
5983 SM_FILE_T *af;
5984 long sff;
5985 struct stat st;
5987 if (tTd(38, 2))
5988 sm_dprintf("stab_map_open(%s, %s, %d)\n",
5989 map->map_mname, map->map_file, mode);
5991 mode &= O_ACCMODE;
5992 if (mode != O_RDONLY)
5994 errno = EPERM;
5995 return false;
5998 sff = SFF_ROOTOK|SFF_REGONLY;
5999 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
6000 sff |= SFF_NOWLINK;
6001 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
6002 sff |= SFF_SAFEDIRPATH;
6003 af = safefopen(map->map_file, O_RDONLY, 0444, sff);
6004 if (af == NULL)
6005 return false;
6006 readaliases(map, af, false, false);
6008 if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
6009 map->map_mtime = st.st_mtime;
6010 (void) sm_io_close(af, SM_TIME_DEFAULT);
6012 return true;
6015 ** Implicit Modules
6017 ** Tries several types. For back compatibility of aliases.
6022 ** IMPL_MAP_LOOKUP -- lookup in best open database
6025 char *
6026 impl_map_lookup(map, name, av, pstat)
6027 MAP *map;
6028 char *name;
6029 char **av;
6030 int *pstat;
6032 if (tTd(38, 20))
6033 sm_dprintf("impl_map_lookup(%s, %s)\n",
6034 map->map_mname, name);
6036 #if NEWDB
6037 if (bitset(MF_IMPL_HASH, map->map_mflags))
6038 return db_map_lookup(map, name, av, pstat);
6039 #endif /* NEWDB */
6040 #if NDBM
6041 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6042 return ndbm_map_lookup(map, name, av, pstat);
6043 #endif /* NDBM */
6044 return stab_map_lookup(map, name, av, pstat);
6048 ** IMPL_MAP_STORE -- store in open databases
6051 void
6052 impl_map_store(map, lhs, rhs)
6053 MAP *map;
6054 char *lhs;
6055 char *rhs;
6057 if (tTd(38, 12))
6058 sm_dprintf("impl_map_store(%s, %s, %s)\n",
6059 map->map_mname, lhs, rhs);
6060 #if NEWDB
6061 if (bitset(MF_IMPL_HASH, map->map_mflags))
6062 db_map_store(map, lhs, rhs);
6063 #endif /* NEWDB */
6064 #if NDBM
6065 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6066 ndbm_map_store(map, lhs, rhs);
6067 #endif /* NDBM */
6068 stab_map_store(map, lhs, rhs);
6072 ** IMPL_MAP_OPEN -- implicit database open
6075 bool
6076 impl_map_open(map, mode)
6077 MAP *map;
6078 int mode;
6080 if (tTd(38, 2))
6081 sm_dprintf("impl_map_open(%s, %s, %d)\n",
6082 map->map_mname, map->map_file, mode);
6084 mode &= O_ACCMODE;
6085 #if NEWDB
6086 map->map_mflags |= MF_IMPL_HASH;
6087 if (hash_map_open(map, mode))
6089 # ifdef NDBM_YP_COMPAT
6090 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
6091 # endif /* NDBM_YP_COMPAT */
6092 return true;
6094 else
6095 map->map_mflags &= ~MF_IMPL_HASH;
6096 #endif /* NEWDB */
6097 #if NDBM
6098 map->map_mflags |= MF_IMPL_NDBM;
6099 if (ndbm_map_open(map, mode))
6101 return true;
6103 else
6104 map->map_mflags &= ~MF_IMPL_NDBM;
6105 #endif /* NDBM */
6107 #if defined(NEWDB) || defined(NDBM)
6108 if (Verbose)
6109 message("WARNING: cannot open alias database %s%s",
6110 map->map_file,
6111 mode == O_RDONLY ? "; reading text version" : "");
6112 #else /* defined(NEWDB) || defined(NDBM) */
6113 if (mode != O_RDONLY)
6114 usrerr("Cannot rebuild aliases: no database format defined");
6115 #endif /* defined(NEWDB) || defined(NDBM) */
6117 if (mode == O_RDONLY)
6118 return stab_map_open(map, mode);
6119 else
6120 return false;
6125 ** IMPL_MAP_CLOSE -- close any open database(s)
6128 void
6129 impl_map_close(map)
6130 MAP *map;
6132 if (tTd(38, 9))
6133 sm_dprintf("impl_map_close(%s, %s, %lx)\n",
6134 map->map_mname, map->map_file, map->map_mflags);
6135 #if NEWDB
6136 if (bitset(MF_IMPL_HASH, map->map_mflags))
6138 db_map_close(map);
6139 map->map_mflags &= ~MF_IMPL_HASH;
6141 #endif /* NEWDB */
6143 #if NDBM
6144 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6146 ndbm_map_close(map);
6147 map->map_mflags &= ~MF_IMPL_NDBM;
6149 #endif /* NDBM */
6152 ** User map class.
6154 ** Provides access to the system password file.
6158 ** USER_MAP_OPEN -- open user map
6160 ** Really just binds field names to field numbers.
6163 bool
6164 user_map_open(map, mode)
6165 MAP *map;
6166 int mode;
6168 if (tTd(38, 2))
6169 sm_dprintf("user_map_open(%s, %d)\n",
6170 map->map_mname, mode);
6172 mode &= O_ACCMODE;
6173 if (mode != O_RDONLY)
6175 /* issue a pseudo-error message */
6176 errno = SM_EMAPCANTWRITE;
6177 return false;
6179 if (map->map_valcolnm == NULL)
6180 /* EMPTY */
6181 /* nothing */ ;
6182 else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
6183 map->map_valcolno = 1;
6184 else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
6185 map->map_valcolno = 2;
6186 else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
6187 map->map_valcolno = 3;
6188 else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
6189 map->map_valcolno = 4;
6190 else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
6191 map->map_valcolno = 5;
6192 else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
6193 map->map_valcolno = 6;
6194 else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
6195 map->map_valcolno = 7;
6196 else
6198 syserr("User map %s: unknown column name %s",
6199 map->map_mname, map->map_valcolnm);
6200 return false;
6202 return true;
6207 ** USER_MAP_LOOKUP -- look up a user in the passwd file.
6210 /* ARGSUSED3 */
6211 char *
6212 user_map_lookup(map, key, av, statp)
6213 MAP *map;
6214 char *key;
6215 char **av;
6216 int *statp;
6218 auto bool fuzzy;
6219 SM_MBDB_T user;
6221 if (tTd(38, 20))
6222 sm_dprintf("user_map_lookup(%s, %s)\n",
6223 map->map_mname, key);
6225 *statp = finduser(key, &fuzzy, &user);
6226 if (*statp != EX_OK)
6227 return NULL;
6228 if (bitset(MF_MATCHONLY, map->map_mflags))
6229 return map_rewrite(map, key, strlen(key), NULL);
6230 else
6232 char *rwval = NULL;
6233 char buf[30];
6235 switch (map->map_valcolno)
6237 case 0:
6238 case 1:
6239 rwval = user.mbdb_name;
6240 break;
6242 case 2:
6243 rwval = "x"; /* passwd no longer supported */
6244 break;
6246 case 3:
6247 (void) sm_snprintf(buf, sizeof(buf), "%d",
6248 (int) user.mbdb_uid);
6249 rwval = buf;
6250 break;
6252 case 4:
6253 (void) sm_snprintf(buf, sizeof(buf), "%d",
6254 (int) user.mbdb_gid);
6255 rwval = buf;
6256 break;
6258 case 5:
6259 rwval = user.mbdb_fullname;
6260 break;
6262 case 6:
6263 rwval = user.mbdb_homedir;
6264 break;
6266 case 7:
6267 rwval = user.mbdb_shell;
6268 break;
6269 default:
6270 syserr("user_map %s: bogus field %d",
6271 map->map_mname, map->map_valcolno);
6272 return NULL;
6274 return map_rewrite(map, rwval, strlen(rwval), av);
6278 ** Program map type.
6280 ** This provides access to arbitrary programs. It should be used
6281 ** only very sparingly, since there is no way to bound the cost
6282 ** of invoking an arbitrary program.
6285 char *
6286 prog_map_lookup(map, name, av, statp)
6287 MAP *map;
6288 char *name;
6289 char **av;
6290 int *statp;
6292 int i;
6293 int save_errno;
6294 int fd;
6295 int status;
6296 auto pid_t pid;
6297 register char *p;
6298 char *rval;
6299 char *argv[MAXPV + 1];
6300 char buf[MAXLINE];
6302 if (tTd(38, 20))
6303 sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6304 map->map_mname, name, map->map_file);
6306 i = 0;
6307 argv[i++] = map->map_file;
6308 if (map->map_rebuild != NULL)
6310 (void) sm_strlcpy(buf, map->map_rebuild, sizeof(buf));
6311 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
6313 if (i >= MAXPV - 1)
6314 break;
6315 argv[i++] = p;
6318 argv[i++] = name;
6319 argv[i] = NULL;
6320 if (tTd(38, 21))
6322 sm_dprintf("prog_open:");
6323 for (i = 0; argv[i] != NULL; i++)
6324 sm_dprintf(" %s", argv[i]);
6325 sm_dprintf("\n");
6327 (void) sm_blocksignal(SIGCHLD);
6328 pid = prog_open(argv, &fd, CurEnv);
6329 if (pid < 0)
6331 if (!bitset(MF_OPTIONAL, map->map_mflags))
6332 syserr("prog_map_lookup(%s) failed (%s) -- closing",
6333 map->map_mname, sm_errstring(errno));
6334 else if (tTd(38, 9))
6335 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6336 map->map_mname, sm_errstring(errno));
6337 map->map_mflags &= ~(MF_VALID|MF_OPEN);
6338 *statp = EX_OSFILE;
6339 return NULL;
6341 i = read(fd, buf, sizeof(buf) - 1);
6342 if (i < 0)
6344 syserr("prog_map_lookup(%s): read error %s",
6345 map->map_mname, sm_errstring(errno));
6346 rval = NULL;
6348 else if (i == 0)
6350 if (tTd(38, 20))
6351 sm_dprintf("prog_map_lookup(%s): empty answer\n",
6352 map->map_mname);
6353 rval = NULL;
6355 else
6357 buf[i] = '\0';
6358 p = strchr(buf, '\n');
6359 if (p != NULL)
6360 *p = '\0';
6362 /* collect the return value */
6363 if (bitset(MF_MATCHONLY, map->map_mflags))
6364 rval = map_rewrite(map, name, strlen(name), NULL);
6365 else
6366 rval = map_rewrite(map, buf, strlen(buf), av);
6368 /* now flush any additional output */
6369 while ((i = read(fd, buf, sizeof(buf))) > 0)
6370 continue;
6373 /* wait for the process to terminate */
6374 (void) close(fd);
6375 status = waitfor(pid);
6376 save_errno = errno;
6377 (void) sm_releasesignal(SIGCHLD);
6378 errno = save_errno;
6380 if (status == -1)
6382 syserr("prog_map_lookup(%s): wait error %s",
6383 map->map_mname, sm_errstring(errno));
6384 *statp = EX_SOFTWARE;
6385 rval = NULL;
6387 else if (WIFEXITED(status))
6389 if ((*statp = WEXITSTATUS(status)) != EX_OK)
6390 rval = NULL;
6392 else
6394 syserr("prog_map_lookup(%s): child died on signal %d",
6395 map->map_mname, status);
6396 *statp = EX_UNAVAILABLE;
6397 rval = NULL;
6399 return rval;
6402 ** Sequenced map type.
6404 ** Tries each map in order until something matches, much like
6405 ** implicit. Stores go to the first map in the list that can
6406 ** support storing.
6408 ** This is slightly unusual in that there are two interfaces.
6409 ** The "sequence" interface lets you stack maps arbitrarily.
6410 ** The "switch" interface builds a sequence map by looking
6411 ** at a system-dependent configuration file such as
6412 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6414 ** We don't need an explicit open, since all maps are
6415 ** opened on demand.
6419 ** SEQ_MAP_PARSE -- Sequenced map parsing
6422 bool
6423 seq_map_parse(map, ap)
6424 MAP *map;
6425 char *ap;
6427 int maxmap;
6429 if (tTd(38, 2))
6430 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
6431 maxmap = 0;
6432 while (*ap != '\0')
6434 register char *p;
6435 STAB *s;
6437 /* find beginning of map name */
6438 while (isascii(*ap) && isspace(*ap))
6439 ap++;
6440 for (p = ap;
6441 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
6442 p++)
6443 continue;
6444 if (*p != '\0')
6445 *p++ = '\0';
6446 while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
6447 p++;
6448 if (*ap == '\0')
6450 ap = p;
6451 continue;
6453 s = stab(ap, ST_MAP, ST_FIND);
6454 if (s == NULL)
6456 syserr("Sequence map %s: unknown member map %s",
6457 map->map_mname, ap);
6459 else if (maxmap >= MAXMAPSTACK)
6461 syserr("Sequence map %s: too many member maps (%d max)",
6462 map->map_mname, MAXMAPSTACK);
6463 maxmap++;
6465 else if (maxmap < MAXMAPSTACK)
6467 map->map_stack[maxmap++] = &s->s_map;
6469 ap = p;
6471 return true;
6475 ** SWITCH_MAP_OPEN -- open a switched map
6477 ** This looks at the system-dependent configuration and builds
6478 ** a sequence map that does the same thing.
6480 ** Every system must define a switch_map_find routine in conf.c
6481 ** that will return the list of service types associated with a
6482 ** given service class.
6485 bool
6486 switch_map_open(map, mode)
6487 MAP *map;
6488 int mode;
6490 int mapno;
6491 int nmaps;
6492 char *maptype[MAXMAPSTACK];
6494 if (tTd(38, 2))
6495 sm_dprintf("switch_map_open(%s, %s, %d)\n",
6496 map->map_mname, map->map_file, mode);
6498 mode &= O_ACCMODE;
6499 nmaps = switch_map_find(map->map_file, maptype, map->map_return);
6500 if (tTd(38, 19))
6502 sm_dprintf("\tswitch_map_find => %d\n", nmaps);
6503 for (mapno = 0; mapno < nmaps; mapno++)
6504 sm_dprintf("\t\t%s\n", maptype[mapno]);
6506 if (nmaps <= 0 || nmaps > MAXMAPSTACK)
6507 return false;
6509 for (mapno = 0; mapno < nmaps; mapno++)
6511 register STAB *s;
6512 char nbuf[MAXNAME + 1];
6514 if (maptype[mapno] == NULL)
6515 continue;
6516 (void) sm_strlcpyn(nbuf, sizeof(nbuf), 3,
6517 map->map_mname, ".", maptype[mapno]);
6518 s = stab(nbuf, ST_MAP, ST_FIND);
6519 if (s == NULL)
6521 syserr("Switch map %s: unknown member map %s",
6522 map->map_mname, nbuf);
6524 else
6526 map->map_stack[mapno] = &s->s_map;
6527 if (tTd(38, 4))
6528 sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6529 mapno,
6530 s->s_map.map_class->map_cname,
6531 nbuf);
6534 return true;
6537 #if 0
6539 ** SEQ_MAP_CLOSE -- close all underlying maps
6542 void
6543 seq_map_close(map)
6544 MAP *map;
6546 int mapno;
6548 if (tTd(38, 9))
6549 sm_dprintf("seq_map_close(%s)\n", map->map_mname);
6551 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6553 MAP *mm = map->map_stack[mapno];
6555 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
6556 continue;
6557 mm->map_mflags |= MF_CLOSING;
6558 mm->map_class->map_close(mm);
6559 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
6562 #endif /* 0 */
6565 ** SEQ_MAP_LOOKUP -- sequenced map lookup
6568 char *
6569 seq_map_lookup(map, key, args, pstat)
6570 MAP *map;
6571 char *key;
6572 char **args;
6573 int *pstat;
6575 int mapno;
6576 int mapbit = 0x01;
6577 bool tempfail = false;
6579 if (tTd(38, 20))
6580 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
6582 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
6584 MAP *mm = map->map_stack[mapno];
6585 char *rv;
6587 if (mm == NULL)
6588 continue;
6589 if (!bitset(MF_OPEN, mm->map_mflags) &&
6590 !openmap(mm))
6592 if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
6594 *pstat = EX_UNAVAILABLE;
6595 return NULL;
6597 continue;
6599 *pstat = EX_OK;
6600 rv = mm->map_class->map_lookup(mm, key, args, pstat);
6601 if (rv != NULL)
6602 return rv;
6603 if (*pstat == EX_TEMPFAIL)
6605 if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
6606 return NULL;
6607 tempfail = true;
6609 else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
6610 break;
6612 if (tempfail)
6613 *pstat = EX_TEMPFAIL;
6614 else if (*pstat == EX_OK)
6615 *pstat = EX_NOTFOUND;
6616 return NULL;
6620 ** SEQ_MAP_STORE -- sequenced map store
6623 void
6624 seq_map_store(map, key, val)
6625 MAP *map;
6626 char *key;
6627 char *val;
6629 int mapno;
6631 if (tTd(38, 12))
6632 sm_dprintf("seq_map_store(%s, %s, %s)\n",
6633 map->map_mname, key, val);
6635 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6637 MAP *mm = map->map_stack[mapno];
6639 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
6640 continue;
6642 mm->map_class->map_store(mm, key, val);
6643 return;
6645 syserr("seq_map_store(%s, %s, %s): no writable map",
6646 map->map_mname, key, val);
6649 ** NULL stubs
6652 /* ARGSUSED */
6653 bool
6654 null_map_open(map, mode)
6655 MAP *map;
6656 int mode;
6658 return true;
6661 /* ARGSUSED */
6662 void
6663 null_map_close(map)
6664 MAP *map;
6666 return;
6669 char *
6670 null_map_lookup(map, key, args, pstat)
6671 MAP *map;
6672 char *key;
6673 char **args;
6674 int *pstat;
6676 *pstat = EX_NOTFOUND;
6677 return NULL;
6680 /* ARGSUSED */
6681 void
6682 null_map_store(map, key, val)
6683 MAP *map;
6684 char *key;
6685 char *val;
6687 return;
6691 ** BOGUS stubs
6694 char *
6695 bogus_map_lookup(map, key, args, pstat)
6696 MAP *map;
6697 char *key;
6698 char **args;
6699 int *pstat;
6701 *pstat = EX_TEMPFAIL;
6702 return NULL;
6705 MAPCLASS BogusMapClass =
6707 "bogus-map", NULL, 0,
6708 NULL, bogus_map_lookup, null_map_store,
6709 null_map_open, null_map_close,
6712 ** MACRO modules
6715 char *
6716 macro_map_lookup(map, name, av, statp)
6717 MAP *map;
6718 char *name;
6719 char **av;
6720 int *statp;
6722 int mid;
6724 if (tTd(38, 20))
6725 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
6726 name == NULL ? "NULL" : name);
6728 if (name == NULL ||
6729 *name == '\0' ||
6730 (mid = macid(name)) == 0)
6732 *statp = EX_CONFIG;
6733 return NULL;
6736 if (av[1] == NULL)
6737 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
6738 else
6739 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
6741 *statp = EX_OK;
6742 return "";
6745 ** REGEX modules
6748 #if MAP_REGEX
6750 # include <regex.h>
6752 # define DEFAULT_DELIM CONDELSE
6753 # define END_OF_FIELDS -1
6754 # define ERRBUF_SIZE 80
6755 # define MAX_MATCH 32
6757 # define xnalloc(s) memset(xalloc(s), '\0', s);
6759 struct regex_map
6761 regex_t *regex_pattern_buf; /* xalloc it */
6762 int *regex_subfields; /* move to type MAP */
6763 char *regex_delim; /* move to type MAP */
6766 static int parse_fields __P((char *, int *, int, int));
6767 static char *regex_map_rewrite __P((MAP *, const char*, size_t, char **));
6769 static int
6770 parse_fields(s, ibuf, blen, nr_substrings)
6771 char *s;
6772 int *ibuf; /* array */
6773 int blen; /* number of elements in ibuf */
6774 int nr_substrings; /* number of substrings in the pattern */
6776 register char *cp;
6777 int i = 0;
6778 bool lastone = false;
6780 blen--; /* for terminating END_OF_FIELDS */
6781 cp = s;
6784 for (;; cp++)
6786 if (*cp == ',')
6788 *cp = '\0';
6789 break;
6791 if (*cp == '\0')
6793 lastone = true;
6794 break;
6797 if (i < blen)
6799 int val = atoi(s);
6801 if (val < 0 || val >= nr_substrings)
6803 syserr("field (%d) out of range, only %d substrings in pattern",
6804 val, nr_substrings);
6805 return -1;
6807 ibuf[i++] = val;
6809 else
6811 syserr("too many fields, %d max", blen);
6812 return -1;
6814 s = ++cp;
6815 } while (!lastone);
6816 ibuf[i] = END_OF_FIELDS;
6817 return i;
6820 bool
6821 regex_map_init(map, ap)
6822 MAP *map;
6823 char *ap;
6825 int regerr;
6826 struct regex_map *map_p;
6827 register char *p;
6828 char *sub_param = NULL;
6829 int pflags;
6830 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
6832 if (tTd(38, 2))
6833 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6834 map->map_mname, ap);
6836 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
6837 p = ap;
6838 map_p = (struct regex_map *) xnalloc(sizeof(*map_p));
6839 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
6841 for (;;)
6843 while (isascii(*p) && isspace(*p))
6844 p++;
6845 if (*p != '-')
6846 break;
6847 switch (*++p)
6849 case 'n': /* not */
6850 map->map_mflags |= MF_REGEX_NOT;
6851 break;
6853 case 'f': /* case sensitive */
6854 map->map_mflags |= MF_NOFOLDCASE;
6855 pflags &= ~REG_ICASE;
6856 break;
6858 case 'b': /* basic regular expressions */
6859 pflags &= ~REG_EXTENDED;
6860 break;
6862 case 's': /* substring match () syntax */
6863 sub_param = ++p;
6864 pflags &= ~REG_NOSUB;
6865 break;
6867 case 'd': /* delimiter */
6868 map_p->regex_delim = ++p;
6869 break;
6871 case 'a': /* map append */
6872 map->map_app = ++p;
6873 break;
6875 case 'm': /* matchonly */
6876 map->map_mflags |= MF_MATCHONLY;
6877 break;
6879 case 'q':
6880 map->map_mflags |= MF_KEEPQUOTES;
6881 break;
6883 case 'S':
6884 map->map_spacesub = *++p;
6885 break;
6887 case 'D':
6888 map->map_mflags |= MF_DEFER;
6889 break;
6892 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
6893 p++;
6894 if (*p != '\0')
6895 *p++ = '\0';
6897 if (tTd(38, 3))
6898 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
6900 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
6902 /* Errorhandling */
6903 char errbuf[ERRBUF_SIZE];
6905 (void) regerror(regerr, map_p->regex_pattern_buf,
6906 errbuf, sizeof(errbuf));
6907 syserr("pattern-compile-error: %s", errbuf);
6908 sm_free(map_p->regex_pattern_buf); /* XXX */
6909 sm_free(map_p); /* XXX */
6910 return false;
6913 if (map->map_app != NULL)
6914 map->map_app = newstr(map->map_app);
6915 if (map_p->regex_delim != NULL)
6916 map_p->regex_delim = newstr(map_p->regex_delim);
6917 else
6918 map_p->regex_delim = defdstr;
6920 if (!bitset(REG_NOSUB, pflags))
6922 /* substring matching */
6923 int substrings;
6924 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
6926 substrings = map_p->regex_pattern_buf->re_nsub + 1;
6928 if (tTd(38, 3))
6929 sm_dprintf("regex_map_init: nr of substrings %d\n",
6930 substrings);
6932 if (substrings >= MAX_MATCH)
6934 syserr("too many substrings, %d max", MAX_MATCH);
6935 sm_free(map_p->regex_pattern_buf); /* XXX */
6936 sm_free(map_p); /* XXX */
6937 return false;
6939 if (sub_param != NULL && sub_param[0] != '\0')
6941 /* optional parameter -sfields */
6942 if (parse_fields(sub_param, fields,
6943 MAX_MATCH + 1, substrings) == -1)
6944 return false;
6946 else
6948 int i;
6950 /* set default fields */
6951 for (i = 0; i < substrings; i++)
6952 fields[i] = i;
6953 fields[i] = END_OF_FIELDS;
6955 map_p->regex_subfields = fields;
6956 if (tTd(38, 3))
6958 int *ip;
6960 sm_dprintf("regex_map_init: subfields");
6961 for (ip = fields; *ip != END_OF_FIELDS; ip++)
6962 sm_dprintf(" %d", *ip);
6963 sm_dprintf("\n");
6966 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */
6967 return true;
6970 static char *
6971 regex_map_rewrite(map, s, slen, av)
6972 MAP *map;
6973 const char *s;
6974 size_t slen;
6975 char **av;
6977 if (bitset(MF_MATCHONLY, map->map_mflags))
6978 return map_rewrite(map, av[0], strlen(av[0]), NULL);
6979 else
6980 return map_rewrite(map, s, slen, av);
6983 char *
6984 regex_map_lookup(map, name, av, statp)
6985 MAP *map;
6986 char *name;
6987 char **av;
6988 int *statp;
6990 int reg_res;
6991 struct regex_map *map_p;
6992 regmatch_t pmatch[MAX_MATCH];
6994 if (tTd(38, 20))
6996 char **cpp;
6998 sm_dprintf("regex_map_lookup: key '%s'\n", name);
6999 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7000 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
7003 map_p = (struct regex_map *)(map->map_db1);
7004 reg_res = regexec(map_p->regex_pattern_buf,
7005 name, MAX_MATCH, pmatch, 0);
7007 if (bitset(MF_REGEX_NOT, map->map_mflags))
7009 /* option -n */
7010 if (reg_res == REG_NOMATCH)
7011 return regex_map_rewrite(map, "", (size_t) 0, av);
7012 else
7013 return NULL;
7015 if (reg_res == REG_NOMATCH)
7016 return NULL;
7018 if (map_p->regex_subfields != NULL)
7020 /* option -s */
7021 static char retbuf[MAXNAME];
7022 int fields[MAX_MATCH + 1];
7023 bool first = true;
7024 int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
7025 bool quotemode = false, bslashmode = false;
7026 register char *dp, *sp;
7027 char *endp, *ldp;
7028 int *ip;
7030 dp = retbuf;
7031 ldp = retbuf + sizeof(retbuf) - 1;
7033 if (av[1] != NULL)
7035 if (parse_fields(av[1], fields, MAX_MATCH + 1,
7036 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
7038 *statp = EX_CONFIG;
7039 return NULL;
7041 ip = fields;
7043 else
7044 ip = map_p->regex_subfields;
7046 for ( ; *ip != END_OF_FIELDS; ip++)
7048 if (!first)
7050 for (sp = map_p->regex_delim; *sp; sp++)
7052 if (dp < ldp)
7053 *dp++ = *sp;
7056 else
7057 first = false;
7059 if (*ip >= MAX_MATCH ||
7060 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
7061 continue;
7063 sp = name + pmatch[*ip].rm_so;
7064 endp = name + pmatch[*ip].rm_eo;
7065 for (; endp > sp; sp++)
7067 if (dp < ldp)
7069 if (bslashmode)
7071 *dp++ = *sp;
7072 bslashmode = false;
7074 else if (quotemode && *sp != '"' &&
7075 *sp != '\\')
7077 *dp++ = *sp;
7079 else switch (*dp++ = *sp)
7081 case '\\':
7082 bslashmode = true;
7083 break;
7085 case '(':
7086 cmntcnt++;
7087 break;
7089 case ')':
7090 cmntcnt--;
7091 break;
7093 case '<':
7094 anglecnt++;
7095 break;
7097 case '>':
7098 anglecnt--;
7099 break;
7101 case ' ':
7102 spacecnt++;
7103 break;
7105 case '"':
7106 quotemode = !quotemode;
7107 break;
7112 if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
7113 bslashmode || spacecnt != 0)
7115 sm_syslog(LOG_WARNING, NOQID,
7116 "Warning: regex may cause prescan() failure map=%s lookup=%s",
7117 map->map_mname, name);
7118 return NULL;
7121 *dp = '\0';
7123 return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
7125 return regex_map_rewrite(map, "", (size_t)0, av);
7127 #endif /* MAP_REGEX */
7129 ** NSD modules
7131 #if MAP_NSD
7133 # include <ndbm.h>
7134 # define _DATUM_DEFINED
7135 # include <ns_api.h>
7137 typedef struct ns_map_list
7139 ns_map_t *map; /* XXX ns_ ? */
7140 char *mapname;
7141 struct ns_map_list *next;
7142 } ns_map_list_t;
7144 static ns_map_t *
7145 ns_map_t_find(mapname)
7146 char *mapname;
7148 static ns_map_list_t *ns_maps = NULL;
7149 ns_map_list_t *ns_map;
7151 /* walk the list of maps looking for the correctly named map */
7152 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
7154 if (strcmp(ns_map->mapname, mapname) == 0)
7155 break;
7158 /* if we are looking at a NULL ns_map_list_t, then create a new one */
7159 if (ns_map == NULL)
7161 ns_map = (ns_map_list_t *) xalloc(sizeof(*ns_map));
7162 ns_map->mapname = newstr(mapname);
7163 ns_map->map = (ns_map_t *) xalloc(sizeof(*ns_map->map));
7164 memset(ns_map->map, '\0', sizeof(*ns_map->map));
7165 ns_map->next = ns_maps;
7166 ns_maps = ns_map;
7168 return ns_map->map;
7171 char *
7172 nsd_map_lookup(map, name, av, statp)
7173 MAP *map;
7174 char *name;
7175 char **av;
7176 int *statp;
7178 int buflen, r;
7179 char *p;
7180 ns_map_t *ns_map;
7181 char keybuf[MAXNAME + 1];
7182 char buf[MAXLINE];
7184 if (tTd(38, 20))
7185 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
7187 buflen = strlen(name);
7188 if (buflen > sizeof(keybuf) - 1)
7189 buflen = sizeof(keybuf) - 1; /* XXX simply cut off? */
7190 memmove(keybuf, name, buflen);
7191 keybuf[buflen] = '\0';
7192 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7193 makelower(keybuf);
7195 ns_map = ns_map_t_find(map->map_file);
7196 if (ns_map == NULL)
7198 if (tTd(38, 20))
7199 sm_dprintf("nsd_map_t_find failed\n");
7200 *statp = EX_UNAVAILABLE;
7201 return NULL;
7203 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
7204 buf, sizeof(buf));
7205 if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
7207 *statp = EX_TEMPFAIL;
7208 return NULL;
7210 if (r == NS_BADREQ
7211 # ifdef NS_NOPERM
7212 || r == NS_NOPERM
7213 # endif /* NS_NOPERM */
7216 *statp = EX_CONFIG;
7217 return NULL;
7219 if (r != NS_SUCCESS)
7221 *statp = EX_NOTFOUND;
7222 return NULL;
7225 *statp = EX_OK;
7227 /* Null out trailing \n */
7228 if ((p = strchr(buf, '\n')) != NULL)
7229 *p = '\0';
7231 return map_rewrite(map, buf, strlen(buf), av);
7233 #endif /* MAP_NSD */
7235 char *
7236 arith_map_lookup(map, name, av, statp)
7237 MAP *map;
7238 char *name;
7239 char **av;
7240 int *statp;
7242 long r;
7243 long v[2];
7244 bool res = false;
7245 bool boolres;
7246 static char result[16];
7247 char **cpp;
7249 if (tTd(38, 2))
7251 sm_dprintf("arith_map_lookup: key '%s'\n", name);
7252 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7253 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
7255 r = 0;
7256 boolres = false;
7257 cpp = av;
7258 *statp = EX_OK;
7261 ** read arguments for arith map
7262 ** - no check is made whether they are really numbers
7263 ** - just ignores args after the second
7266 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
7267 v[r++] = strtol(*cpp, NULL, 0);
7269 /* operator and (at least) two operands given? */
7270 if (name != NULL && r == 2)
7272 switch (*name)
7274 case '|':
7275 r = v[0] | v[1];
7276 break;
7278 case '&':
7279 r = v[0] & v[1];
7280 break;
7282 case '%':
7283 if (v[1] == 0)
7284 return NULL;
7285 r = v[0] % v[1];
7286 break;
7287 case '+':
7288 r = v[0] + v[1];
7289 break;
7291 case '-':
7292 r = v[0] - v[1];
7293 break;
7295 case '*':
7296 r = v[0] * v[1];
7297 break;
7299 case '/':
7300 if (v[1] == 0)
7301 return NULL;
7302 r = v[0] / v[1];
7303 break;
7305 case 'l':
7306 res = v[0] < v[1];
7307 boolres = true;
7308 break;
7310 case '=':
7311 res = v[0] == v[1];
7312 boolres = true;
7313 break;
7315 case 'r':
7316 r = v[1] - v[0] + 1;
7317 if (r <= 0)
7318 return NULL;
7319 r = get_random() % r + v[0];
7320 break;
7322 default:
7323 /* XXX */
7324 *statp = EX_CONFIG;
7325 if (LogLevel > 10)
7326 sm_syslog(LOG_WARNING, NOQID,
7327 "arith_map: unknown operator %c",
7328 isprint(*name) ? *name : '?');
7329 return NULL;
7331 if (boolres)
7332 (void) sm_snprintf(result, sizeof(result),
7333 res ? "TRUE" : "FALSE");
7334 else
7335 (void) sm_snprintf(result, sizeof(result), "%ld", r);
7336 return result;
7338 *statp = EX_CONFIG;
7339 return NULL;
7342 #if SOCKETMAP
7344 # if NETINET || NETINET6
7345 # include <arpa/inet.h>
7346 # endif /* NETINET || NETINET6 */
7348 # define socket_map_next map_stack[0]
7351 ** SOCKET_MAP_OPEN -- open socket table
7354 bool
7355 socket_map_open(map, mode)
7356 MAP *map;
7357 int mode;
7359 STAB *s;
7360 int sock = 0;
7361 SOCKADDR_LEN_T addrlen = 0;
7362 int addrno = 0;
7363 int save_errno;
7364 char *p;
7365 char *colon;
7366 char *at;
7367 struct hostent *hp = NULL;
7368 SOCKADDR addr;
7370 if (tTd(38, 2))
7371 sm_dprintf("socket_map_open(%s, %s, %d)\n",
7372 map->map_mname, map->map_file, mode);
7374 mode &= O_ACCMODE;
7376 /* sendmail doesn't have the ability to write to SOCKET (yet) */
7377 if (mode != O_RDONLY)
7379 /* issue a pseudo-error message */
7380 errno = SM_EMAPCANTWRITE;
7381 return false;
7384 if (*map->map_file == '\0')
7386 syserr("socket map \"%s\": empty or missing socket information",
7387 map->map_mname);
7388 return false;
7391 s = socket_map_findconn(map->map_file);
7392 if (s->s_socketmap != NULL)
7394 /* Copy open connection */
7395 map->map_db1 = s->s_socketmap->map_db1;
7397 /* Add this map as head of linked list */
7398 map->socket_map_next = s->s_socketmap;
7399 s->s_socketmap = map;
7401 if (tTd(38, 2))
7402 sm_dprintf("using cached connection\n");
7403 return true;
7406 if (tTd(38, 2))
7407 sm_dprintf("opening new connection\n");
7409 /* following code is ripped from milter.c */
7410 /* XXX It should be put in a library... */
7412 /* protocol:filename or protocol:port@host */
7413 memset(&addr, '\0', sizeof(addr));
7414 p = map->map_file;
7415 colon = strchr(p, ':');
7416 if (colon != NULL)
7418 *colon = '\0';
7420 if (*p == '\0')
7422 # if NETUNIX
7423 /* default to AF_UNIX */
7424 addr.sa.sa_family = AF_UNIX;
7425 # else /* NETUNIX */
7426 # if NETINET
7427 /* default to AF_INET */
7428 addr.sa.sa_family = AF_INET;
7429 # else /* NETINET */
7430 # if NETINET6
7431 /* default to AF_INET6 */
7432 addr.sa.sa_family = AF_INET6;
7433 # else /* NETINET6 */
7434 /* no protocols available */
7435 syserr("socket map \"%s\": no valid socket protocols available",
7436 map->map_mname);
7437 return false;
7438 # endif /* NETINET6 */
7439 # endif /* NETINET */
7440 # endif /* NETUNIX */
7442 # if NETUNIX
7443 else if (sm_strcasecmp(p, "unix") == 0 ||
7444 sm_strcasecmp(p, "local") == 0)
7445 addr.sa.sa_family = AF_UNIX;
7446 # endif /* NETUNIX */
7447 # if NETINET
7448 else if (sm_strcasecmp(p, "inet") == 0)
7449 addr.sa.sa_family = AF_INET;
7450 # endif /* NETINET */
7451 # if NETINET6
7452 else if (sm_strcasecmp(p, "inet6") == 0)
7453 addr.sa.sa_family = AF_INET6;
7454 # endif /* NETINET6 */
7455 else
7457 # ifdef EPROTONOSUPPORT
7458 errno = EPROTONOSUPPORT;
7459 # else /* EPROTONOSUPPORT */
7460 errno = EINVAL;
7461 # endif /* EPROTONOSUPPORT */
7462 syserr("socket map \"%s\": unknown socket type %s",
7463 map->map_mname, p);
7464 return false;
7466 *colon++ = ':';
7468 else
7470 colon = p;
7471 #if NETUNIX
7472 /* default to AF_UNIX */
7473 addr.sa.sa_family = AF_UNIX;
7474 #else /* NETUNIX */
7475 # if NETINET
7476 /* default to AF_INET */
7477 addr.sa.sa_family = AF_INET;
7478 # else /* NETINET */
7479 # if NETINET6
7480 /* default to AF_INET6 */
7481 addr.sa.sa_family = AF_INET6;
7482 # else /* NETINET6 */
7483 syserr("socket map \"%s\": unknown socket type %s",
7484 map->map_mname, p);
7485 return false;
7486 # endif /* NETINET6 */
7487 # endif /* NETINET */
7488 #endif /* NETUNIX */
7491 # if NETUNIX
7492 if (addr.sa.sa_family == AF_UNIX)
7494 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7496 at = colon;
7497 if (strlen(colon) >= sizeof(addr.sunix.sun_path))
7499 syserr("socket map \"%s\": local socket name %s too long",
7500 map->map_mname, colon);
7501 return false;
7503 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7504 S_IRUSR|S_IWUSR, NULL);
7506 if (errno != 0)
7508 /* if not safe, don't create */
7509 syserr("socket map \"%s\": local socket name %s unsafe",
7510 map->map_mname, colon);
7511 return false;
7514 (void) sm_strlcpy(addr.sunix.sun_path, colon,
7515 sizeof(addr.sunix.sun_path));
7516 addrlen = sizeof(struct sockaddr_un);
7518 else
7519 # endif /* NETUNIX */
7520 # if NETINET || NETINET6
7521 if (false
7522 # if NETINET
7523 || addr.sa.sa_family == AF_INET
7524 # endif /* NETINET */
7525 # if NETINET6
7526 || addr.sa.sa_family == AF_INET6
7527 # endif /* NETINET6 */
7530 unsigned short port;
7532 /* Parse port@host */
7533 at = strchr(colon, '@');
7534 if (at == NULL)
7536 syserr("socket map \"%s\": bad address %s (expected port@host)",
7537 map->map_mname, colon);
7538 return false;
7540 *at = '\0';
7541 if (isascii(*colon) && isdigit(*colon))
7542 port = htons((unsigned short) atoi(colon));
7543 else
7545 # ifdef NO_GETSERVBYNAME
7546 syserr("socket map \"%s\": invalid port number %s",
7547 map->map_mname, colon);
7548 return false;
7549 # else /* NO_GETSERVBYNAME */
7550 register struct servent *sp;
7552 sp = getservbyname(colon, "tcp");
7553 if (sp == NULL)
7555 syserr("socket map \"%s\": unknown port name %s",
7556 map->map_mname, colon);
7557 return false;
7559 port = sp->s_port;
7560 # endif /* NO_GETSERVBYNAME */
7562 *at++ = '@';
7563 if (*at == '[')
7565 char *end;
7567 end = strchr(at, ']');
7568 if (end != NULL)
7570 bool found = false;
7571 # if NETINET
7572 unsigned long hid = INADDR_NONE;
7573 # endif /* NETINET */
7574 # if NETINET6
7575 struct sockaddr_in6 hid6;
7576 # endif /* NETINET6 */
7578 *end = '\0';
7579 # if NETINET
7580 if (addr.sa.sa_family == AF_INET &&
7581 (hid = inet_addr(&at[1])) != INADDR_NONE)
7583 addr.sin.sin_addr.s_addr = hid;
7584 addr.sin.sin_port = port;
7585 found = true;
7587 # endif /* NETINET */
7588 # if NETINET6
7589 (void) memset(&hid6, '\0', sizeof(hid6));
7590 if (addr.sa.sa_family == AF_INET6 &&
7591 anynet_pton(AF_INET6, &at[1],
7592 &hid6.sin6_addr) == 1)
7594 addr.sin6.sin6_addr = hid6.sin6_addr;
7595 addr.sin6.sin6_port = port;
7596 found = true;
7598 # endif /* NETINET6 */
7599 *end = ']';
7600 if (!found)
7602 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7603 map->map_mname, at);
7604 return false;
7607 else
7609 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7610 map->map_mname, at);
7611 return false;
7614 else
7616 hp = sm_gethostbyname(at, addr.sa.sa_family);
7617 if (hp == NULL)
7619 syserr("socket map \"%s\": Unknown host name %s",
7620 map->map_mname, at);
7621 return false;
7623 addr.sa.sa_family = hp->h_addrtype;
7624 switch (hp->h_addrtype)
7626 # if NETINET
7627 case AF_INET:
7628 memmove(&addr.sin.sin_addr,
7629 hp->h_addr, INADDRSZ);
7630 addr.sin.sin_port = port;
7631 addrlen = sizeof(struct sockaddr_in);
7632 addrno = 1;
7633 break;
7634 # endif /* NETINET */
7636 # if NETINET6
7637 case AF_INET6:
7638 memmove(&addr.sin6.sin6_addr,
7639 hp->h_addr, IN6ADDRSZ);
7640 addr.sin6.sin6_port = port;
7641 addrlen = sizeof(struct sockaddr_in6);
7642 addrno = 1;
7643 break;
7644 # endif /* NETINET6 */
7646 default:
7647 syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7648 map->map_mname, at, hp->h_addrtype);
7649 # if NETINET6
7650 freehostent(hp);
7651 # endif /* NETINET6 */
7652 return false;
7656 else
7657 # endif /* NETINET || NETINET6 */
7659 syserr("socket map \"%s\": unknown socket protocol",
7660 map->map_mname);
7661 return false;
7664 /* nope, actually connecting */
7665 for (;;)
7667 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
7668 if (sock < 0)
7670 save_errno = errno;
7671 if (tTd(38, 5))
7672 sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7673 map->map_mname,
7674 sm_errstring(save_errno));
7675 # if NETINET6
7676 if (hp != NULL)
7677 freehostent(hp);
7678 # endif /* NETINET6 */
7679 return false;
7682 if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
7683 break;
7685 /* couldn't connect.... try next address */
7686 save_errno = errno;
7687 p = CurHostName;
7688 CurHostName = at;
7689 if (tTd(38, 5))
7690 sm_dprintf("socket_open (%s): open %s failed: %s\n",
7691 map->map_mname, at, sm_errstring(save_errno));
7692 CurHostName = p;
7693 (void) close(sock);
7695 /* try next address */
7696 if (hp != NULL && hp->h_addr_list[addrno] != NULL)
7698 switch (addr.sa.sa_family)
7700 # if NETINET
7701 case AF_INET:
7702 memmove(&addr.sin.sin_addr,
7703 hp->h_addr_list[addrno++],
7704 INADDRSZ);
7705 break;
7706 # endif /* NETINET */
7708 # if NETINET6
7709 case AF_INET6:
7710 memmove(&addr.sin6.sin6_addr,
7711 hp->h_addr_list[addrno++],
7712 IN6ADDRSZ);
7713 break;
7714 # endif /* NETINET6 */
7716 default:
7717 if (tTd(38, 5))
7718 sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7719 map->map_mname, at,
7720 hp->h_addrtype);
7721 # if NETINET6
7722 freehostent(hp);
7723 # endif /* NETINET6 */
7724 return false;
7726 continue;
7728 p = CurHostName;
7729 CurHostName = at;
7730 if (tTd(38, 5))
7731 sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7732 map->map_mname, sm_errstring(save_errno));
7733 CurHostName = p;
7734 # if NETINET6
7735 if (hp != NULL)
7736 freehostent(hp);
7737 # endif /* NETINET6 */
7738 return false;
7740 # if NETINET6
7741 if (hp != NULL)
7743 freehostent(hp);
7744 hp = NULL;
7746 # endif /* NETINET6 */
7747 if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
7748 SM_TIME_DEFAULT,
7749 (void *) &sock,
7750 SM_IO_RDWR,
7751 NULL)) == NULL)
7753 close(sock);
7754 if (tTd(38, 2))
7755 sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7756 map->map_mname, sm_errstring(errno));
7757 return false;
7760 /* Save connection for reuse */
7761 s->s_socketmap = map;
7762 return true;
7766 ** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7768 ** Cache SOCKET connections based on the connection specifier
7769 ** and PID so we don't have multiple connections open to
7770 ** the same server for different maps. Need a separate connection
7771 ** per PID since a parent process may close the map before the
7772 ** child is done with it.
7774 ** Parameters:
7775 ** conn -- SOCKET map connection specifier
7777 ** Returns:
7778 ** Symbol table entry for the SOCKET connection.
7781 static STAB *
7782 socket_map_findconn(conn)
7783 const char *conn;
7785 char *nbuf;
7786 STAB *SM_NONVOLATILE s = NULL;
7788 nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
7789 SM_TRY
7790 s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
7791 SM_FINALLY
7792 sm_free(nbuf);
7793 SM_END_TRY
7794 return s;
7798 ** SOCKET_MAP_CLOSE -- close the socket
7801 void
7802 socket_map_close(map)
7803 MAP *map;
7805 STAB *s;
7806 MAP *smap;
7808 if (tTd(38, 20))
7809 sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
7810 (long) CurrentPid);
7812 /* Check if already closed */
7813 if (map->map_db1 == NULL)
7815 if (tTd(38, 20))
7816 sm_dprintf("socket_map_close(%s) already closed\n",
7817 map->map_file);
7818 return;
7820 sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
7822 /* Mark all the maps that share the connection as closed */
7823 s = socket_map_findconn(map->map_file);
7824 smap = s->s_socketmap;
7825 while (smap != NULL)
7827 MAP *next;
7829 if (tTd(38, 2) && smap != map)
7830 sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7831 map->map_mname, smap->map_mname);
7833 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
7834 smap->map_db1 = NULL;
7835 next = smap->socket_map_next;
7836 smap->socket_map_next = NULL;
7837 smap = next;
7839 s->s_socketmap = NULL;
7843 ** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7846 char *
7847 socket_map_lookup(map, name, av, statp)
7848 MAP *map;
7849 char *name;
7850 char **av;
7851 int *statp;
7853 unsigned int nettolen, replylen, recvlen;
7854 char *replybuf, *rval, *value, *status, *key;
7855 SM_FILE_T *f;
7856 char keybuf[MAXNAME + 1];
7858 replybuf = NULL;
7859 rval = NULL;
7860 f = (SM_FILE_T *)map->map_db1;
7861 if (tTd(38, 20))
7862 sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7863 map->map_mname, name, map->map_file);
7865 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7867 nettolen = strlen(name);
7868 if (nettolen > sizeof(keybuf) - 1)
7869 nettolen = sizeof(keybuf) - 1;
7870 memmove(keybuf, name, nettolen);
7871 keybuf[nettolen] = '\0';
7872 makelower(keybuf);
7873 key = keybuf;
7875 else
7876 key = name;
7878 nettolen = strlen(map->map_mname) + 1 + strlen(key);
7879 SM_ASSERT(nettolen > strlen(map->map_mname));
7880 SM_ASSERT(nettolen > strlen(key));
7881 if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
7882 nettolen, map->map_mname, key) == SM_IO_EOF) ||
7883 (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
7884 (sm_io_error(f)))
7886 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
7887 map->map_mname);
7888 *statp = EX_TEMPFAIL;
7889 goto errcl;
7892 if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
7894 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
7895 map->map_mname);
7896 *statp = EX_TEMPFAIL;
7897 goto errcl;
7899 if (replylen > SOCKETMAP_MAXL)
7901 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
7902 map->map_mname, replylen);
7903 *statp = EX_TEMPFAIL;
7904 goto errcl;
7906 if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
7908 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
7909 map->map_mname);
7910 *statp = EX_TEMPFAIL;
7911 goto error;
7914 replybuf = (char *) sm_malloc(replylen + 1);
7915 if (replybuf == NULL)
7917 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
7918 map->map_mname, replylen + 1);
7919 *statp = EX_OSERR;
7920 goto error;
7923 recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
7924 if (recvlen < replylen)
7926 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
7927 map->map_mname, recvlen, replylen);
7928 *statp = EX_TEMPFAIL;
7929 goto errcl;
7931 if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
7933 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
7934 map->map_mname);
7935 *statp = EX_TEMPFAIL;
7936 goto errcl;
7938 status = replybuf;
7939 replybuf[recvlen] = '\0';
7940 value = strchr(replybuf, ' ');
7941 if (value != NULL)
7943 *value = '\0';
7944 value++;
7946 if (strcmp(status, "OK") == 0)
7948 *statp = EX_OK;
7950 /* collect the return value */
7951 if (bitset(MF_MATCHONLY, map->map_mflags))
7952 rval = map_rewrite(map, key, strlen(key), NULL);
7953 else
7954 rval = map_rewrite(map, value, strlen(value), av);
7956 else if (strcmp(status, "NOTFOUND") == 0)
7958 *statp = EX_NOTFOUND;
7959 if (tTd(38, 20))
7960 sm_dprintf("socket_map_lookup(%s): %s not found\n",
7961 map->map_mname, key);
7963 else
7965 if (tTd(38, 5))
7966 sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
7967 map->map_mname, key, status,
7968 value ? value : "");
7969 if ((strcmp(status, "TEMP") == 0) ||
7970 (strcmp(status, "TIMEOUT") == 0))
7971 *statp = EX_TEMPFAIL;
7972 else if(strcmp(status, "PERM") == 0)
7973 *statp = EX_UNAVAILABLE;
7974 else
7975 *statp = EX_PROTOCOL;
7978 if (replybuf != NULL)
7979 sm_free(replybuf);
7980 return rval;
7982 errcl:
7983 socket_map_close(map);
7984 error:
7985 if (replybuf != NULL)
7986 sm_free(replybuf);
7987 return rval;
7989 #endif /* SOCKETMAP */