Update zoneinfo database.
[dragonfly/netmp.git] / contrib / sendmail / src / map.c
blob83623276aca5b2a36a04bfcae6b0d77d72db228c
1 /*
2 * Copyright (c) 1998-2003 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.645.2.10 2003/07/24 18:24:17 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 #if NEWDB
43 # if DB_VERSION_MAJOR < 2
44 static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
45 # endif /* DB_VERSION_MAJOR < 2 */
46 # if DB_VERSION_MAJOR == 2
47 static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
48 # endif /* DB_VERSION_MAJOR == 2 */
49 # if DB_VERSION_MAJOR > 2
50 static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **));
51 # endif /* DB_VERSION_MAJOR > 2 */
52 #endif /* NEWDB */
53 static bool extract_canonname __P((char *, char *, char *, char[], int));
54 static void map_close __P((STAB *, int));
55 static void map_init __P((STAB *, int));
56 #ifdef LDAPMAP
57 static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *));
58 #endif /* LDAPMAP */
59 #if NISPLUS
60 static bool nisplus_getcanonname __P((char *, int, int *));
61 #endif /* NISPLUS */
62 #if NIS
63 static bool nis_getcanonname __P((char *, int, int *));
64 #endif /* NIS */
65 #if NETINFO
66 static bool ni_getcanonname __P((char *, int, int *));
67 #endif /* NETINFO */
68 static bool text_getcanonname __P((char *, int, int *));
70 /* default error message for trying to open a map in write mode */
71 #ifdef ENOSYS
72 # define SM_EMAPCANTWRITE ENOSYS
73 #else /* ENOSYS */
74 # ifdef EFTYPE
75 # define SM_EMAPCANTWRITE EFTYPE
76 # else /* EFTYPE */
77 # define SM_EMAPCANTWRITE ENXIO
78 # endif /* EFTYPE */
79 #endif /* ENOSYS */
82 ** MAP.C -- implementations for various map classes.
84 ** Each map class implements a series of functions:
86 ** bool map_parse(MAP *map, char *args)
87 ** Parse the arguments from the config file. Return true
88 ** if they were ok, false otherwise. Fill in map with the
89 ** values.
91 ** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
92 ** Look up the key in the given map. If found, do any
93 ** rewriting the map wants (including "args" if desired)
94 ** and return the value. Set *pstat to the appropriate status
95 ** on error and return NULL. Args will be NULL if called
96 ** from the alias routines, although this should probably
97 ** not be relied upon. It is suggested you call map_rewrite
98 ** to return the results -- it takes care of null termination
99 ** and uses a dynamically expanded buffer as needed.
101 ** void map_store(MAP *map, char *key, char *value)
102 ** Store the key:value pair in the map.
104 ** bool map_open(MAP *map, int mode)
105 ** Open the map for the indicated mode. Mode should
106 ** be either O_RDONLY or O_RDWR. Return true if it
107 ** was opened successfully, false otherwise. If the open
108 ** failed and the MF_OPTIONAL flag is not set, it should
109 ** also print an error. If the MF_ALIAS bit is set
110 ** and this map class understands the @:@ convention, it
111 ** should call aliaswait() before returning.
113 ** void map_close(MAP *map)
114 ** Close the map.
116 ** This file also includes the implementation for getcanonname.
117 ** It is currently implemented in a pretty ad-hoc manner; it ought
118 ** to be more properly integrated into the map structure.
121 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
122 # define LOCK_ON_OPEN 1 /* we can open/create a locked file */
123 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
124 # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
125 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
128 ** MAP_PARSEARGS -- parse config line arguments for database lookup
130 ** This is a generic version of the map_parse method.
132 ** Parameters:
133 ** map -- the map being initialized.
134 ** ap -- a pointer to the args on the config line.
136 ** Returns:
137 ** true -- if everything parsed OK.
138 ** false -- otherwise.
140 ** Side Effects:
141 ** null terminates the filename; stores it in map
144 bool
145 map_parseargs(map, ap)
146 MAP *map;
147 char *ap;
149 register char *p = ap;
152 ** There is no check whether there is really an argument,
153 ** but that's not important enough to warrant extra code.
156 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
157 map->map_spacesub = SpaceSub; /* default value */
158 for (;;)
160 while (isascii(*p) && isspace(*p))
161 p++;
162 if (*p != '-')
163 break;
164 switch (*++p)
166 case 'N':
167 map->map_mflags |= MF_INCLNULL;
168 map->map_mflags &= ~MF_TRY0NULL;
169 break;
171 case 'O':
172 map->map_mflags &= ~MF_TRY1NULL;
173 break;
175 case 'o':
176 map->map_mflags |= MF_OPTIONAL;
177 break;
179 case 'f':
180 map->map_mflags |= MF_NOFOLDCASE;
181 break;
183 case 'm':
184 map->map_mflags |= MF_MATCHONLY;
185 break;
187 case 'A':
188 map->map_mflags |= MF_APPEND;
189 break;
191 case 'q':
192 map->map_mflags |= MF_KEEPQUOTES;
193 break;
195 case 'a':
196 map->map_app = ++p;
197 break;
199 case 'T':
200 map->map_tapp = ++p;
201 break;
203 case 'k':
204 while (isascii(*++p) && isspace(*p))
205 continue;
206 map->map_keycolnm = p;
207 break;
209 case 'v':
210 while (isascii(*++p) && isspace(*p))
211 continue;
212 map->map_valcolnm = p;
213 break;
215 case 'z':
216 if (*++p != '\\')
217 map->map_coldelim = *p;
218 else
220 switch (*++p)
222 case 'n':
223 map->map_coldelim = '\n';
224 break;
226 case 't':
227 map->map_coldelim = '\t';
228 break;
230 default:
231 map->map_coldelim = '\\';
234 break;
236 case 't':
237 map->map_mflags |= MF_NODEFER;
238 break;
241 case 'S':
242 map->map_spacesub = *++p;
243 break;
245 case 'D':
246 map->map_mflags |= MF_DEFER;
247 break;
249 default:
250 syserr("Illegal option %c map %s", *p, map->map_mname);
251 break;
253 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
254 p++;
255 if (*p != '\0')
256 *p++ = '\0';
258 if (map->map_app != NULL)
259 map->map_app = newstr(map->map_app);
260 if (map->map_tapp != NULL)
261 map->map_tapp = newstr(map->map_tapp);
262 if (map->map_keycolnm != NULL)
263 map->map_keycolnm = newstr(map->map_keycolnm);
264 if (map->map_valcolnm != NULL)
265 map->map_valcolnm = newstr(map->map_valcolnm);
267 if (*p != '\0')
269 map->map_file = p;
270 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
271 p++;
272 if (*p != '\0')
273 *p++ = '\0';
274 map->map_file = newstr(map->map_file);
277 while (*p != '\0' && isascii(*p) && isspace(*p))
278 p++;
279 if (*p != '\0')
280 map->map_rebuild = newstr(p);
282 if (map->map_file == NULL &&
283 !bitset(MCF_OPTFILE, map->map_class->map_cflags))
285 syserr("No file name for %s map %s",
286 map->map_class->map_cname, map->map_mname);
287 return false;
289 return true;
292 ** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
294 ** It also adds the map_app string. It can be used as a utility
295 ** in the map_lookup method.
297 ** Parameters:
298 ** map -- the map that causes this.
299 ** s -- the string to rewrite, NOT necessarily null terminated.
300 ** slen -- the length of s.
301 ** av -- arguments to interpolate into buf.
303 ** Returns:
304 ** Pointer to rewritten result. This is static data that
305 ** should be copied if it is to be saved!
308 char *
309 map_rewrite(map, s, slen, av)
310 register MAP *map;
311 register const char *s;
312 size_t slen;
313 char **av;
315 register char *bp;
316 register char c;
317 char **avp;
318 register char *ap;
319 size_t l;
320 size_t len;
321 static size_t buflen = 0;
322 static char *buf = NULL;
324 if (tTd(39, 1))
326 sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
327 if (av == NULL)
328 sm_dprintf(" (nullv)");
329 else
331 for (avp = av; *avp != NULL; avp++)
332 sm_dprintf("\n\t%s", *avp);
334 sm_dprintf("\n");
337 /* count expected size of output (can safely overestimate) */
338 l = len = slen;
339 if (av != NULL)
341 const char *sp = s;
343 while (l-- > 0 && (c = *sp++) != '\0')
345 if (c != '%')
346 continue;
347 if (l-- <= 0)
348 break;
349 c = *sp++;
350 if (!(isascii(c) && isdigit(c)))
351 continue;
352 for (avp = av; --c >= '0' && *avp != NULL; avp++)
353 continue;
354 if (*avp == NULL)
355 continue;
356 len += strlen(*avp);
359 if (map->map_app != NULL)
360 len += strlen(map->map_app);
361 if (buflen < ++len)
363 /* need to malloc additional space */
364 buflen = len;
365 if (buf != NULL)
366 sm_free(buf);
367 buf = sm_pmalloc_x(buflen);
370 bp = buf;
371 if (av == NULL)
373 memmove(bp, s, slen);
374 bp += slen;
376 /* assert(len > slen); */
377 len -= slen;
379 else
381 while (slen-- > 0 && (c = *s++) != '\0')
383 if (c != '%')
385 pushc:
386 if (len-- <= 1)
387 break;
388 *bp++ = c;
389 continue;
391 if (slen-- <= 0 || (c = *s++) == '\0')
392 c = '%';
393 if (c == '%')
394 goto pushc;
395 if (!(isascii(c) && isdigit(c)))
397 if (len-- <= 1)
398 break;
399 *bp++ = '%';
400 goto pushc;
402 for (avp = av; --c >= '0' && *avp != NULL; avp++)
403 continue;
404 if (*avp == NULL)
405 continue;
407 /* transliterate argument into output string */
408 for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
409 *bp++ = c;
412 if (map->map_app != NULL && len > 0)
413 (void) sm_strlcpy(bp, map->map_app, len);
414 else
415 *bp = '\0';
416 if (tTd(39, 1))
417 sm_dprintf("map_rewrite => %s\n", buf);
418 return buf;
421 ** INITMAPS -- rebuild alias maps
423 ** Parameters:
424 ** none.
426 ** Returns:
427 ** none.
430 void
431 initmaps()
433 #if XDEBUG
434 checkfd012("entering initmaps");
435 #endif /* XDEBUG */
436 stabapply(map_init, 0);
437 #if XDEBUG
438 checkfd012("exiting initmaps");
439 #endif /* XDEBUG */
442 ** MAP_INIT -- rebuild a map
444 ** Parameters:
445 ** s -- STAB entry: if map: try to rebuild
446 ** unused -- unused variable
448 ** Returns:
449 ** none.
451 ** Side Effects:
452 ** will close already open rebuildable map.
455 /* ARGSUSED1 */
456 static void
457 map_init(s, unused)
458 register STAB *s;
459 int unused;
461 register MAP *map;
463 /* has to be a map */
464 if (s->s_symtype != ST_MAP)
465 return;
467 map = &s->s_map;
468 if (!bitset(MF_VALID, map->map_mflags))
469 return;
471 if (tTd(38, 2))
472 sm_dprintf("map_init(%s:%s, %s)\n",
473 map->map_class->map_cname == NULL ? "NULL" :
474 map->map_class->map_cname,
475 map->map_mname == NULL ? "NULL" : map->map_mname,
476 map->map_file == NULL ? "NULL" : map->map_file);
478 if (!bitset(MF_ALIAS, map->map_mflags) ||
479 !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
481 if (tTd(38, 3))
482 sm_dprintf("\tnot rebuildable\n");
483 return;
486 /* if already open, close it (for nested open) */
487 if (bitset(MF_OPEN, map->map_mflags))
489 map->map_mflags |= MF_CLOSING;
490 map->map_class->map_close(map);
491 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
494 (void) rebuildaliases(map, false);
495 return;
498 ** OPENMAP -- open a map
500 ** Parameters:
501 ** map -- map to open (it must not be open).
503 ** Returns:
504 ** whether open succeeded.
507 bool
508 openmap(map)
509 MAP *map;
511 bool restore = false;
512 bool savehold = HoldErrs;
513 bool savequick = QuickAbort;
514 int saveerrors = Errors;
516 if (!bitset(MF_VALID, map->map_mflags))
517 return false;
519 /* better safe than sorry... */
520 if (bitset(MF_OPEN, map->map_mflags))
521 return true;
523 /* Don't send a map open error out via SMTP */
524 if ((OnlyOneError || QuickAbort) &&
525 (OpMode == MD_SMTP || OpMode == MD_DAEMON))
527 restore = true;
528 HoldErrs = true;
529 QuickAbort = false;
532 errno = 0;
533 if (map->map_class->map_open(map, O_RDONLY))
535 if (tTd(38, 4))
536 sm_dprintf("openmap()\t%s:%s %s: valid\n",
537 map->map_class->map_cname == NULL ? "NULL" :
538 map->map_class->map_cname,
539 map->map_mname == NULL ? "NULL" :
540 map->map_mname,
541 map->map_file == NULL ? "NULL" :
542 map->map_file);
543 map->map_mflags |= MF_OPEN;
544 map->map_pid = CurrentPid;
546 else
548 if (tTd(38, 4))
549 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
550 map->map_class->map_cname == NULL ? "NULL" :
551 map->map_class->map_cname,
552 map->map_mname == NULL ? "NULL" :
553 map->map_mname,
554 map->map_file == NULL ? "NULL" :
555 map->map_file,
556 errno == 0 ? "" : ": ",
557 errno == 0 ? "" : sm_errstring(errno));
558 if (!bitset(MF_OPTIONAL, map->map_mflags))
560 extern MAPCLASS BogusMapClass;
562 map->map_orgclass = map->map_class;
563 map->map_class = &BogusMapClass;
564 map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
565 map->map_pid = CurrentPid;
567 else
569 /* don't try again */
570 map->map_mflags &= ~MF_VALID;
574 if (restore)
576 Errors = saveerrors;
577 HoldErrs = savehold;
578 QuickAbort = savequick;
581 return bitset(MF_OPEN, map->map_mflags);
584 ** CLOSEMAPS -- close all open maps opened by the current pid.
586 ** Parameters:
587 ** bogus -- only close bogus maps.
589 ** Returns:
590 ** none.
593 void
594 closemaps(bogus)
595 bool bogus;
597 stabapply(map_close, bogus);
600 ** MAP_CLOSE -- close a map opened by the current pid.
602 ** Parameters:
603 ** s -- STAB entry: if map: try to close
604 ** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
606 ** Returns:
607 ** none.
610 /* ARGSUSED1 */
611 static void
612 map_close(s, bogus)
613 register STAB *s;
614 int bogus; /* int because of stabapply(), used as bool */
616 MAP *map;
617 extern MAPCLASS BogusMapClass;
619 if (s->s_symtype != ST_MAP)
620 return;
622 map = &s->s_map;
625 ** close the map iff:
626 ** it is valid and open and opened by this process
627 ** and (!bogus or it's a bogus map or it is not persistent)
628 ** negate this: return iff
629 ** it is not valid or it is not open or not opened by this process
630 ** or (bogus and it's not a bogus map and it's not not-persistent)
633 if (!bitset(MF_VALID, map->map_mflags) ||
634 !bitset(MF_OPEN, map->map_mflags) ||
635 bitset(MF_CLOSING, map->map_mflags) ||
636 map->map_pid != CurrentPid ||
637 (bogus && map->map_class != &BogusMapClass &&
638 !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
639 return;
641 if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
642 map->map_orgclass != &BogusMapClass)
643 map->map_class = map->map_orgclass;
644 if (tTd(38, 5))
645 sm_dprintf("closemaps: closing %s (%s)\n",
646 map->map_mname == NULL ? "NULL" : map->map_mname,
647 map->map_file == NULL ? "NULL" : map->map_file);
649 if (!bitset(MF_OPENBOGUS, map->map_mflags))
651 map->map_mflags |= MF_CLOSING;
652 map->map_class->map_close(map);
654 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
657 ** GETCANONNAME -- look up name using service switch
659 ** Parameters:
660 ** host -- the host name to look up.
661 ** hbsize -- the size of the host buffer.
662 ** trymx -- if set, try MX records.
663 ** pttl -- pointer to return TTL (can be NULL).
665 ** Returns:
666 ** true -- if the host was found.
667 ** false -- otherwise.
670 bool
671 getcanonname(host, hbsize, trymx, pttl)
672 char *host;
673 int hbsize;
674 bool trymx;
675 int *pttl;
677 int nmaps;
678 int mapno;
679 bool found = false;
680 bool got_tempfail = false;
681 auto int status;
682 char *maptype[MAXMAPSTACK];
683 short mapreturn[MAXMAPACTIONS];
685 nmaps = switch_map_find("hosts", maptype, mapreturn);
686 if (pttl != 0)
687 *pttl = SM_DEFAULT_TTL;
688 for (mapno = 0; mapno < nmaps; mapno++)
690 int i;
692 if (tTd(38, 20))
693 sm_dprintf("getcanonname(%s), trying %s\n",
694 host, maptype[mapno]);
695 if (strcmp("files", maptype[mapno]) == 0)
697 found = text_getcanonname(host, hbsize, &status);
699 #if NIS
700 else if (strcmp("nis", maptype[mapno]) == 0)
702 found = nis_getcanonname(host, hbsize, &status);
704 #endif /* NIS */
705 #if NISPLUS
706 else if (strcmp("nisplus", maptype[mapno]) == 0)
708 found = nisplus_getcanonname(host, hbsize, &status);
710 #endif /* NISPLUS */
711 #if NAMED_BIND
712 else if (strcmp("dns", maptype[mapno]) == 0)
714 found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
716 #endif /* NAMED_BIND */
717 #if NETINFO
718 else if (strcmp("netinfo", maptype[mapno]) == 0)
720 found = ni_getcanonname(host, hbsize, &status);
722 #endif /* NETINFO */
723 else
725 found = false;
726 status = EX_UNAVAILABLE;
730 ** Heuristic: if $m is not set, we are running during system
731 ** startup. In this case, when a name is apparently found
732 ** but has no dot, treat is as not found. This avoids
733 ** problems if /etc/hosts has no FQDN but is listed first
734 ** in the service switch.
737 if (found &&
738 (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
739 break;
741 /* see if we should continue */
742 if (status == EX_TEMPFAIL)
744 i = MA_TRYAGAIN;
745 got_tempfail = true;
747 else if (status == EX_NOTFOUND)
748 i = MA_NOTFOUND;
749 else
750 i = MA_UNAVAIL;
751 if (bitset(1 << mapno, mapreturn[i]))
752 break;
755 if (found)
757 char *d;
759 if (tTd(38, 20))
760 sm_dprintf("getcanonname(%s), found\n", host);
763 ** If returned name is still single token, compensate
764 ** by tagging on $m. This is because some sites set
765 ** up their DNS or NIS databases wrong.
768 if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
770 d = macvalue('m', CurEnv);
771 if (d != NULL &&
772 hbsize > (int) (strlen(host) + strlen(d) + 1))
774 if (host[strlen(host) - 1] != '.')
775 (void) sm_strlcat2(host, ".", d,
776 hbsize);
777 else
778 (void) sm_strlcat(host, d, hbsize);
780 else
781 return false;
783 return true;
786 if (tTd(38, 20))
787 sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
788 status);
790 if (got_tempfail)
791 SM_SET_H_ERRNO(TRY_AGAIN);
792 else
793 SM_SET_H_ERRNO(HOST_NOT_FOUND);
795 return false;
798 ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
800 ** Parameters:
801 ** name -- the name against which to match.
802 ** dot -- where to reinsert '.' to get FQDN
803 ** line -- the /etc/hosts line.
804 ** cbuf -- the location to store the result.
805 ** cbuflen -- the size of cbuf.
807 ** Returns:
808 ** true -- if the line matched the desired name.
809 ** false -- otherwise.
812 static bool
813 extract_canonname(name, dot, line, cbuf, cbuflen)
814 char *name;
815 char *dot;
816 char *line;
817 char cbuf[];
818 int cbuflen;
820 int i;
821 char *p;
822 bool found = false;
824 cbuf[0] = '\0';
825 if (line[0] == '#')
826 return false;
828 for (i = 1; ; i++)
830 char nbuf[MAXNAME + 1];
832 p = get_column(line, i, '\0', nbuf, sizeof nbuf);
833 if (p == NULL)
834 break;
835 if (*p == '\0')
836 continue;
837 if (cbuf[0] == '\0' ||
838 (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
840 (void) sm_strlcpy(cbuf, p, cbuflen);
842 if (sm_strcasecmp(name, p) == 0)
843 found = true;
844 else if (dot != NULL)
846 /* try looking for the FQDN as well */
847 *dot = '.';
848 if (sm_strcasecmp(name, p) == 0)
849 found = true;
850 *dot = '\0';
853 if (found && strchr(cbuf, '.') == NULL)
855 /* try to add a domain on the end of the name */
856 char *domain = macvalue('m', CurEnv);
858 if (domain != NULL &&
859 strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
861 p = &cbuf[i];
862 *p++ = '.';
863 (void) sm_strlcpy(p, domain, cbuflen - i - 1);
866 return found;
870 ** DNS modules
873 #if NAMED_BIND
874 # if DNSMAP
876 # include "sm_resolve.h"
877 # if NETINET || NETINET6
878 # include <arpa/inet.h>
879 # endif /* NETINET || NETINET6 */
882 ** DNS_MAP_OPEN -- stub to check proper value for dns map type
885 bool
886 dns_map_open(map, mode)
887 MAP *map;
888 int mode;
890 if (tTd(38,2))
891 sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
893 mode &= O_ACCMODE;
894 if (mode != O_RDONLY)
896 /* issue a pseudo-error message */
897 errno = SM_EMAPCANTWRITE;
898 return false;
900 return true;
904 ** DNS_MAP_PARSEARGS -- parse dns map definition args.
906 ** Parameters:
907 ** map -- pointer to MAP
908 ** args -- pointer to the args on the config line.
910 ** Returns:
911 ** true -- if everything parsed OK.
912 ** false -- otherwise.
915 # if _FFR_DNSMAP_MULTILIMIT
916 # if !_FFR_DNSMAP_MULTI
917 ERROR README: You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT
918 # endif /* ! _FFR_DNSMAP_MULTI */
919 # endif /* _FFR_DNSMAP_MULTILIMIT */
921 # if _FFR_DNSMAP_MULTI
922 # if _FFR_DNSMAP_MULTILIMIT
923 # define map_sizelimit map_lockfd /* overload field */
924 # endif /* _FFR_DNSMAP_MULTILIMIT */
925 # endif /* _FFR_DNSMAP_MULTI */
927 struct dns_map
929 int dns_m_type;
932 bool
933 dns_map_parseargs(map,args)
934 MAP *map;
935 char *args;
937 register char *p = args;
938 struct dns_map *map_p;
940 map_p = (struct dns_map *) xalloc(sizeof *map_p);
941 map_p->dns_m_type = -1;
942 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
944 for (;;)
946 while (isascii(*p) && isspace(*p))
947 p++;
948 if (*p != '-')
949 break;
950 switch (*++p)
952 case 'N':
953 map->map_mflags |= MF_INCLNULL;
954 map->map_mflags &= ~MF_TRY0NULL;
955 break;
957 case 'O':
958 map->map_mflags &= ~MF_TRY1NULL;
959 break;
961 case 'o':
962 map->map_mflags |= MF_OPTIONAL;
963 break;
965 case 'f':
966 map->map_mflags |= MF_NOFOLDCASE;
967 break;
969 case 'm':
970 map->map_mflags |= MF_MATCHONLY;
971 break;
973 case 'A':
974 map->map_mflags |= MF_APPEND;
975 break;
977 case 'q':
978 map->map_mflags |= MF_KEEPQUOTES;
979 break;
981 case 't':
982 map->map_mflags |= MF_NODEFER;
983 break;
985 case 'a':
986 map->map_app = ++p;
987 break;
989 case 'T':
990 map->map_tapp = ++p;
991 break;
993 case 'd':
995 char *h;
997 ++p;
998 h = strchr(p, ' ');
999 if (h != NULL)
1000 *h = '\0';
1001 map->map_timeout = convtime(p, 's');
1002 if (h != NULL)
1003 *h = ' ';
1005 break;
1007 case 'r':
1008 while (isascii(*++p) && isspace(*p))
1009 continue;
1010 map->map_retry = atoi(p);
1011 break;
1013 # if _FFR_DNSMAP_MULTI
1014 case 'z':
1015 if (*++p != '\\')
1016 map->map_coldelim = *p;
1017 else
1019 switch (*++p)
1021 case 'n':
1022 map->map_coldelim = '\n';
1023 break;
1025 case 't':
1026 map->map_coldelim = '\t';
1027 break;
1029 default:
1030 map->map_coldelim = '\\';
1033 break;
1035 # if _FFR_DNSMAP_MULTILIMIT
1036 case 'Z':
1037 while (isascii(*++p) && isspace(*p))
1038 continue;
1039 map->map_sizelimit = atoi(p);
1040 break;
1041 # endif /* _FFR_DNSMAP_MULTILIMIT */
1042 # endif /* _FFR_DNSMAP_MULTI */
1044 /* Start of dns_map specific args */
1045 case 'R': /* search field */
1047 char *h;
1049 while (isascii(*++p) && isspace(*p))
1050 continue;
1051 h = strchr(p, ' ');
1052 if (h != NULL)
1053 *h = '\0';
1054 map_p->dns_m_type = dns_string_to_type(p);
1055 if (h != NULL)
1056 *h = ' ';
1057 if (map_p->dns_m_type < 0)
1058 syserr("dns map %s: wrong type %s",
1059 map->map_mname, p);
1061 break;
1063 # if _FFR_DNSMAP_BASE
1064 case 'B': /* base domain */
1066 char *h;
1068 while (isascii(*++p) && isspace(*p))
1069 continue;
1070 h = strchr(p, ' ');
1071 if (h != NULL)
1072 *h = '\0';
1075 ** slight abuse of map->map_file; it isn't
1076 ** used otherwise in this map type.
1079 map->map_file = newstr(p);
1080 if (h != NULL)
1081 *h = ' ';
1083 break;
1084 # endif /* _FFR_DNSMAP_BASE */
1087 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1088 p++;
1089 if (*p != '\0')
1090 *p++ = '\0';
1092 if (map_p->dns_m_type < 0)
1093 syserr("dns map %s: missing -R type", map->map_mname);
1094 if (map->map_app != NULL)
1095 map->map_app = newstr(map->map_app);
1096 if (map->map_tapp != NULL)
1097 map->map_tapp = newstr(map->map_tapp);
1100 ** Assumption: assert(sizeof int <= sizeof(ARBPTR_T));
1101 ** Even if this assumption is wrong, we use only one byte,
1102 ** so it doesn't really matter.
1105 map->map_db1 = (ARBPTR_T) map_p;
1106 return true;
1110 ** DNS_MAP_LOOKUP -- perform dns map lookup.
1112 ** Parameters:
1113 ** map -- pointer to MAP
1114 ** name -- name to lookup
1115 ** av -- arguments to interpolate into buf.
1116 ** statp -- pointer to status (EX_)
1118 ** Returns:
1119 ** result of lookup if succeeded.
1120 ** NULL -- otherwise.
1123 char *
1124 dns_map_lookup(map, name, av, statp)
1125 MAP *map;
1126 char *name;
1127 char **av;
1128 int *statp;
1130 # if _FFR_DNSMAP_MULTI
1131 # if _FFR_DNSMAP_MULTILIMIT
1132 int resnum = 0;
1133 # endif /* _FFR_DNSMAP_MULTILIMIT */
1134 # endif /* _FFR_DNSMAP_MULTI */
1135 char *vp = NULL, *result = NULL;
1136 size_t vsize;
1137 struct dns_map *map_p;
1138 RESOURCE_RECORD_T *rr = NULL;
1139 DNS_REPLY_T *r = NULL;
1140 # if NETINET6
1141 static char buf6[INET6_ADDRSTRLEN];
1142 # endif /* NETINET6 */
1144 if (tTd(38, 20))
1145 sm_dprintf("dns_map_lookup(%s, %s)\n",
1146 map->map_mname, name);
1148 map_p = (struct dns_map *)(map->map_db1);
1149 # if _FFR_DNSMAP_BASE
1150 if (map->map_file != NULL && *map->map_file != '\0')
1152 size_t len;
1153 char *appdomain;
1155 len = strlen(map->map_file) + strlen(name) + 2;
1156 appdomain = (char *) sm_malloc(len);
1157 if (appdomain == NULL)
1159 *statp = EX_UNAVAILABLE;
1160 return NULL;
1162 (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
1163 r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
1164 map->map_timeout, map->map_retry);
1165 sm_free(appdomain);
1167 else
1168 # endif /* _FFR_DNSMAP_BASE */
1170 r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
1171 map->map_timeout, map->map_retry);
1174 if (r == NULL)
1176 result = NULL;
1177 if (h_errno == TRY_AGAIN || transienterror(errno))
1178 *statp = EX_TEMPFAIL;
1179 else
1180 *statp = EX_NOTFOUND;
1181 goto cleanup;
1183 *statp = EX_OK;
1184 for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
1186 char *type = NULL;
1187 char *value = NULL;
1189 switch (rr->rr_type)
1191 case T_NS:
1192 type = "T_NS";
1193 value = rr->rr_u.rr_txt;
1194 break;
1195 case T_CNAME:
1196 type = "T_CNAME";
1197 value = rr->rr_u.rr_txt;
1198 break;
1199 case T_AFSDB:
1200 type = "T_AFSDB";
1201 value = rr->rr_u.rr_mx->mx_r_domain;
1202 break;
1203 case T_SRV:
1204 type = "T_SRV";
1205 value = rr->rr_u.rr_srv->srv_r_target;
1206 break;
1207 case T_PTR:
1208 type = "T_PTR";
1209 value = rr->rr_u.rr_txt;
1210 break;
1211 case T_TXT:
1212 type = "T_TXT";
1213 value = rr->rr_u.rr_txt;
1214 break;
1215 case T_MX:
1216 type = "T_MX";
1217 value = rr->rr_u.rr_mx->mx_r_domain;
1218 break;
1219 # if NETINET
1220 case T_A:
1221 type = "T_A";
1222 value = inet_ntoa(*(rr->rr_u.rr_a));
1223 break;
1224 # endif /* NETINET */
1225 # if NETINET6
1226 case T_AAAA:
1227 type = "T_AAAA";
1228 value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
1229 sizeof buf6);
1230 break;
1231 # endif /* NETINET6 */
1234 (void) strreplnonprt(value, 'X');
1235 if (map_p->dns_m_type != rr->rr_type)
1237 if (tTd(38, 40))
1238 sm_dprintf("\tskipping type %s (%d) value %s\n",
1239 type != NULL ? type : "<UNKNOWN>",
1240 rr->rr_type,
1241 value != NULL ? value : "<NO VALUE>");
1242 continue;
1245 # if NETINET6
1246 if (rr->rr_type == T_AAAA && value == NULL)
1248 result = NULL;
1249 *statp = EX_DATAERR;
1250 if (tTd(38, 40))
1251 sm_dprintf("\tbad T_AAAA conversion\n");
1252 goto cleanup;
1254 # endif /* NETINET6 */
1255 if (tTd(38, 40))
1256 sm_dprintf("\tfound type %s (%d) value %s\n",
1257 type != NULL ? type : "<UNKNOWN>",
1258 rr->rr_type,
1259 value != NULL ? value : "<NO VALUE>");
1260 # if _FFR_DNSMAP_MULTI
1261 if (value != NULL &&
1262 (map->map_coldelim == '\0' ||
1263 # if _FFR_DNSMAP_MULTILIMIT
1264 map->map_sizelimit == 1 ||
1265 # endif /* _FFR_DNSMAP_MULTILIMIT */
1266 bitset(MF_MATCHONLY, map->map_mflags)))
1268 /* Only care about the first match */
1269 vp = newstr(value);
1270 break;
1272 else if (vp == NULL)
1274 /* First result */
1275 vp = newstr(value);
1277 else
1279 /* concatenate the results */
1280 int sz;
1281 char *new;
1283 sz = strlen(vp) + strlen(value) + 2;
1284 new = xalloc(sz);
1285 (void) sm_snprintf(new, sz, "%s%c%s",
1286 vp, map->map_coldelim, value);
1287 sm_free(vp);
1288 vp = new;
1289 # if _FFR_DNSMAP_MULTILIMIT
1290 if (map->map_sizelimit > 0 &&
1291 ++resnum >= map->map_sizelimit)
1292 break;
1293 # endif /* _FFR_DNSMAP_MULTILIMIT */
1295 # else /* _FFR_DNSMAP_MULTI */
1296 vp = value;
1297 break;
1298 # endif /* _FFR_DNSMAP_MULTI */
1300 if (vp == NULL)
1302 result = NULL;
1303 *statp = EX_NOTFOUND;
1304 if (tTd(38, 40))
1305 sm_dprintf("\tno match found\n");
1306 goto cleanup;
1309 # if _FFR_DNSMAP_MULTI
1310 /* Cleanly truncate for rulesets */
1311 truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
1312 # endif /* _FFR_DNSMAP_MULTI */
1314 vsize = strlen(vp);
1316 if (LogLevel > 9)
1317 sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
1318 name, vp);
1319 if (bitset(MF_MATCHONLY, map->map_mflags))
1320 result = map_rewrite(map, name, strlen(name), NULL);
1321 else
1322 result = map_rewrite(map, vp, vsize, av);
1324 cleanup:
1325 # if _FFR_DNSMAP_MULTI
1326 if (vp != NULL)
1327 sm_free(vp);
1328 # endif /* _FFR_DNSMAP_MULTI */
1329 if (r != NULL)
1330 dns_free_data(r);
1331 return result;
1333 # endif /* DNSMAP */
1334 #endif /* NAMED_BIND */
1337 ** NDBM modules
1340 #if NDBM
1343 ** NDBM_MAP_OPEN -- DBM-style map open
1346 bool
1347 ndbm_map_open(map, mode)
1348 MAP *map;
1349 int mode;
1351 register DBM *dbm;
1352 int save_errno;
1353 int dfd;
1354 int pfd;
1355 long sff;
1356 int ret;
1357 int smode = S_IREAD;
1358 char dirfile[MAXPATHLEN];
1359 char pagfile[MAXPATHLEN];
1360 struct stat st;
1361 struct stat std, stp;
1363 if (tTd(38, 2))
1364 sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1365 map->map_mname, map->map_file, mode);
1366 map->map_lockfd = -1;
1367 mode &= O_ACCMODE;
1369 /* do initial file and directory checks */
1370 if (sm_strlcpyn(dirfile, sizeof dirfile, 2,
1371 map->map_file, ".dir") >= sizeof dirfile ||
1372 sm_strlcpyn(pagfile, sizeof pagfile, 2,
1373 map->map_file, ".pag") >= sizeof pagfile)
1375 errno = 0;
1376 if (!bitset(MF_OPTIONAL, map->map_mflags))
1377 syserr("dbm map \"%s\": map file %s name too long",
1378 map->map_mname, map->map_file);
1379 return false;
1381 sff = SFF_ROOTOK|SFF_REGONLY;
1382 if (mode == O_RDWR)
1384 sff |= SFF_CREAT;
1385 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1386 sff |= SFF_NOSLINK;
1387 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1388 sff |= SFF_NOHLINK;
1389 smode = S_IWRITE;
1391 else
1393 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1394 sff |= SFF_NOWLINK;
1396 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1397 sff |= SFF_SAFEDIRPATH;
1398 ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
1399 sff, smode, &std);
1400 if (ret == 0)
1401 ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
1402 sff, smode, &stp);
1404 if (ret != 0)
1406 char *prob = "unsafe";
1408 /* cannot open this map */
1409 if (ret == ENOENT)
1410 prob = "missing";
1411 if (tTd(38, 2))
1412 sm_dprintf("\t%s map file: %d\n", prob, ret);
1413 if (!bitset(MF_OPTIONAL, map->map_mflags))
1414 syserr("dbm map \"%s\": %s map file %s",
1415 map->map_mname, prob, map->map_file);
1416 return false;
1418 if (std.st_mode == ST_MODE_NOFILE)
1419 mode |= O_CREAT|O_EXCL;
1421 # if LOCK_ON_OPEN
1422 if (mode == O_RDONLY)
1423 mode |= O_SHLOCK;
1424 else
1425 mode |= O_TRUNC|O_EXLOCK;
1426 # else /* LOCK_ON_OPEN */
1427 if ((mode & O_ACCMODE) == O_RDWR)
1429 # if NOFTRUNCATE
1431 ** Warning: race condition. Try to lock the file as
1432 ** quickly as possible after opening it.
1433 ** This may also have security problems on some systems,
1434 ** but there isn't anything we can do about it.
1437 mode |= O_TRUNC;
1438 # else /* NOFTRUNCATE */
1440 ** This ugly code opens the map without truncating it,
1441 ** locks the file, then truncates it. Necessary to
1442 ** avoid race conditions.
1445 int dirfd;
1446 int pagfd;
1447 long sff = SFF_CREAT|SFF_OPENASROOT;
1449 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1450 sff |= SFF_NOSLINK;
1451 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1452 sff |= SFF_NOHLINK;
1454 dirfd = safeopen(dirfile, mode, DBMMODE, sff);
1455 pagfd = safeopen(pagfile, mode, DBMMODE, sff);
1457 if (dirfd < 0 || pagfd < 0)
1459 save_errno = errno;
1460 if (dirfd >= 0)
1461 (void) close(dirfd);
1462 if (pagfd >= 0)
1463 (void) close(pagfd);
1464 errno = save_errno;
1465 syserr("ndbm_map_open: cannot create database %s",
1466 map->map_file);
1467 return false;
1469 if (ftruncate(dirfd, (off_t) 0) < 0 ||
1470 ftruncate(pagfd, (off_t) 0) < 0)
1472 save_errno = errno;
1473 (void) close(dirfd);
1474 (void) close(pagfd);
1475 errno = save_errno;
1476 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1477 map->map_file);
1478 return false;
1481 /* if new file, get "before" bits for later filechanged check */
1482 if (std.st_mode == ST_MODE_NOFILE &&
1483 (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
1485 save_errno = errno;
1486 (void) close(dirfd);
1487 (void) close(pagfd);
1488 errno = save_errno;
1489 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1490 map->map_file);
1491 return false;
1494 /* have to save the lock for the duration (bletch) */
1495 map->map_lockfd = dirfd;
1496 (void) close(pagfd);
1498 /* twiddle bits for dbm_open */
1499 mode &= ~(O_CREAT|O_EXCL);
1500 # endif /* NOFTRUNCATE */
1502 # endif /* LOCK_ON_OPEN */
1504 /* open the database */
1505 dbm = dbm_open(map->map_file, mode, DBMMODE);
1506 if (dbm == NULL)
1508 save_errno = errno;
1509 if (bitset(MF_ALIAS, map->map_mflags) &&
1510 aliaswait(map, ".pag", false))
1511 return true;
1512 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1513 if (map->map_lockfd >= 0)
1514 (void) close(map->map_lockfd);
1515 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1516 errno = save_errno;
1517 if (!bitset(MF_OPTIONAL, map->map_mflags))
1518 syserr("Cannot open DBM database %s", map->map_file);
1519 return false;
1521 dfd = dbm_dirfno(dbm);
1522 pfd = dbm_pagfno(dbm);
1523 if (dfd == pfd)
1525 /* heuristic: if files are linked, this is actually gdbm */
1526 dbm_close(dbm);
1527 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1528 if (map->map_lockfd >= 0)
1529 (void) close(map->map_lockfd);
1530 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1531 errno = 0;
1532 syserr("dbm map \"%s\": cannot support GDBM",
1533 map->map_mname);
1534 return false;
1537 if (filechanged(dirfile, dfd, &std) ||
1538 filechanged(pagfile, pfd, &stp))
1540 save_errno = errno;
1541 dbm_close(dbm);
1542 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1543 if (map->map_lockfd >= 0)
1544 (void) close(map->map_lockfd);
1545 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1546 errno = save_errno;
1547 syserr("ndbm_map_open(%s): file changed after open",
1548 map->map_file);
1549 return false;
1552 map->map_db1 = (ARBPTR_T) dbm;
1555 ** Need to set map_mtime before the call to aliaswait()
1556 ** as aliaswait() will call map_lookup() which requires
1557 ** map_mtime to be set
1560 if (fstat(pfd, &st) >= 0)
1561 map->map_mtime = st.st_mtime;
1563 if (mode == O_RDONLY)
1565 # if LOCK_ON_OPEN
1566 if (dfd >= 0)
1567 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1568 if (pfd >= 0)
1569 (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
1570 # endif /* LOCK_ON_OPEN */
1571 if (bitset(MF_ALIAS, map->map_mflags) &&
1572 !aliaswait(map, ".pag", true))
1573 return false;
1575 else
1577 map->map_mflags |= MF_LOCKED;
1578 if (geteuid() == 0 && TrustedUid != 0)
1580 # if HASFCHOWN
1581 if (fchown(dfd, TrustedUid, -1) < 0 ||
1582 fchown(pfd, TrustedUid, -1) < 0)
1584 int err = errno;
1586 sm_syslog(LOG_ALERT, NOQID,
1587 "ownership change on %s failed: %s",
1588 map->map_file, sm_errstring(err));
1589 message("050 ownership change on %s failed: %s",
1590 map->map_file, sm_errstring(err));
1592 # else /* HASFCHOWN */
1593 sm_syslog(LOG_ALERT, NOQID,
1594 "no fchown(): cannot change ownership on %s",
1595 map->map_file);
1596 message("050 no fchown(): cannot change ownership on %s",
1597 map->map_file);
1598 # endif /* HASFCHOWN */
1601 return true;
1606 ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1609 char *
1610 ndbm_map_lookup(map, name, av, statp)
1611 MAP *map;
1612 char *name;
1613 char **av;
1614 int *statp;
1616 datum key, val;
1617 int dfd, pfd;
1618 char keybuf[MAXNAME + 1];
1619 struct stat stbuf;
1621 if (tTd(38, 20))
1622 sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1623 map->map_mname, name);
1625 key.dptr = name;
1626 key.dsize = strlen(name);
1627 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1629 if (key.dsize > sizeof keybuf - 1)
1630 key.dsize = sizeof keybuf - 1;
1631 memmove(keybuf, key.dptr, key.dsize);
1632 keybuf[key.dsize] = '\0';
1633 makelower(keybuf);
1634 key.dptr = keybuf;
1636 lockdbm:
1637 dfd = dbm_dirfno((DBM *) map->map_db1);
1638 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1639 (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
1640 pfd = dbm_pagfno((DBM *) map->map_db1);
1641 if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
1642 stbuf.st_mtime > map->map_mtime)
1644 /* Reopen the database to sync the cache */
1645 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1646 : O_RDONLY;
1648 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1649 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1650 map->map_mflags |= MF_CLOSING;
1651 map->map_class->map_close(map);
1652 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1653 if (map->map_class->map_open(map, omode))
1655 map->map_mflags |= MF_OPEN;
1656 map->map_pid = CurrentPid;
1657 if ((omode && O_ACCMODE) == O_RDWR)
1658 map->map_mflags |= MF_WRITABLE;
1659 goto lockdbm;
1661 else
1663 if (!bitset(MF_OPTIONAL, map->map_mflags))
1665 extern MAPCLASS BogusMapClass;
1667 *statp = EX_TEMPFAIL;
1668 map->map_orgclass = map->map_class;
1669 map->map_class = &BogusMapClass;
1670 map->map_mflags |= MF_OPEN;
1671 map->map_pid = CurrentPid;
1672 syserr("Cannot reopen NDBM database %s",
1673 map->map_file);
1675 return NULL;
1678 val.dptr = NULL;
1679 if (bitset(MF_TRY0NULL, map->map_mflags))
1681 val = dbm_fetch((DBM *) map->map_db1, key);
1682 if (val.dptr != NULL)
1683 map->map_mflags &= ~MF_TRY1NULL;
1685 if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1687 key.dsize++;
1688 val = dbm_fetch((DBM *) map->map_db1, key);
1689 if (val.dptr != NULL)
1690 map->map_mflags &= ~MF_TRY0NULL;
1692 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1693 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1694 if (val.dptr == NULL)
1695 return NULL;
1696 if (bitset(MF_MATCHONLY, map->map_mflags))
1697 return map_rewrite(map, name, strlen(name), NULL);
1698 else
1699 return map_rewrite(map, val.dptr, val.dsize, av);
1704 ** NDBM_MAP_STORE -- store a datum in the database
1707 void
1708 ndbm_map_store(map, lhs, rhs)
1709 register MAP *map;
1710 char *lhs;
1711 char *rhs;
1713 datum key;
1714 datum data;
1715 int status;
1716 char keybuf[MAXNAME + 1];
1718 if (tTd(38, 12))
1719 sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1720 map->map_mname, lhs, rhs);
1722 key.dsize = strlen(lhs);
1723 key.dptr = lhs;
1724 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1726 if (key.dsize > sizeof keybuf - 1)
1727 key.dsize = sizeof keybuf - 1;
1728 memmove(keybuf, key.dptr, key.dsize);
1729 keybuf[key.dsize] = '\0';
1730 makelower(keybuf);
1731 key.dptr = keybuf;
1734 data.dsize = strlen(rhs);
1735 data.dptr = rhs;
1737 if (bitset(MF_INCLNULL, map->map_mflags))
1739 key.dsize++;
1740 data.dsize++;
1743 status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1744 if (status > 0)
1746 if (!bitset(MF_APPEND, map->map_mflags))
1747 message("050 Warning: duplicate alias name %s", lhs);
1748 else
1750 static char *buf = NULL;
1751 static int bufsiz = 0;
1752 auto int xstat;
1753 datum old;
1755 old.dptr = ndbm_map_lookup(map, key.dptr,
1756 (char **) NULL, &xstat);
1757 if (old.dptr != NULL && *(char *) old.dptr != '\0')
1759 old.dsize = strlen(old.dptr);
1760 if (data.dsize + old.dsize + 2 > bufsiz)
1762 if (buf != NULL)
1763 (void) sm_free(buf);
1764 bufsiz = data.dsize + old.dsize + 2;
1765 buf = sm_pmalloc_x(bufsiz);
1767 (void) sm_strlcpyn(buf, bufsiz, 3,
1768 data.dptr, ",", old.dptr);
1769 data.dsize = data.dsize + old.dsize + 1;
1770 data.dptr = buf;
1771 if (tTd(38, 9))
1772 sm_dprintf("ndbm_map_store append=%s\n",
1773 data.dptr);
1776 status = dbm_store((DBM *) map->map_db1,
1777 key, data, DBM_REPLACE);
1779 if (status != 0)
1780 syserr("readaliases: dbm put (%s): %d", lhs, status);
1785 ** NDBM_MAP_CLOSE -- close the database
1788 void
1789 ndbm_map_close(map)
1790 register MAP *map;
1792 if (tTd(38, 9))
1793 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1794 map->map_mname, map->map_file, map->map_mflags);
1796 if (bitset(MF_WRITABLE, map->map_mflags))
1798 # ifdef NDBM_YP_COMPAT
1799 bool inclnull;
1800 char buf[MAXHOSTNAMELEN];
1802 inclnull = bitset(MF_INCLNULL, map->map_mflags);
1803 map->map_mflags &= ~MF_INCLNULL;
1805 if (strstr(map->map_file, "/yp/") != NULL)
1807 long save_mflags = map->map_mflags;
1809 map->map_mflags |= MF_NOFOLDCASE;
1811 (void) sm_snprintf(buf, sizeof buf, "%010ld", curtime());
1812 ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1814 (void) gethostname(buf, sizeof buf);
1815 ndbm_map_store(map, "YP_MASTER_NAME", buf);
1817 map->map_mflags = save_mflags;
1820 if (inclnull)
1821 map->map_mflags |= MF_INCLNULL;
1822 # endif /* NDBM_YP_COMPAT */
1824 /* write out the distinguished alias */
1825 ndbm_map_store(map, "@", "@");
1827 dbm_close((DBM *) map->map_db1);
1829 /* release lock (if needed) */
1830 # if !LOCK_ON_OPEN
1831 if (map->map_lockfd >= 0)
1832 (void) close(map->map_lockfd);
1833 # endif /* !LOCK_ON_OPEN */
1836 #endif /* NDBM */
1838 ** NEWDB (Hash and BTree) Modules
1841 #if NEWDB
1844 ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1846 ** These do rather bizarre locking. If you can lock on open,
1847 ** do that to avoid the condition of opening a database that
1848 ** is being rebuilt. If you don't, we'll try to fake it, but
1849 ** there will be a race condition. If opening for read-only,
1850 ** we immediately release the lock to avoid freezing things up.
1851 ** We really ought to hold the lock, but guarantee that we won't
1852 ** be pokey about it. That's hard to do.
1855 /* these should be K line arguments */
1856 # if DB_VERSION_MAJOR < 2
1857 # define db_cachesize cachesize
1858 # define h_nelem nelem
1859 # ifndef DB_CACHE_SIZE
1860 # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
1861 # endif /* ! DB_CACHE_SIZE */
1862 # ifndef DB_HASH_NELEM
1863 # define DB_HASH_NELEM 4096 /* (starting) size of hash table */
1864 # endif /* ! DB_HASH_NELEM */
1865 # endif /* DB_VERSION_MAJOR < 2 */
1867 bool
1868 bt_map_open(map, mode)
1869 MAP *map;
1870 int mode;
1872 # if DB_VERSION_MAJOR < 2
1873 BTREEINFO btinfo;
1874 # endif /* DB_VERSION_MAJOR < 2 */
1875 # if DB_VERSION_MAJOR == 2
1876 DB_INFO btinfo;
1877 # endif /* DB_VERSION_MAJOR == 2 */
1878 # if DB_VERSION_MAJOR > 2
1879 void *btinfo = NULL;
1880 # endif /* DB_VERSION_MAJOR > 2 */
1882 if (tTd(38, 2))
1883 sm_dprintf("bt_map_open(%s, %s, %d)\n",
1884 map->map_mname, map->map_file, mode);
1886 # if DB_VERSION_MAJOR < 3
1887 memset(&btinfo, '\0', sizeof btinfo);
1888 # ifdef DB_CACHE_SIZE
1889 btinfo.db_cachesize = DB_CACHE_SIZE;
1890 # endif /* DB_CACHE_SIZE */
1891 # endif /* DB_VERSION_MAJOR < 3 */
1893 return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1896 bool
1897 hash_map_open(map, mode)
1898 MAP *map;
1899 int mode;
1901 # if DB_VERSION_MAJOR < 2
1902 HASHINFO hinfo;
1903 # endif /* DB_VERSION_MAJOR < 2 */
1904 # if DB_VERSION_MAJOR == 2
1905 DB_INFO hinfo;
1906 # endif /* DB_VERSION_MAJOR == 2 */
1907 # if DB_VERSION_MAJOR > 2
1908 void *hinfo = NULL;
1909 # endif /* DB_VERSION_MAJOR > 2 */
1911 if (tTd(38, 2))
1912 sm_dprintf("hash_map_open(%s, %s, %d)\n",
1913 map->map_mname, map->map_file, mode);
1915 # if DB_VERSION_MAJOR < 3
1916 memset(&hinfo, '\0', sizeof hinfo);
1917 # ifdef DB_HASH_NELEM
1918 hinfo.h_nelem = DB_HASH_NELEM;
1919 # endif /* DB_HASH_NELEM */
1920 # ifdef DB_CACHE_SIZE
1921 hinfo.db_cachesize = DB_CACHE_SIZE;
1922 # endif /* DB_CACHE_SIZE */
1923 # endif /* DB_VERSION_MAJOR < 3 */
1925 return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1928 static bool
1929 db_map_open(map, mode, mapclassname, dbtype, openinfo)
1930 MAP *map;
1931 int mode;
1932 char *mapclassname;
1933 DBTYPE dbtype;
1934 # if DB_VERSION_MAJOR < 2
1935 const void *openinfo;
1936 # endif /* DB_VERSION_MAJOR < 2 */
1937 # if DB_VERSION_MAJOR == 2
1938 DB_INFO *openinfo;
1939 # endif /* DB_VERSION_MAJOR == 2 */
1940 # if DB_VERSION_MAJOR > 2
1941 void **openinfo;
1942 # endif /* DB_VERSION_MAJOR > 2 */
1944 DB *db = NULL;
1945 int i;
1946 int omode;
1947 int smode = S_IREAD;
1948 int fd;
1949 long sff;
1950 int save_errno;
1951 struct stat st;
1952 char buf[MAXPATHLEN];
1954 /* do initial file and directory checks */
1955 if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
1957 errno = 0;
1958 if (!bitset(MF_OPTIONAL, map->map_mflags))
1959 syserr("map \"%s\": map file %s name too long",
1960 map->map_mname, map->map_file);
1961 return false;
1963 i = strlen(buf);
1964 if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
1966 if (sm_strlcat(buf, ".db", sizeof buf) >= sizeof buf)
1968 errno = 0;
1969 if (!bitset(MF_OPTIONAL, map->map_mflags))
1970 syserr("map \"%s\": map file %s name too long",
1971 map->map_mname, map->map_file);
1972 return false;
1976 mode &= O_ACCMODE;
1977 omode = mode;
1979 sff = SFF_ROOTOK|SFF_REGONLY;
1980 if (mode == O_RDWR)
1982 sff |= SFF_CREAT;
1983 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1984 sff |= SFF_NOSLINK;
1985 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1986 sff |= SFF_NOHLINK;
1987 smode = S_IWRITE;
1989 else
1991 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1992 sff |= SFF_NOWLINK;
1994 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1995 sff |= SFF_SAFEDIRPATH;
1996 i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
1998 if (i != 0)
2000 char *prob = "unsafe";
2002 /* cannot open this map */
2003 if (i == ENOENT)
2004 prob = "missing";
2005 if (tTd(38, 2))
2006 sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
2007 errno = i;
2008 if (!bitset(MF_OPTIONAL, map->map_mflags))
2009 syserr("%s map \"%s\": %s map file %s",
2010 mapclassname, map->map_mname, prob, buf);
2011 return false;
2013 if (st.st_mode == ST_MODE_NOFILE)
2014 omode |= O_CREAT|O_EXCL;
2016 map->map_lockfd = -1;
2018 # if LOCK_ON_OPEN
2019 if (mode == O_RDWR)
2020 omode |= O_TRUNC|O_EXLOCK;
2021 else
2022 omode |= O_SHLOCK;
2023 # else /* LOCK_ON_OPEN */
2025 ** Pre-lock the file to avoid race conditions. In particular,
2026 ** since dbopen returns NULL if the file is zero length, we
2027 ** must have a locked instance around the dbopen.
2030 fd = open(buf, omode, DBMMODE);
2031 if (fd < 0)
2033 if (!bitset(MF_OPTIONAL, map->map_mflags))
2034 syserr("db_map_open: cannot pre-open database %s", buf);
2035 return false;
2038 /* make sure no baddies slipped in just before the open... */
2039 if (filechanged(buf, fd, &st))
2041 save_errno = errno;
2042 (void) close(fd);
2043 errno = save_errno;
2044 syserr("db_map_open(%s): file changed after pre-open", buf);
2045 return false;
2048 /* if new file, get the "before" bits for later filechanged check */
2049 if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
2051 save_errno = errno;
2052 (void) close(fd);
2053 errno = save_errno;
2054 syserr("db_map_open(%s): cannot fstat pre-opened file",
2055 buf);
2056 return false;
2059 /* actually lock the pre-opened file */
2060 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
2061 syserr("db_map_open: cannot lock %s", buf);
2063 /* set up mode bits for dbopen */
2064 if (mode == O_RDWR)
2065 omode |= O_TRUNC;
2066 omode &= ~(O_EXCL|O_CREAT);
2067 # endif /* LOCK_ON_OPEN */
2069 # if DB_VERSION_MAJOR < 2
2070 db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
2071 # else /* DB_VERSION_MAJOR < 2 */
2073 int flags = 0;
2074 # if DB_VERSION_MAJOR > 2
2075 int ret;
2076 # endif /* DB_VERSION_MAJOR > 2 */
2078 if (mode == O_RDONLY)
2079 flags |= DB_RDONLY;
2080 if (bitset(O_CREAT, omode))
2081 flags |= DB_CREATE;
2082 if (bitset(O_TRUNC, omode))
2083 flags |= DB_TRUNCATE;
2084 SM_DB_FLAG_ADD(flags);
2086 # if DB_VERSION_MAJOR > 2
2087 ret = db_create(&db, NULL, 0);
2088 # ifdef DB_CACHE_SIZE
2089 if (ret == 0 && db != NULL)
2091 ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
2092 if (ret != 0)
2094 (void) db->close(db, 0);
2095 db = NULL;
2098 # endif /* DB_CACHE_SIZE */
2099 # ifdef DB_HASH_NELEM
2100 if (dbtype == DB_HASH && ret == 0 && db != NULL)
2102 ret = db->set_h_nelem(db, DB_HASH_NELEM);
2103 if (ret != 0)
2105 (void) db->close(db, 0);
2106 db = NULL;
2109 # endif /* DB_HASH_NELEM */
2110 if (ret == 0 && db != NULL)
2112 ret = db->open(db,
2113 DBTXN /* transaction for DB 4.1 */
2114 buf, NULL, dbtype, flags, DBMMODE);
2115 if (ret != 0)
2117 #ifdef DB_OLD_VERSION
2118 if (ret == DB_OLD_VERSION)
2119 ret = EINVAL;
2120 #endif /* DB_OLD_VERSION */
2121 (void) db->close(db, 0);
2122 db = NULL;
2125 errno = ret;
2126 # else /* DB_VERSION_MAJOR > 2 */
2127 errno = db_open(buf, dbtype, flags, DBMMODE,
2128 NULL, openinfo, &db);
2129 # endif /* DB_VERSION_MAJOR > 2 */
2131 # endif /* DB_VERSION_MAJOR < 2 */
2132 save_errno = errno;
2134 # if !LOCK_ON_OPEN
2135 if (mode == O_RDWR)
2136 map->map_lockfd = fd;
2137 else
2138 (void) close(fd);
2139 # endif /* !LOCK_ON_OPEN */
2141 if (db == NULL)
2143 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2144 aliaswait(map, ".db", false))
2145 return true;
2146 # if !LOCK_ON_OPEN
2147 if (map->map_lockfd >= 0)
2148 (void) close(map->map_lockfd);
2149 # endif /* !LOCK_ON_OPEN */
2150 errno = save_errno;
2151 if (!bitset(MF_OPTIONAL, map->map_mflags))
2152 syserr("Cannot open %s database %s",
2153 mapclassname, buf);
2154 return false;
2157 # if DB_VERSION_MAJOR < 2
2158 fd = db->fd(db);
2159 # else /* DB_VERSION_MAJOR < 2 */
2160 fd = -1;
2161 errno = db->fd(db, &fd);
2162 # endif /* DB_VERSION_MAJOR < 2 */
2163 if (filechanged(buf, fd, &st))
2165 save_errno = errno;
2166 # if DB_VERSION_MAJOR < 2
2167 (void) db->close(db);
2168 # else /* DB_VERSION_MAJOR < 2 */
2169 errno = db->close(db, 0);
2170 # endif /* DB_VERSION_MAJOR < 2 */
2171 # if !LOCK_ON_OPEN
2172 if (map->map_lockfd >= 0)
2173 (void) close(map->map_lockfd);
2174 # endif /* !LOCK_ON_OPEN */
2175 errno = save_errno;
2176 syserr("db_map_open(%s): file changed after open", buf);
2177 return false;
2180 if (mode == O_RDWR)
2181 map->map_mflags |= MF_LOCKED;
2182 # if LOCK_ON_OPEN
2183 if (fd >= 0 && mode == O_RDONLY)
2185 (void) lockfile(fd, buf, NULL, LOCK_UN);
2187 # endif /* LOCK_ON_OPEN */
2189 /* try to make sure that at least the database header is on disk */
2190 if (mode == O_RDWR)
2192 (void) db->sync(db, 0);
2193 if (geteuid() == 0 && TrustedUid != 0)
2195 # if HASFCHOWN
2196 if (fchown(fd, TrustedUid, -1) < 0)
2198 int err = errno;
2200 sm_syslog(LOG_ALERT, NOQID,
2201 "ownership change on %s failed: %s",
2202 buf, sm_errstring(err));
2203 message("050 ownership change on %s failed: %s",
2204 buf, sm_errstring(err));
2206 # else /* HASFCHOWN */
2207 sm_syslog(LOG_ALERT, NOQID,
2208 "no fchown(): cannot change ownership on %s",
2209 map->map_file);
2210 message("050 no fchown(): cannot change ownership on %s",
2211 map->map_file);
2212 # endif /* HASFCHOWN */
2216 map->map_db2 = (ARBPTR_T) db;
2219 ** Need to set map_mtime before the call to aliaswait()
2220 ** as aliaswait() will call map_lookup() which requires
2221 ** map_mtime to be set
2224 if (fd >= 0 && fstat(fd, &st) >= 0)
2225 map->map_mtime = st.st_mtime;
2227 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2228 !aliaswait(map, ".db", true))
2229 return false;
2230 return true;
2235 ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2238 char *
2239 db_map_lookup(map, name, av, statp)
2240 MAP *map;
2241 char *name;
2242 char **av;
2243 int *statp;
2245 DBT key, val;
2246 register DB *db = (DB *) map->map_db2;
2247 int i;
2248 int st;
2249 int save_errno;
2250 int fd;
2251 struct stat stbuf;
2252 char keybuf[MAXNAME + 1];
2253 char buf[MAXPATHLEN];
2255 memset(&key, '\0', sizeof key);
2256 memset(&val, '\0', sizeof val);
2258 if (tTd(38, 20))
2259 sm_dprintf("db_map_lookup(%s, %s)\n",
2260 map->map_mname, name);
2262 if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
2264 errno = 0;
2265 if (!bitset(MF_OPTIONAL, map->map_mflags))
2266 syserr("map \"%s\": map file %s name too long",
2267 map->map_mname, map->map_file);
2268 return NULL;
2270 i = strlen(buf);
2271 if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
2272 buf[i - 3] = '\0';
2274 key.size = strlen(name);
2275 if (key.size > sizeof keybuf - 1)
2276 key.size = sizeof keybuf - 1;
2277 key.data = keybuf;
2278 memmove(keybuf, name, key.size);
2279 keybuf[key.size] = '\0';
2280 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2281 makelower(keybuf);
2282 lockdb:
2283 # if DB_VERSION_MAJOR < 2
2284 fd = db->fd(db);
2285 # else /* DB_VERSION_MAJOR < 2 */
2286 fd = -1;
2287 errno = db->fd(db, &fd);
2288 # endif /* DB_VERSION_MAJOR < 2 */
2289 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2290 (void) lockfile(fd, buf, ".db", LOCK_SH);
2291 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
2293 /* Reopen the database to sync the cache */
2294 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
2295 : O_RDONLY;
2297 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2298 (void) lockfile(fd, buf, ".db", LOCK_UN);
2299 map->map_mflags |= MF_CLOSING;
2300 map->map_class->map_close(map);
2301 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
2302 if (map->map_class->map_open(map, omode))
2304 map->map_mflags |= MF_OPEN;
2305 map->map_pid = CurrentPid;
2306 if ((omode && O_ACCMODE) == O_RDWR)
2307 map->map_mflags |= MF_WRITABLE;
2308 db = (DB *) map->map_db2;
2309 goto lockdb;
2311 else
2313 if (!bitset(MF_OPTIONAL, map->map_mflags))
2315 extern MAPCLASS BogusMapClass;
2317 *statp = EX_TEMPFAIL;
2318 map->map_orgclass = map->map_class;
2319 map->map_class = &BogusMapClass;
2320 map->map_mflags |= MF_OPEN;
2321 map->map_pid = CurrentPid;
2322 syserr("Cannot reopen DB database %s",
2323 map->map_file);
2325 return NULL;
2329 st = 1;
2330 if (bitset(MF_TRY0NULL, map->map_mflags))
2332 # if DB_VERSION_MAJOR < 2
2333 st = db->get(db, &key, &val, 0);
2334 # else /* DB_VERSION_MAJOR < 2 */
2335 errno = db->get(db, NULL, &key, &val, 0);
2336 switch (errno)
2338 case DB_NOTFOUND:
2339 case DB_KEYEMPTY:
2340 st = 1;
2341 break;
2343 case 0:
2344 st = 0;
2345 break;
2347 default:
2348 st = -1;
2349 break;
2351 # endif /* DB_VERSION_MAJOR < 2 */
2352 if (st == 0)
2353 map->map_mflags &= ~MF_TRY1NULL;
2355 if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
2357 key.size++;
2358 # if DB_VERSION_MAJOR < 2
2359 st = db->get(db, &key, &val, 0);
2360 # else /* DB_VERSION_MAJOR < 2 */
2361 errno = db->get(db, NULL, &key, &val, 0);
2362 switch (errno)
2364 case DB_NOTFOUND:
2365 case DB_KEYEMPTY:
2366 st = 1;
2367 break;
2369 case 0:
2370 st = 0;
2371 break;
2373 default:
2374 st = -1;
2375 break;
2377 # endif /* DB_VERSION_MAJOR < 2 */
2378 if (st == 0)
2379 map->map_mflags &= ~MF_TRY0NULL;
2381 save_errno = errno;
2382 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2383 (void) lockfile(fd, buf, ".db", LOCK_UN);
2384 if (st != 0)
2386 errno = save_errno;
2387 if (st < 0)
2388 syserr("db_map_lookup: get (%s)", name);
2389 return NULL;
2391 if (bitset(MF_MATCHONLY, map->map_mflags))
2392 return map_rewrite(map, name, strlen(name), NULL);
2393 else
2394 return map_rewrite(map, val.data, val.size, av);
2399 ** DB_MAP_STORE -- store a datum in the NEWDB database
2402 void
2403 db_map_store(map, lhs, rhs)
2404 register MAP *map;
2405 char *lhs;
2406 char *rhs;
2408 int status;
2409 DBT key;
2410 DBT data;
2411 register DB *db = map->map_db2;
2412 char keybuf[MAXNAME + 1];
2414 memset(&key, '\0', sizeof key);
2415 memset(&data, '\0', sizeof data);
2417 if (tTd(38, 12))
2418 sm_dprintf("db_map_store(%s, %s, %s)\n",
2419 map->map_mname, lhs, rhs);
2421 key.size = strlen(lhs);
2422 key.data = lhs;
2423 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2425 if (key.size > sizeof keybuf - 1)
2426 key.size = sizeof keybuf - 1;
2427 memmove(keybuf, key.data, key.size);
2428 keybuf[key.size] = '\0';
2429 makelower(keybuf);
2430 key.data = keybuf;
2433 data.size = strlen(rhs);
2434 data.data = rhs;
2436 if (bitset(MF_INCLNULL, map->map_mflags))
2438 key.size++;
2439 data.size++;
2442 # if DB_VERSION_MAJOR < 2
2443 status = db->put(db, &key, &data, R_NOOVERWRITE);
2444 # else /* DB_VERSION_MAJOR < 2 */
2445 errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
2446 switch (errno)
2448 case DB_KEYEXIST:
2449 status = 1;
2450 break;
2452 case 0:
2453 status = 0;
2454 break;
2456 default:
2457 status = -1;
2458 break;
2460 # endif /* DB_VERSION_MAJOR < 2 */
2461 if (status > 0)
2463 if (!bitset(MF_APPEND, map->map_mflags))
2464 message("050 Warning: duplicate alias name %s", lhs);
2465 else
2467 static char *buf = NULL;
2468 static int bufsiz = 0;
2469 DBT old;
2471 memset(&old, '\0', sizeof old);
2473 old.data = db_map_lookup(map, key.data,
2474 (char **) NULL, &status);
2475 if (old.data != NULL)
2477 old.size = strlen(old.data);
2478 if (data.size + old.size + 2 > (size_t) bufsiz)
2480 if (buf != NULL)
2481 sm_free(buf);
2482 bufsiz = data.size + old.size + 2;
2483 buf = sm_pmalloc_x(bufsiz);
2485 (void) sm_strlcpyn(buf, bufsiz, 3,
2486 (char *) data.data, ",",
2487 (char *) old.data);
2488 data.size = data.size + old.size + 1;
2489 data.data = buf;
2490 if (tTd(38, 9))
2491 sm_dprintf("db_map_store append=%s\n",
2492 (char *) data.data);
2495 # if DB_VERSION_MAJOR < 2
2496 status = db->put(db, &key, &data, 0);
2497 # else /* DB_VERSION_MAJOR < 2 */
2498 status = errno = db->put(db, NULL, &key, &data, 0);
2499 # endif /* DB_VERSION_MAJOR < 2 */
2501 if (status != 0)
2502 syserr("readaliases: db put (%s)", lhs);
2507 ** DB_MAP_CLOSE -- add distinguished entries and close the database
2510 void
2511 db_map_close(map)
2512 MAP *map;
2514 register DB *db = map->map_db2;
2516 if (tTd(38, 9))
2517 sm_dprintf("db_map_close(%s, %s, %lx)\n",
2518 map->map_mname, map->map_file, map->map_mflags);
2520 if (bitset(MF_WRITABLE, map->map_mflags))
2522 /* write out the distinguished alias */
2523 db_map_store(map, "@", "@");
2526 (void) db->sync(db, 0);
2528 # if !LOCK_ON_OPEN
2529 if (map->map_lockfd >= 0)
2530 (void) close(map->map_lockfd);
2531 # endif /* !LOCK_ON_OPEN */
2533 # if DB_VERSION_MAJOR < 2
2534 if (db->close(db) != 0)
2535 # else /* DB_VERSION_MAJOR < 2 */
2537 ** Berkeley DB can use internal shared memory
2538 ** locking for its memory pool. Closing a map
2539 ** opened by another process will interfere
2540 ** with the shared memory and locks of the parent
2541 ** process leaving things in a bad state.
2545 ** If this map was not opened by the current
2546 ** process, do not close the map but recover
2547 ** the file descriptor.
2550 if (map->map_pid != CurrentPid)
2552 int fd = -1;
2554 errno = db->fd(db, &fd);
2555 if (fd >= 0)
2556 (void) close(fd);
2557 return;
2560 if ((errno = db->close(db, 0)) != 0)
2561 # endif /* DB_VERSION_MAJOR < 2 */
2562 syserr("db_map_close(%s, %s, %lx): db close failure",
2563 map->map_mname, map->map_file, map->map_mflags);
2565 #endif /* NEWDB */
2567 ** NIS Modules
2570 #if NIS
2572 # ifndef YPERR_BUSY
2573 # define YPERR_BUSY 16
2574 # endif /* ! YPERR_BUSY */
2577 ** NIS_MAP_OPEN -- open DBM map
2580 bool
2581 nis_map_open(map, mode)
2582 MAP *map;
2583 int mode;
2585 int yperr;
2586 register char *p;
2587 auto char *vp;
2588 auto int vsize;
2590 if (tTd(38, 2))
2591 sm_dprintf("nis_map_open(%s, %s, %d)\n",
2592 map->map_mname, map->map_file, mode);
2594 mode &= O_ACCMODE;
2595 if (mode != O_RDONLY)
2597 /* issue a pseudo-error message */
2598 errno = SM_EMAPCANTWRITE;
2599 return false;
2602 p = strchr(map->map_file, '@');
2603 if (p != NULL)
2605 *p++ = '\0';
2606 if (*p != '\0')
2607 map->map_domain = p;
2610 if (*map->map_file == '\0')
2611 map->map_file = "mail.aliases";
2613 if (map->map_domain == NULL)
2615 yperr = yp_get_default_domain(&map->map_domain);
2616 if (yperr != 0)
2618 if (!bitset(MF_OPTIONAL, map->map_mflags))
2619 syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2620 map->map_file);
2621 return false;
2625 /* check to see if this map actually exists */
2626 vp = NULL;
2627 yperr = yp_match(map->map_domain, map->map_file, "@", 1,
2628 &vp, &vsize);
2629 if (tTd(38, 10))
2630 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2631 map->map_domain, map->map_file, yperr_string(yperr));
2632 if (vp != NULL)
2633 sm_free(vp);
2635 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
2638 ** We ought to be calling aliaswait() here if this is an
2639 ** alias file, but powerful HP-UX NIS servers apparently
2640 ** don't insert the @:@ token into the alias map when it
2641 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
2644 # if 0
2645 if (!bitset(MF_ALIAS, map->map_mflags) ||
2646 aliaswait(map, NULL, true))
2647 # endif /* 0 */
2648 return true;
2651 if (!bitset(MF_OPTIONAL, map->map_mflags))
2653 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2654 map->map_file, map->map_domain, yperr_string(yperr));
2657 return false;
2662 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map
2665 /* ARGSUSED3 */
2666 char *
2667 nis_map_lookup(map, name, av, statp)
2668 MAP *map;
2669 char *name;
2670 char **av;
2671 int *statp;
2673 char *vp;
2674 auto int vsize;
2675 int buflen;
2676 int yperr;
2677 char keybuf[MAXNAME + 1];
2678 char *SM_NONVOLATILE result = NULL;
2680 if (tTd(38, 20))
2681 sm_dprintf("nis_map_lookup(%s, %s)\n",
2682 map->map_mname, name);
2684 buflen = strlen(name);
2685 if (buflen > sizeof keybuf - 1)
2686 buflen = sizeof keybuf - 1;
2687 memmove(keybuf, name, buflen);
2688 keybuf[buflen] = '\0';
2689 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2690 makelower(keybuf);
2691 yperr = YPERR_KEY;
2692 vp = NULL;
2693 if (bitset(MF_TRY0NULL, map->map_mflags))
2695 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2696 &vp, &vsize);
2697 if (yperr == 0)
2698 map->map_mflags &= ~MF_TRY1NULL;
2700 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2702 SM_FREE_CLR(vp);
2703 buflen++;
2704 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2705 &vp, &vsize);
2706 if (yperr == 0)
2707 map->map_mflags &= ~MF_TRY0NULL;
2709 if (yperr != 0)
2711 if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2712 map->map_mflags &= ~(MF_VALID|MF_OPEN);
2713 if (vp != NULL)
2714 sm_free(vp);
2715 return NULL;
2717 SM_TRY
2718 if (bitset(MF_MATCHONLY, map->map_mflags))
2719 result = map_rewrite(map, name, strlen(name), NULL);
2720 else
2721 result = map_rewrite(map, vp, vsize, av);
2722 SM_FINALLY
2723 if (vp != NULL)
2724 sm_free(vp);
2725 SM_END_TRY
2726 return result;
2731 ** NIS_GETCANONNAME -- look up canonical name in NIS
2734 static bool
2735 nis_getcanonname(name, hbsize, statp)
2736 char *name;
2737 int hbsize;
2738 int *statp;
2740 char *vp;
2741 auto int vsize;
2742 int keylen;
2743 int yperr;
2744 static bool try0null = true;
2745 static bool try1null = true;
2746 static char *yp_domain = NULL;
2747 char host_record[MAXLINE];
2748 char cbuf[MAXNAME];
2749 char nbuf[MAXNAME + 1];
2751 if (tTd(38, 20))
2752 sm_dprintf("nis_getcanonname(%s)\n", name);
2754 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
2756 *statp = EX_UNAVAILABLE;
2757 return false;
2759 (void) shorten_hostname(nbuf);
2760 keylen = strlen(nbuf);
2762 if (yp_domain == NULL)
2763 (void) yp_get_default_domain(&yp_domain);
2764 makelower(nbuf);
2765 yperr = YPERR_KEY;
2766 vp = NULL;
2767 if (try0null)
2769 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2770 &vp, &vsize);
2771 if (yperr == 0)
2772 try1null = false;
2774 if (yperr == YPERR_KEY && try1null)
2776 SM_FREE_CLR(vp);
2777 keylen++;
2778 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2779 &vp, &vsize);
2780 if (yperr == 0)
2781 try0null = false;
2783 if (yperr != 0)
2785 if (yperr == YPERR_KEY)
2786 *statp = EX_NOHOST;
2787 else if (yperr == YPERR_BUSY)
2788 *statp = EX_TEMPFAIL;
2789 else
2790 *statp = EX_UNAVAILABLE;
2791 if (vp != NULL)
2792 sm_free(vp);
2793 return false;
2795 (void) sm_strlcpy(host_record, vp, sizeof host_record);
2796 sm_free(vp);
2797 if (tTd(38, 44))
2798 sm_dprintf("got record `%s'\n", host_record);
2799 vp = strpbrk(host_record, "#\n");
2800 if (vp != NULL)
2801 *vp = '\0';
2802 if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof cbuf))
2804 /* this should not happen, but.... */
2805 *statp = EX_NOHOST;
2806 return false;
2808 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
2810 *statp = EX_UNAVAILABLE;
2811 return false;
2813 *statp = EX_OK;
2814 return true;
2817 #endif /* NIS */
2819 ** NISPLUS Modules
2821 ** This code donated by Sun Microsystems.
2824 #if NISPLUS
2826 # undef NIS /* symbol conflict in nis.h */
2827 # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2828 # include <rpcsvc/nis.h>
2829 # include <rpcsvc/nislib.h>
2831 # define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2832 # define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2833 # define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2834 # define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
2837 ** NISPLUS_MAP_OPEN -- open nisplus table
2840 bool
2841 nisplus_map_open(map, mode)
2842 MAP *map;
2843 int mode;
2845 nis_result *res = NULL;
2846 int retry_cnt, max_col, i;
2847 char qbuf[MAXLINE + NIS_MAXNAMELEN];
2849 if (tTd(38, 2))
2850 sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2851 map->map_mname, map->map_file, mode);
2853 mode &= O_ACCMODE;
2854 if (mode != O_RDONLY)
2856 errno = EPERM;
2857 return false;
2860 if (*map->map_file == '\0')
2861 map->map_file = "mail_aliases.org_dir";
2863 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2865 /* set default NISPLUS Domain to $m */
2866 map->map_domain = newstr(nisplus_default_domain());
2867 if (tTd(38, 2))
2868 sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2869 map->map_file, map->map_domain);
2871 if (!PARTIAL_NAME(map->map_file))
2873 map->map_domain = newstr("");
2874 (void) sm_strlcpy(qbuf, map->map_file, sizeof qbuf);
2876 else
2878 /* check to see if this map actually exists */
2879 (void) sm_strlcpyn(qbuf, sizeof qbuf, 3,
2880 map->map_file, ".", map->map_domain);
2883 retry_cnt = 0;
2884 while (res == NULL || res->status != NIS_SUCCESS)
2886 res = nis_lookup(qbuf, FOLLOW_LINKS);
2887 switch (res->status)
2889 case NIS_SUCCESS:
2890 break;
2892 case NIS_TRYAGAIN:
2893 case NIS_RPCERROR:
2894 case NIS_NAMEUNREACHABLE:
2895 if (retry_cnt++ > 4)
2897 errno = EAGAIN;
2898 return false;
2900 /* try not to overwhelm hosed server */
2901 sleep(2);
2902 break;
2904 default: /* all other nisplus errors */
2905 # if 0
2906 if (!bitset(MF_OPTIONAL, map->map_mflags))
2907 syserr("451 4.3.5 Cannot find table %s.%s: %s",
2908 map->map_file, map->map_domain,
2909 nis_sperrno(res->status));
2910 # endif /* 0 */
2911 errno = EAGAIN;
2912 return false;
2916 if (NIS_RES_NUMOBJ(res) != 1 ||
2917 (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
2919 if (tTd(38, 10))
2920 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
2921 # if 0
2922 if (!bitset(MF_OPTIONAL, map->map_mflags))
2923 syserr("451 4.3.5 %s.%s: %s is not a table",
2924 map->map_file, map->map_domain,
2925 nis_sperrno(res->status));
2926 # endif /* 0 */
2927 errno = EBADF;
2928 return false;
2930 /* default key column is column 0 */
2931 if (map->map_keycolnm == NULL)
2932 map->map_keycolnm = newstr(COL_NAME(res,0));
2934 max_col = COL_MAX(res);
2936 /* verify the key column exist */
2937 for (i = 0; i < max_col; i++)
2939 if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
2940 break;
2942 if (i == max_col)
2944 if (tTd(38, 2))
2945 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
2946 map->map_file, map->map_keycolnm);
2947 errno = ENOENT;
2948 return false;
2951 /* default value column is the last column */
2952 if (map->map_valcolnm == NULL)
2954 map->map_valcolno = max_col - 1;
2955 return true;
2958 for (i = 0; i< max_col; i++)
2960 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
2962 map->map_valcolno = i;
2963 return true;
2967 if (tTd(38, 2))
2968 sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
2969 map->map_file, map->map_keycolnm);
2970 errno = ENOENT;
2971 return false;
2976 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
2979 char *
2980 nisplus_map_lookup(map, name, av, statp)
2981 MAP *map;
2982 char *name;
2983 char **av;
2984 int *statp;
2986 char *p;
2987 auto int vsize;
2988 char *skp;
2989 int skleft;
2990 char search_key[MAXNAME + 4];
2991 char qbuf[MAXLINE + NIS_MAXNAMELEN];
2992 nis_result *result;
2994 if (tTd(38, 20))
2995 sm_dprintf("nisplus_map_lookup(%s, %s)\n",
2996 map->map_mname, name);
2998 if (!bitset(MF_OPEN, map->map_mflags))
3000 if (nisplus_map_open(map, O_RDONLY))
3002 map->map_mflags |= MF_OPEN;
3003 map->map_pid = CurrentPid;
3005 else
3007 *statp = EX_UNAVAILABLE;
3008 return NULL;
3013 ** Copy the name to the key buffer, escaping double quote characters
3014 ** by doubling them and quoting "]" and "," to avoid having the
3015 ** NIS+ parser choke on them.
3018 skleft = sizeof search_key - 4;
3019 skp = search_key;
3020 for (p = name; *p != '\0' && skleft > 0; p++)
3022 switch (*p)
3024 case ']':
3025 case ',':
3026 /* quote the character */
3027 *skp++ = '"';
3028 *skp++ = *p;
3029 *skp++ = '"';
3030 skleft -= 3;
3031 break;
3033 case '"':
3034 /* double the quote */
3035 *skp++ = '"';
3036 skleft--;
3037 /* FALLTHROUGH */
3039 default:
3040 *skp++ = *p;
3041 skleft--;
3042 break;
3045 *skp = '\0';
3046 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3047 makelower(search_key);
3049 /* construct the query */
3050 if (PARTIAL_NAME(map->map_file))
3051 (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
3052 map->map_keycolnm, search_key, map->map_file,
3053 map->map_domain);
3054 else
3055 (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
3056 map->map_keycolnm, search_key, map->map_file);
3058 if (tTd(38, 20))
3059 sm_dprintf("qbuf=%s\n", qbuf);
3060 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
3061 if (result->status == NIS_SUCCESS)
3063 int count;
3064 char *str;
3066 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3068 if (LogLevel > 10)
3069 sm_syslog(LOG_WARNING, CurEnv->e_id,
3070 "%s: lookup error, expected 1 entry, got %d",
3071 map->map_file, count);
3073 /* ignore second entry */
3074 if (tTd(38, 20))
3075 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3076 name, count);
3079 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
3080 /* set the length of the result */
3081 if (p == NULL)
3082 p = "";
3083 vsize = strlen(p);
3084 if (tTd(38, 20))
3085 sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3086 name, p);
3087 if (bitset(MF_MATCHONLY, map->map_mflags))
3088 str = map_rewrite(map, name, strlen(name), NULL);
3089 else
3090 str = map_rewrite(map, p, vsize, av);
3091 nis_freeresult(result);
3092 *statp = EX_OK;
3093 return str;
3095 else
3097 if (result->status == NIS_NOTFOUND)
3098 *statp = EX_NOTFOUND;
3099 else if (result->status == NIS_TRYAGAIN)
3100 *statp = EX_TEMPFAIL;
3101 else
3103 *statp = EX_UNAVAILABLE;
3104 map->map_mflags &= ~(MF_VALID|MF_OPEN);
3107 if (tTd(38, 20))
3108 sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
3109 nis_freeresult(result);
3110 return NULL;
3116 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3119 static bool
3120 nisplus_getcanonname(name, hbsize, statp)
3121 char *name;
3122 int hbsize;
3123 int *statp;
3125 char *vp;
3126 auto int vsize;
3127 nis_result *result;
3128 char *p;
3129 char nbuf[MAXNAME + 1];
3130 char qbuf[MAXLINE + NIS_MAXNAMELEN];
3132 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
3134 *statp = EX_UNAVAILABLE;
3135 return false;
3137 (void) shorten_hostname(nbuf);
3139 p = strchr(nbuf, '.');
3140 if (p == NULL)
3142 /* single token */
3143 (void) sm_snprintf(qbuf, sizeof qbuf,
3144 "[name=%s],hosts.org_dir", nbuf);
3146 else if (p[1] != '\0')
3148 /* multi token -- take only first token in nbuf */
3149 *p = '\0';
3150 (void) sm_snprintf(qbuf, sizeof qbuf,
3151 "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
3153 else
3155 *statp = EX_NOHOST;
3156 return false;
3159 if (tTd(38, 20))
3160 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3161 name, qbuf);
3163 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
3164 NULL, NULL);
3166 if (result->status == NIS_SUCCESS)
3168 int count;
3169 char *domain;
3171 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3173 if (LogLevel > 10)
3174 sm_syslog(LOG_WARNING, CurEnv->e_id,
3175 "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3176 count);
3178 /* ignore second entry */
3179 if (tTd(38, 20))
3180 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3181 name, count);
3184 if (tTd(38, 20))
3185 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3186 name, (NIS_RES_OBJECT(result))->zo_domain);
3189 vp = ((NIS_RES_OBJECT(result))->EN_col(0));
3190 vsize = strlen(vp);
3191 if (tTd(38, 20))
3192 sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3193 name, vp);
3194 if (strchr(vp, '.') != NULL)
3196 domain = "";
3198 else
3200 domain = macvalue('m', CurEnv);
3201 if (domain == NULL)
3202 domain = "";
3204 if (hbsize > vsize + (int) strlen(domain) + 1)
3206 if (domain[0] == '\0')
3207 (void) sm_strlcpy(name, vp, hbsize);
3208 else
3209 (void) sm_snprintf(name, hbsize,
3210 "%s.%s", vp, domain);
3211 *statp = EX_OK;
3213 else
3214 *statp = EX_NOHOST;
3215 nis_freeresult(result);
3216 return true;
3218 else
3220 if (result->status == NIS_NOTFOUND)
3221 *statp = EX_NOHOST;
3222 else if (result->status == NIS_TRYAGAIN)
3223 *statp = EX_TEMPFAIL;
3224 else
3225 *statp = EX_UNAVAILABLE;
3227 if (tTd(38, 20))
3228 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3229 name, result->status, *statp);
3230 nis_freeresult(result);
3231 return false;
3234 char *
3235 nisplus_default_domain()
3237 static char default_domain[MAXNAME + 1] = "";
3238 char *p;
3240 if (default_domain[0] != '\0')
3241 return default_domain;
3243 p = nis_local_directory();
3244 (void) sm_strlcpy(default_domain, p, sizeof default_domain);
3245 return default_domain;
3248 #endif /* NISPLUS */
3250 ** LDAP Modules
3254 ** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3257 #if defined(LDAPMAP) || defined(PH_MAP)
3259 # if PH_MAP
3260 # define ph_map_dequote ldapmap_dequote
3261 # endif /* PH_MAP */
3263 static char *ldapmap_dequote __P((char *));
3265 static char *
3266 ldapmap_dequote(str)
3267 char *str;
3269 char *p;
3270 char *start;
3272 if (str == NULL)
3273 return NULL;
3275 p = str;
3276 if (*p == '"')
3278 /* Should probably swallow initial whitespace here */
3279 start = ++p;
3281 else
3282 return str;
3283 while (*p != '"' && *p != '\0')
3284 p++;
3285 if (*p != '\0')
3286 *p = '\0';
3287 return start;
3289 #endif /* defined(LDAPMAP) || defined(PH_MAP) */
3291 #if LDAPMAP
3293 static SM_LDAP_STRUCT *LDAPDefaults = NULL;
3296 ** LDAPMAP_OPEN -- open LDAP map
3298 ** Connect to the LDAP server. Re-use existing connections since a
3299 ** single server connection to a host (with the same host, port,
3300 ** bind DN, and secret) can answer queries for multiple maps.
3303 bool
3304 ldapmap_open(map, mode)
3305 MAP *map;
3306 int mode;
3308 SM_LDAP_STRUCT *lmap;
3309 STAB *s;
3311 if (tTd(38, 2))
3312 sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
3314 mode &= O_ACCMODE;
3316 /* sendmail doesn't have the ability to write to LDAP (yet) */
3317 if (mode != O_RDONLY)
3319 /* issue a pseudo-error message */
3320 errno = SM_EMAPCANTWRITE;
3321 return false;
3324 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3326 s = ldapmap_findconn(lmap);
3327 if (s->s_lmap != NULL)
3329 /* Already have a connection open to this LDAP server */
3330 lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
3331 lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
3333 /* Add this map as head of linked list */
3334 lmap->ldap_next = s->s_lmap;
3335 s->s_lmap = map;
3337 if (tTd(38, 2))
3338 sm_dprintf("using cached connection\n");
3339 return true;
3342 if (tTd(38, 2))
3343 sm_dprintf("opening new connection\n");
3345 /* No connection yet, connect */
3346 if (!sm_ldap_start(map->map_mname, lmap))
3348 if (errno == ETIMEDOUT)
3350 if (LogLevel > 1)
3351 sm_syslog(LOG_NOTICE, CurEnv->e_id,
3352 "timeout conning to LDAP server %.100s",
3353 lmap->ldap_target == NULL ? "localhost" : lmap->ldap_target);
3356 if (!bitset(MF_OPTIONAL, map->map_mflags))
3358 if (bitset(MF_NODEFER, map->map_mflags))
3359 syserr("%s failed to %s in map %s",
3360 # if USE_LDAP_INIT
3361 "ldap_init/ldap_bind",
3362 # else /* USE_LDAP_INIT */
3363 "ldap_open",
3364 # endif /* USE_LDAP_INIT */
3365 lmap->ldap_target == NULL ? "localhost"
3366 : lmap->ldap_target,
3367 map->map_mname);
3368 else
3369 syserr("451 4.3.5 %s failed to %s in map %s",
3370 # if USE_LDAP_INIT
3371 "ldap_init/ldap_bind",
3372 # else /* USE_LDAP_INIT */
3373 "ldap_open",
3374 # endif /* USE_LDAP_INIT */
3375 lmap->ldap_target == NULL ? "localhost"
3376 : lmap->ldap_target,
3377 map->map_mname);
3379 return false;
3382 /* Save connection for reuse */
3383 s->s_lmap = map;
3384 return true;
3388 ** LDAPMAP_CLOSE -- close ldap map
3391 void
3392 ldapmap_close(map)
3393 MAP *map;
3395 SM_LDAP_STRUCT *lmap;
3396 STAB *s;
3398 if (tTd(38, 2))
3399 sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
3401 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3403 /* Check if already closed */
3404 if (lmap->ldap_ld == NULL)
3405 return;
3407 /* Close the LDAP connection */
3408 sm_ldap_close(lmap);
3410 /* Mark all the maps that share the connection as closed */
3411 s = ldapmap_findconn(lmap);
3413 while (s->s_lmap != NULL)
3415 MAP *smap = s->s_lmap;
3417 if (tTd(38, 2) && smap != map)
3418 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3419 map->map_mname, smap->map_mname);
3420 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
3421 lmap = (SM_LDAP_STRUCT *) smap->map_db1;
3422 lmap->ldap_ld = NULL;
3423 s->s_lmap = lmap->ldap_next;
3424 lmap->ldap_next = NULL;
3428 # ifdef SUNET_ID
3430 ** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3431 ** This only makes sense at Stanford University.
3434 static char *
3435 sunet_id_hash(str)
3436 char *str;
3438 char *p, *p_last;
3440 p = str;
3441 p_last = p;
3442 while (*p != '\0')
3444 if (islower(*p) || isdigit(*p))
3446 *p_last = *p;
3447 p_last++;
3449 else if (isupper(*p))
3451 *p_last = tolower(*p);
3452 p_last++;
3454 ++p;
3456 if (*p_last != '\0')
3457 *p_last = '\0';
3458 return str;
3460 # endif /* SUNET_ID */
3463 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3466 char *
3467 ldapmap_lookup(map, name, av, statp)
3468 MAP *map;
3469 char *name;
3470 char **av;
3471 int *statp;
3473 # if _FFR_LDAP_RECURSION
3474 int plen = 0;
3475 int psize = 0;
3476 # else /* _FFR_LDAP_RECURSION */
3477 int entries = 0;
3478 int i;
3479 int ret;
3480 int vsize;
3481 # endif /* _FFR_LDAP_RECURSION */
3482 int msgid;
3483 int save_errno;
3484 char *vp, *p;
3485 char *result = NULL;
3486 SM_LDAP_STRUCT *lmap = NULL;
3487 char keybuf[MAXNAME + 1];
3489 if (tTd(38, 20))
3490 sm_dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name);
3492 /* Get ldap struct pointer from map */
3493 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3494 sm_ldap_setopts(lmap->ldap_ld, lmap);
3496 (void) sm_strlcpy(keybuf, name, sizeof keybuf);
3498 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3500 # ifdef SUNET_ID
3501 sunet_id_hash(keybuf);
3502 # else /* SUNET_ID */
3503 makelower(keybuf);
3504 # endif /* SUNET_ID */
3507 msgid = sm_ldap_search(lmap, keybuf);
3508 if (msgid == -1)
3510 errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
3511 save_errno = errno;
3512 if (!bitset(MF_OPTIONAL, map->map_mflags))
3514 if (bitset(MF_NODEFER, map->map_mflags))
3515 syserr("Error in ldap_search using %s in map %s",
3516 keybuf, map->map_mname);
3517 else
3518 syserr("451 4.3.5 Error in ldap_search using %s in map %s",
3519 keybuf, map->map_mname);
3521 *statp = EX_TEMPFAIL;
3522 switch (save_errno - E_LDAPBASE)
3524 # ifdef LDAP_SERVER_DOWN
3525 case LDAP_SERVER_DOWN:
3526 # endif /* LDAP_SERVER_DOWN */
3527 case LDAP_TIMEOUT:
3528 case LDAP_UNAVAILABLE:
3529 /* server disappeared, try reopen on next search */
3530 ldapmap_close(map);
3531 break;
3533 errno = save_errno;
3534 return NULL;
3537 *statp = EX_NOTFOUND;
3538 vp = NULL;
3540 # if _FFR_LDAP_RECURSION
3542 int flags;
3543 SM_RPOOL_T *rpool;
3545 flags = 0;
3546 if (bitset(MF_SINGLEMATCH, map->map_mflags))
3547 flags |= SM_LDAP_SINGLEMATCH;
3548 if (bitset(MF_MATCHONLY, map->map_mflags))
3549 flags |= SM_LDAP_MATCHONLY;
3551 /* Create an rpool for search related memory usage */
3552 rpool = sm_rpool_new_x(NULL);
3554 p = NULL;
3555 *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
3556 rpool, &p, &plen, &psize, NULL);
3557 save_errno = errno;
3559 /* Copy result so rpool can be freed */
3560 if (*statp == EX_OK && p != NULL)
3561 vp = newstr(p);
3562 sm_rpool_free(rpool);
3564 /* need to restart LDAP connection? */
3565 if (*statp == EX_RESTART)
3567 *statp = EX_TEMPFAIL;
3568 ldapmap_close(map);
3571 errno = save_errno;
3572 if (*statp != EX_OK && *statp != EX_NOTFOUND)
3574 if (!bitset(MF_OPTIONAL, map->map_mflags))
3576 if (bitset(MF_NODEFER, map->map_mflags))
3577 syserr("Error getting LDAP results in map %s",
3578 map->map_mname);
3579 else
3580 syserr("451 4.3.5 Error getting LDAP results in map %s",
3581 map->map_mname);
3583 errno = save_errno;
3584 return NULL;
3587 # else /* _FFR_LDAP_RECURSION */
3589 /* Get results */
3590 while ((ret = ldap_result(lmap->ldap_ld, msgid, 0,
3591 (lmap->ldap_timeout.tv_sec == 0 ? NULL :
3592 &(lmap->ldap_timeout)),
3593 &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
3595 LDAPMessage *entry;
3597 if (bitset(MF_SINGLEMATCH, map->map_mflags))
3599 entries += ldap_count_entries(lmap->ldap_ld,
3600 lmap->ldap_res);
3601 if (entries > 1)
3603 *statp = EX_NOTFOUND;
3604 if (lmap->ldap_res != NULL)
3606 ldap_msgfree(lmap->ldap_res);
3607 lmap->ldap_res = NULL;
3609 (void) ldap_abandon(lmap->ldap_ld, msgid);
3610 if (vp != NULL)
3611 sm_free(vp); /* XXX */
3612 if (tTd(38, 25))
3613 sm_dprintf("ldap search found multiple on a single match query\n");
3614 return NULL;
3618 /* If we don't want multiple values and we have one, break */
3619 if (map->map_coldelim == '\0' && vp != NULL)
3620 break;
3622 /* Cycle through all entries */
3623 for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
3624 entry != NULL;
3625 entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
3627 BerElement *ber;
3628 char *attr;
3629 char **vals = NULL;
3632 ** If matching only and found an entry,
3633 ** no need to spin through attributes
3636 if (*statp == EX_OK &&
3637 bitset(MF_MATCHONLY, map->map_mflags))
3638 continue;
3640 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
3642 ** Reset value to prevent lingering
3643 ** LDAP_DECODING_ERROR due to
3644 ** OpenLDAP 1.X's hack (see below)
3647 lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
3648 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
3650 for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
3651 &ber);
3652 attr != NULL;
3653 attr = ldap_next_attribute(lmap->ldap_ld, entry,
3654 ber))
3656 char *tmp, *vp_tmp;
3658 if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
3660 vals = ldap_get_values(lmap->ldap_ld,
3661 entry,
3662 attr);
3663 if (vals == NULL)
3665 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
3666 if (save_errno == LDAP_SUCCESS)
3668 ldap_memfree(attr);
3669 continue;
3672 /* Must be an error */
3673 save_errno += E_LDAPBASE;
3674 if (!bitset(MF_OPTIONAL,
3675 map->map_mflags))
3677 errno = save_errno;
3678 if (bitset(MF_NODEFER,
3679 map->map_mflags))
3680 syserr("Error getting LDAP values in map %s",
3681 map->map_mname);
3682 else
3683 syserr("451 4.3.5 Error getting LDAP values in map %s",
3684 map->map_mname);
3686 *statp = EX_TEMPFAIL;
3687 ldap_memfree(attr);
3688 if (lmap->ldap_res != NULL)
3690 ldap_msgfree(lmap->ldap_res);
3691 lmap->ldap_res = NULL;
3693 (void) ldap_abandon(lmap->ldap_ld,
3694 msgid);
3695 if (vp != NULL)
3696 sm_free(vp); /* XXX */
3697 errno = save_errno;
3698 return NULL;
3702 *statp = EX_OK;
3704 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
3706 ** Reset value to prevent lingering
3707 ** LDAP_DECODING_ERROR due to
3708 ** OpenLDAP 1.X's hack (see below)
3711 lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
3712 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
3715 ** If matching only,
3716 ** no need to spin through entries
3719 if (bitset(MF_MATCHONLY, map->map_mflags))
3721 if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
3722 ldap_value_free(vals);
3724 ldap_memfree(attr);
3725 continue;
3729 ** If we don't want multiple values,
3730 ** return first found.
3733 if (map->map_coldelim == '\0')
3735 if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
3737 vp = newstr(attr);
3738 ldap_memfree(attr);
3739 break;
3742 if (vals[0] == NULL)
3744 ldap_value_free(vals);
3745 ldap_memfree(attr);
3746 continue;
3749 vsize = strlen(vals[0]) + 1;
3750 if (lmap->ldap_attrsep != '\0')
3751 vsize += strlen(attr) + 1;
3752 vp = xalloc(vsize);
3753 if (lmap->ldap_attrsep != '\0')
3754 sm_snprintf(vp, vsize,
3755 "%s%c%s",
3756 attr,
3757 lmap->ldap_attrsep,
3758 vals[0]);
3759 else
3760 sm_strlcpy(vp, vals[0], vsize);
3761 ldap_value_free(vals);
3762 ldap_memfree(attr);
3763 break;
3766 /* attributes only */
3767 if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
3769 if (vp == NULL)
3770 vp = newstr(attr);
3771 else
3773 vsize = strlen(vp) +
3774 strlen(attr) + 2;
3775 tmp = xalloc(vsize);
3776 (void) sm_snprintf(tmp,
3777 vsize, "%s%c%s",
3778 vp, map->map_coldelim,
3779 attr);
3780 sm_free(vp); /* XXX */
3781 vp = tmp;
3783 ldap_memfree(attr);
3784 continue;
3788 ** If there is more than one,
3789 ** munge then into a map_coldelim
3790 ** separated string
3793 vsize = 0;
3794 for (i = 0; vals[i] != NULL; i++)
3796 vsize += strlen(vals[i]) + 1;
3797 if (lmap->ldap_attrsep != '\0')
3798 vsize += strlen(attr) + 1;
3800 vp_tmp = xalloc(vsize);
3801 *vp_tmp = '\0';
3803 p = vp_tmp;
3804 for (i = 0; vals[i] != NULL; i++)
3806 if (lmap->ldap_attrsep != '\0')
3808 p += sm_strlcpy(p, attr,
3809 vsize - (p - vp_tmp));
3810 if (p >= vp_tmp + vsize)
3811 syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values");
3812 *p++ = lmap->ldap_attrsep;
3814 p += sm_strlcpy(p, vals[i],
3815 vsize - (p - vp_tmp));
3816 if (p >= vp_tmp + vsize)
3817 syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values");
3818 if (vals[i + 1] != NULL)
3819 *p++ = map->map_coldelim;
3822 ldap_value_free(vals);
3823 ldap_memfree(attr);
3824 if (vp == NULL)
3826 vp = vp_tmp;
3827 continue;
3829 vsize = strlen(vp) + strlen(vp_tmp) + 2;
3830 tmp = xalloc(vsize);
3831 (void) sm_snprintf(tmp, vsize, "%s%c%s",
3832 vp, map->map_coldelim, vp_tmp);
3834 sm_free(vp); /* XXX */
3835 sm_free(vp_tmp); /* XXX */
3836 vp = tmp;
3838 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
3841 ** We check errno != LDAP_DECODING_ERROR since
3842 ** OpenLDAP 1.X has a very ugly *undocumented*
3843 ** hack of returning this error code from
3844 ** ldap_next_attribute() if the library freed the
3845 ** ber attribute. See:
3846 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
3849 if (save_errno != LDAP_SUCCESS &&
3850 save_errno != LDAP_DECODING_ERROR)
3852 /* Must be an error */
3853 save_errno += E_LDAPBASE;
3854 if (!bitset(MF_OPTIONAL, map->map_mflags))
3856 errno = save_errno;
3857 if (bitset(MF_NODEFER, map->map_mflags))
3858 syserr("Error getting LDAP attributes in map %s",
3859 map->map_mname);
3860 else
3861 syserr("451 4.3.5 Error getting LDAP attributes in map %s",
3862 map->map_mname);
3864 *statp = EX_TEMPFAIL;
3865 if (lmap->ldap_res != NULL)
3867 ldap_msgfree(lmap->ldap_res);
3868 lmap->ldap_res = NULL;
3870 (void) ldap_abandon(lmap->ldap_ld, msgid);
3871 if (vp != NULL)
3872 sm_free(vp); /* XXX */
3873 errno = save_errno;
3874 return NULL;
3877 /* We don't want multiple values and we have one */
3878 if (map->map_coldelim == '\0' && vp != NULL)
3879 break;
3881 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
3882 if (save_errno != LDAP_SUCCESS &&
3883 save_errno != LDAP_DECODING_ERROR)
3885 /* Must be an error */
3886 save_errno += E_LDAPBASE;
3887 if (!bitset(MF_OPTIONAL, map->map_mflags))
3889 errno = save_errno;
3890 if (bitset(MF_NODEFER, map->map_mflags))
3891 syserr("Error getting LDAP entries in map %s",
3892 map->map_mname);
3893 else
3894 syserr("451 4.3.5 Error getting LDAP entries in map %s",
3895 map->map_mname);
3897 *statp = EX_TEMPFAIL;
3898 if (lmap->ldap_res != NULL)
3900 ldap_msgfree(lmap->ldap_res);
3901 lmap->ldap_res = NULL;
3903 (void) ldap_abandon(lmap->ldap_ld, msgid);
3904 if (vp != NULL)
3905 sm_free(vp); /* XXX */
3906 errno = save_errno;
3907 return NULL;
3909 ldap_msgfree(lmap->ldap_res);
3910 lmap->ldap_res = NULL;
3913 if (ret == 0)
3914 save_errno = ETIMEDOUT;
3915 else
3916 save_errno = sm_ldap_geterrno(lmap->ldap_ld);
3917 if (save_errno != LDAP_SUCCESS)
3919 if (ret != 0)
3920 save_errno += E_LDAPBASE;
3922 if (!bitset(MF_OPTIONAL, map->map_mflags))
3924 errno = save_errno;
3925 if (bitset(MF_NODEFER, map->map_mflags))
3926 syserr("Error getting LDAP results in map %s",
3927 map->map_mname);
3928 else
3929 syserr("451 4.3.5 Error getting LDAP results in map %s",
3930 map->map_mname);
3932 *statp = EX_TEMPFAIL;
3933 if (vp != NULL)
3934 sm_free(vp); /* XXX */
3936 switch (save_errno - E_LDAPBASE)
3938 # ifdef LDAP_SERVER_DOWN
3939 case LDAP_SERVER_DOWN:
3940 # endif /* LDAP_SERVER_DOWN */
3941 case LDAP_TIMEOUT:
3942 case LDAP_UNAVAILABLE:
3943 /* server disappeared, try reopen on next search */
3944 ldapmap_close(map);
3945 break;
3947 errno = save_errno;
3948 return NULL;
3950 # endif /* _FFR_LDAP_RECURSION */
3952 /* Did we match anything? */
3953 if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
3954 return NULL;
3956 if (*statp == EX_OK)
3958 if (LogLevel > 9)
3959 sm_syslog(LOG_INFO, CurEnv->e_id,
3960 "ldap %.100s => %s", name,
3961 vp == NULL ? "<NULL>" : vp);
3962 if (bitset(MF_MATCHONLY, map->map_mflags))
3963 result = map_rewrite(map, name, strlen(name), NULL);
3964 else
3966 /* vp != NULL according to test above */
3967 result = map_rewrite(map, vp, strlen(vp), av);
3969 if (vp != NULL)
3970 sm_free(vp); /* XXX */
3972 return result;
3976 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server
3978 ** Cache LDAP connections based on the host, port, bind DN,
3979 ** secret, and PID so we don't have multiple connections open to
3980 ** the same server for different maps. Need a separate connection
3981 ** per PID since a parent process may close the map before the
3982 ** child is done with it.
3984 ** Parameters:
3985 ** lmap -- LDAP map information
3987 ** Returns:
3988 ** Symbol table entry for the LDAP connection.
3991 static STAB *
3992 ldapmap_findconn(lmap)
3993 SM_LDAP_STRUCT *lmap;
3995 char *format;
3996 char *nbuf;
3997 STAB *SM_NONVOLATILE s = NULL;
3999 # if _FFR_LDAP_SETVERSION
4000 format = "%s%c%d%c%d%c%s%c%s%d";
4001 # else /* _FFR_LDAP_SETVERSION */
4002 format = "%s%c%d%c%s%c%s%d";
4003 # endif /* _FFR_LDAP_SETVERSION */
4004 nbuf = sm_stringf_x(format,
4005 (lmap->ldap_target == NULL ? "localhost"
4006 : lmap->ldap_target),
4007 CONDELSE,
4008 lmap->ldap_port,
4009 CONDELSE,
4010 # if _FFR_LDAP_SETVERSION
4011 lmap->ldap_version,
4012 CONDELSE,
4013 # endif /* _FFR_LDAP_SETVERSION */
4014 (lmap->ldap_binddn == NULL ? ""
4015 : lmap->ldap_binddn),
4016 CONDELSE,
4017 (lmap->ldap_secret == NULL ? ""
4018 : lmap->ldap_secret),
4019 (int) CurrentPid);
4020 SM_TRY
4021 s = stab(nbuf, ST_LMAP, ST_ENTER);
4022 SM_FINALLY
4023 sm_free(nbuf);
4024 SM_END_TRY
4025 return s;
4028 ** LDAPMAP_PARSEARGS -- parse ldap map definition args.
4031 static struct lamvalues LDAPAuthMethods[] =
4033 { "none", LDAP_AUTH_NONE },
4034 { "simple", LDAP_AUTH_SIMPLE },
4035 # ifdef LDAP_AUTH_KRBV4
4036 { "krbv4", LDAP_AUTH_KRBV4 },
4037 # endif /* LDAP_AUTH_KRBV4 */
4038 { NULL, 0 }
4041 static struct ladvalues LDAPAliasDereference[] =
4043 { "never", LDAP_DEREF_NEVER },
4044 { "always", LDAP_DEREF_ALWAYS },
4045 { "search", LDAP_DEREF_SEARCHING },
4046 { "find", LDAP_DEREF_FINDING },
4047 { NULL, 0 }
4050 static struct lssvalues LDAPSearchScope[] =
4052 { "base", LDAP_SCOPE_BASE },
4053 { "one", LDAP_SCOPE_ONELEVEL },
4054 { "sub", LDAP_SCOPE_SUBTREE },
4055 { NULL, 0 }
4058 bool
4059 ldapmap_parseargs(map, args)
4060 MAP *map;
4061 char *args;
4063 bool secretread = true;
4064 # if _FFR_LDAP_URI
4065 bool ldaphost = false;
4066 # endif /* _FFR_LDAP_URI */
4067 int i;
4068 register char *p = args;
4069 SM_LDAP_STRUCT *lmap;
4070 struct lamvalues *lam;
4071 struct ladvalues *lad;
4072 struct lssvalues *lss;
4073 char ldapfilt[MAXLINE];
4074 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
4076 /* Get ldap struct pointer from map */
4077 lmap = (SM_LDAP_STRUCT *) map->map_db1;
4079 /* Check if setting the initial LDAP defaults */
4080 if (lmap == NULL || lmap != LDAPDefaults)
4082 /* We need to alloc an SM_LDAP_STRUCT struct */
4083 lmap = (SM_LDAP_STRUCT *) xalloc(sizeof *lmap);
4084 if (LDAPDefaults == NULL)
4085 sm_ldap_clear(lmap);
4086 else
4087 STRUCTCOPY(*LDAPDefaults, *lmap);
4090 /* there is no check whether there is really an argument */
4091 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4092 map->map_spacesub = SpaceSub; /* default value */
4094 /* Check if setting up an alias or file class LDAP map */
4095 if (bitset(MF_ALIAS, map->map_mflags))
4097 /* Comma separate if used as an alias file */
4098 map->map_coldelim = ',';
4099 if (*args == '\0')
4101 int n;
4102 char *lc;
4103 char jbuf[MAXHOSTNAMELEN];
4104 char lcbuf[MAXLINE];
4106 /* Get $j */
4107 expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
4108 if (jbuf[0] == '\0')
4110 (void) sm_strlcpy(jbuf, "localhost",
4111 sizeof jbuf);
4114 lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
4115 if (lc == NULL)
4116 lc = "";
4117 else
4119 expand(lc, lcbuf, sizeof lcbuf, CurEnv);
4120 lc = lcbuf;
4123 n = sm_snprintf(ldapfilt, sizeof ldapfilt,
4124 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
4125 lc, jbuf);
4126 if (n >= sizeof ldapfilt)
4128 syserr("%s: Default LDAP string too long",
4129 map->map_mname);
4130 return false;
4133 /* default args for an alias LDAP entry */
4134 lmap->ldap_filter = ldapfilt;
4135 lmap->ldap_attr[0] = "sendmailMTAAliasValue";
4136 lmap->ldap_attr[1] = NULL;
4139 else if (bitset(MF_FILECLASS, map->map_mflags))
4141 /* Space separate if used as a file class file */
4142 map->map_coldelim = ' ';
4145 for (;;)
4147 while (isascii(*p) && isspace(*p))
4148 p++;
4149 if (*p != '-')
4150 break;
4151 switch (*++p)
4153 case 'N':
4154 map->map_mflags |= MF_INCLNULL;
4155 map->map_mflags &= ~MF_TRY0NULL;
4156 break;
4158 case 'O':
4159 map->map_mflags &= ~MF_TRY1NULL;
4160 break;
4162 case 'o':
4163 map->map_mflags |= MF_OPTIONAL;
4164 break;
4166 case 'f':
4167 map->map_mflags |= MF_NOFOLDCASE;
4168 break;
4170 case 'm':
4171 map->map_mflags |= MF_MATCHONLY;
4172 break;
4174 case 'A':
4175 map->map_mflags |= MF_APPEND;
4176 break;
4178 case 'q':
4179 map->map_mflags |= MF_KEEPQUOTES;
4180 break;
4182 case 'a':
4183 map->map_app = ++p;
4184 break;
4186 case 'T':
4187 map->map_tapp = ++p;
4188 break;
4190 case 't':
4191 map->map_mflags |= MF_NODEFER;
4192 break;
4194 case 'S':
4195 map->map_spacesub = *++p;
4196 break;
4198 case 'D':
4199 map->map_mflags |= MF_DEFER;
4200 break;
4202 case 'z':
4203 if (*++p != '\\')
4204 map->map_coldelim = *p;
4205 else
4207 switch (*++p)
4209 case 'n':
4210 map->map_coldelim = '\n';
4211 break;
4213 case 't':
4214 map->map_coldelim = '\t';
4215 break;
4217 default:
4218 map->map_coldelim = '\\';
4221 break;
4223 /* Start of ldapmap specific args */
4224 case 'V':
4225 if (*++p != '\\')
4226 lmap->ldap_attrsep = *p;
4227 else
4229 switch (*++p)
4231 case 'n':
4232 lmap->ldap_attrsep = '\n';
4233 break;
4235 case 't':
4236 lmap->ldap_attrsep = '\t';
4237 break;
4239 default:
4240 lmap->ldap_attrsep = '\\';
4243 break;
4245 case 'k': /* search field */
4246 while (isascii(*++p) && isspace(*p))
4247 continue;
4248 lmap->ldap_filter = p;
4249 break;
4251 case 'v': /* attr to return */
4252 while (isascii(*++p) && isspace(*p))
4253 continue;
4254 lmap->ldap_attr[0] = p;
4255 lmap->ldap_attr[1] = NULL;
4256 break;
4258 case '1':
4259 map->map_mflags |= MF_SINGLEMATCH;
4260 break;
4262 /* args stolen from ldapsearch.c */
4263 case 'R': /* don't auto chase referrals */
4264 # ifdef LDAP_REFERRALS
4265 lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
4266 # else /* LDAP_REFERRALS */
4267 syserr("compile with -DLDAP_REFERRALS for referral support");
4268 # endif /* LDAP_REFERRALS */
4269 break;
4271 case 'n': /* retrieve attribute names only */
4272 lmap->ldap_attrsonly = LDAPMAP_TRUE;
4273 break;
4275 case 'r': /* alias dereferencing */
4276 while (isascii(*++p) && isspace(*p))
4277 continue;
4279 if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
4280 p += 11;
4282 for (lad = LDAPAliasDereference;
4283 lad != NULL && lad->lad_name != NULL; lad++)
4285 if (sm_strncasecmp(p, lad->lad_name,
4286 strlen(lad->lad_name)) == 0)
4287 break;
4289 if (lad->lad_name != NULL)
4290 lmap->ldap_deref = lad->lad_code;
4291 else
4293 /* bad config line */
4294 if (!bitset(MCF_OPTFILE,
4295 map->map_class->map_cflags))
4297 char *ptr;
4299 if ((ptr = strchr(p, ' ')) != NULL)
4300 *ptr = '\0';
4301 syserr("Deref must be [never|always|search|find] (not %s) in map %s",
4302 p, map->map_mname);
4303 if (ptr != NULL)
4304 *ptr = ' ';
4305 return false;
4308 break;
4310 case 's': /* search scope */
4311 while (isascii(*++p) && isspace(*p))
4312 continue;
4314 if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
4315 p += 11;
4317 for (lss = LDAPSearchScope;
4318 lss != NULL && lss->lss_name != NULL; lss++)
4320 if (sm_strncasecmp(p, lss->lss_name,
4321 strlen(lss->lss_name)) == 0)
4322 break;
4324 if (lss->lss_name != NULL)
4325 lmap->ldap_scope = lss->lss_code;
4326 else
4328 /* bad config line */
4329 if (!bitset(MCF_OPTFILE,
4330 map->map_class->map_cflags))
4332 char *ptr;
4334 if ((ptr = strchr(p, ' ')) != NULL)
4335 *ptr = '\0';
4336 syserr("Scope must be [base|one|sub] (not %s) in map %s",
4337 p, map->map_mname);
4338 if (ptr != NULL)
4339 *ptr = ' ';
4340 return false;
4343 break;
4345 case 'h': /* ldap host */
4346 while (isascii(*++p) && isspace(*p))
4347 continue;
4348 # if _FFR_LDAP_URI
4349 if (lmap->ldap_uri)
4351 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4352 map->map_mname);
4353 return false;
4355 ldaphost = true;
4356 # endif /* _FFR_LDAP_URI */
4357 lmap->ldap_target = p;
4358 break;
4360 case 'b': /* search base */
4361 while (isascii(*++p) && isspace(*p))
4362 continue;
4363 lmap->ldap_base = p;
4364 break;
4366 case 'p': /* ldap port */
4367 while (isascii(*++p) && isspace(*p))
4368 continue;
4369 lmap->ldap_port = atoi(p);
4370 break;
4372 case 'l': /* time limit */
4373 while (isascii(*++p) && isspace(*p))
4374 continue;
4375 lmap->ldap_timelimit = atoi(p);
4376 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
4377 break;
4379 case 'Z':
4380 while (isascii(*++p) && isspace(*p))
4381 continue;
4382 lmap->ldap_sizelimit = atoi(p);
4383 break;
4385 case 'd': /* Dn to bind to server as */
4386 while (isascii(*++p) && isspace(*p))
4387 continue;
4388 lmap->ldap_binddn = p;
4389 break;
4391 case 'M': /* Method for binding */
4392 while (isascii(*++p) && isspace(*p))
4393 continue;
4395 if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
4396 p += 10;
4398 for (lam = LDAPAuthMethods;
4399 lam != NULL && lam->lam_name != NULL; lam++)
4401 if (sm_strncasecmp(p, lam->lam_name,
4402 strlen(lam->lam_name)) == 0)
4403 break;
4405 if (lam->lam_name != NULL)
4406 lmap->ldap_method = lam->lam_code;
4407 else
4409 /* bad config line */
4410 if (!bitset(MCF_OPTFILE,
4411 map->map_class->map_cflags))
4413 char *ptr;
4415 if ((ptr = strchr(p, ' ')) != NULL)
4416 *ptr = '\0';
4417 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4418 p, map->map_mname);
4419 if (ptr != NULL)
4420 *ptr = ' ';
4421 return false;
4425 break;
4428 ** This is a string that is dependent on the
4429 ** method used defined above.
4432 case 'P': /* Secret password for binding */
4433 while (isascii(*++p) && isspace(*p))
4434 continue;
4435 lmap->ldap_secret = p;
4436 secretread = false;
4437 break;
4439 # if _FFR_LDAP_URI
4440 case 'H': /* Use LDAP URI */
4441 # if !USE_LDAP_INIT
4442 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4443 map->map_mname);
4444 return false;
4445 # else /* !USE_LDAP_INIT */
4446 if (ldaphost)
4448 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4449 map->map_mname);
4450 return false;
4452 while (isascii(*++p) && isspace(*p))
4453 continue;
4454 lmap->ldap_target = p;
4455 lmap->ldap_uri = true;
4456 break;
4457 # endif /* !USE_LDAP_INIT */
4458 # endif /* _FFR_LDAP_URI */
4460 # if _FFR_LDAP_SETVERSION
4461 case 'w':
4462 /* -w should be for passwd, -P should be for version */
4463 while (isascii(*++p) && isspace(*p))
4464 continue;
4465 lmap->ldap_version = atoi(p);
4466 # ifdef LDAP_VERSION_MAX
4467 if (lmap->ldap_version > LDAP_VERSION_MAX)
4469 syserr("LDAP version %d exceeds max of %d in map %s",
4470 lmap->ldap_version, LDAP_VERSION_MAX,
4471 map->map_mname);
4472 return false;
4474 # endif /* LDAP_VERSION_MAX */
4475 # ifdef LDAP_VERSION_MIN
4476 if (lmap->ldap_version < LDAP_VERSION_MIN)
4478 syserr("LDAP version %d is lower than min of %d in map %s",
4479 lmap->ldap_version, LDAP_VERSION_MIN,
4480 map->map_mname);
4481 return false;
4483 # endif /* LDAP_VERSION_MIN */
4484 break;
4485 # endif /* _FFR_LDAP_SETVERSION */
4487 default:
4488 syserr("Illegal option %c map %s", *p, map->map_mname);
4489 break;
4492 /* need to account for quoted strings here */
4493 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4495 if (*p == '"')
4497 while (*++p != '"' && *p != '\0')
4498 continue;
4499 if (*p != '\0')
4500 p++;
4502 else
4503 p++;
4506 if (*p != '\0')
4507 *p++ = '\0';
4510 if (map->map_app != NULL)
4511 map->map_app = newstr(ldapmap_dequote(map->map_app));
4512 if (map->map_tapp != NULL)
4513 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
4516 ** We need to swallow up all the stuff into a struct
4517 ** and dump it into map->map_dbptr1
4520 if (lmap->ldap_target != NULL &&
4521 (LDAPDefaults == NULL ||
4522 LDAPDefaults == lmap ||
4523 LDAPDefaults->ldap_target != lmap->ldap_target))
4524 lmap->ldap_target = newstr(ldapmap_dequote(lmap->ldap_target));
4525 map->map_domain = lmap->ldap_target;
4527 if (lmap->ldap_binddn != NULL &&
4528 (LDAPDefaults == NULL ||
4529 LDAPDefaults == lmap ||
4530 LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
4531 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
4533 if (lmap->ldap_secret != NULL &&
4534 (LDAPDefaults == NULL ||
4535 LDAPDefaults == lmap ||
4536 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4538 SM_FILE_T *sfd;
4539 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
4541 if (DontLockReadFiles)
4542 sff |= SFF_NOLOCK;
4544 /* need to use method to map secret to passwd string */
4545 switch (lmap->ldap_method)
4547 case LDAP_AUTH_NONE:
4548 /* Do nothing */
4549 break;
4551 case LDAP_AUTH_SIMPLE:
4554 ** Secret is the name of a file with
4555 ** the first line as the password.
4558 /* Already read in the secret? */
4559 if (secretread)
4560 break;
4562 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
4563 O_RDONLY, 0, sff);
4564 if (sfd == NULL)
4566 syserr("LDAP map: cannot open secret %s",
4567 ldapmap_dequote(lmap->ldap_secret));
4568 return false;
4570 lmap->ldap_secret = sfgets(m_tmp, sizeof m_tmp,
4571 sfd, TimeOuts.to_fileopen,
4572 "ldapmap_parseargs");
4573 (void) sm_io_close(sfd, SM_TIME_DEFAULT);
4574 if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
4576 syserr("LDAP map: secret in %s too long",
4577 ldapmap_dequote(lmap->ldap_secret));
4578 return false;
4580 if (lmap->ldap_secret != NULL &&
4581 strlen(m_tmp) > 0)
4583 /* chomp newline */
4584 if (m_tmp[strlen(m_tmp) - 1] == '\n')
4585 m_tmp[strlen(m_tmp) - 1] = '\0';
4587 lmap->ldap_secret = m_tmp;
4589 break;
4591 # ifdef LDAP_AUTH_KRBV4
4592 case LDAP_AUTH_KRBV4:
4595 ** Secret is where the ticket file is
4596 ** stashed
4599 (void) sm_snprintf(m_tmp, sizeof m_tmp,
4600 "KRBTKFILE=%s",
4601 ldapmap_dequote(lmap->ldap_secret));
4602 lmap->ldap_secret = m_tmp;
4603 break;
4604 # endif /* LDAP_AUTH_KRBV4 */
4606 default: /* Should NEVER get here */
4607 syserr("LDAP map: Illegal value in lmap method");
4608 return false;
4609 /* NOTREACHED */
4610 break;
4614 if (lmap->ldap_secret != NULL &&
4615 (LDAPDefaults == NULL ||
4616 LDAPDefaults == lmap ||
4617 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4618 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
4620 if (lmap->ldap_base != NULL &&
4621 (LDAPDefaults == NULL ||
4622 LDAPDefaults == lmap ||
4623 LDAPDefaults->ldap_base != lmap->ldap_base))
4624 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
4627 ** Save the server from extra work. If request is for a single
4628 ** match, tell the server to only return enough records to
4629 ** determine if there is a single match or not. This can not
4630 ** be one since the server would only return one and we wouldn't
4631 ** know if there were others available.
4634 if (bitset(MF_SINGLEMATCH, map->map_mflags))
4635 lmap->ldap_sizelimit = 2;
4637 /* If setting defaults, don't process ldap_filter and ldap_attr */
4638 if (lmap == LDAPDefaults)
4639 return true;
4641 if (lmap->ldap_filter != NULL)
4642 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
4643 else
4645 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
4647 syserr("No filter given in map %s", map->map_mname);
4648 return false;
4652 if (lmap->ldap_attr[0] != NULL)
4654 # if _FFR_LDAP_RECURSION
4655 bool recurse = false;
4656 bool normalseen = false;
4657 # endif /* _FFR_LDAP_RECURSION */
4659 i = 0;
4660 p = ldapmap_dequote(lmap->ldap_attr[0]);
4661 lmap->ldap_attr[0] = NULL;
4663 # if _FFR_LDAP_RECURSION
4664 /* Prime the attr list with the objectClass attribute */
4665 lmap->ldap_attr[i] = "objectClass";
4666 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
4667 lmap->ldap_attr_needobjclass[i] = NULL;
4668 i++;
4669 # endif /* _FFR_LDAP_RECURSION */
4671 while (p != NULL)
4673 char *v;
4675 while (isascii(*p) && isspace(*p))
4676 p++;
4677 if (*p == '\0')
4678 break;
4679 v = p;
4680 p = strchr(v, ',');
4681 if (p != NULL)
4682 *p++ = '\0';
4684 if (i >= LDAPMAP_MAX_ATTR)
4686 syserr("Too many return attributes in %s (max %d)",
4687 map->map_mname, LDAPMAP_MAX_ATTR);
4688 return false;
4690 if (*v != '\0')
4692 # if _FFR_LDAP_RECURSION
4693 int j;
4694 int use;
4695 char *type;
4696 char *needobjclass;
4698 type = strchr(v, ':');
4699 if (type != NULL)
4701 *type++ = '\0';
4702 needobjclass = strchr(type, ':');
4703 if (needobjclass != NULL)
4704 *needobjclass++ = '\0';
4706 else
4708 needobjclass = NULL;
4711 use = i;
4713 /* allow override on "objectClass" type */
4714 if (sm_strcasecmp(v, "objectClass") == 0 &&
4715 lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
4717 use = 0;
4719 else
4722 ** Don't add something to attribute
4723 ** list twice.
4726 for (j = 1; j < i; j++)
4728 if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
4730 syserr("Duplicate attribute (%s) in %s",
4731 v, map->map_mname);
4732 return false;
4736 lmap->ldap_attr[use] = newstr(v);
4737 if (needobjclass != NULL &&
4738 *needobjclass != '\0' &&
4739 *needobjclass != '*')
4741 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
4743 else
4745 lmap->ldap_attr_needobjclass[use] = NULL;
4750 if (type != NULL && *type != '\0')
4752 if (sm_strcasecmp(type, "dn") == 0)
4754 recurse = true;
4755 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
4757 else if (sm_strcasecmp(type, "filter") == 0)
4759 recurse = true;
4760 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
4762 else if (sm_strcasecmp(type, "url") == 0)
4764 recurse = true;
4765 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
4767 else if (sm_strcasecmp(type, "normal") == 0)
4769 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4770 normalseen = true;
4772 else
4774 syserr("Unknown attribute type (%s) in %s",
4775 type, map->map_mname);
4776 return false;
4779 else
4781 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4782 normalseen = true;
4784 # else /* _FFR_LDAP_RECURSION */
4785 lmap->ldap_attr[i] = newstr(v);
4786 # endif /* _FFR_LDAP_RECURSION */
4787 i++;
4790 lmap->ldap_attr[i] = NULL;
4791 # if _FFR_LDAP_RECURSION
4792 if (recurse && !normalseen)
4794 syserr("LDAP recursion requested in %s but no returnable attribute given",
4795 map->map_mname);
4796 return false;
4798 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
4800 syserr("LDAP recursion requested in %s can not be used with -n",
4801 map->map_mname);
4802 return false;
4804 # endif /* _FFR_LDAP_RECURSION */
4806 map->map_db1 = (ARBPTR_T) lmap;
4807 return true;
4811 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4813 ** Parameters:
4814 ** spec -- map argument string from LDAPDefaults option
4816 ** Returns:
4817 ** None.
4820 void
4821 ldapmap_set_defaults(spec)
4822 char *spec;
4824 STAB *class;
4825 MAP map;
4827 /* Allocate and set the default values */
4828 if (LDAPDefaults == NULL)
4829 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
4830 sm_ldap_clear(LDAPDefaults);
4832 memset(&map, '\0', sizeof map);
4834 /* look up the class */
4835 class = stab("ldap", ST_MAPCLASS, ST_FIND);
4836 if (class == NULL)
4838 syserr("readcf: LDAPDefaultSpec: class ldap not available");
4839 return;
4841 map.map_class = &class->s_mapclass;
4842 map.map_db1 = (ARBPTR_T) LDAPDefaults;
4843 map.map_mname = "O LDAPDefaultSpec";
4845 (void) ldapmap_parseargs(&map, spec);
4847 /* These should never be set in LDAPDefaults */
4848 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
4849 map.map_spacesub != SpaceSub ||
4850 map.map_app != NULL ||
4851 map.map_tapp != NULL)
4853 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4854 SM_FREE_CLR(map.map_app);
4855 SM_FREE_CLR(map.map_tapp);
4858 if (LDAPDefaults->ldap_filter != NULL)
4860 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4862 /* don't free, it isn't malloc'ed in parseargs */
4863 LDAPDefaults->ldap_filter = NULL;
4866 if (LDAPDefaults->ldap_attr[0] != NULL)
4868 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4869 /* don't free, they aren't malloc'ed in parseargs */
4870 LDAPDefaults->ldap_attr[0] = NULL;
4873 #endif /* LDAPMAP */
4875 ** PH map
4878 #if PH_MAP
4881 ** Support for the CCSO Nameserver (ph/qi).
4882 ** This code is intended to replace the so-called "ph mailer".
4883 ** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support.
4886 /* what version of the ph map code we're running */
4887 static char phmap_id[128];
4889 /* sendmail version for phmap id string */
4890 extern const char Version[];
4892 /* assume we're using nph-1.1.x if not specified */
4893 # ifndef NPH_VERSION
4894 # define NPH_VERSION 10100
4895 # endif
4897 /* compatibility for versions older than nph-1.2.0 */
4898 # if NPH_VERSION < 10200
4899 # define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
4900 # define PH_OPEN_DONTID PH_DONTID
4901 # define PH_CLOSE_FAST PH_FASTCLOSE
4902 # define PH_ERR_DATAERR PH_DATAERR
4903 # define PH_ERR_NOMATCH PH_NOMATCH
4904 # endif /* NPH_VERSION < 10200 */
4907 ** PH_MAP_PARSEARGS -- parse ph map definition args.
4910 bool
4911 ph_map_parseargs(map, args)
4912 MAP *map;
4913 char *args;
4915 register bool done;
4916 register char *p = args;
4917 PH_MAP_STRUCT *pmap = NULL;
4919 /* initialize version string */
4920 (void) sm_snprintf(phmap_id, sizeof phmap_id,
4921 "sendmail-%s phmap-20010529 libphclient-%s",
4922 Version, libphclient_version);
4924 pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap);
4926 /* defaults */
4927 pmap->ph_servers = NULL;
4928 pmap->ph_field_list = NULL;
4929 pmap->ph = NULL;
4930 pmap->ph_timeout = 0;
4931 pmap->ph_fastclose = 0;
4933 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4934 for (;;)
4936 while (isascii(*p) && isspace(*p))
4937 p++;
4938 if (*p != '-')
4939 break;
4940 switch (*++p)
4942 case 'N':
4943 map->map_mflags |= MF_INCLNULL;
4944 map->map_mflags &= ~MF_TRY0NULL;
4945 break;
4947 case 'O':
4948 map->map_mflags &= ~MF_TRY1NULL;
4949 break;
4951 case 'o':
4952 map->map_mflags |= MF_OPTIONAL;
4953 break;
4955 case 'f':
4956 map->map_mflags |= MF_NOFOLDCASE;
4957 break;
4959 case 'm':
4960 map->map_mflags |= MF_MATCHONLY;
4961 break;
4963 case 'A':
4964 map->map_mflags |= MF_APPEND;
4965 break;
4967 case 'q':
4968 map->map_mflags |= MF_KEEPQUOTES;
4969 break;
4971 case 't':
4972 map->map_mflags |= MF_NODEFER;
4973 break;
4975 case 'a':
4976 map->map_app = ++p;
4977 break;
4979 case 'T':
4980 map->map_tapp = ++p;
4981 break;
4983 case 'l':
4984 while (isascii(*++p) && isspace(*p))
4985 continue;
4986 pmap->ph_timeout = atoi(p);
4987 break;
4989 case 'S':
4990 map->map_spacesub = *++p;
4991 break;
4993 case 'D':
4994 map->map_mflags |= MF_DEFER;
4995 break;
4997 case 'h': /* PH server list */
4998 while (isascii(*++p) && isspace(*p))
4999 continue;
5000 pmap->ph_servers = p;
5001 break;
5003 case 'v':
5004 sm_syslog(LOG_WARNING, NULL,
5005 "ph_map_parseargs: WARNING: -v option will be removed in a future release - please use -k instead");
5006 /* intentional fallthrough for backward compatibility */
5007 /* FALLTHROUGH */
5009 case 'k': /* fields to search for */
5010 while (isascii(*++p) && isspace(*p))
5011 continue;
5012 pmap->ph_field_list = p;
5013 break;
5015 default:
5016 syserr("ph_map_parseargs: unknown option -%c", *p);
5019 /* try to account for quoted strings */
5020 done = isascii(*p) && isspace(*p);
5021 while (*p != '\0' && !done)
5023 if (*p == '"')
5025 while (*++p != '"' && *p != '\0')
5026 continue;
5027 if (*p != '\0')
5028 p++;
5030 else
5031 p++;
5032 done = isascii(*p) && isspace(*p);
5035 if (*p != '\0')
5036 *p++ = '\0';
5039 if (map->map_app != NULL)
5040 map->map_app = newstr(ph_map_dequote(map->map_app));
5041 if (map->map_tapp != NULL)
5042 map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
5044 if (pmap->ph_field_list != NULL)
5045 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
5047 if (pmap->ph_servers != NULL)
5048 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
5049 else
5051 syserr("ph_map_parseargs: -h flag is required");
5052 return false;
5055 map->map_db1 = (ARBPTR_T) pmap;
5056 return true;
5060 ** PH_MAP_CLOSE -- close the connection to the ph server
5063 void
5064 ph_map_close(map)
5065 MAP *map;
5067 PH_MAP_STRUCT *pmap;
5069 pmap = (PH_MAP_STRUCT *)map->map_db1;
5070 if (tTd(38, 9))
5071 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
5072 map->map_mname, pmap->ph_fastclose);
5075 if (pmap->ph != NULL)
5077 ph_set_sendhook(pmap->ph, NULL);
5078 ph_set_recvhook(pmap->ph, NULL);
5079 ph_close(pmap->ph, pmap->ph_fastclose);
5082 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
5085 static jmp_buf PHTimeout;
5087 /* ARGSUSED */
5088 static void
5089 ph_timeout(unused)
5090 int unused;
5093 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
5094 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
5095 ** DOING.
5098 errno = ETIMEDOUT;
5099 longjmp(PHTimeout, 1);
5102 static void
5103 #if NPH_VERSION >= 10200
5104 ph_map_send_debug(appdata, text)
5105 void *appdata;
5106 #else
5107 ph_map_send_debug(text)
5108 #endif
5109 char *text;
5111 if (LogLevel > 9)
5112 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5113 "ph_map_send_debug: ==> %s", text);
5114 if (tTd(38, 20))
5115 sm_dprintf("ph_map_send_debug: ==> %s\n", text);
5118 static void
5119 #if NPH_VERSION >= 10200
5120 ph_map_recv_debug(appdata, text)
5121 void *appdata;
5122 #else
5123 ph_map_recv_debug(text)
5124 #endif
5125 char *text;
5127 if (LogLevel > 10)
5128 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5129 "ph_map_recv_debug: <== %s", text);
5130 if (tTd(38, 21))
5131 sm_dprintf("ph_map_recv_debug: <== %s\n", text);
5135 ** PH_MAP_OPEN -- sub for opening PH map
5137 bool
5138 ph_map_open(map, mode)
5139 MAP *map;
5140 int mode;
5142 PH_MAP_STRUCT *pmap;
5143 register SM_EVENT *ev = NULL;
5144 int save_errno = 0;
5145 char *hostlist, *host;
5147 if (tTd(38, 2))
5148 sm_dprintf("ph_map_open(%s)\n", map->map_mname);
5150 mode &= O_ACCMODE;
5151 if (mode != O_RDONLY)
5153 /* issue a pseudo-error message */
5154 errno = SM_EMAPCANTWRITE;
5155 return false;
5158 if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
5159 bitset(MF_DEFER, map->map_mflags))
5161 if (tTd(9, 1))
5162 sm_dprintf("ph_map_open(%s) => DEFERRED\n",
5163 map->map_mname);
5166 ** Unset MF_DEFER here so that map_lookup() returns
5167 ** a temporary failure using the bogus map and
5168 ** map->map_tapp instead of the default permanent error.
5171 map->map_mflags &= ~MF_DEFER;
5172 return false;
5175 pmap = (PH_MAP_STRUCT *)map->map_db1;
5176 pmap->ph_fastclose = 0; /* refresh field for reopen */
5178 /* try each host in the list */
5179 hostlist = newstr(pmap->ph_servers);
5180 for (host = strtok(hostlist, " ");
5181 host != NULL;
5182 host = strtok(NULL, " "))
5184 /* set timeout */
5185 if (pmap->ph_timeout != 0)
5187 if (setjmp(PHTimeout) != 0)
5189 ev = NULL;
5190 if (LogLevel > 1)
5191 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5192 "timeout connecting to PH server %.100s",
5193 host);
5194 errno = ETIMEDOUT;
5195 goto ph_map_open_abort;
5197 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
5200 /* open connection to server */
5201 if (ph_open(&(pmap->ph), host,
5202 PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
5203 ph_map_send_debug, ph_map_recv_debug
5204 #if NPH_VERSION >= 10200
5205 , NULL
5206 #endif
5207 ) == 0
5208 && ph_id(pmap->ph, phmap_id) == 0)
5210 if (ev != NULL)
5211 sm_clrevent(ev);
5212 sm_free(hostlist); /* XXX */
5213 return true;
5216 ph_map_open_abort:
5217 save_errno = errno;
5218 if (ev != NULL)
5219 sm_clrevent(ev);
5220 pmap->ph_fastclose = PH_CLOSE_FAST;
5221 ph_map_close(map);
5222 errno = save_errno;
5225 if (bitset(MF_NODEFER, map->map_mflags))
5227 if (errno == 0)
5228 errno = EAGAIN;
5229 syserr("ph_map_open: %s: cannot connect to PH server",
5230 map->map_mname);
5232 else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
5233 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5234 "ph_map_open: %s: cannot connect to PH server",
5235 map->map_mname);
5236 sm_free(hostlist); /* XXX */
5237 return false;
5241 ** PH_MAP_LOOKUP -- look up key from ph server
5244 char *
5245 ph_map_lookup(map, key, args, pstat)
5246 MAP *map;
5247 char *key;
5248 char **args;
5249 int *pstat;
5251 int i, save_errno = 0;
5252 register SM_EVENT *ev = NULL;
5253 PH_MAP_STRUCT *pmap;
5254 char *value = NULL;
5256 pmap = (PH_MAP_STRUCT *)map->map_db1;
5258 *pstat = EX_OK;
5260 /* set timeout */
5261 if (pmap->ph_timeout != 0)
5263 if (setjmp(PHTimeout) != 0)
5265 ev = NULL;
5266 if (LogLevel > 1)
5267 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5268 "timeout during PH lookup of %.100s",
5269 key);
5270 errno = ETIMEDOUT;
5271 *pstat = EX_TEMPFAIL;
5272 goto ph_map_lookup_abort;
5274 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
5277 /* perform lookup */
5278 i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
5279 if (i == -1)
5280 *pstat = EX_TEMPFAIL;
5281 else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
5282 *pstat = EX_UNAVAILABLE;
5284 ph_map_lookup_abort:
5285 if (ev != NULL)
5286 sm_clrevent(ev);
5289 ** Close the connection if the timer popped
5290 ** or we got a temporary PH error
5293 if (*pstat == EX_TEMPFAIL)
5295 save_errno = errno;
5296 pmap->ph_fastclose = PH_CLOSE_FAST;
5297 ph_map_close(map);
5298 errno = save_errno;
5301 if (*pstat == EX_OK)
5303 if (tTd(38,20))
5304 sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
5306 if (bitset(MF_MATCHONLY, map->map_mflags))
5307 return map_rewrite(map, key, strlen(key), NULL);
5308 else
5309 return map_rewrite(map, value, strlen(value), args);
5312 return NULL;
5314 #endif /* PH_MAP */
5316 ** syslog map
5319 #define map_prio map_lockfd /* overload field */
5322 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
5325 bool
5326 syslog_map_parseargs(map, args)
5327 MAP *map;
5328 char *args;
5330 char *p = args;
5331 char *priority = NULL;
5333 /* there is no check whether there is really an argument */
5334 while (*p != '\0')
5336 while (isascii(*p) && isspace(*p))
5337 p++;
5338 if (*p != '-')
5339 break;
5340 ++p;
5341 if (*p == 'D')
5343 map->map_mflags |= MF_DEFER;
5344 ++p;
5346 else if (*p == 'S')
5348 map->map_spacesub = *++p;
5349 if (*p != '\0')
5350 p++;
5352 else if (*p == 'L')
5354 while (*++p != '\0' && isascii(*p) && isspace(*p))
5355 continue;
5356 if (*p == '\0')
5357 break;
5358 priority = p;
5359 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5360 p++;
5361 if (*p != '\0')
5362 *p++ = '\0';
5364 else
5366 syserr("Illegal option %c map syslog", *p);
5367 ++p;
5371 if (priority == NULL)
5372 map->map_prio = LOG_INFO;
5373 else
5375 if (sm_strncasecmp("LOG_", priority, 4) == 0)
5376 priority += 4;
5378 #ifdef LOG_EMERG
5379 if (sm_strcasecmp("EMERG", priority) == 0)
5380 map->map_prio = LOG_EMERG;
5381 else
5382 #endif /* LOG_EMERG */
5383 #ifdef LOG_ALERT
5384 if (sm_strcasecmp("ALERT", priority) == 0)
5385 map->map_prio = LOG_ALERT;
5386 else
5387 #endif /* LOG_ALERT */
5388 #ifdef LOG_CRIT
5389 if (sm_strcasecmp("CRIT", priority) == 0)
5390 map->map_prio = LOG_CRIT;
5391 else
5392 #endif /* LOG_CRIT */
5393 #ifdef LOG_ERR
5394 if (sm_strcasecmp("ERR", priority) == 0)
5395 map->map_prio = LOG_ERR;
5396 else
5397 #endif /* LOG_ERR */
5398 #ifdef LOG_WARNING
5399 if (sm_strcasecmp("WARNING", priority) == 0)
5400 map->map_prio = LOG_WARNING;
5401 else
5402 #endif /* LOG_WARNING */
5403 #ifdef LOG_NOTICE
5404 if (sm_strcasecmp("NOTICE", priority) == 0)
5405 map->map_prio = LOG_NOTICE;
5406 else
5407 #endif /* LOG_NOTICE */
5408 #ifdef LOG_INFO
5409 if (sm_strcasecmp("INFO", priority) == 0)
5410 map->map_prio = LOG_INFO;
5411 else
5412 #endif /* LOG_INFO */
5413 #ifdef LOG_DEBUG
5414 if (sm_strcasecmp("DEBUG", priority) == 0)
5415 map->map_prio = LOG_DEBUG;
5416 else
5417 #endif /* LOG_DEBUG */
5419 syserr("syslog_map_parseargs: Unknown priority %s",
5420 priority);
5421 return false;
5424 return true;
5428 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
5431 char *
5432 syslog_map_lookup(map, string, args, statp)
5433 MAP *map;
5434 char *string;
5435 char **args;
5436 int *statp;
5438 char *ptr = map_rewrite(map, string, strlen(string), args);
5440 if (ptr != NULL)
5442 if (tTd(38, 20))
5443 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5444 map->map_mname, map->map_prio, ptr);
5446 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
5449 *statp = EX_OK;
5450 return "";
5454 ** HESIOD Modules
5457 #if HESIOD
5459 bool
5460 hes_map_open(map, mode)
5461 MAP *map;
5462 int mode;
5464 if (tTd(38, 2))
5465 sm_dprintf("hes_map_open(%s, %s, %d)\n",
5466 map->map_mname, map->map_file, mode);
5468 if (mode != O_RDONLY)
5470 /* issue a pseudo-error message */
5471 errno = SM_EMAPCANTWRITE;
5472 return false;
5475 # ifdef HESIOD_INIT
5476 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
5477 return true;
5479 if (!bitset(MF_OPTIONAL, map->map_mflags))
5480 syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5481 sm_errstring(errno));
5482 return false;
5483 # else /* HESIOD_INIT */
5484 if (hes_error() == HES_ER_UNINIT)
5485 hes_init();
5486 switch (hes_error())
5488 case HES_ER_OK:
5489 case HES_ER_NOTFOUND:
5490 return true;
5493 if (!bitset(MF_OPTIONAL, map->map_mflags))
5494 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5496 return false;
5497 # endif /* HESIOD_INIT */
5500 char *
5501 hes_map_lookup(map, name, av, statp)
5502 MAP *map;
5503 char *name;
5504 char **av;
5505 int *statp;
5507 char **hp;
5509 if (tTd(38, 20))
5510 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
5512 if (name[0] == '\\')
5514 char *np;
5515 int nl;
5516 int save_errno;
5517 char nbuf[MAXNAME];
5519 nl = strlen(name);
5520 if (nl < sizeof nbuf - 1)
5521 np = nbuf;
5522 else
5523 np = xalloc(strlen(name) + 2);
5524 np[0] = '\\';
5525 (void) sm_strlcpy(&np[1], name, (sizeof nbuf) - 1);
5526 # ifdef HESIOD_INIT
5527 hp = hesiod_resolve(HesiodContext, np, map->map_file);
5528 # else /* HESIOD_INIT */
5529 hp = hes_resolve(np, map->map_file);
5530 # endif /* HESIOD_INIT */
5531 save_errno = errno;
5532 if (np != nbuf)
5533 sm_free(np); /* XXX */
5534 errno = save_errno;
5536 else
5538 # ifdef HESIOD_INIT
5539 hp = hesiod_resolve(HesiodContext, name, map->map_file);
5540 # else /* HESIOD_INIT */
5541 hp = hes_resolve(name, map->map_file);
5542 # endif /* HESIOD_INIT */
5544 # ifdef HESIOD_INIT
5545 if (hp == NULL || *hp == NULL)
5547 switch (errno)
5549 case ENOENT:
5550 *statp = EX_NOTFOUND;
5551 break;
5552 case ECONNREFUSED:
5553 *statp = EX_TEMPFAIL;
5554 break;
5555 case EMSGSIZE:
5556 case ENOMEM:
5557 default:
5558 *statp = EX_UNAVAILABLE;
5559 break;
5561 if (hp != NULL)
5562 hesiod_free_list(HesiodContext, hp);
5563 return NULL;
5565 # else /* HESIOD_INIT */
5566 if (hp == NULL || hp[0] == NULL)
5568 switch (hes_error())
5570 case HES_ER_OK:
5571 *statp = EX_OK;
5572 break;
5574 case HES_ER_NOTFOUND:
5575 *statp = EX_NOTFOUND;
5576 break;
5578 case HES_ER_CONFIG:
5579 *statp = EX_UNAVAILABLE;
5580 break;
5582 case HES_ER_NET:
5583 *statp = EX_TEMPFAIL;
5584 break;
5586 return NULL;
5588 # endif /* HESIOD_INIT */
5590 if (bitset(MF_MATCHONLY, map->map_mflags))
5591 return map_rewrite(map, name, strlen(name), NULL);
5592 else
5593 return map_rewrite(map, hp[0], strlen(hp[0]), av);
5597 ** HES_MAP_CLOSE -- free the Hesiod context
5600 void
5601 hes_map_close(map)
5602 MAP *map;
5604 if (tTd(38, 20))
5605 sm_dprintf("hes_map_close(%s)\n", map->map_file);
5607 # ifdef HESIOD_INIT
5608 /* Free the hesiod context */
5609 if (HesiodContext != NULL)
5611 hesiod_end(HesiodContext);
5612 HesiodContext = NULL;
5614 # endif /* HESIOD_INIT */
5617 #endif /* HESIOD */
5619 ** NeXT NETINFO Modules
5622 #if NETINFO
5624 # define NETINFO_DEFAULT_DIR "/aliases"
5625 # define NETINFO_DEFAULT_PROPERTY "members"
5628 ** NI_MAP_OPEN -- open NetInfo Aliases
5631 bool
5632 ni_map_open(map, mode)
5633 MAP *map;
5634 int mode;
5636 if (tTd(38, 2))
5637 sm_dprintf("ni_map_open(%s, %s, %d)\n",
5638 map->map_mname, map->map_file, mode);
5639 mode &= O_ACCMODE;
5641 if (*map->map_file == '\0')
5642 map->map_file = NETINFO_DEFAULT_DIR;
5644 if (map->map_valcolnm == NULL)
5645 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
5647 if (map->map_coldelim == '\0')
5649 if (bitset(MF_ALIAS, map->map_mflags))
5650 map->map_coldelim = ',';
5651 else if (bitset(MF_FILECLASS, map->map_mflags))
5652 map->map_coldelim = ' ';
5654 return true;
5659 ** NI_MAP_LOOKUP -- look up a datum in NetInfo
5662 char *
5663 ni_map_lookup(map, name, av, statp)
5664 MAP *map;
5665 char *name;
5666 char **av;
5667 int *statp;
5669 char *res;
5670 char *propval;
5672 if (tTd(38, 20))
5673 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
5675 propval = ni_propval(map->map_file, map->map_keycolnm, name,
5676 map->map_valcolnm, map->map_coldelim);
5678 if (propval == NULL)
5679 return NULL;
5681 SM_TRY
5682 if (bitset(MF_MATCHONLY, map->map_mflags))
5683 res = map_rewrite(map, name, strlen(name), NULL);
5684 else
5685 res = map_rewrite(map, propval, strlen(propval), av);
5686 SM_FINALLY
5687 sm_free(propval);
5688 SM_END_TRY
5689 return res;
5693 static bool
5694 ni_getcanonname(name, hbsize, statp)
5695 char *name;
5696 int hbsize;
5697 int *statp;
5699 char *vptr;
5700 char *ptr;
5701 char nbuf[MAXNAME + 1];
5703 if (tTd(38, 20))
5704 sm_dprintf("ni_getcanonname(%s)\n", name);
5706 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5708 *statp = EX_UNAVAILABLE;
5709 return false;
5711 (void) shorten_hostname(nbuf);
5713 /* we only accept single token search key */
5714 if (strchr(nbuf, '.'))
5716 *statp = EX_NOHOST;
5717 return false;
5720 /* Do the search */
5721 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
5723 if (vptr == NULL)
5725 *statp = EX_NOHOST;
5726 return false;
5729 /* Only want the first machine name */
5730 if ((ptr = strchr(vptr, '\n')) != NULL)
5731 *ptr = '\0';
5733 if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
5735 sm_free(vptr);
5736 *statp = EX_UNAVAILABLE;
5737 return true;
5739 sm_free(vptr);
5740 *statp = EX_OK;
5741 return false;
5743 #endif /* NETINFO */
5745 ** TEXT (unindexed text file) Modules
5747 ** This code donated by Sun Microsystems.
5750 #define map_sff map_lockfd /* overload field */
5754 ** TEXT_MAP_OPEN -- open text table
5757 bool
5758 text_map_open(map, mode)
5759 MAP *map;
5760 int mode;
5762 long sff;
5763 int i;
5765 if (tTd(38, 2))
5766 sm_dprintf("text_map_open(%s, %s, %d)\n",
5767 map->map_mname, map->map_file, mode);
5769 mode &= O_ACCMODE;
5770 if (mode != O_RDONLY)
5772 errno = EPERM;
5773 return false;
5776 if (*map->map_file == '\0')
5778 syserr("text map \"%s\": file name required",
5779 map->map_mname);
5780 return false;
5783 if (map->map_file[0] != '/')
5785 syserr("text map \"%s\": file name must be fully qualified",
5786 map->map_mname);
5787 return false;
5790 sff = SFF_ROOTOK|SFF_REGONLY;
5791 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5792 sff |= SFF_NOWLINK;
5793 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5794 sff |= SFF_SAFEDIRPATH;
5795 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
5796 sff, S_IRUSR, NULL)) != 0)
5798 int save_errno = errno;
5800 /* cannot open this map */
5801 if (tTd(38, 2))
5802 sm_dprintf("\tunsafe map file: %d\n", i);
5803 errno = save_errno;
5804 if (!bitset(MF_OPTIONAL, map->map_mflags))
5805 syserr("text map \"%s\": unsafe map file %s",
5806 map->map_mname, map->map_file);
5807 return false;
5810 if (map->map_keycolnm == NULL)
5811 map->map_keycolno = 0;
5812 else
5814 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
5816 syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5817 map->map_mname, map->map_file,
5818 map->map_keycolnm);
5819 return false;
5821 map->map_keycolno = atoi(map->map_keycolnm);
5824 if (map->map_valcolnm == NULL)
5825 map->map_valcolno = 0;
5826 else
5828 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
5830 syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5831 map->map_mname, map->map_file,
5832 map->map_valcolnm);
5833 return false;
5835 map->map_valcolno = atoi(map->map_valcolnm);
5838 if (tTd(38, 2))
5840 sm_dprintf("text_map_open(%s, %s): delimiter = ",
5841 map->map_mname, map->map_file);
5842 if (map->map_coldelim == '\0')
5843 sm_dprintf("(white space)\n");
5844 else
5845 sm_dprintf("%c\n", map->map_coldelim);
5848 map->map_sff = sff;
5849 return true;
5854 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5857 char *
5858 text_map_lookup(map, name, av, statp)
5859 MAP *map;
5860 char *name;
5861 char **av;
5862 int *statp;
5864 char *vp;
5865 auto int vsize;
5866 int buflen;
5867 SM_FILE_T *f;
5868 char delim;
5869 int key_idx;
5870 bool found_it;
5871 long sff = map->map_sff;
5872 char search_key[MAXNAME + 1];
5873 char linebuf[MAXLINE];
5874 char buf[MAXNAME + 1];
5876 found_it = false;
5877 if (tTd(38, 20))
5878 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
5880 buflen = strlen(name);
5881 if (buflen > sizeof search_key - 1)
5882 buflen = sizeof search_key - 1; /* XXX just cut if off? */
5883 memmove(search_key, name, buflen);
5884 search_key[buflen] = '\0';
5885 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
5886 makelower(search_key);
5888 f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
5889 if (f == NULL)
5891 map->map_mflags &= ~(MF_VALID|MF_OPEN);
5892 *statp = EX_UNAVAILABLE;
5893 return NULL;
5895 key_idx = map->map_keycolno;
5896 delim = map->map_coldelim;
5897 while (sm_io_fgets(f, SM_TIME_DEFAULT,
5898 linebuf, sizeof linebuf) != NULL)
5900 char *p;
5902 /* skip comment line */
5903 if (linebuf[0] == '#')
5904 continue;
5905 p = strchr(linebuf, '\n');
5906 if (p != NULL)
5907 *p = '\0';
5908 p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
5909 if (p != NULL && sm_strcasecmp(search_key, p) == 0)
5911 found_it = true;
5912 break;
5915 (void) sm_io_close(f, SM_TIME_DEFAULT);
5916 if (!found_it)
5918 *statp = EX_NOTFOUND;
5919 return NULL;
5921 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
5922 if (vp == NULL)
5924 *statp = EX_NOTFOUND;
5925 return NULL;
5927 vsize = strlen(vp);
5928 *statp = EX_OK;
5929 if (bitset(MF_MATCHONLY, map->map_mflags))
5930 return map_rewrite(map, name, strlen(name), NULL);
5931 else
5932 return map_rewrite(map, vp, vsize, av);
5936 ** TEXT_GETCANONNAME -- look up canonical name in hosts file
5939 static bool
5940 text_getcanonname(name, hbsize, statp)
5941 char *name;
5942 int hbsize;
5943 int *statp;
5945 bool found;
5946 char *dot;
5947 SM_FILE_T *f;
5948 char linebuf[MAXLINE];
5949 char cbuf[MAXNAME + 1];
5950 char nbuf[MAXNAME + 1];
5952 if (tTd(38, 20))
5953 sm_dprintf("text_getcanonname(%s)\n", name);
5955 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5957 *statp = EX_UNAVAILABLE;
5958 return false;
5960 dot = shorten_hostname(nbuf);
5962 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
5963 NULL);
5964 if (f == NULL)
5966 *statp = EX_UNAVAILABLE;
5967 return false;
5969 found = false;
5970 while (!found &&
5971 sm_io_fgets(f, SM_TIME_DEFAULT,
5972 linebuf, sizeof linebuf) != NULL)
5974 char *p = strpbrk(linebuf, "#\n");
5976 if (p != NULL)
5977 *p = '\0';
5978 if (linebuf[0] != '\0')
5979 found = extract_canonname(nbuf, dot, linebuf,
5980 cbuf, sizeof cbuf);
5982 (void) sm_io_close(f, SM_TIME_DEFAULT);
5983 if (!found)
5985 *statp = EX_NOHOST;
5986 return false;
5989 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
5991 *statp = EX_UNAVAILABLE;
5992 return false;
5994 *statp = EX_OK;
5995 return true;
5998 ** STAB (Symbol Table) Modules
6003 ** STAB_MAP_LOOKUP -- look up alias in symbol table
6006 /* ARGSUSED2 */
6007 char *
6008 stab_map_lookup(map, name, av, pstat)
6009 register MAP *map;
6010 char *name;
6011 char **av;
6012 int *pstat;
6014 register STAB *s;
6016 if (tTd(38, 20))
6017 sm_dprintf("stab_lookup(%s, %s)\n",
6018 map->map_mname, name);
6020 s = stab(name, ST_ALIAS, ST_FIND);
6021 if (s != NULL)
6022 return s->s_alias;
6023 return NULL;
6028 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
6031 void
6032 stab_map_store(map, lhs, rhs)
6033 register MAP *map;
6034 char *lhs;
6035 char *rhs;
6037 register STAB *s;
6039 s = stab(lhs, ST_ALIAS, ST_ENTER);
6040 s->s_alias = newstr(rhs);
6045 ** STAB_MAP_OPEN -- initialize (reads data file)
6047 ** This is a wierd case -- it is only intended as a fallback for
6048 ** aliases. For this reason, opens for write (only during a
6049 ** "newaliases") always fails, and opens for read open the
6050 ** actual underlying text file instead of the database.
6053 bool
6054 stab_map_open(map, mode)
6055 register MAP *map;
6056 int mode;
6058 SM_FILE_T *af;
6059 long sff;
6060 struct stat st;
6062 if (tTd(38, 2))
6063 sm_dprintf("stab_map_open(%s, %s, %d)\n",
6064 map->map_mname, map->map_file, mode);
6066 mode &= O_ACCMODE;
6067 if (mode != O_RDONLY)
6069 errno = EPERM;
6070 return false;
6073 sff = SFF_ROOTOK|SFF_REGONLY;
6074 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
6075 sff |= SFF_NOWLINK;
6076 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
6077 sff |= SFF_SAFEDIRPATH;
6078 af = safefopen(map->map_file, O_RDONLY, 0444, sff);
6079 if (af == NULL)
6080 return false;
6081 readaliases(map, af, false, false);
6083 if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
6084 map->map_mtime = st.st_mtime;
6085 (void) sm_io_close(af, SM_TIME_DEFAULT);
6087 return true;
6090 ** Implicit Modules
6092 ** Tries several types. For back compatibility of aliases.
6097 ** IMPL_MAP_LOOKUP -- lookup in best open database
6100 char *
6101 impl_map_lookup(map, name, av, pstat)
6102 MAP *map;
6103 char *name;
6104 char **av;
6105 int *pstat;
6107 if (tTd(38, 20))
6108 sm_dprintf("impl_map_lookup(%s, %s)\n",
6109 map->map_mname, name);
6111 #if NEWDB
6112 if (bitset(MF_IMPL_HASH, map->map_mflags))
6113 return db_map_lookup(map, name, av, pstat);
6114 #endif /* NEWDB */
6115 #if NDBM
6116 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6117 return ndbm_map_lookup(map, name, av, pstat);
6118 #endif /* NDBM */
6119 return stab_map_lookup(map, name, av, pstat);
6123 ** IMPL_MAP_STORE -- store in open databases
6126 void
6127 impl_map_store(map, lhs, rhs)
6128 MAP *map;
6129 char *lhs;
6130 char *rhs;
6132 if (tTd(38, 12))
6133 sm_dprintf("impl_map_store(%s, %s, %s)\n",
6134 map->map_mname, lhs, rhs);
6135 #if NEWDB
6136 if (bitset(MF_IMPL_HASH, map->map_mflags))
6137 db_map_store(map, lhs, rhs);
6138 #endif /* NEWDB */
6139 #if NDBM
6140 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6141 ndbm_map_store(map, lhs, rhs);
6142 #endif /* NDBM */
6143 stab_map_store(map, lhs, rhs);
6147 ** IMPL_MAP_OPEN -- implicit database open
6150 bool
6151 impl_map_open(map, mode)
6152 MAP *map;
6153 int mode;
6155 if (tTd(38, 2))
6156 sm_dprintf("impl_map_open(%s, %s, %d)\n",
6157 map->map_mname, map->map_file, mode);
6159 mode &= O_ACCMODE;
6160 #if NEWDB
6161 map->map_mflags |= MF_IMPL_HASH;
6162 if (hash_map_open(map, mode))
6164 # ifdef NDBM_YP_COMPAT
6165 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
6166 # endif /* NDBM_YP_COMPAT */
6167 return true;
6169 else
6170 map->map_mflags &= ~MF_IMPL_HASH;
6171 #endif /* NEWDB */
6172 #if NDBM
6173 map->map_mflags |= MF_IMPL_NDBM;
6174 if (ndbm_map_open(map, mode))
6176 return true;
6178 else
6179 map->map_mflags &= ~MF_IMPL_NDBM;
6180 #endif /* NDBM */
6182 #if defined(NEWDB) || defined(NDBM)
6183 if (Verbose)
6184 message("WARNING: cannot open alias database %s%s",
6185 map->map_file,
6186 mode == O_RDONLY ? "; reading text version" : "");
6187 #else /* defined(NEWDB) || defined(NDBM) */
6188 if (mode != O_RDONLY)
6189 usrerr("Cannot rebuild aliases: no database format defined");
6190 #endif /* defined(NEWDB) || defined(NDBM) */
6192 if (mode == O_RDONLY)
6193 return stab_map_open(map, mode);
6194 else
6195 return false;
6200 ** IMPL_MAP_CLOSE -- close any open database(s)
6203 void
6204 impl_map_close(map)
6205 MAP *map;
6207 if (tTd(38, 9))
6208 sm_dprintf("impl_map_close(%s, %s, %lx)\n",
6209 map->map_mname, map->map_file, map->map_mflags);
6210 #if NEWDB
6211 if (bitset(MF_IMPL_HASH, map->map_mflags))
6213 db_map_close(map);
6214 map->map_mflags &= ~MF_IMPL_HASH;
6216 #endif /* NEWDB */
6218 #if NDBM
6219 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6221 ndbm_map_close(map);
6222 map->map_mflags &= ~MF_IMPL_NDBM;
6224 #endif /* NDBM */
6227 ** User map class.
6229 ** Provides access to the system password file.
6233 ** USER_MAP_OPEN -- open user map
6235 ** Really just binds field names to field numbers.
6238 bool
6239 user_map_open(map, mode)
6240 MAP *map;
6241 int mode;
6243 if (tTd(38, 2))
6244 sm_dprintf("user_map_open(%s, %d)\n",
6245 map->map_mname, mode);
6247 mode &= O_ACCMODE;
6248 if (mode != O_RDONLY)
6250 /* issue a pseudo-error message */
6251 errno = SM_EMAPCANTWRITE;
6252 return false;
6254 if (map->map_valcolnm == NULL)
6255 /* EMPTY */
6256 /* nothing */ ;
6257 else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
6258 map->map_valcolno = 1;
6259 else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
6260 map->map_valcolno = 2;
6261 else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
6262 map->map_valcolno = 3;
6263 else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
6264 map->map_valcolno = 4;
6265 else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
6266 map->map_valcolno = 5;
6267 else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
6268 map->map_valcolno = 6;
6269 else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
6270 map->map_valcolno = 7;
6271 else
6273 syserr("User map %s: unknown column name %s",
6274 map->map_mname, map->map_valcolnm);
6275 return false;
6277 return true;
6282 ** USER_MAP_LOOKUP -- look up a user in the passwd file.
6285 /* ARGSUSED3 */
6286 char *
6287 user_map_lookup(map, key, av, statp)
6288 MAP *map;
6289 char *key;
6290 char **av;
6291 int *statp;
6293 auto bool fuzzy;
6294 SM_MBDB_T user;
6296 if (tTd(38, 20))
6297 sm_dprintf("user_map_lookup(%s, %s)\n",
6298 map->map_mname, key);
6300 *statp = finduser(key, &fuzzy, &user);
6301 if (*statp != EX_OK)
6302 return NULL;
6303 if (bitset(MF_MATCHONLY, map->map_mflags))
6304 return map_rewrite(map, key, strlen(key), NULL);
6305 else
6307 char *rwval = NULL;
6308 char buf[30];
6310 switch (map->map_valcolno)
6312 case 0:
6313 case 1:
6314 rwval = user.mbdb_name;
6315 break;
6317 case 2:
6318 rwval = "x"; /* passwd no longer supported */
6319 break;
6321 case 3:
6322 (void) sm_snprintf(buf, sizeof buf, "%d",
6323 (int) user.mbdb_uid);
6324 rwval = buf;
6325 break;
6327 case 4:
6328 (void) sm_snprintf(buf, sizeof buf, "%d",
6329 (int) user.mbdb_gid);
6330 rwval = buf;
6331 break;
6333 case 5:
6334 rwval = user.mbdb_fullname;
6335 break;
6337 case 6:
6338 rwval = user.mbdb_homedir;
6339 break;
6341 case 7:
6342 rwval = user.mbdb_shell;
6343 break;
6345 return map_rewrite(map, rwval, strlen(rwval), av);
6349 ** Program map type.
6351 ** This provides access to arbitrary programs. It should be used
6352 ** only very sparingly, since there is no way to bound the cost
6353 ** of invoking an arbitrary program.
6356 char *
6357 prog_map_lookup(map, name, av, statp)
6358 MAP *map;
6359 char *name;
6360 char **av;
6361 int *statp;
6363 int i;
6364 int save_errno;
6365 int fd;
6366 int status;
6367 auto pid_t pid;
6368 register char *p;
6369 char *rval;
6370 char *argv[MAXPV + 1];
6371 char buf[MAXLINE];
6373 if (tTd(38, 20))
6374 sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6375 map->map_mname, name, map->map_file);
6377 i = 0;
6378 argv[i++] = map->map_file;
6379 if (map->map_rebuild != NULL)
6381 (void) sm_strlcpy(buf, map->map_rebuild, sizeof buf);
6382 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
6384 if (i >= MAXPV - 1)
6385 break;
6386 argv[i++] = p;
6389 argv[i++] = name;
6390 argv[i] = NULL;
6391 if (tTd(38, 21))
6393 sm_dprintf("prog_open:");
6394 for (i = 0; argv[i] != NULL; i++)
6395 sm_dprintf(" %s", argv[i]);
6396 sm_dprintf("\n");
6398 (void) sm_blocksignal(SIGCHLD);
6399 pid = prog_open(argv, &fd, CurEnv);
6400 if (pid < 0)
6402 if (!bitset(MF_OPTIONAL, map->map_mflags))
6403 syserr("prog_map_lookup(%s) failed (%s) -- closing",
6404 map->map_mname, sm_errstring(errno));
6405 else if (tTd(38, 9))
6406 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6407 map->map_mname, sm_errstring(errno));
6408 map->map_mflags &= ~(MF_VALID|MF_OPEN);
6409 *statp = EX_OSFILE;
6410 return NULL;
6412 i = read(fd, buf, sizeof buf - 1);
6413 if (i < 0)
6415 syserr("prog_map_lookup(%s): read error %s",
6416 map->map_mname, sm_errstring(errno));
6417 rval = NULL;
6419 else if (i == 0)
6421 if (tTd(38, 20))
6422 sm_dprintf("prog_map_lookup(%s): empty answer\n",
6423 map->map_mname);
6424 rval = NULL;
6426 else
6428 buf[i] = '\0';
6429 p = strchr(buf, '\n');
6430 if (p != NULL)
6431 *p = '\0';
6433 /* collect the return value */
6434 if (bitset(MF_MATCHONLY, map->map_mflags))
6435 rval = map_rewrite(map, name, strlen(name), NULL);
6436 else
6437 rval = map_rewrite(map, buf, strlen(buf), av);
6439 /* now flush any additional output */
6440 while ((i = read(fd, buf, sizeof buf)) > 0)
6441 continue;
6444 /* wait for the process to terminate */
6445 (void) close(fd);
6446 status = waitfor(pid);
6447 save_errno = errno;
6448 (void) sm_releasesignal(SIGCHLD);
6449 errno = save_errno;
6451 if (status == -1)
6453 syserr("prog_map_lookup(%s): wait error %s",
6454 map->map_mname, sm_errstring(errno));
6455 *statp = EX_SOFTWARE;
6456 rval = NULL;
6458 else if (WIFEXITED(status))
6460 if ((*statp = WEXITSTATUS(status)) != EX_OK)
6461 rval = NULL;
6463 else
6465 syserr("prog_map_lookup(%s): child died on signal %d",
6466 map->map_mname, status);
6467 *statp = EX_UNAVAILABLE;
6468 rval = NULL;
6470 return rval;
6473 ** Sequenced map type.
6475 ** Tries each map in order until something matches, much like
6476 ** implicit. Stores go to the first map in the list that can
6477 ** support storing.
6479 ** This is slightly unusual in that there are two interfaces.
6480 ** The "sequence" interface lets you stack maps arbitrarily.
6481 ** The "switch" interface builds a sequence map by looking
6482 ** at a system-dependent configuration file such as
6483 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6485 ** We don't need an explicit open, since all maps are
6486 ** opened on demand.
6490 ** SEQ_MAP_PARSE -- Sequenced map parsing
6493 bool
6494 seq_map_parse(map, ap)
6495 MAP *map;
6496 char *ap;
6498 int maxmap;
6500 if (tTd(38, 2))
6501 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
6502 maxmap = 0;
6503 while (*ap != '\0')
6505 register char *p;
6506 STAB *s;
6508 /* find beginning of map name */
6509 while (isascii(*ap) && isspace(*ap))
6510 ap++;
6511 for (p = ap;
6512 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
6513 p++)
6514 continue;
6515 if (*p != '\0')
6516 *p++ = '\0';
6517 while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
6518 p++;
6519 if (*ap == '\0')
6521 ap = p;
6522 continue;
6524 s = stab(ap, ST_MAP, ST_FIND);
6525 if (s == NULL)
6527 syserr("Sequence map %s: unknown member map %s",
6528 map->map_mname, ap);
6530 else if (maxmap >= MAXMAPSTACK)
6532 syserr("Sequence map %s: too many member maps (%d max)",
6533 map->map_mname, MAXMAPSTACK);
6534 maxmap++;
6536 else if (maxmap < MAXMAPSTACK)
6538 map->map_stack[maxmap++] = &s->s_map;
6540 ap = p;
6542 return true;
6546 ** SWITCH_MAP_OPEN -- open a switched map
6548 ** This looks at the system-dependent configuration and builds
6549 ** a sequence map that does the same thing.
6551 ** Every system must define a switch_map_find routine in conf.c
6552 ** that will return the list of service types associated with a
6553 ** given service class.
6556 bool
6557 switch_map_open(map, mode)
6558 MAP *map;
6559 int mode;
6561 int mapno;
6562 int nmaps;
6563 char *maptype[MAXMAPSTACK];
6565 if (tTd(38, 2))
6566 sm_dprintf("switch_map_open(%s, %s, %d)\n",
6567 map->map_mname, map->map_file, mode);
6569 mode &= O_ACCMODE;
6570 nmaps = switch_map_find(map->map_file, maptype, map->map_return);
6571 if (tTd(38, 19))
6573 sm_dprintf("\tswitch_map_find => %d\n", nmaps);
6574 for (mapno = 0; mapno < nmaps; mapno++)
6575 sm_dprintf("\t\t%s\n", maptype[mapno]);
6577 if (nmaps <= 0 || nmaps > MAXMAPSTACK)
6578 return false;
6580 for (mapno = 0; mapno < nmaps; mapno++)
6582 register STAB *s;
6583 char nbuf[MAXNAME + 1];
6585 if (maptype[mapno] == NULL)
6586 continue;
6587 (void) sm_strlcpyn(nbuf, sizeof nbuf, 3,
6588 map->map_mname, ".", maptype[mapno]);
6589 s = stab(nbuf, ST_MAP, ST_FIND);
6590 if (s == NULL)
6592 syserr("Switch map %s: unknown member map %s",
6593 map->map_mname, nbuf);
6595 else
6597 map->map_stack[mapno] = &s->s_map;
6598 if (tTd(38, 4))
6599 sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6600 mapno,
6601 s->s_map.map_class->map_cname,
6602 nbuf);
6605 return true;
6608 #if 0
6610 ** SEQ_MAP_CLOSE -- close all underlying maps
6613 void
6614 seq_map_close(map)
6615 MAP *map;
6617 int mapno;
6619 if (tTd(38, 9))
6620 sm_dprintf("seq_map_close(%s)\n", map->map_mname);
6622 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6624 MAP *mm = map->map_stack[mapno];
6626 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
6627 continue;
6628 mm->map_mflags |= MF_CLOSING;
6629 mm->map_class->map_close(mm);
6630 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
6633 #endif /* 0 */
6636 ** SEQ_MAP_LOOKUP -- sequenced map lookup
6639 char *
6640 seq_map_lookup(map, key, args, pstat)
6641 MAP *map;
6642 char *key;
6643 char **args;
6644 int *pstat;
6646 int mapno;
6647 int mapbit = 0x01;
6648 bool tempfail = false;
6650 if (tTd(38, 20))
6651 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
6653 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
6655 MAP *mm = map->map_stack[mapno];
6656 char *rv;
6658 if (mm == NULL)
6659 continue;
6660 if (!bitset(MF_OPEN, mm->map_mflags) &&
6661 !openmap(mm))
6663 if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
6665 *pstat = EX_UNAVAILABLE;
6666 return NULL;
6668 continue;
6670 *pstat = EX_OK;
6671 rv = mm->map_class->map_lookup(mm, key, args, pstat);
6672 if (rv != NULL)
6673 return rv;
6674 if (*pstat == EX_TEMPFAIL)
6676 if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
6677 return NULL;
6678 tempfail = true;
6680 else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
6681 break;
6683 if (tempfail)
6684 *pstat = EX_TEMPFAIL;
6685 else if (*pstat == EX_OK)
6686 *pstat = EX_NOTFOUND;
6687 return NULL;
6691 ** SEQ_MAP_STORE -- sequenced map store
6694 void
6695 seq_map_store(map, key, val)
6696 MAP *map;
6697 char *key;
6698 char *val;
6700 int mapno;
6702 if (tTd(38, 12))
6703 sm_dprintf("seq_map_store(%s, %s, %s)\n",
6704 map->map_mname, key, val);
6706 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6708 MAP *mm = map->map_stack[mapno];
6710 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
6711 continue;
6713 mm->map_class->map_store(mm, key, val);
6714 return;
6716 syserr("seq_map_store(%s, %s, %s): no writable map",
6717 map->map_mname, key, val);
6720 ** NULL stubs
6723 /* ARGSUSED */
6724 bool
6725 null_map_open(map, mode)
6726 MAP *map;
6727 int mode;
6729 return true;
6732 /* ARGSUSED */
6733 void
6734 null_map_close(map)
6735 MAP *map;
6737 return;
6740 char *
6741 null_map_lookup(map, key, args, pstat)
6742 MAP *map;
6743 char *key;
6744 char **args;
6745 int *pstat;
6747 *pstat = EX_NOTFOUND;
6748 return NULL;
6751 /* ARGSUSED */
6752 void
6753 null_map_store(map, key, val)
6754 MAP *map;
6755 char *key;
6756 char *val;
6758 return;
6762 ** BOGUS stubs
6765 char *
6766 bogus_map_lookup(map, key, args, pstat)
6767 MAP *map;
6768 char *key;
6769 char **args;
6770 int *pstat;
6772 *pstat = EX_TEMPFAIL;
6773 return NULL;
6776 MAPCLASS BogusMapClass =
6778 "bogus-map", NULL, 0,
6779 NULL, bogus_map_lookup, null_map_store,
6780 null_map_open, null_map_close,
6783 ** MACRO modules
6786 char *
6787 macro_map_lookup(map, name, av, statp)
6788 MAP *map;
6789 char *name;
6790 char **av;
6791 int *statp;
6793 int mid;
6795 if (tTd(38, 20))
6796 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
6797 name == NULL ? "NULL" : name);
6799 if (name == NULL ||
6800 *name == '\0' ||
6801 (mid = macid(name)) == 0)
6803 *statp = EX_CONFIG;
6804 return NULL;
6807 if (av[1] == NULL)
6808 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
6809 else
6810 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
6812 *statp = EX_OK;
6813 return "";
6816 ** REGEX modules
6819 #if MAP_REGEX
6821 # include <regex.h>
6823 # define DEFAULT_DELIM CONDELSE
6824 # define END_OF_FIELDS -1
6825 # define ERRBUF_SIZE 80
6826 # define MAX_MATCH 32
6828 # define xnalloc(s) memset(xalloc(s), '\0', s);
6830 struct regex_map
6832 regex_t *regex_pattern_buf; /* xalloc it */
6833 int *regex_subfields; /* move to type MAP */
6834 char *regex_delim; /* move to type MAP */
6837 static int
6838 parse_fields(s, ibuf, blen, nr_substrings)
6839 char *s;
6840 int *ibuf; /* array */
6841 int blen; /* number of elements in ibuf */
6842 int nr_substrings; /* number of substrings in the pattern */
6844 register char *cp;
6845 int i = 0;
6846 bool lastone = false;
6848 blen--; /* for terminating END_OF_FIELDS */
6849 cp = s;
6852 for (;; cp++)
6854 if (*cp == ',')
6856 *cp = '\0';
6857 break;
6859 if (*cp == '\0')
6861 lastone = true;
6862 break;
6865 if (i < blen)
6867 int val = atoi(s);
6869 if (val < 0 || val >= nr_substrings)
6871 syserr("field (%d) out of range, only %d substrings in pattern",
6872 val, nr_substrings);
6873 return -1;
6875 ibuf[i++] = val;
6877 else
6879 syserr("too many fields, %d max", blen);
6880 return -1;
6882 s = ++cp;
6883 } while (!lastone);
6884 ibuf[i] = END_OF_FIELDS;
6885 return i;
6888 bool
6889 regex_map_init(map, ap)
6890 MAP *map;
6891 char *ap;
6893 int regerr;
6894 struct regex_map *map_p;
6895 register char *p;
6896 char *sub_param = NULL;
6897 int pflags;
6898 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
6900 if (tTd(38, 2))
6901 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6902 map->map_mname, ap);
6904 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
6905 p = ap;
6906 map_p = (struct regex_map *) xnalloc(sizeof *map_p);
6907 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
6909 for (;;)
6911 while (isascii(*p) && isspace(*p))
6912 p++;
6913 if (*p != '-')
6914 break;
6915 switch (*++p)
6917 case 'n': /* not */
6918 map->map_mflags |= MF_REGEX_NOT;
6919 break;
6921 case 'f': /* case sensitive */
6922 map->map_mflags |= MF_NOFOLDCASE;
6923 pflags &= ~REG_ICASE;
6924 break;
6926 case 'b': /* basic regular expressions */
6927 pflags &= ~REG_EXTENDED;
6928 break;
6930 case 's': /* substring match () syntax */
6931 sub_param = ++p;
6932 pflags &= ~REG_NOSUB;
6933 break;
6935 case 'd': /* delimiter */
6936 map_p->regex_delim = ++p;
6937 break;
6939 case 'a': /* map append */
6940 map->map_app = ++p;
6941 break;
6943 case 'm': /* matchonly */
6944 map->map_mflags |= MF_MATCHONLY;
6945 break;
6947 case 'q':
6948 map->map_mflags |= MF_KEEPQUOTES;
6949 break;
6951 case 'S':
6952 map->map_spacesub = *++p;
6953 break;
6955 case 'D':
6956 map->map_mflags |= MF_DEFER;
6957 break;
6960 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
6961 p++;
6962 if (*p != '\0')
6963 *p++ = '\0';
6965 if (tTd(38, 3))
6966 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
6968 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
6970 /* Errorhandling */
6971 char errbuf[ERRBUF_SIZE];
6973 (void) regerror(regerr, map_p->regex_pattern_buf,
6974 errbuf, sizeof errbuf);
6975 syserr("pattern-compile-error: %s", errbuf);
6976 sm_free(map_p->regex_pattern_buf); /* XXX */
6977 sm_free(map_p); /* XXX */
6978 return false;
6981 if (map->map_app != NULL)
6982 map->map_app = newstr(map->map_app);
6983 if (map_p->regex_delim != NULL)
6984 map_p->regex_delim = newstr(map_p->regex_delim);
6985 else
6986 map_p->regex_delim = defdstr;
6988 if (!bitset(REG_NOSUB, pflags))
6990 /* substring matching */
6991 int substrings;
6992 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
6994 substrings = map_p->regex_pattern_buf->re_nsub + 1;
6996 if (tTd(38, 3))
6997 sm_dprintf("regex_map_init: nr of substrings %d\n",
6998 substrings);
7000 if (substrings >= MAX_MATCH)
7002 syserr("too many substrings, %d max", MAX_MATCH);
7003 sm_free(map_p->regex_pattern_buf); /* XXX */
7004 sm_free(map_p); /* XXX */
7005 return false;
7007 if (sub_param != NULL && sub_param[0] != '\0')
7009 /* optional parameter -sfields */
7010 if (parse_fields(sub_param, fields,
7011 MAX_MATCH + 1, substrings) == -1)
7012 return false;
7014 else
7016 int i;
7018 /* set default fields */
7019 for (i = 0; i < substrings; i++)
7020 fields[i] = i;
7021 fields[i] = END_OF_FIELDS;
7023 map_p->regex_subfields = fields;
7024 if (tTd(38, 3))
7026 int *ip;
7028 sm_dprintf("regex_map_init: subfields");
7029 for (ip = fields; *ip != END_OF_FIELDS; ip++)
7030 sm_dprintf(" %d", *ip);
7031 sm_dprintf("\n");
7034 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */
7035 return true;
7038 static char *
7039 regex_map_rewrite(map, s, slen, av)
7040 MAP *map;
7041 const char *s;
7042 size_t slen;
7043 char **av;
7045 if (bitset(MF_MATCHONLY, map->map_mflags))
7046 return map_rewrite(map, av[0], strlen(av[0]), NULL);
7047 else
7048 return map_rewrite(map, s, slen, av);
7051 char *
7052 regex_map_lookup(map, name, av, statp)
7053 MAP *map;
7054 char *name;
7055 char **av;
7056 int *statp;
7058 int reg_res;
7059 struct regex_map *map_p;
7060 regmatch_t pmatch[MAX_MATCH];
7062 if (tTd(38, 20))
7064 char **cpp;
7066 sm_dprintf("regex_map_lookup: key '%s'\n", name);
7067 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7068 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
7071 map_p = (struct regex_map *)(map->map_db1);
7072 reg_res = regexec(map_p->regex_pattern_buf,
7073 name, MAX_MATCH, pmatch, 0);
7075 if (bitset(MF_REGEX_NOT, map->map_mflags))
7077 /* option -n */
7078 if (reg_res == REG_NOMATCH)
7079 return regex_map_rewrite(map, "", (size_t) 0, av);
7080 else
7081 return NULL;
7083 if (reg_res == REG_NOMATCH)
7084 return NULL;
7086 if (map_p->regex_subfields != NULL)
7088 /* option -s */
7089 static char retbuf[MAXNAME];
7090 int fields[MAX_MATCH + 1];
7091 bool first = true;
7092 int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
7093 bool quotemode = false, bslashmode = false;
7094 register char *dp, *sp;
7095 char *endp, *ldp;
7096 int *ip;
7098 dp = retbuf;
7099 ldp = retbuf + sizeof(retbuf) - 1;
7101 if (av[1] != NULL)
7103 if (parse_fields(av[1], fields, MAX_MATCH + 1,
7104 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
7106 *statp = EX_CONFIG;
7107 return NULL;
7109 ip = fields;
7111 else
7112 ip = map_p->regex_subfields;
7114 for ( ; *ip != END_OF_FIELDS; ip++)
7116 if (!first)
7118 for (sp = map_p->regex_delim; *sp; sp++)
7120 if (dp < ldp)
7121 *dp++ = *sp;
7124 else
7125 first = false;
7127 if (*ip >= MAX_MATCH ||
7128 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
7129 continue;
7131 sp = name + pmatch[*ip].rm_so;
7132 endp = name + pmatch[*ip].rm_eo;
7133 for (; endp > sp; sp++)
7135 if (dp < ldp)
7137 if (bslashmode)
7139 *dp++ = *sp;
7140 bslashmode = false;
7142 else if (quotemode && *sp != '"' &&
7143 *sp != '\\')
7145 *dp++ = *sp;
7147 else switch (*dp++ = *sp)
7149 case '\\':
7150 bslashmode = true;
7151 break;
7153 case '(':
7154 cmntcnt++;
7155 break;
7157 case ')':
7158 cmntcnt--;
7159 break;
7161 case '<':
7162 anglecnt++;
7163 break;
7165 case '>':
7166 anglecnt--;
7167 break;
7169 case ' ':
7170 spacecnt++;
7171 break;
7173 case '"':
7174 quotemode = !quotemode;
7175 break;
7180 if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
7181 bslashmode || spacecnt != 0)
7183 sm_syslog(LOG_WARNING, NOQID,
7184 "Warning: regex may cause prescan() failure map=%s lookup=%s",
7185 map->map_mname, name);
7186 return NULL;
7189 *dp = '\0';
7191 return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
7193 return regex_map_rewrite(map, "", (size_t)0, av);
7195 #endif /* MAP_REGEX */
7197 ** NSD modules
7199 #if MAP_NSD
7201 # include <ndbm.h>
7202 # define _DATUM_DEFINED
7203 # include <ns_api.h>
7205 typedef struct ns_map_list
7207 ns_map_t *map; /* XXX ns_ ? */
7208 char *mapname;
7209 struct ns_map_list *next;
7210 } ns_map_list_t;
7212 static ns_map_t *
7213 ns_map_t_find(mapname)
7214 char *mapname;
7216 static ns_map_list_t *ns_maps = NULL;
7217 ns_map_list_t *ns_map;
7219 /* walk the list of maps looking for the correctly named map */
7220 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
7222 if (strcmp(ns_map->mapname, mapname) == 0)
7223 break;
7226 /* if we are looking at a NULL ns_map_list_t, then create a new one */
7227 if (ns_map == NULL)
7229 ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map);
7230 ns_map->mapname = newstr(mapname);
7231 ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map);
7232 memset(ns_map->map, '\0', sizeof *ns_map->map);
7233 ns_map->next = ns_maps;
7234 ns_maps = ns_map;
7236 return ns_map->map;
7239 char *
7240 nsd_map_lookup(map, name, av, statp)
7241 MAP *map;
7242 char *name;
7243 char **av;
7244 int *statp;
7246 int buflen, r;
7247 char *p;
7248 ns_map_t *ns_map;
7249 char keybuf[MAXNAME + 1];
7250 char buf[MAXLINE];
7252 if (tTd(38, 20))
7253 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
7255 buflen = strlen(name);
7256 if (buflen > sizeof keybuf - 1)
7257 buflen = sizeof keybuf - 1; /* XXX simply cut off? */
7258 memmove(keybuf, name, buflen);
7259 keybuf[buflen] = '\0';
7260 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7261 makelower(keybuf);
7263 ns_map = ns_map_t_find(map->map_file);
7264 if (ns_map == NULL)
7266 if (tTd(38, 20))
7267 sm_dprintf("nsd_map_t_find failed\n");
7268 *statp = EX_UNAVAILABLE;
7269 return NULL;
7271 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
7272 buf, sizeof buf);
7273 if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
7275 *statp = EX_TEMPFAIL;
7276 return NULL;
7278 if (r == NS_BADREQ
7279 # ifdef NS_NOPERM
7280 || r == NS_NOPERM
7281 # endif /* NS_NOPERM */
7284 *statp = EX_CONFIG;
7285 return NULL;
7287 if (r != NS_SUCCESS)
7289 *statp = EX_NOTFOUND;
7290 return NULL;
7293 *statp = EX_OK;
7295 /* Null out trailing \n */
7296 if ((p = strchr(buf, '\n')) != NULL)
7297 *p = '\0';
7299 return map_rewrite(map, buf, strlen(buf), av);
7301 #endif /* MAP_NSD */
7303 char *
7304 arith_map_lookup(map, name, av, statp)
7305 MAP *map;
7306 char *name;
7307 char **av;
7308 int *statp;
7310 long r;
7311 long v[2];
7312 bool res = false;
7313 bool boolres;
7314 static char result[16];
7315 char **cpp;
7317 if (tTd(38, 2))
7319 sm_dprintf("arith_map_lookup: key '%s'\n", name);
7320 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7321 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
7323 r = 0;
7324 boolres = false;
7325 cpp = av;
7326 *statp = EX_OK;
7329 ** read arguments for arith map
7330 ** - no check is made whether they are really numbers
7331 ** - just ignores args after the second
7334 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
7335 v[r++] = strtol(*cpp, NULL, 0);
7337 /* operator and (at least) two operands given? */
7338 if (name != NULL && r == 2)
7340 switch (*name)
7342 case '|':
7343 r = v[0] | v[1];
7344 break;
7346 case '&':
7347 r = v[0] & v[1];
7348 break;
7350 case '%':
7351 if (v[1] == 0)
7352 return NULL;
7353 r = v[0] % v[1];
7354 break;
7355 case '+':
7356 r = v[0] + v[1];
7357 break;
7359 case '-':
7360 r = v[0] - v[1];
7361 break;
7363 case '*':
7364 r = v[0] * v[1];
7365 break;
7367 case '/':
7368 if (v[1] == 0)
7369 return NULL;
7370 r = v[0] / v[1];
7371 break;
7373 case 'l':
7374 res = v[0] < v[1];
7375 boolres = true;
7376 break;
7378 case '=':
7379 res = v[0] == v[1];
7380 boolres = true;
7381 break;
7383 default:
7384 /* XXX */
7385 *statp = EX_CONFIG;
7386 if (LogLevel > 10)
7387 sm_syslog(LOG_WARNING, NOQID,
7388 "arith_map: unknown operator %c",
7389 isprint(*name) ? *name : '?');
7390 return NULL;
7392 if (boolres)
7393 (void) sm_snprintf(result, sizeof result,
7394 res ? "TRUE" : "FALSE");
7395 else
7396 (void) sm_snprintf(result, sizeof result, "%ld", r);
7397 return result;
7399 *statp = EX_CONFIG;
7400 return NULL;