1:1 Userland threading stage 4.1/4:
[dragonfly.git] / contrib / sendmail-8.13.8 / sendmail / map.c
blob9a7bf7a558c8b379861083a6fd8ffa549034c546
1 /*
2 * Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers.
3 * All rights reserved.
4 * Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved.
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
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.672 2006/04/18 01:26:41 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 *));
69 #if SOCKETMAP
70 static STAB *socket_map_findconn __P((const char*));
72 /* XXX arbitrary limit for sanity */
73 # define SOCKETMAP_MAXL 1000000
74 #endif /* SOCKETMAP */
76 /* default error message for trying to open a map in write mode */
77 #ifdef ENOSYS
78 # define SM_EMAPCANTWRITE ENOSYS
79 #else /* ENOSYS */
80 # ifdef EFTYPE
81 # define SM_EMAPCANTWRITE EFTYPE
82 # else /* EFTYPE */
83 # define SM_EMAPCANTWRITE ENXIO
84 # endif /* EFTYPE */
85 #endif /* ENOSYS */
88 ** MAP.C -- implementations for various map classes.
90 ** Each map class implements a series of functions:
92 ** bool map_parse(MAP *map, char *args)
93 ** Parse the arguments from the config file. Return true
94 ** if they were ok, false otherwise. Fill in map with the
95 ** values.
97 ** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
98 ** Look up the key in the given map. If found, do any
99 ** rewriting the map wants (including "args" if desired)
100 ** and return the value. Set *pstat to the appropriate status
101 ** on error and return NULL. Args will be NULL if called
102 ** from the alias routines, although this should probably
103 ** not be relied upon. It is suggested you call map_rewrite
104 ** to return the results -- it takes care of null termination
105 ** and uses a dynamically expanded buffer as needed.
107 ** void map_store(MAP *map, char *key, char *value)
108 ** Store the key:value pair in the map.
110 ** bool map_open(MAP *map, int mode)
111 ** Open the map for the indicated mode. Mode should
112 ** be either O_RDONLY or O_RDWR. Return true if it
113 ** was opened successfully, false otherwise. If the open
114 ** failed and the MF_OPTIONAL flag is not set, it should
115 ** also print an error. If the MF_ALIAS bit is set
116 ** and this map class understands the @:@ convention, it
117 ** should call aliaswait() before returning.
119 ** void map_close(MAP *map)
120 ** Close the map.
122 ** This file also includes the implementation for getcanonname.
123 ** It is currently implemented in a pretty ad-hoc manner; it ought
124 ** to be more properly integrated into the map structure.
127 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
128 # define LOCK_ON_OPEN 1 /* we can open/create a locked file */
129 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
130 # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
131 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
134 ** MAP_PARSEARGS -- parse config line arguments for database lookup
136 ** This is a generic version of the map_parse method.
138 ** Parameters:
139 ** map -- the map being initialized.
140 ** ap -- a pointer to the args on the config line.
142 ** Returns:
143 ** true -- if everything parsed OK.
144 ** false -- otherwise.
146 ** Side Effects:
147 ** null terminates the filename; stores it in map
150 bool
151 map_parseargs(map, ap)
152 MAP *map;
153 char *ap;
155 register char *p = ap;
158 ** There is no check whether there is really an argument,
159 ** but that's not important enough to warrant extra code.
162 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
163 map->map_spacesub = SpaceSub; /* default value */
164 for (;;)
166 while (isascii(*p) && isspace(*p))
167 p++;
168 if (*p != '-')
169 break;
170 switch (*++p)
172 case 'N':
173 map->map_mflags |= MF_INCLNULL;
174 map->map_mflags &= ~MF_TRY0NULL;
175 break;
177 case 'O':
178 map->map_mflags &= ~MF_TRY1NULL;
179 break;
181 case 'o':
182 map->map_mflags |= MF_OPTIONAL;
183 break;
185 case 'f':
186 map->map_mflags |= MF_NOFOLDCASE;
187 break;
189 case 'm':
190 map->map_mflags |= MF_MATCHONLY;
191 break;
193 case 'A':
194 map->map_mflags |= MF_APPEND;
195 break;
197 case 'q':
198 map->map_mflags |= MF_KEEPQUOTES;
199 break;
201 case 'a':
202 map->map_app = ++p;
203 break;
205 case 'T':
206 map->map_tapp = ++p;
207 break;
209 case 'k':
210 while (isascii(*++p) && isspace(*p))
211 continue;
212 map->map_keycolnm = p;
213 break;
215 case 'v':
216 while (isascii(*++p) && isspace(*p))
217 continue;
218 map->map_valcolnm = p;
219 break;
221 case 'z':
222 if (*++p != '\\')
223 map->map_coldelim = *p;
224 else
226 switch (*++p)
228 case 'n':
229 map->map_coldelim = '\n';
230 break;
232 case 't':
233 map->map_coldelim = '\t';
234 break;
236 default:
237 map->map_coldelim = '\\';
240 break;
242 case 't':
243 map->map_mflags |= MF_NODEFER;
244 break;
247 case 'S':
248 map->map_spacesub = *++p;
249 break;
251 case 'D':
252 map->map_mflags |= MF_DEFER;
253 break;
255 default:
256 syserr("Illegal option %c map %s", *p, map->map_mname);
257 break;
259 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
260 p++;
261 if (*p != '\0')
262 *p++ = '\0';
264 if (map->map_app != NULL)
265 map->map_app = newstr(map->map_app);
266 if (map->map_tapp != NULL)
267 map->map_tapp = newstr(map->map_tapp);
268 if (map->map_keycolnm != NULL)
269 map->map_keycolnm = newstr(map->map_keycolnm);
270 if (map->map_valcolnm != NULL)
271 map->map_valcolnm = newstr(map->map_valcolnm);
273 if (*p != '\0')
275 map->map_file = p;
276 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
277 p++;
278 if (*p != '\0')
279 *p++ = '\0';
280 map->map_file = newstr(map->map_file);
283 while (*p != '\0' && isascii(*p) && isspace(*p))
284 p++;
285 if (*p != '\0')
286 map->map_rebuild = newstr(p);
288 if (map->map_file == NULL &&
289 !bitset(MCF_OPTFILE, map->map_class->map_cflags))
291 syserr("No file name for %s map %s",
292 map->map_class->map_cname, map->map_mname);
293 return false;
295 return true;
298 ** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
300 ** It also adds the map_app string. It can be used as a utility
301 ** in the map_lookup method.
303 ** Parameters:
304 ** map -- the map that causes this.
305 ** s -- the string to rewrite, NOT necessarily null terminated.
306 ** slen -- the length of s.
307 ** av -- arguments to interpolate into buf.
309 ** Returns:
310 ** Pointer to rewritten result. This is static data that
311 ** should be copied if it is to be saved!
314 char *
315 map_rewrite(map, s, slen, av)
316 register MAP *map;
317 register const char *s;
318 size_t slen;
319 char **av;
321 register char *bp;
322 register char c;
323 char **avp;
324 register char *ap;
325 size_t l;
326 size_t len;
327 static size_t buflen = 0;
328 static char *buf = NULL;
330 if (tTd(39, 1))
332 sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
333 if (av == NULL)
334 sm_dprintf(" (nullv)");
335 else
337 for (avp = av; *avp != NULL; avp++)
338 sm_dprintf("\n\t%s", *avp);
340 sm_dprintf("\n");
343 /* count expected size of output (can safely overestimate) */
344 l = len = slen;
345 if (av != NULL)
347 const char *sp = s;
349 while (l-- > 0 && (c = *sp++) != '\0')
351 if (c != '%')
352 continue;
353 if (l-- <= 0)
354 break;
355 c = *sp++;
356 if (!(isascii(c) && isdigit(c)))
357 continue;
358 for (avp = av; --c >= '0' && *avp != NULL; avp++)
359 continue;
360 if (*avp == NULL)
361 continue;
362 len += strlen(*avp);
365 if (map->map_app != NULL)
366 len += strlen(map->map_app);
367 if (buflen < ++len)
369 /* need to malloc additional space */
370 buflen = len;
371 if (buf != NULL)
372 sm_free(buf);
373 buf = sm_pmalloc_x(buflen);
376 bp = buf;
377 if (av == NULL)
379 memmove(bp, s, slen);
380 bp += slen;
382 /* assert(len > slen); */
383 len -= slen;
385 else
387 while (slen-- > 0 && (c = *s++) != '\0')
389 if (c != '%')
391 pushc:
392 if (len-- <= 1)
393 break;
394 *bp++ = c;
395 continue;
397 if (slen-- <= 0 || (c = *s++) == '\0')
398 c = '%';
399 if (c == '%')
400 goto pushc;
401 if (!(isascii(c) && isdigit(c)))
403 if (len-- <= 1)
404 break;
405 *bp++ = '%';
406 goto pushc;
408 for (avp = av; --c >= '0' && *avp != NULL; avp++)
409 continue;
410 if (*avp == NULL)
411 continue;
413 /* transliterate argument into output string */
414 for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
415 *bp++ = c;
418 if (map->map_app != NULL && len > 0)
419 (void) sm_strlcpy(bp, map->map_app, len);
420 else
421 *bp = '\0';
422 if (tTd(39, 1))
423 sm_dprintf("map_rewrite => %s\n", buf);
424 return buf;
427 ** INITMAPS -- rebuild alias maps
429 ** Parameters:
430 ** none.
432 ** Returns:
433 ** none.
436 void
437 initmaps()
439 #if XDEBUG
440 checkfd012("entering initmaps");
441 #endif /* XDEBUG */
442 stabapply(map_init, 0);
443 #if XDEBUG
444 checkfd012("exiting initmaps");
445 #endif /* XDEBUG */
448 ** MAP_INIT -- rebuild a map
450 ** Parameters:
451 ** s -- STAB entry: if map: try to rebuild
452 ** unused -- unused variable
454 ** Returns:
455 ** none.
457 ** Side Effects:
458 ** will close already open rebuildable map.
461 /* ARGSUSED1 */
462 static void
463 map_init(s, unused)
464 register STAB *s;
465 int unused;
467 register MAP *map;
469 /* has to be a map */
470 if (s->s_symtype != ST_MAP)
471 return;
473 map = &s->s_map;
474 if (!bitset(MF_VALID, map->map_mflags))
475 return;
477 if (tTd(38, 2))
478 sm_dprintf("map_init(%s:%s, %s)\n",
479 map->map_class->map_cname == NULL ? "NULL" :
480 map->map_class->map_cname,
481 map->map_mname == NULL ? "NULL" : map->map_mname,
482 map->map_file == NULL ? "NULL" : map->map_file);
484 if (!bitset(MF_ALIAS, map->map_mflags) ||
485 !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
487 if (tTd(38, 3))
488 sm_dprintf("\tnot rebuildable\n");
489 return;
492 /* if already open, close it (for nested open) */
493 if (bitset(MF_OPEN, map->map_mflags))
495 map->map_mflags |= MF_CLOSING;
496 map->map_class->map_close(map);
497 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
500 (void) rebuildaliases(map, false);
501 return;
504 ** OPENMAP -- open a map
506 ** Parameters:
507 ** map -- map to open (it must not be open).
509 ** Returns:
510 ** whether open succeeded.
513 bool
514 openmap(map)
515 MAP *map;
517 bool restore = false;
518 bool savehold = HoldErrs;
519 bool savequick = QuickAbort;
520 int saveerrors = Errors;
522 if (!bitset(MF_VALID, map->map_mflags))
523 return false;
525 /* better safe than sorry... */
526 if (bitset(MF_OPEN, map->map_mflags))
527 return true;
529 /* Don't send a map open error out via SMTP */
530 if ((OnlyOneError || QuickAbort) &&
531 (OpMode == MD_SMTP || OpMode == MD_DAEMON))
533 restore = true;
534 HoldErrs = true;
535 QuickAbort = false;
538 errno = 0;
539 if (map->map_class->map_open(map, O_RDONLY))
541 if (tTd(38, 4))
542 sm_dprintf("openmap()\t%s:%s %s: valid\n",
543 map->map_class->map_cname == NULL ? "NULL" :
544 map->map_class->map_cname,
545 map->map_mname == NULL ? "NULL" :
546 map->map_mname,
547 map->map_file == NULL ? "NULL" :
548 map->map_file);
549 map->map_mflags |= MF_OPEN;
550 map->map_pid = CurrentPid;
552 else
554 if (tTd(38, 4))
555 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
556 map->map_class->map_cname == NULL ? "NULL" :
557 map->map_class->map_cname,
558 map->map_mname == NULL ? "NULL" :
559 map->map_mname,
560 map->map_file == NULL ? "NULL" :
561 map->map_file,
562 errno == 0 ? "" : ": ",
563 errno == 0 ? "" : sm_errstring(errno));
564 if (!bitset(MF_OPTIONAL, map->map_mflags))
566 extern MAPCLASS BogusMapClass;
568 map->map_orgclass = map->map_class;
569 map->map_class = &BogusMapClass;
570 map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
571 map->map_pid = CurrentPid;
573 else
575 /* don't try again */
576 map->map_mflags &= ~MF_VALID;
580 if (restore)
582 Errors = saveerrors;
583 HoldErrs = savehold;
584 QuickAbort = savequick;
587 return bitset(MF_OPEN, map->map_mflags);
590 ** CLOSEMAPS -- close all open maps opened by the current pid.
592 ** Parameters:
593 ** bogus -- only close bogus maps.
595 ** Returns:
596 ** none.
599 void
600 closemaps(bogus)
601 bool bogus;
603 stabapply(map_close, bogus);
606 ** MAP_CLOSE -- close a map opened by the current pid.
608 ** Parameters:
609 ** s -- STAB entry: if map: try to close
610 ** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
612 ** Returns:
613 ** none.
616 /* ARGSUSED1 */
617 static void
618 map_close(s, bogus)
619 register STAB *s;
620 int bogus; /* int because of stabapply(), used as bool */
622 MAP *map;
623 extern MAPCLASS BogusMapClass;
625 if (s->s_symtype != ST_MAP)
626 return;
628 map = &s->s_map;
631 ** close the map iff:
632 ** it is valid and open and opened by this process
633 ** and (!bogus or it's a bogus map or it is not persistent)
634 ** negate this: return iff
635 ** it is not valid or it is not open or not opened by this process
636 ** or (bogus and it's not a bogus map and it's not not-persistent)
639 if (!bitset(MF_VALID, map->map_mflags) ||
640 !bitset(MF_OPEN, map->map_mflags) ||
641 bitset(MF_CLOSING, map->map_mflags) ||
642 map->map_pid != CurrentPid ||
643 (bogus && map->map_class != &BogusMapClass &&
644 !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
645 return;
647 if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
648 map->map_orgclass != &BogusMapClass)
649 map->map_class = map->map_orgclass;
650 if (tTd(38, 5))
651 sm_dprintf("closemaps: closing %s (%s)\n",
652 map->map_mname == NULL ? "NULL" : map->map_mname,
653 map->map_file == NULL ? "NULL" : map->map_file);
655 if (!bitset(MF_OPENBOGUS, map->map_mflags))
657 map->map_mflags |= MF_CLOSING;
658 map->map_class->map_close(map);
660 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
663 ** GETCANONNAME -- look up name using service switch
665 ** Parameters:
666 ** host -- the host name to look up.
667 ** hbsize -- the size of the host buffer.
668 ** trymx -- if set, try MX records.
669 ** pttl -- pointer to return TTL (can be NULL).
671 ** Returns:
672 ** true -- if the host was found.
673 ** false -- otherwise.
676 bool
677 getcanonname(host, hbsize, trymx, pttl)
678 char *host;
679 int hbsize;
680 bool trymx;
681 int *pttl;
683 int nmaps;
684 int mapno;
685 bool found = false;
686 bool got_tempfail = false;
687 auto int status;
688 char *maptype[MAXMAPSTACK];
689 short mapreturn[MAXMAPACTIONS];
691 nmaps = switch_map_find("hosts", maptype, mapreturn);
692 if (pttl != 0)
693 *pttl = SM_DEFAULT_TTL;
694 for (mapno = 0; mapno < nmaps; mapno++)
696 int i;
698 if (tTd(38, 20))
699 sm_dprintf("getcanonname(%s), trying %s\n",
700 host, maptype[mapno]);
701 if (strcmp("files", maptype[mapno]) == 0)
703 found = text_getcanonname(host, hbsize, &status);
705 #if NIS
706 else if (strcmp("nis", maptype[mapno]) == 0)
708 found = nis_getcanonname(host, hbsize, &status);
710 #endif /* NIS */
711 #if NISPLUS
712 else if (strcmp("nisplus", maptype[mapno]) == 0)
714 found = nisplus_getcanonname(host, hbsize, &status);
716 #endif /* NISPLUS */
717 #if NAMED_BIND
718 else if (strcmp("dns", maptype[mapno]) == 0)
720 found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
722 #endif /* NAMED_BIND */
723 #if NETINFO
724 else if (strcmp("netinfo", maptype[mapno]) == 0)
726 found = ni_getcanonname(host, hbsize, &status);
728 #endif /* NETINFO */
729 else
731 found = false;
732 status = EX_UNAVAILABLE;
736 ** Heuristic: if $m is not set, we are running during system
737 ** startup. In this case, when a name is apparently found
738 ** but has no dot, treat is as not found. This avoids
739 ** problems if /etc/hosts has no FQDN but is listed first
740 ** in the service switch.
743 if (found &&
744 (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
745 break;
747 /* see if we should continue */
748 if (status == EX_TEMPFAIL)
750 i = MA_TRYAGAIN;
751 got_tempfail = true;
753 else if (status == EX_NOTFOUND)
754 i = MA_NOTFOUND;
755 else
756 i = MA_UNAVAIL;
757 if (bitset(1 << mapno, mapreturn[i]))
758 break;
761 if (found)
763 char *d;
765 if (tTd(38, 20))
766 sm_dprintf("getcanonname(%s), found\n", host);
769 ** If returned name is still single token, compensate
770 ** by tagging on $m. This is because some sites set
771 ** up their DNS or NIS databases wrong.
774 if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
776 d = macvalue('m', CurEnv);
777 if (d != NULL &&
778 hbsize > (int) (strlen(host) + strlen(d) + 1))
780 if (host[strlen(host) - 1] != '.')
781 (void) sm_strlcat2(host, ".", d,
782 hbsize);
783 else
784 (void) sm_strlcat(host, d, hbsize);
786 else
787 return false;
789 return true;
792 if (tTd(38, 20))
793 sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
794 status);
796 if (got_tempfail)
797 SM_SET_H_ERRNO(TRY_AGAIN);
798 else
799 SM_SET_H_ERRNO(HOST_NOT_FOUND);
801 return false;
804 ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
806 ** Parameters:
807 ** name -- the name against which to match.
808 ** dot -- where to reinsert '.' to get FQDN
809 ** line -- the /etc/hosts line.
810 ** cbuf -- the location to store the result.
811 ** cbuflen -- the size of cbuf.
813 ** Returns:
814 ** true -- if the line matched the desired name.
815 ** false -- otherwise.
818 static bool
819 extract_canonname(name, dot, line, cbuf, cbuflen)
820 char *name;
821 char *dot;
822 char *line;
823 char cbuf[];
824 int cbuflen;
826 int i;
827 char *p;
828 bool found = false;
830 cbuf[0] = '\0';
831 if (line[0] == '#')
832 return false;
834 for (i = 1; ; i++)
836 char nbuf[MAXNAME + 1];
838 p = get_column(line, i, '\0', nbuf, sizeof nbuf);
839 if (p == NULL)
840 break;
841 if (*p == '\0')
842 continue;
843 if (cbuf[0] == '\0' ||
844 (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
846 (void) sm_strlcpy(cbuf, p, cbuflen);
848 if (sm_strcasecmp(name, p) == 0)
849 found = true;
850 else if (dot != NULL)
852 /* try looking for the FQDN as well */
853 *dot = '.';
854 if (sm_strcasecmp(name, p) == 0)
855 found = true;
856 *dot = '\0';
859 if (found && strchr(cbuf, '.') == NULL)
861 /* try to add a domain on the end of the name */
862 char *domain = macvalue('m', CurEnv);
864 if (domain != NULL &&
865 strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
867 p = &cbuf[i];
868 *p++ = '.';
869 (void) sm_strlcpy(p, domain, cbuflen - i - 1);
872 return found;
876 ** DNS modules
879 #if NAMED_BIND
880 # if DNSMAP
882 # include "sm_resolve.h"
883 # if NETINET || NETINET6
884 # include <arpa/inet.h>
885 # endif /* NETINET || NETINET6 */
888 ** DNS_MAP_OPEN -- stub to check proper value for dns map type
891 bool
892 dns_map_open(map, mode)
893 MAP *map;
894 int mode;
896 if (tTd(38,2))
897 sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
899 mode &= O_ACCMODE;
900 if (mode != O_RDONLY)
902 /* issue a pseudo-error message */
903 errno = SM_EMAPCANTWRITE;
904 return false;
906 return true;
910 ** DNS_MAP_PARSEARGS -- parse dns map definition args.
912 ** Parameters:
913 ** map -- pointer to MAP
914 ** args -- pointer to the args on the config line.
916 ** Returns:
917 ** true -- if everything parsed OK.
918 ** false -- otherwise.
921 # if _FFR_DNSMAP_MULTILIMIT
922 # if !_FFR_DNSMAP_MULTI
923 ERROR README: You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT
924 # endif /* ! _FFR_DNSMAP_MULTI */
925 # endif /* _FFR_DNSMAP_MULTILIMIT */
927 # if _FFR_DNSMAP_MULTI
928 # if _FFR_DNSMAP_MULTILIMIT
929 # define map_sizelimit map_lockfd /* overload field */
930 # endif /* _FFR_DNSMAP_MULTILIMIT */
931 # endif /* _FFR_DNSMAP_MULTI */
933 struct dns_map
935 int dns_m_type;
938 bool
939 dns_map_parseargs(map,args)
940 MAP *map;
941 char *args;
943 register char *p = args;
944 struct dns_map *map_p;
946 map_p = (struct dns_map *) xalloc(sizeof *map_p);
947 map_p->dns_m_type = -1;
948 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
950 for (;;)
952 while (isascii(*p) && isspace(*p))
953 p++;
954 if (*p != '-')
955 break;
956 switch (*++p)
958 case 'N':
959 map->map_mflags |= MF_INCLNULL;
960 map->map_mflags &= ~MF_TRY0NULL;
961 break;
963 case 'O':
964 map->map_mflags &= ~MF_TRY1NULL;
965 break;
967 case 'o':
968 map->map_mflags |= MF_OPTIONAL;
969 break;
971 case 'f':
972 map->map_mflags |= MF_NOFOLDCASE;
973 break;
975 case 'm':
976 map->map_mflags |= MF_MATCHONLY;
977 break;
979 case 'A':
980 map->map_mflags |= MF_APPEND;
981 break;
983 case 'q':
984 map->map_mflags |= MF_KEEPQUOTES;
985 break;
987 case 't':
988 map->map_mflags |= MF_NODEFER;
989 break;
991 case 'a':
992 map->map_app = ++p;
993 break;
995 case 'T':
996 map->map_tapp = ++p;
997 break;
999 case 'd':
1001 char *h;
1003 ++p;
1004 h = strchr(p, ' ');
1005 if (h != NULL)
1006 *h = '\0';
1007 map->map_timeout = convtime(p, 's');
1008 if (h != NULL)
1009 *h = ' ';
1011 break;
1013 case 'r':
1014 while (isascii(*++p) && isspace(*p))
1015 continue;
1016 map->map_retry = atoi(p);
1017 break;
1019 # if _FFR_DNSMAP_MULTI
1020 case 'z':
1021 if (*++p != '\\')
1022 map->map_coldelim = *p;
1023 else
1025 switch (*++p)
1027 case 'n':
1028 map->map_coldelim = '\n';
1029 break;
1031 case 't':
1032 map->map_coldelim = '\t';
1033 break;
1035 default:
1036 map->map_coldelim = '\\';
1039 break;
1041 # if _FFR_DNSMAP_MULTILIMIT
1042 case 'Z':
1043 while (isascii(*++p) && isspace(*p))
1044 continue;
1045 map->map_sizelimit = atoi(p);
1046 break;
1047 # endif /* _FFR_DNSMAP_MULTILIMIT */
1048 # endif /* _FFR_DNSMAP_MULTI */
1050 /* Start of dns_map specific args */
1051 case 'R': /* search field */
1053 char *h;
1055 while (isascii(*++p) && isspace(*p))
1056 continue;
1057 h = strchr(p, ' ');
1058 if (h != NULL)
1059 *h = '\0';
1060 map_p->dns_m_type = dns_string_to_type(p);
1061 if (h != NULL)
1062 *h = ' ';
1063 if (map_p->dns_m_type < 0)
1064 syserr("dns map %s: wrong type %s",
1065 map->map_mname, p);
1067 break;
1069 # if _FFR_DNSMAP_BASE
1070 case 'B': /* base domain */
1072 char *h;
1074 while (isascii(*++p) && isspace(*p))
1075 continue;
1076 h = strchr(p, ' ');
1077 if (h != NULL)
1078 *h = '\0';
1081 ** slight abuse of map->map_file; it isn't
1082 ** used otherwise in this map type.
1085 map->map_file = newstr(p);
1086 if (h != NULL)
1087 *h = ' ';
1089 break;
1090 # endif /* _FFR_DNSMAP_BASE */
1093 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1094 p++;
1095 if (*p != '\0')
1096 *p++ = '\0';
1098 if (map_p->dns_m_type < 0)
1099 syserr("dns map %s: missing -R type", map->map_mname);
1100 if (map->map_app != NULL)
1101 map->map_app = newstr(map->map_app);
1102 if (map->map_tapp != NULL)
1103 map->map_tapp = newstr(map->map_tapp);
1106 ** Assumption: assert(sizeof int <= sizeof(ARBPTR_T));
1107 ** Even if this assumption is wrong, we use only one byte,
1108 ** so it doesn't really matter.
1111 map->map_db1 = (ARBPTR_T) map_p;
1112 return true;
1116 ** DNS_MAP_LOOKUP -- perform dns map lookup.
1118 ** Parameters:
1119 ** map -- pointer to MAP
1120 ** name -- name to lookup
1121 ** av -- arguments to interpolate into buf.
1122 ** statp -- pointer to status (EX_)
1124 ** Returns:
1125 ** result of lookup if succeeded.
1126 ** NULL -- otherwise.
1129 char *
1130 dns_map_lookup(map, name, av, statp)
1131 MAP *map;
1132 char *name;
1133 char **av;
1134 int *statp;
1136 # if _FFR_DNSMAP_MULTI
1137 # if _FFR_DNSMAP_MULTILIMIT
1138 int resnum = 0;
1139 # endif /* _FFR_DNSMAP_MULTILIMIT */
1140 # endif /* _FFR_DNSMAP_MULTI */
1141 char *vp = NULL, *result = NULL;
1142 size_t vsize;
1143 struct dns_map *map_p;
1144 RESOURCE_RECORD_T *rr = NULL;
1145 DNS_REPLY_T *r = NULL;
1146 # if NETINET6
1147 static char buf6[INET6_ADDRSTRLEN];
1148 # endif /* NETINET6 */
1150 if (tTd(38, 20))
1151 sm_dprintf("dns_map_lookup(%s, %s)\n",
1152 map->map_mname, name);
1154 map_p = (struct dns_map *)(map->map_db1);
1155 # if _FFR_DNSMAP_BASE
1156 if (map->map_file != NULL && *map->map_file != '\0')
1158 size_t len;
1159 char *appdomain;
1161 len = strlen(map->map_file) + strlen(name) + 2;
1162 appdomain = (char *) sm_malloc(len);
1163 if (appdomain == NULL)
1165 *statp = EX_UNAVAILABLE;
1166 return NULL;
1168 (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
1169 r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
1170 map->map_timeout, map->map_retry);
1171 sm_free(appdomain);
1173 else
1174 # endif /* _FFR_DNSMAP_BASE */
1176 r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
1177 map->map_timeout, map->map_retry);
1180 if (r == NULL)
1182 result = NULL;
1183 if (h_errno == TRY_AGAIN || transienterror(errno))
1184 *statp = EX_TEMPFAIL;
1185 else
1186 *statp = EX_NOTFOUND;
1187 goto cleanup;
1189 *statp = EX_OK;
1190 for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
1192 char *type = NULL;
1193 char *value = NULL;
1195 switch (rr->rr_type)
1197 case T_NS:
1198 type = "T_NS";
1199 value = rr->rr_u.rr_txt;
1200 break;
1201 case T_CNAME:
1202 type = "T_CNAME";
1203 value = rr->rr_u.rr_txt;
1204 break;
1205 case T_AFSDB:
1206 type = "T_AFSDB";
1207 value = rr->rr_u.rr_mx->mx_r_domain;
1208 break;
1209 case T_SRV:
1210 type = "T_SRV";
1211 value = rr->rr_u.rr_srv->srv_r_target;
1212 break;
1213 case T_PTR:
1214 type = "T_PTR";
1215 value = rr->rr_u.rr_txt;
1216 break;
1217 case T_TXT:
1218 type = "T_TXT";
1219 value = rr->rr_u.rr_txt;
1220 break;
1221 case T_MX:
1222 type = "T_MX";
1223 value = rr->rr_u.rr_mx->mx_r_domain;
1224 break;
1225 # if NETINET
1226 case T_A:
1227 type = "T_A";
1228 value = inet_ntoa(*(rr->rr_u.rr_a));
1229 break;
1230 # endif /* NETINET */
1231 # if NETINET6
1232 case T_AAAA:
1233 type = "T_AAAA";
1234 value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
1235 sizeof buf6);
1236 break;
1237 # endif /* NETINET6 */
1240 (void) strreplnonprt(value, 'X');
1241 if (map_p->dns_m_type != rr->rr_type)
1243 if (tTd(38, 40))
1244 sm_dprintf("\tskipping type %s (%d) value %s\n",
1245 type != NULL ? type : "<UNKNOWN>",
1246 rr->rr_type,
1247 value != NULL ? value : "<NO VALUE>");
1248 continue;
1251 # if NETINET6
1252 if (rr->rr_type == T_AAAA && value == NULL)
1254 result = NULL;
1255 *statp = EX_DATAERR;
1256 if (tTd(38, 40))
1257 sm_dprintf("\tbad T_AAAA conversion\n");
1258 goto cleanup;
1260 # endif /* NETINET6 */
1261 if (tTd(38, 40))
1262 sm_dprintf("\tfound type %s (%d) value %s\n",
1263 type != NULL ? type : "<UNKNOWN>",
1264 rr->rr_type,
1265 value != NULL ? value : "<NO VALUE>");
1266 # if _FFR_DNSMAP_MULTI
1267 if (value != NULL &&
1268 (map->map_coldelim == '\0' ||
1269 # if _FFR_DNSMAP_MULTILIMIT
1270 map->map_sizelimit == 1 ||
1271 # endif /* _FFR_DNSMAP_MULTILIMIT */
1272 bitset(MF_MATCHONLY, map->map_mflags)))
1274 /* Only care about the first match */
1275 vp = newstr(value);
1276 break;
1278 else if (vp == NULL)
1280 /* First result */
1281 vp = newstr(value);
1283 else
1285 /* concatenate the results */
1286 int sz;
1287 char *new;
1289 sz = strlen(vp) + strlen(value) + 2;
1290 new = xalloc(sz);
1291 (void) sm_snprintf(new, sz, "%s%c%s",
1292 vp, map->map_coldelim, value);
1293 sm_free(vp);
1294 vp = new;
1295 # if _FFR_DNSMAP_MULTILIMIT
1296 if (map->map_sizelimit > 0 &&
1297 ++resnum >= map->map_sizelimit)
1298 break;
1299 # endif /* _FFR_DNSMAP_MULTILIMIT */
1301 # else /* _FFR_DNSMAP_MULTI */
1302 vp = value;
1303 break;
1304 # endif /* _FFR_DNSMAP_MULTI */
1306 if (vp == NULL)
1308 result = NULL;
1309 *statp = EX_NOTFOUND;
1310 if (tTd(38, 40))
1311 sm_dprintf("\tno match found\n");
1312 goto cleanup;
1315 # if _FFR_DNSMAP_MULTI
1316 /* Cleanly truncate for rulesets */
1317 truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
1318 # endif /* _FFR_DNSMAP_MULTI */
1320 vsize = strlen(vp);
1322 if (LogLevel > 9)
1323 sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
1324 name, vp);
1325 if (bitset(MF_MATCHONLY, map->map_mflags))
1326 result = map_rewrite(map, name, strlen(name), NULL);
1327 else
1328 result = map_rewrite(map, vp, vsize, av);
1330 cleanup:
1331 # if _FFR_DNSMAP_MULTI
1332 if (vp != NULL)
1333 sm_free(vp);
1334 # endif /* _FFR_DNSMAP_MULTI */
1335 if (r != NULL)
1336 dns_free_data(r);
1337 return result;
1339 # endif /* DNSMAP */
1340 #endif /* NAMED_BIND */
1343 ** NDBM modules
1346 #if NDBM
1349 ** NDBM_MAP_OPEN -- DBM-style map open
1352 bool
1353 ndbm_map_open(map, mode)
1354 MAP *map;
1355 int mode;
1357 register DBM *dbm;
1358 int save_errno;
1359 int dfd;
1360 int pfd;
1361 long sff;
1362 int ret;
1363 int smode = S_IREAD;
1364 char dirfile[MAXPATHLEN];
1365 char pagfile[MAXPATHLEN];
1366 struct stat st;
1367 struct stat std, stp;
1369 if (tTd(38, 2))
1370 sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1371 map->map_mname, map->map_file, mode);
1372 map->map_lockfd = -1;
1373 mode &= O_ACCMODE;
1375 /* do initial file and directory checks */
1376 if (sm_strlcpyn(dirfile, sizeof dirfile, 2,
1377 map->map_file, ".dir") >= sizeof dirfile ||
1378 sm_strlcpyn(pagfile, sizeof pagfile, 2,
1379 map->map_file, ".pag") >= sizeof pagfile)
1381 errno = 0;
1382 if (!bitset(MF_OPTIONAL, map->map_mflags))
1383 syserr("dbm map \"%s\": map file %s name too long",
1384 map->map_mname, map->map_file);
1385 return false;
1387 sff = SFF_ROOTOK|SFF_REGONLY;
1388 if (mode == O_RDWR)
1390 sff |= SFF_CREAT;
1391 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1392 sff |= SFF_NOSLINK;
1393 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1394 sff |= SFF_NOHLINK;
1395 smode = S_IWRITE;
1397 else
1399 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1400 sff |= SFF_NOWLINK;
1402 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1403 sff |= SFF_SAFEDIRPATH;
1404 ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
1405 sff, smode, &std);
1406 if (ret == 0)
1407 ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
1408 sff, smode, &stp);
1410 if (ret != 0)
1412 char *prob = "unsafe";
1414 /* cannot open this map */
1415 if (ret == ENOENT)
1416 prob = "missing";
1417 if (tTd(38, 2))
1418 sm_dprintf("\t%s map file: %d\n", prob, ret);
1419 if (!bitset(MF_OPTIONAL, map->map_mflags))
1420 syserr("dbm map \"%s\": %s map file %s",
1421 map->map_mname, prob, map->map_file);
1422 return false;
1424 if (std.st_mode == ST_MODE_NOFILE)
1425 mode |= O_CREAT|O_EXCL;
1427 # if LOCK_ON_OPEN
1428 if (mode == O_RDONLY)
1429 mode |= O_SHLOCK;
1430 else
1431 mode |= O_TRUNC|O_EXLOCK;
1432 # else /* LOCK_ON_OPEN */
1433 if ((mode & O_ACCMODE) == O_RDWR)
1435 # if NOFTRUNCATE
1437 ** Warning: race condition. Try to lock the file as
1438 ** quickly as possible after opening it.
1439 ** This may also have security problems on some systems,
1440 ** but there isn't anything we can do about it.
1443 mode |= O_TRUNC;
1444 # else /* NOFTRUNCATE */
1446 ** This ugly code opens the map without truncating it,
1447 ** locks the file, then truncates it. Necessary to
1448 ** avoid race conditions.
1451 int dirfd;
1452 int pagfd;
1453 long sff = SFF_CREAT|SFF_OPENASROOT;
1455 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1456 sff |= SFF_NOSLINK;
1457 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1458 sff |= SFF_NOHLINK;
1460 dirfd = safeopen(dirfile, mode, DBMMODE, sff);
1461 pagfd = safeopen(pagfile, mode, DBMMODE, sff);
1463 if (dirfd < 0 || pagfd < 0)
1465 save_errno = errno;
1466 if (dirfd >= 0)
1467 (void) close(dirfd);
1468 if (pagfd >= 0)
1469 (void) close(pagfd);
1470 errno = save_errno;
1471 syserr("ndbm_map_open: cannot create database %s",
1472 map->map_file);
1473 return false;
1475 if (ftruncate(dirfd, (off_t) 0) < 0 ||
1476 ftruncate(pagfd, (off_t) 0) < 0)
1478 save_errno = errno;
1479 (void) close(dirfd);
1480 (void) close(pagfd);
1481 errno = save_errno;
1482 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1483 map->map_file);
1484 return false;
1487 /* if new file, get "before" bits for later filechanged check */
1488 if (std.st_mode == ST_MODE_NOFILE &&
1489 (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
1491 save_errno = errno;
1492 (void) close(dirfd);
1493 (void) close(pagfd);
1494 errno = save_errno;
1495 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1496 map->map_file);
1497 return false;
1500 /* have to save the lock for the duration (bletch) */
1501 map->map_lockfd = dirfd;
1502 (void) close(pagfd);
1504 /* twiddle bits for dbm_open */
1505 mode &= ~(O_CREAT|O_EXCL);
1506 # endif /* NOFTRUNCATE */
1508 # endif /* LOCK_ON_OPEN */
1510 /* open the database */
1511 dbm = dbm_open(map->map_file, mode, DBMMODE);
1512 if (dbm == NULL)
1514 save_errno = errno;
1515 if (bitset(MF_ALIAS, map->map_mflags) &&
1516 aliaswait(map, ".pag", false))
1517 return true;
1518 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1519 if (map->map_lockfd >= 0)
1520 (void) close(map->map_lockfd);
1521 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1522 errno = save_errno;
1523 if (!bitset(MF_OPTIONAL, map->map_mflags))
1524 syserr("Cannot open DBM database %s", map->map_file);
1525 return false;
1527 dfd = dbm_dirfno(dbm);
1528 pfd = dbm_pagfno(dbm);
1529 if (dfd == pfd)
1531 /* heuristic: if files are linked, this is actually gdbm */
1532 dbm_close(dbm);
1533 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1534 if (map->map_lockfd >= 0)
1535 (void) close(map->map_lockfd);
1536 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1537 errno = 0;
1538 syserr("dbm map \"%s\": cannot support GDBM",
1539 map->map_mname);
1540 return false;
1543 if (filechanged(dirfile, dfd, &std) ||
1544 filechanged(pagfile, pfd, &stp))
1546 save_errno = errno;
1547 dbm_close(dbm);
1548 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1549 if (map->map_lockfd >= 0)
1550 (void) close(map->map_lockfd);
1551 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1552 errno = save_errno;
1553 syserr("ndbm_map_open(%s): file changed after open",
1554 map->map_file);
1555 return false;
1558 map->map_db1 = (ARBPTR_T) dbm;
1561 ** Need to set map_mtime before the call to aliaswait()
1562 ** as aliaswait() will call map_lookup() which requires
1563 ** map_mtime to be set
1566 if (fstat(pfd, &st) >= 0)
1567 map->map_mtime = st.st_mtime;
1569 if (mode == O_RDONLY)
1571 # if LOCK_ON_OPEN
1572 if (dfd >= 0)
1573 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1574 if (pfd >= 0)
1575 (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
1576 # endif /* LOCK_ON_OPEN */
1577 if (bitset(MF_ALIAS, map->map_mflags) &&
1578 !aliaswait(map, ".pag", true))
1579 return false;
1581 else
1583 map->map_mflags |= MF_LOCKED;
1584 if (geteuid() == 0 && TrustedUid != 0)
1586 # if HASFCHOWN
1587 if (fchown(dfd, TrustedUid, -1) < 0 ||
1588 fchown(pfd, TrustedUid, -1) < 0)
1590 int err = errno;
1592 sm_syslog(LOG_ALERT, NOQID,
1593 "ownership change on %s failed: %s",
1594 map->map_file, sm_errstring(err));
1595 message("050 ownership change on %s failed: %s",
1596 map->map_file, sm_errstring(err));
1598 # else /* HASFCHOWN */
1599 sm_syslog(LOG_ALERT, NOQID,
1600 "no fchown(): cannot change ownership on %s",
1601 map->map_file);
1602 message("050 no fchown(): cannot change ownership on %s",
1603 map->map_file);
1604 # endif /* HASFCHOWN */
1607 return true;
1612 ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1615 char *
1616 ndbm_map_lookup(map, name, av, statp)
1617 MAP *map;
1618 char *name;
1619 char **av;
1620 int *statp;
1622 datum key, val;
1623 int dfd, pfd;
1624 char keybuf[MAXNAME + 1];
1625 struct stat stbuf;
1627 if (tTd(38, 20))
1628 sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1629 map->map_mname, name);
1631 key.dptr = name;
1632 key.dsize = strlen(name);
1633 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1635 if (key.dsize > sizeof keybuf - 1)
1636 key.dsize = sizeof keybuf - 1;
1637 memmove(keybuf, key.dptr, key.dsize);
1638 keybuf[key.dsize] = '\0';
1639 makelower(keybuf);
1640 key.dptr = keybuf;
1642 lockdbm:
1643 dfd = dbm_dirfno((DBM *) map->map_db1);
1644 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1645 (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
1646 pfd = dbm_pagfno((DBM *) map->map_db1);
1647 if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
1648 stbuf.st_mtime > map->map_mtime)
1650 /* Reopen the database to sync the cache */
1651 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1652 : O_RDONLY;
1654 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1655 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1656 map->map_mflags |= MF_CLOSING;
1657 map->map_class->map_close(map);
1658 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1659 if (map->map_class->map_open(map, omode))
1661 map->map_mflags |= MF_OPEN;
1662 map->map_pid = CurrentPid;
1663 if ((omode && O_ACCMODE) == O_RDWR)
1664 map->map_mflags |= MF_WRITABLE;
1665 goto lockdbm;
1667 else
1669 if (!bitset(MF_OPTIONAL, map->map_mflags))
1671 extern MAPCLASS BogusMapClass;
1673 *statp = EX_TEMPFAIL;
1674 map->map_orgclass = map->map_class;
1675 map->map_class = &BogusMapClass;
1676 map->map_mflags |= MF_OPEN;
1677 map->map_pid = CurrentPid;
1678 syserr("Cannot reopen NDBM database %s",
1679 map->map_file);
1681 return NULL;
1684 val.dptr = NULL;
1685 if (bitset(MF_TRY0NULL, map->map_mflags))
1687 val = dbm_fetch((DBM *) map->map_db1, key);
1688 if (val.dptr != NULL)
1689 map->map_mflags &= ~MF_TRY1NULL;
1691 if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1693 key.dsize++;
1694 val = dbm_fetch((DBM *) map->map_db1, key);
1695 if (val.dptr != NULL)
1696 map->map_mflags &= ~MF_TRY0NULL;
1698 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1699 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1700 if (val.dptr == NULL)
1701 return NULL;
1702 if (bitset(MF_MATCHONLY, map->map_mflags))
1703 return map_rewrite(map, name, strlen(name), NULL);
1704 else
1705 return map_rewrite(map, val.dptr, val.dsize, av);
1710 ** NDBM_MAP_STORE -- store a datum in the database
1713 void
1714 ndbm_map_store(map, lhs, rhs)
1715 register MAP *map;
1716 char *lhs;
1717 char *rhs;
1719 datum key;
1720 datum data;
1721 int status;
1722 char keybuf[MAXNAME + 1];
1724 if (tTd(38, 12))
1725 sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1726 map->map_mname, lhs, rhs);
1728 key.dsize = strlen(lhs);
1729 key.dptr = lhs;
1730 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1732 if (key.dsize > sizeof keybuf - 1)
1733 key.dsize = sizeof keybuf - 1;
1734 memmove(keybuf, key.dptr, key.dsize);
1735 keybuf[key.dsize] = '\0';
1736 makelower(keybuf);
1737 key.dptr = keybuf;
1740 data.dsize = strlen(rhs);
1741 data.dptr = rhs;
1743 if (bitset(MF_INCLNULL, map->map_mflags))
1745 key.dsize++;
1746 data.dsize++;
1749 status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1750 if (status > 0)
1752 if (!bitset(MF_APPEND, map->map_mflags))
1753 message("050 Warning: duplicate alias name %s", lhs);
1754 else
1756 static char *buf = NULL;
1757 static int bufsiz = 0;
1758 auto int xstat;
1759 datum old;
1761 old.dptr = ndbm_map_lookup(map, key.dptr,
1762 (char **) NULL, &xstat);
1763 if (old.dptr != NULL && *(char *) old.dptr != '\0')
1765 old.dsize = strlen(old.dptr);
1766 if (data.dsize + old.dsize + 2 > bufsiz)
1768 if (buf != NULL)
1769 (void) sm_free(buf);
1770 bufsiz = data.dsize + old.dsize + 2;
1771 buf = sm_pmalloc_x(bufsiz);
1773 (void) sm_strlcpyn(buf, bufsiz, 3,
1774 data.dptr, ",", old.dptr);
1775 data.dsize = data.dsize + old.dsize + 1;
1776 data.dptr = buf;
1777 if (tTd(38, 9))
1778 sm_dprintf("ndbm_map_store append=%s\n",
1779 data.dptr);
1782 status = dbm_store((DBM *) map->map_db1,
1783 key, data, DBM_REPLACE);
1785 if (status != 0)
1786 syserr("readaliases: dbm put (%s): %d", lhs, status);
1791 ** NDBM_MAP_CLOSE -- close the database
1794 void
1795 ndbm_map_close(map)
1796 register MAP *map;
1798 if (tTd(38, 9))
1799 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1800 map->map_mname, map->map_file, map->map_mflags);
1802 if (bitset(MF_WRITABLE, map->map_mflags))
1804 # ifdef NDBM_YP_COMPAT
1805 bool inclnull;
1806 char buf[MAXHOSTNAMELEN];
1808 inclnull = bitset(MF_INCLNULL, map->map_mflags);
1809 map->map_mflags &= ~MF_INCLNULL;
1811 if (strstr(map->map_file, "/yp/") != NULL)
1813 long save_mflags = map->map_mflags;
1815 map->map_mflags |= MF_NOFOLDCASE;
1817 (void) sm_snprintf(buf, sizeof buf, "%010ld", curtime());
1818 ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1820 (void) gethostname(buf, sizeof buf);
1821 ndbm_map_store(map, "YP_MASTER_NAME", buf);
1823 map->map_mflags = save_mflags;
1826 if (inclnull)
1827 map->map_mflags |= MF_INCLNULL;
1828 # endif /* NDBM_YP_COMPAT */
1830 /* write out the distinguished alias */
1831 ndbm_map_store(map, "@", "@");
1833 dbm_close((DBM *) map->map_db1);
1835 /* release lock (if needed) */
1836 # if !LOCK_ON_OPEN
1837 if (map->map_lockfd >= 0)
1838 (void) close(map->map_lockfd);
1839 # endif /* !LOCK_ON_OPEN */
1842 #endif /* NDBM */
1844 ** NEWDB (Hash and BTree) Modules
1847 #if NEWDB
1850 ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1852 ** These do rather bizarre locking. If you can lock on open,
1853 ** do that to avoid the condition of opening a database that
1854 ** is being rebuilt. If you don't, we'll try to fake it, but
1855 ** there will be a race condition. If opening for read-only,
1856 ** we immediately release the lock to avoid freezing things up.
1857 ** We really ought to hold the lock, but guarantee that we won't
1858 ** be pokey about it. That's hard to do.
1861 /* these should be K line arguments */
1862 # if DB_VERSION_MAJOR < 2
1863 # define db_cachesize cachesize
1864 # define h_nelem nelem
1865 # ifndef DB_CACHE_SIZE
1866 # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
1867 # endif /* ! DB_CACHE_SIZE */
1868 # ifndef DB_HASH_NELEM
1869 # define DB_HASH_NELEM 4096 /* (starting) size of hash table */
1870 # endif /* ! DB_HASH_NELEM */
1871 # endif /* DB_VERSION_MAJOR < 2 */
1873 bool
1874 bt_map_open(map, mode)
1875 MAP *map;
1876 int mode;
1878 # if DB_VERSION_MAJOR < 2
1879 BTREEINFO btinfo;
1880 # endif /* DB_VERSION_MAJOR < 2 */
1881 # if DB_VERSION_MAJOR == 2
1882 DB_INFO btinfo;
1883 # endif /* DB_VERSION_MAJOR == 2 */
1884 # if DB_VERSION_MAJOR > 2
1885 void *btinfo = NULL;
1886 # endif /* DB_VERSION_MAJOR > 2 */
1888 if (tTd(38, 2))
1889 sm_dprintf("bt_map_open(%s, %s, %d)\n",
1890 map->map_mname, map->map_file, mode);
1892 # if DB_VERSION_MAJOR < 3
1893 memset(&btinfo, '\0', sizeof btinfo);
1894 # ifdef DB_CACHE_SIZE
1895 btinfo.db_cachesize = DB_CACHE_SIZE;
1896 # endif /* DB_CACHE_SIZE */
1897 # endif /* DB_VERSION_MAJOR < 3 */
1899 return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1902 bool
1903 hash_map_open(map, mode)
1904 MAP *map;
1905 int mode;
1907 # if DB_VERSION_MAJOR < 2
1908 HASHINFO hinfo;
1909 # endif /* DB_VERSION_MAJOR < 2 */
1910 # if DB_VERSION_MAJOR == 2
1911 DB_INFO hinfo;
1912 # endif /* DB_VERSION_MAJOR == 2 */
1913 # if DB_VERSION_MAJOR > 2
1914 void *hinfo = NULL;
1915 # endif /* DB_VERSION_MAJOR > 2 */
1917 if (tTd(38, 2))
1918 sm_dprintf("hash_map_open(%s, %s, %d)\n",
1919 map->map_mname, map->map_file, mode);
1921 # if DB_VERSION_MAJOR < 3
1922 memset(&hinfo, '\0', sizeof hinfo);
1923 # ifdef DB_HASH_NELEM
1924 hinfo.h_nelem = DB_HASH_NELEM;
1925 # endif /* DB_HASH_NELEM */
1926 # ifdef DB_CACHE_SIZE
1927 hinfo.db_cachesize = DB_CACHE_SIZE;
1928 # endif /* DB_CACHE_SIZE */
1929 # endif /* DB_VERSION_MAJOR < 3 */
1931 return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1934 static bool
1935 db_map_open(map, mode, mapclassname, dbtype, openinfo)
1936 MAP *map;
1937 int mode;
1938 char *mapclassname;
1939 DBTYPE dbtype;
1940 # if DB_VERSION_MAJOR < 2
1941 const void *openinfo;
1942 # endif /* DB_VERSION_MAJOR < 2 */
1943 # if DB_VERSION_MAJOR == 2
1944 DB_INFO *openinfo;
1945 # endif /* DB_VERSION_MAJOR == 2 */
1946 # if DB_VERSION_MAJOR > 2
1947 void **openinfo;
1948 # endif /* DB_VERSION_MAJOR > 2 */
1950 DB *db = NULL;
1951 int i;
1952 int omode;
1953 int smode = S_IREAD;
1954 int fd;
1955 long sff;
1956 int save_errno;
1957 struct stat st;
1958 char buf[MAXPATHLEN];
1960 /* do initial file and directory checks */
1961 if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
1963 errno = 0;
1964 if (!bitset(MF_OPTIONAL, map->map_mflags))
1965 syserr("map \"%s\": map file %s name too long",
1966 map->map_mname, map->map_file);
1967 return false;
1969 i = strlen(buf);
1970 if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
1972 if (sm_strlcat(buf, ".db", sizeof buf) >= sizeof buf)
1974 errno = 0;
1975 if (!bitset(MF_OPTIONAL, map->map_mflags))
1976 syserr("map \"%s\": map file %s name too long",
1977 map->map_mname, map->map_file);
1978 return false;
1982 mode &= O_ACCMODE;
1983 omode = mode;
1985 sff = SFF_ROOTOK|SFF_REGONLY;
1986 if (mode == O_RDWR)
1988 sff |= SFF_CREAT;
1989 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1990 sff |= SFF_NOSLINK;
1991 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1992 sff |= SFF_NOHLINK;
1993 smode = S_IWRITE;
1995 else
1997 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1998 sff |= SFF_NOWLINK;
2000 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
2001 sff |= SFF_SAFEDIRPATH;
2002 i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
2004 if (i != 0)
2006 char *prob = "unsafe";
2008 /* cannot open this map */
2009 if (i == ENOENT)
2010 prob = "missing";
2011 if (tTd(38, 2))
2012 sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
2013 errno = i;
2014 if (!bitset(MF_OPTIONAL, map->map_mflags))
2015 syserr("%s map \"%s\": %s map file %s",
2016 mapclassname, map->map_mname, prob, buf);
2017 return false;
2019 if (st.st_mode == ST_MODE_NOFILE)
2020 omode |= O_CREAT|O_EXCL;
2022 map->map_lockfd = -1;
2024 # if LOCK_ON_OPEN
2025 if (mode == O_RDWR)
2026 omode |= O_TRUNC|O_EXLOCK;
2027 else
2028 omode |= O_SHLOCK;
2029 # else /* LOCK_ON_OPEN */
2031 ** Pre-lock the file to avoid race conditions. In particular,
2032 ** since dbopen returns NULL if the file is zero length, we
2033 ** must have a locked instance around the dbopen.
2036 fd = open(buf, omode, DBMMODE);
2037 if (fd < 0)
2039 if (!bitset(MF_OPTIONAL, map->map_mflags))
2040 syserr("db_map_open: cannot pre-open database %s", buf);
2041 return false;
2044 /* make sure no baddies slipped in just before the open... */
2045 if (filechanged(buf, fd, &st))
2047 save_errno = errno;
2048 (void) close(fd);
2049 errno = save_errno;
2050 syserr("db_map_open(%s): file changed after pre-open", buf);
2051 return false;
2054 /* if new file, get the "before" bits for later filechanged check */
2055 if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
2057 save_errno = errno;
2058 (void) close(fd);
2059 errno = save_errno;
2060 syserr("db_map_open(%s): cannot fstat pre-opened file",
2061 buf);
2062 return false;
2065 /* actually lock the pre-opened file */
2066 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
2067 syserr("db_map_open: cannot lock %s", buf);
2069 /* set up mode bits for dbopen */
2070 if (mode == O_RDWR)
2071 omode |= O_TRUNC;
2072 omode &= ~(O_EXCL|O_CREAT);
2073 # endif /* LOCK_ON_OPEN */
2075 # if DB_VERSION_MAJOR < 2
2076 db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
2077 # else /* DB_VERSION_MAJOR < 2 */
2079 int flags = 0;
2080 # if DB_VERSION_MAJOR > 2
2081 int ret;
2082 # endif /* DB_VERSION_MAJOR > 2 */
2084 if (mode == O_RDONLY)
2085 flags |= DB_RDONLY;
2086 if (bitset(O_CREAT, omode))
2087 flags |= DB_CREATE;
2088 if (bitset(O_TRUNC, omode))
2089 flags |= DB_TRUNCATE;
2090 SM_DB_FLAG_ADD(flags);
2092 # if DB_VERSION_MAJOR > 2
2093 ret = db_create(&db, NULL, 0);
2094 # ifdef DB_CACHE_SIZE
2095 if (ret == 0 && db != NULL)
2097 ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
2098 if (ret != 0)
2100 (void) db->close(db, 0);
2101 db = NULL;
2104 # endif /* DB_CACHE_SIZE */
2105 # ifdef DB_HASH_NELEM
2106 if (dbtype == DB_HASH && ret == 0 && db != NULL)
2108 ret = db->set_h_nelem(db, DB_HASH_NELEM);
2109 if (ret != 0)
2111 (void) db->close(db, 0);
2112 db = NULL;
2115 # endif /* DB_HASH_NELEM */
2116 if (ret == 0 && db != NULL)
2118 ret = db->open(db,
2119 DBTXN /* transaction for DB 4.1 */
2120 buf, NULL, dbtype, flags, DBMMODE);
2121 if (ret != 0)
2123 #ifdef DB_OLD_VERSION
2124 if (ret == DB_OLD_VERSION)
2125 ret = EINVAL;
2126 #endif /* DB_OLD_VERSION */
2127 (void) db->close(db, 0);
2128 db = NULL;
2131 errno = ret;
2132 # else /* DB_VERSION_MAJOR > 2 */
2133 errno = db_open(buf, dbtype, flags, DBMMODE,
2134 NULL, openinfo, &db);
2135 # endif /* DB_VERSION_MAJOR > 2 */
2137 # endif /* DB_VERSION_MAJOR < 2 */
2138 save_errno = errno;
2140 # if !LOCK_ON_OPEN
2141 if (mode == O_RDWR)
2142 map->map_lockfd = fd;
2143 else
2144 (void) close(fd);
2145 # endif /* !LOCK_ON_OPEN */
2147 if (db == NULL)
2149 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2150 aliaswait(map, ".db", false))
2151 return true;
2152 # if !LOCK_ON_OPEN
2153 if (map->map_lockfd >= 0)
2154 (void) close(map->map_lockfd);
2155 # endif /* !LOCK_ON_OPEN */
2156 errno = save_errno;
2157 if (!bitset(MF_OPTIONAL, map->map_mflags))
2158 syserr("Cannot open %s database %s",
2159 mapclassname, buf);
2160 return false;
2163 # if DB_VERSION_MAJOR < 2
2164 fd = db->fd(db);
2165 # else /* DB_VERSION_MAJOR < 2 */
2166 fd = -1;
2167 errno = db->fd(db, &fd);
2168 # endif /* DB_VERSION_MAJOR < 2 */
2169 if (filechanged(buf, fd, &st))
2171 save_errno = errno;
2172 # if DB_VERSION_MAJOR < 2
2173 (void) db->close(db);
2174 # else /* DB_VERSION_MAJOR < 2 */
2175 errno = db->close(db, 0);
2176 # endif /* DB_VERSION_MAJOR < 2 */
2177 # if !LOCK_ON_OPEN
2178 if (map->map_lockfd >= 0)
2179 (void) close(map->map_lockfd);
2180 # endif /* !LOCK_ON_OPEN */
2181 errno = save_errno;
2182 syserr("db_map_open(%s): file changed after open", buf);
2183 return false;
2186 if (mode == O_RDWR)
2187 map->map_mflags |= MF_LOCKED;
2188 # if LOCK_ON_OPEN
2189 if (fd >= 0 && mode == O_RDONLY)
2191 (void) lockfile(fd, buf, NULL, LOCK_UN);
2193 # endif /* LOCK_ON_OPEN */
2195 /* try to make sure that at least the database header is on disk */
2196 if (mode == O_RDWR)
2198 (void) db->sync(db, 0);
2199 if (geteuid() == 0 && TrustedUid != 0)
2201 # if HASFCHOWN
2202 if (fchown(fd, TrustedUid, -1) < 0)
2204 int err = errno;
2206 sm_syslog(LOG_ALERT, NOQID,
2207 "ownership change on %s failed: %s",
2208 buf, sm_errstring(err));
2209 message("050 ownership change on %s failed: %s",
2210 buf, sm_errstring(err));
2212 # else /* HASFCHOWN */
2213 sm_syslog(LOG_ALERT, NOQID,
2214 "no fchown(): cannot change ownership on %s",
2215 map->map_file);
2216 message("050 no fchown(): cannot change ownership on %s",
2217 map->map_file);
2218 # endif /* HASFCHOWN */
2222 map->map_db2 = (ARBPTR_T) db;
2225 ** Need to set map_mtime before the call to aliaswait()
2226 ** as aliaswait() will call map_lookup() which requires
2227 ** map_mtime to be set
2230 if (fd >= 0 && fstat(fd, &st) >= 0)
2231 map->map_mtime = st.st_mtime;
2233 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2234 !aliaswait(map, ".db", true))
2235 return false;
2236 return true;
2241 ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2244 char *
2245 db_map_lookup(map, name, av, statp)
2246 MAP *map;
2247 char *name;
2248 char **av;
2249 int *statp;
2251 DBT key, val;
2252 register DB *db = (DB *) map->map_db2;
2253 int i;
2254 int st;
2255 int save_errno;
2256 int fd;
2257 struct stat stbuf;
2258 char keybuf[MAXNAME + 1];
2259 char buf[MAXPATHLEN];
2261 memset(&key, '\0', sizeof key);
2262 memset(&val, '\0', sizeof val);
2264 if (tTd(38, 20))
2265 sm_dprintf("db_map_lookup(%s, %s)\n",
2266 map->map_mname, name);
2268 if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf)
2270 errno = 0;
2271 if (!bitset(MF_OPTIONAL, map->map_mflags))
2272 syserr("map \"%s\": map file %s name too long",
2273 map->map_mname, map->map_file);
2274 return NULL;
2276 i = strlen(buf);
2277 if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
2278 buf[i - 3] = '\0';
2280 key.size = strlen(name);
2281 if (key.size > sizeof keybuf - 1)
2282 key.size = sizeof keybuf - 1;
2283 key.data = keybuf;
2284 memmove(keybuf, name, key.size);
2285 keybuf[key.size] = '\0';
2286 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2287 makelower(keybuf);
2288 lockdb:
2289 # if DB_VERSION_MAJOR < 2
2290 fd = db->fd(db);
2291 # else /* DB_VERSION_MAJOR < 2 */
2292 fd = -1;
2293 errno = db->fd(db, &fd);
2294 # endif /* DB_VERSION_MAJOR < 2 */
2295 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2296 (void) lockfile(fd, buf, ".db", LOCK_SH);
2297 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
2299 /* Reopen the database to sync the cache */
2300 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
2301 : O_RDONLY;
2303 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2304 (void) lockfile(fd, buf, ".db", LOCK_UN);
2305 map->map_mflags |= MF_CLOSING;
2306 map->map_class->map_close(map);
2307 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
2308 if (map->map_class->map_open(map, omode))
2310 map->map_mflags |= MF_OPEN;
2311 map->map_pid = CurrentPid;
2312 if ((omode && O_ACCMODE) == O_RDWR)
2313 map->map_mflags |= MF_WRITABLE;
2314 db = (DB *) map->map_db2;
2315 goto lockdb;
2317 else
2319 if (!bitset(MF_OPTIONAL, map->map_mflags))
2321 extern MAPCLASS BogusMapClass;
2323 *statp = EX_TEMPFAIL;
2324 map->map_orgclass = map->map_class;
2325 map->map_class = &BogusMapClass;
2326 map->map_mflags |= MF_OPEN;
2327 map->map_pid = CurrentPid;
2328 syserr("Cannot reopen DB database %s",
2329 map->map_file);
2331 return NULL;
2335 st = 1;
2336 if (bitset(MF_TRY0NULL, map->map_mflags))
2338 # if DB_VERSION_MAJOR < 2
2339 st = db->get(db, &key, &val, 0);
2340 # else /* DB_VERSION_MAJOR < 2 */
2341 errno = db->get(db, NULL, &key, &val, 0);
2342 switch (errno)
2344 case DB_NOTFOUND:
2345 case DB_KEYEMPTY:
2346 st = 1;
2347 break;
2349 case 0:
2350 st = 0;
2351 break;
2353 default:
2354 st = -1;
2355 break;
2357 # endif /* DB_VERSION_MAJOR < 2 */
2358 if (st == 0)
2359 map->map_mflags &= ~MF_TRY1NULL;
2361 if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
2363 key.size++;
2364 # if DB_VERSION_MAJOR < 2
2365 st = db->get(db, &key, &val, 0);
2366 # else /* DB_VERSION_MAJOR < 2 */
2367 errno = db->get(db, NULL, &key, &val, 0);
2368 switch (errno)
2370 case DB_NOTFOUND:
2371 case DB_KEYEMPTY:
2372 st = 1;
2373 break;
2375 case 0:
2376 st = 0;
2377 break;
2379 default:
2380 st = -1;
2381 break;
2383 # endif /* DB_VERSION_MAJOR < 2 */
2384 if (st == 0)
2385 map->map_mflags &= ~MF_TRY0NULL;
2387 save_errno = errno;
2388 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2389 (void) lockfile(fd, buf, ".db", LOCK_UN);
2390 if (st != 0)
2392 errno = save_errno;
2393 if (st < 0)
2394 syserr("db_map_lookup: get (%s)", name);
2395 return NULL;
2397 if (bitset(MF_MATCHONLY, map->map_mflags))
2398 return map_rewrite(map, name, strlen(name), NULL);
2399 else
2400 return map_rewrite(map, val.data, val.size, av);
2405 ** DB_MAP_STORE -- store a datum in the NEWDB database
2408 void
2409 db_map_store(map, lhs, rhs)
2410 register MAP *map;
2411 char *lhs;
2412 char *rhs;
2414 int status;
2415 DBT key;
2416 DBT data;
2417 register DB *db = map->map_db2;
2418 char keybuf[MAXNAME + 1];
2420 memset(&key, '\0', sizeof key);
2421 memset(&data, '\0', sizeof data);
2423 if (tTd(38, 12))
2424 sm_dprintf("db_map_store(%s, %s, %s)\n",
2425 map->map_mname, lhs, rhs);
2427 key.size = strlen(lhs);
2428 key.data = lhs;
2429 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2431 if (key.size > sizeof keybuf - 1)
2432 key.size = sizeof keybuf - 1;
2433 memmove(keybuf, key.data, key.size);
2434 keybuf[key.size] = '\0';
2435 makelower(keybuf);
2436 key.data = keybuf;
2439 data.size = strlen(rhs);
2440 data.data = rhs;
2442 if (bitset(MF_INCLNULL, map->map_mflags))
2444 key.size++;
2445 data.size++;
2448 # if DB_VERSION_MAJOR < 2
2449 status = db->put(db, &key, &data, R_NOOVERWRITE);
2450 # else /* DB_VERSION_MAJOR < 2 */
2451 errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
2452 switch (errno)
2454 case DB_KEYEXIST:
2455 status = 1;
2456 break;
2458 case 0:
2459 status = 0;
2460 break;
2462 default:
2463 status = -1;
2464 break;
2466 # endif /* DB_VERSION_MAJOR < 2 */
2467 if (status > 0)
2469 if (!bitset(MF_APPEND, map->map_mflags))
2470 message("050 Warning: duplicate alias name %s", lhs);
2471 else
2473 static char *buf = NULL;
2474 static int bufsiz = 0;
2475 DBT old;
2477 memset(&old, '\0', sizeof old);
2479 old.data = db_map_lookup(map, key.data,
2480 (char **) NULL, &status);
2481 if (old.data != NULL)
2483 old.size = strlen(old.data);
2484 if (data.size + old.size + 2 > (size_t) bufsiz)
2486 if (buf != NULL)
2487 sm_free(buf);
2488 bufsiz = data.size + old.size + 2;
2489 buf = sm_pmalloc_x(bufsiz);
2491 (void) sm_strlcpyn(buf, bufsiz, 3,
2492 (char *) data.data, ",",
2493 (char *) old.data);
2494 data.size = data.size + old.size + 1;
2495 data.data = buf;
2496 if (tTd(38, 9))
2497 sm_dprintf("db_map_store append=%s\n",
2498 (char *) data.data);
2501 # if DB_VERSION_MAJOR < 2
2502 status = db->put(db, &key, &data, 0);
2503 # else /* DB_VERSION_MAJOR < 2 */
2504 status = errno = db->put(db, NULL, &key, &data, 0);
2505 # endif /* DB_VERSION_MAJOR < 2 */
2507 if (status != 0)
2508 syserr("readaliases: db put (%s)", lhs);
2513 ** DB_MAP_CLOSE -- add distinguished entries and close the database
2516 void
2517 db_map_close(map)
2518 MAP *map;
2520 register DB *db = map->map_db2;
2522 if (tTd(38, 9))
2523 sm_dprintf("db_map_close(%s, %s, %lx)\n",
2524 map->map_mname, map->map_file, map->map_mflags);
2526 if (bitset(MF_WRITABLE, map->map_mflags))
2528 /* write out the distinguished alias */
2529 db_map_store(map, "@", "@");
2532 (void) db->sync(db, 0);
2534 # if !LOCK_ON_OPEN
2535 if (map->map_lockfd >= 0)
2536 (void) close(map->map_lockfd);
2537 # endif /* !LOCK_ON_OPEN */
2539 # if DB_VERSION_MAJOR < 2
2540 if (db->close(db) != 0)
2541 # else /* DB_VERSION_MAJOR < 2 */
2543 ** Berkeley DB can use internal shared memory
2544 ** locking for its memory pool. Closing a map
2545 ** opened by another process will interfere
2546 ** with the shared memory and locks of the parent
2547 ** process leaving things in a bad state.
2551 ** If this map was not opened by the current
2552 ** process, do not close the map but recover
2553 ** the file descriptor.
2556 if (map->map_pid != CurrentPid)
2558 int fd = -1;
2560 errno = db->fd(db, &fd);
2561 if (fd >= 0)
2562 (void) close(fd);
2563 return;
2566 if ((errno = db->close(db, 0)) != 0)
2567 # endif /* DB_VERSION_MAJOR < 2 */
2568 syserr("db_map_close(%s, %s, %lx): db close failure",
2569 map->map_mname, map->map_file, map->map_mflags);
2571 #endif /* NEWDB */
2573 ** NIS Modules
2576 #if NIS
2578 # ifndef YPERR_BUSY
2579 # define YPERR_BUSY 16
2580 # endif /* ! YPERR_BUSY */
2583 ** NIS_MAP_OPEN -- open DBM map
2586 bool
2587 nis_map_open(map, mode)
2588 MAP *map;
2589 int mode;
2591 int yperr;
2592 register char *p;
2593 auto char *vp;
2594 auto int vsize;
2596 if (tTd(38, 2))
2597 sm_dprintf("nis_map_open(%s, %s, %d)\n",
2598 map->map_mname, map->map_file, mode);
2600 mode &= O_ACCMODE;
2601 if (mode != O_RDONLY)
2603 /* issue a pseudo-error message */
2604 errno = SM_EMAPCANTWRITE;
2605 return false;
2608 p = strchr(map->map_file, '@');
2609 if (p != NULL)
2611 *p++ = '\0';
2612 if (*p != '\0')
2613 map->map_domain = p;
2616 if (*map->map_file == '\0')
2617 map->map_file = "mail.aliases";
2619 if (map->map_domain == NULL)
2621 yperr = yp_get_default_domain(&map->map_domain);
2622 if (yperr != 0)
2624 if (!bitset(MF_OPTIONAL, map->map_mflags))
2625 syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2626 map->map_file);
2627 return false;
2631 /* check to see if this map actually exists */
2632 vp = NULL;
2633 yperr = yp_match(map->map_domain, map->map_file, "@", 1,
2634 &vp, &vsize);
2635 if (tTd(38, 10))
2636 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2637 map->map_domain, map->map_file, yperr_string(yperr));
2638 if (vp != NULL)
2639 sm_free(vp);
2641 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
2644 ** We ought to be calling aliaswait() here if this is an
2645 ** alias file, but powerful HP-UX NIS servers apparently
2646 ** don't insert the @:@ token into the alias map when it
2647 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
2650 # if 0
2651 if (!bitset(MF_ALIAS, map->map_mflags) ||
2652 aliaswait(map, NULL, true))
2653 # endif /* 0 */
2654 return true;
2657 if (!bitset(MF_OPTIONAL, map->map_mflags))
2659 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2660 map->map_file, map->map_domain, yperr_string(yperr));
2663 return false;
2668 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map
2671 /* ARGSUSED3 */
2672 char *
2673 nis_map_lookup(map, name, av, statp)
2674 MAP *map;
2675 char *name;
2676 char **av;
2677 int *statp;
2679 char *vp;
2680 auto int vsize;
2681 int buflen;
2682 int yperr;
2683 char keybuf[MAXNAME + 1];
2684 char *SM_NONVOLATILE result = NULL;
2686 if (tTd(38, 20))
2687 sm_dprintf("nis_map_lookup(%s, %s)\n",
2688 map->map_mname, name);
2690 buflen = strlen(name);
2691 if (buflen > sizeof keybuf - 1)
2692 buflen = sizeof keybuf - 1;
2693 memmove(keybuf, name, buflen);
2694 keybuf[buflen] = '\0';
2695 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2696 makelower(keybuf);
2697 yperr = YPERR_KEY;
2698 vp = NULL;
2699 if (bitset(MF_TRY0NULL, map->map_mflags))
2701 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2702 &vp, &vsize);
2703 if (yperr == 0)
2704 map->map_mflags &= ~MF_TRY1NULL;
2706 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2708 SM_FREE_CLR(vp);
2709 buflen++;
2710 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2711 &vp, &vsize);
2712 if (yperr == 0)
2713 map->map_mflags &= ~MF_TRY0NULL;
2715 if (yperr != 0)
2717 if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2718 map->map_mflags &= ~(MF_VALID|MF_OPEN);
2719 if (vp != NULL)
2720 sm_free(vp);
2721 return NULL;
2723 SM_TRY
2724 if (bitset(MF_MATCHONLY, map->map_mflags))
2725 result = map_rewrite(map, name, strlen(name), NULL);
2726 else
2727 result = map_rewrite(map, vp, vsize, av);
2728 SM_FINALLY
2729 if (vp != NULL)
2730 sm_free(vp);
2731 SM_END_TRY
2732 return result;
2737 ** NIS_GETCANONNAME -- look up canonical name in NIS
2740 static bool
2741 nis_getcanonname(name, hbsize, statp)
2742 char *name;
2743 int hbsize;
2744 int *statp;
2746 char *vp;
2747 auto int vsize;
2748 int keylen;
2749 int yperr;
2750 static bool try0null = true;
2751 static bool try1null = true;
2752 static char *yp_domain = NULL;
2753 char host_record[MAXLINE];
2754 char cbuf[MAXNAME];
2755 char nbuf[MAXNAME + 1];
2757 if (tTd(38, 20))
2758 sm_dprintf("nis_getcanonname(%s)\n", name);
2760 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
2762 *statp = EX_UNAVAILABLE;
2763 return false;
2765 (void) shorten_hostname(nbuf);
2766 keylen = strlen(nbuf);
2768 if (yp_domain == NULL)
2769 (void) yp_get_default_domain(&yp_domain);
2770 makelower(nbuf);
2771 yperr = YPERR_KEY;
2772 vp = NULL;
2773 if (try0null)
2775 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2776 &vp, &vsize);
2777 if (yperr == 0)
2778 try1null = false;
2780 if (yperr == YPERR_KEY && try1null)
2782 SM_FREE_CLR(vp);
2783 keylen++;
2784 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2785 &vp, &vsize);
2786 if (yperr == 0)
2787 try0null = false;
2789 if (yperr != 0)
2791 if (yperr == YPERR_KEY)
2792 *statp = EX_NOHOST;
2793 else if (yperr == YPERR_BUSY)
2794 *statp = EX_TEMPFAIL;
2795 else
2796 *statp = EX_UNAVAILABLE;
2797 if (vp != NULL)
2798 sm_free(vp);
2799 return false;
2801 (void) sm_strlcpy(host_record, vp, sizeof host_record);
2802 sm_free(vp);
2803 if (tTd(38, 44))
2804 sm_dprintf("got record `%s'\n", host_record);
2805 vp = strpbrk(host_record, "#\n");
2806 if (vp != NULL)
2807 *vp = '\0';
2808 if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof cbuf))
2810 /* this should not happen, but.... */
2811 *statp = EX_NOHOST;
2812 return false;
2814 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
2816 *statp = EX_UNAVAILABLE;
2817 return false;
2819 *statp = EX_OK;
2820 return true;
2823 #endif /* NIS */
2825 ** NISPLUS Modules
2827 ** This code donated by Sun Microsystems.
2830 #if NISPLUS
2832 # undef NIS /* symbol conflict in nis.h */
2833 # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2834 # include <rpcsvc/nis.h>
2835 # include <rpcsvc/nislib.h>
2837 # define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2838 # define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2839 # define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2840 # define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
2843 ** NISPLUS_MAP_OPEN -- open nisplus table
2846 bool
2847 nisplus_map_open(map, mode)
2848 MAP *map;
2849 int mode;
2851 nis_result *res = NULL;
2852 int retry_cnt, max_col, i;
2853 char qbuf[MAXLINE + NIS_MAXNAMELEN];
2855 if (tTd(38, 2))
2856 sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2857 map->map_mname, map->map_file, mode);
2859 mode &= O_ACCMODE;
2860 if (mode != O_RDONLY)
2862 errno = EPERM;
2863 return false;
2866 if (*map->map_file == '\0')
2867 map->map_file = "mail_aliases.org_dir";
2869 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2871 /* set default NISPLUS Domain to $m */
2872 map->map_domain = newstr(nisplus_default_domain());
2873 if (tTd(38, 2))
2874 sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2875 map->map_file, map->map_domain);
2877 if (!PARTIAL_NAME(map->map_file))
2879 map->map_domain = newstr("");
2880 (void) sm_strlcpy(qbuf, map->map_file, sizeof qbuf);
2882 else
2884 /* check to see if this map actually exists */
2885 (void) sm_strlcpyn(qbuf, sizeof qbuf, 3,
2886 map->map_file, ".", map->map_domain);
2889 retry_cnt = 0;
2890 while (res == NULL || res->status != NIS_SUCCESS)
2892 res = nis_lookup(qbuf, FOLLOW_LINKS);
2893 switch (res->status)
2895 case NIS_SUCCESS:
2896 break;
2898 case NIS_TRYAGAIN:
2899 case NIS_RPCERROR:
2900 case NIS_NAMEUNREACHABLE:
2901 if (retry_cnt++ > 4)
2903 errno = EAGAIN;
2904 return false;
2906 /* try not to overwhelm hosed server */
2907 sleep(2);
2908 break;
2910 default: /* all other nisplus errors */
2911 # if 0
2912 if (!bitset(MF_OPTIONAL, map->map_mflags))
2913 syserr("451 4.3.5 Cannot find table %s.%s: %s",
2914 map->map_file, map->map_domain,
2915 nis_sperrno(res->status));
2916 # endif /* 0 */
2917 errno = EAGAIN;
2918 return false;
2922 if (NIS_RES_NUMOBJ(res) != 1 ||
2923 (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
2925 if (tTd(38, 10))
2926 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
2927 # if 0
2928 if (!bitset(MF_OPTIONAL, map->map_mflags))
2929 syserr("451 4.3.5 %s.%s: %s is not a table",
2930 map->map_file, map->map_domain,
2931 nis_sperrno(res->status));
2932 # endif /* 0 */
2933 errno = EBADF;
2934 return false;
2936 /* default key column is column 0 */
2937 if (map->map_keycolnm == NULL)
2938 map->map_keycolnm = newstr(COL_NAME(res,0));
2940 max_col = COL_MAX(res);
2942 /* verify the key column exist */
2943 for (i = 0; i < max_col; i++)
2945 if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
2946 break;
2948 if (i == max_col)
2950 if (tTd(38, 2))
2951 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
2952 map->map_file, map->map_keycolnm);
2953 errno = ENOENT;
2954 return false;
2957 /* default value column is the last column */
2958 if (map->map_valcolnm == NULL)
2960 map->map_valcolno = max_col - 1;
2961 return true;
2964 for (i = 0; i< max_col; i++)
2966 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
2968 map->map_valcolno = i;
2969 return true;
2973 if (tTd(38, 2))
2974 sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
2975 map->map_file, map->map_keycolnm);
2976 errno = ENOENT;
2977 return false;
2982 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
2985 char *
2986 nisplus_map_lookup(map, name, av, statp)
2987 MAP *map;
2988 char *name;
2989 char **av;
2990 int *statp;
2992 char *p;
2993 auto int vsize;
2994 char *skp;
2995 int skleft;
2996 char search_key[MAXNAME + 4];
2997 char qbuf[MAXLINE + NIS_MAXNAMELEN];
2998 nis_result *result;
3000 if (tTd(38, 20))
3001 sm_dprintf("nisplus_map_lookup(%s, %s)\n",
3002 map->map_mname, name);
3004 if (!bitset(MF_OPEN, map->map_mflags))
3006 if (nisplus_map_open(map, O_RDONLY))
3008 map->map_mflags |= MF_OPEN;
3009 map->map_pid = CurrentPid;
3011 else
3013 *statp = EX_UNAVAILABLE;
3014 return NULL;
3019 ** Copy the name to the key buffer, escaping double quote characters
3020 ** by doubling them and quoting "]" and "," to avoid having the
3021 ** NIS+ parser choke on them.
3024 skleft = sizeof search_key - 4;
3025 skp = search_key;
3026 for (p = name; *p != '\0' && skleft > 0; p++)
3028 switch (*p)
3030 case ']':
3031 case ',':
3032 /* quote the character */
3033 *skp++ = '"';
3034 *skp++ = *p;
3035 *skp++ = '"';
3036 skleft -= 3;
3037 break;
3039 case '"':
3040 /* double the quote */
3041 *skp++ = '"';
3042 skleft--;
3043 /* FALLTHROUGH */
3045 default:
3046 *skp++ = *p;
3047 skleft--;
3048 break;
3051 *skp = '\0';
3052 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3053 makelower(search_key);
3055 /* construct the query */
3056 if (PARTIAL_NAME(map->map_file))
3057 (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
3058 map->map_keycolnm, search_key, map->map_file,
3059 map->map_domain);
3060 else
3061 (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
3062 map->map_keycolnm, search_key, map->map_file);
3064 if (tTd(38, 20))
3065 sm_dprintf("qbuf=%s\n", qbuf);
3066 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
3067 if (result->status == NIS_SUCCESS)
3069 int count;
3070 char *str;
3072 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3074 if (LogLevel > 10)
3075 sm_syslog(LOG_WARNING, CurEnv->e_id,
3076 "%s: lookup error, expected 1 entry, got %d",
3077 map->map_file, count);
3079 /* ignore second entry */
3080 if (tTd(38, 20))
3081 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3082 name, count);
3085 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
3086 /* set the length of the result */
3087 if (p == NULL)
3088 p = "";
3089 vsize = strlen(p);
3090 if (tTd(38, 20))
3091 sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3092 name, p);
3093 if (bitset(MF_MATCHONLY, map->map_mflags))
3094 str = map_rewrite(map, name, strlen(name), NULL);
3095 else
3096 str = map_rewrite(map, p, vsize, av);
3097 nis_freeresult(result);
3098 *statp = EX_OK;
3099 return str;
3101 else
3103 if (result->status == NIS_NOTFOUND)
3104 *statp = EX_NOTFOUND;
3105 else if (result->status == NIS_TRYAGAIN)
3106 *statp = EX_TEMPFAIL;
3107 else
3109 *statp = EX_UNAVAILABLE;
3110 map->map_mflags &= ~(MF_VALID|MF_OPEN);
3113 if (tTd(38, 20))
3114 sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
3115 nis_freeresult(result);
3116 return NULL;
3122 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3125 static bool
3126 nisplus_getcanonname(name, hbsize, statp)
3127 char *name;
3128 int hbsize;
3129 int *statp;
3131 char *vp;
3132 auto int vsize;
3133 nis_result *result;
3134 char *p;
3135 char nbuf[MAXNAME + 1];
3136 char qbuf[MAXLINE + NIS_MAXNAMELEN];
3138 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
3140 *statp = EX_UNAVAILABLE;
3141 return false;
3143 (void) shorten_hostname(nbuf);
3145 p = strchr(nbuf, '.');
3146 if (p == NULL)
3148 /* single token */
3149 (void) sm_snprintf(qbuf, sizeof qbuf,
3150 "[name=%s],hosts.org_dir", nbuf);
3152 else if (p[1] != '\0')
3154 /* multi token -- take only first token in nbuf */
3155 *p = '\0';
3156 (void) sm_snprintf(qbuf, sizeof qbuf,
3157 "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
3159 else
3161 *statp = EX_NOHOST;
3162 return false;
3165 if (tTd(38, 20))
3166 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3167 name, qbuf);
3169 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
3170 NULL, NULL);
3172 if (result->status == NIS_SUCCESS)
3174 int count;
3175 char *domain;
3177 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3179 if (LogLevel > 10)
3180 sm_syslog(LOG_WARNING, CurEnv->e_id,
3181 "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3182 count);
3184 /* ignore second entry */
3185 if (tTd(38, 20))
3186 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3187 name, count);
3190 if (tTd(38, 20))
3191 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3192 name, (NIS_RES_OBJECT(result))->zo_domain);
3195 vp = ((NIS_RES_OBJECT(result))->EN_col(0));
3196 vsize = strlen(vp);
3197 if (tTd(38, 20))
3198 sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3199 name, vp);
3200 if (strchr(vp, '.') != NULL)
3202 domain = "";
3204 else
3206 domain = macvalue('m', CurEnv);
3207 if (domain == NULL)
3208 domain = "";
3210 if (hbsize > vsize + (int) strlen(domain) + 1)
3212 if (domain[0] == '\0')
3213 (void) sm_strlcpy(name, vp, hbsize);
3214 else
3215 (void) sm_snprintf(name, hbsize,
3216 "%s.%s", vp, domain);
3217 *statp = EX_OK;
3219 else
3220 *statp = EX_NOHOST;
3221 nis_freeresult(result);
3222 return true;
3224 else
3226 if (result->status == NIS_NOTFOUND)
3227 *statp = EX_NOHOST;
3228 else if (result->status == NIS_TRYAGAIN)
3229 *statp = EX_TEMPFAIL;
3230 else
3231 *statp = EX_UNAVAILABLE;
3233 if (tTd(38, 20))
3234 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3235 name, result->status, *statp);
3236 nis_freeresult(result);
3237 return false;
3240 char *
3241 nisplus_default_domain()
3243 static char default_domain[MAXNAME + 1] = "";
3244 char *p;
3246 if (default_domain[0] != '\0')
3247 return default_domain;
3249 p = nis_local_directory();
3250 (void) sm_strlcpy(default_domain, p, sizeof default_domain);
3251 return default_domain;
3254 #endif /* NISPLUS */
3256 ** LDAP Modules
3260 ** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3263 #if defined(LDAPMAP) || defined(PH_MAP)
3265 # if PH_MAP
3266 # define ph_map_dequote ldapmap_dequote
3267 # endif /* PH_MAP */
3269 static char *ldapmap_dequote __P((char *));
3271 static char *
3272 ldapmap_dequote(str)
3273 char *str;
3275 char *p;
3276 char *start;
3278 if (str == NULL)
3279 return NULL;
3281 p = str;
3282 if (*p == '"')
3284 /* Should probably swallow initial whitespace here */
3285 start = ++p;
3287 else
3288 return str;
3289 while (*p != '"' && *p != '\0')
3290 p++;
3291 if (*p != '\0')
3292 *p = '\0';
3293 return start;
3295 #endif /* defined(LDAPMAP) || defined(PH_MAP) */
3297 #if LDAPMAP
3299 static SM_LDAP_STRUCT *LDAPDefaults = NULL;
3302 ** LDAPMAP_OPEN -- open LDAP map
3304 ** Connect to the LDAP server. Re-use existing connections since a
3305 ** single server connection to a host (with the same host, port,
3306 ** bind DN, and secret) can answer queries for multiple maps.
3309 bool
3310 ldapmap_open(map, mode)
3311 MAP *map;
3312 int mode;
3314 SM_LDAP_STRUCT *lmap;
3315 STAB *s;
3316 char *id;
3318 if (tTd(38, 2))
3319 sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
3321 mode &= O_ACCMODE;
3323 /* sendmail doesn't have the ability to write to LDAP (yet) */
3324 if (mode != O_RDONLY)
3326 /* issue a pseudo-error message */
3327 errno = SM_EMAPCANTWRITE;
3328 return false;
3331 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3333 s = ldapmap_findconn(lmap);
3334 if (s->s_lmap != NULL)
3336 /* Already have a connection open to this LDAP server */
3337 lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
3338 lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
3340 /* Add this map as head of linked list */
3341 lmap->ldap_next = s->s_lmap;
3342 s->s_lmap = map;
3344 if (tTd(38, 2))
3345 sm_dprintf("using cached connection\n");
3346 return true;
3349 if (tTd(38, 2))
3350 sm_dprintf("opening new connection\n");
3352 if (lmap->ldap_host != NULL)
3353 id = lmap->ldap_host;
3354 else if (lmap->ldap_uri != NULL)
3355 id = lmap->ldap_uri;
3356 else
3357 id = "localhost";
3359 /* No connection yet, connect */
3360 if (!sm_ldap_start(map->map_mname, lmap))
3362 if (errno == ETIMEDOUT)
3364 if (LogLevel > 1)
3365 sm_syslog(LOG_NOTICE, CurEnv->e_id,
3366 "timeout conning to LDAP server %.100s",
3367 id);
3370 if (!bitset(MF_OPTIONAL, map->map_mflags))
3372 if (bitset(MF_NODEFER, map->map_mflags))
3374 syserr("%s failed to %s in map %s",
3375 # if USE_LDAP_INIT
3376 "ldap_init/ldap_bind",
3377 # else /* USE_LDAP_INIT */
3378 "ldap_open",
3379 # endif /* USE_LDAP_INIT */
3380 id, map->map_mname);
3382 else
3384 syserr("451 4.3.5 %s failed to %s in map %s",
3385 # if USE_LDAP_INIT
3386 "ldap_init/ldap_bind",
3387 # else /* USE_LDAP_INIT */
3388 "ldap_open",
3389 # endif /* USE_LDAP_INIT */
3390 id, map->map_mname);
3393 return false;
3396 /* Save connection for reuse */
3397 s->s_lmap = map;
3398 return true;
3402 ** LDAPMAP_CLOSE -- close ldap map
3405 void
3406 ldapmap_close(map)
3407 MAP *map;
3409 SM_LDAP_STRUCT *lmap;
3410 STAB *s;
3412 if (tTd(38, 2))
3413 sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
3415 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3417 /* Check if already closed */
3418 if (lmap->ldap_ld == NULL)
3419 return;
3421 /* Close the LDAP connection */
3422 sm_ldap_close(lmap);
3424 /* Mark all the maps that share the connection as closed */
3425 s = ldapmap_findconn(lmap);
3427 while (s->s_lmap != NULL)
3429 MAP *smap = s->s_lmap;
3431 if (tTd(38, 2) && smap != map)
3432 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3433 map->map_mname, smap->map_mname);
3434 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
3435 lmap = (SM_LDAP_STRUCT *) smap->map_db1;
3436 lmap->ldap_ld = NULL;
3437 s->s_lmap = lmap->ldap_next;
3438 lmap->ldap_next = NULL;
3442 # ifdef SUNET_ID
3444 ** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3445 ** This only makes sense at Stanford University.
3448 static char *
3449 sunet_id_hash(str)
3450 char *str;
3452 char *p, *p_last;
3454 p = str;
3455 p_last = p;
3456 while (*p != '\0')
3458 if (islower(*p) || isdigit(*p))
3460 *p_last = *p;
3461 p_last++;
3463 else if (isupper(*p))
3465 *p_last = tolower(*p);
3466 p_last++;
3468 ++p;
3470 if (*p_last != '\0')
3471 *p_last = '\0';
3472 return str;
3474 # endif /* SUNET_ID */
3477 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3480 char *
3481 ldapmap_lookup(map, name, av, statp)
3482 MAP *map;
3483 char *name;
3484 char **av;
3485 int *statp;
3487 int flags;
3488 int plen = 0;
3489 int psize = 0;
3490 int msgid;
3491 int save_errno;
3492 char *vp, *p;
3493 char *result = NULL;
3494 SM_RPOOL_T *rpool;
3495 SM_LDAP_STRUCT *lmap = NULL;
3496 char keybuf[MAXKEY];
3498 if (tTd(38, 20))
3499 sm_dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name);
3501 /* Get ldap struct pointer from map */
3502 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3503 sm_ldap_setopts(lmap->ldap_ld, lmap);
3505 (void) sm_strlcpy(keybuf, name, sizeof keybuf);
3507 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3509 # ifdef SUNET_ID
3510 sunet_id_hash(keybuf);
3511 # else /* SUNET_ID */
3512 makelower(keybuf);
3513 # endif /* SUNET_ID */
3516 msgid = sm_ldap_search(lmap, keybuf);
3517 if (msgid == -1)
3519 errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
3520 save_errno = errno;
3521 if (!bitset(MF_OPTIONAL, map->map_mflags))
3523 if (bitset(MF_NODEFER, map->map_mflags))
3524 syserr("Error in ldap_search using %s in map %s",
3525 keybuf, map->map_mname);
3526 else
3527 syserr("451 4.3.5 Error in ldap_search using %s in map %s",
3528 keybuf, map->map_mname);
3530 *statp = EX_TEMPFAIL;
3531 switch (save_errno - E_LDAPBASE)
3533 # ifdef LDAP_SERVER_DOWN
3534 case LDAP_SERVER_DOWN:
3535 # endif /* LDAP_SERVER_DOWN */
3536 case LDAP_TIMEOUT:
3537 case LDAP_UNAVAILABLE:
3538 /* server disappeared, try reopen on next search */
3539 ldapmap_close(map);
3540 break;
3542 errno = save_errno;
3543 return NULL;
3546 *statp = EX_NOTFOUND;
3547 vp = NULL;
3549 flags = 0;
3550 if (bitset(MF_SINGLEMATCH, map->map_mflags))
3551 flags |= SM_LDAP_SINGLEMATCH;
3552 if (bitset(MF_MATCHONLY, map->map_mflags))
3553 flags |= SM_LDAP_MATCHONLY;
3554 # if _FFR_LDAP_SINGLEDN
3555 if (bitset(MF_SINGLEDN, map->map_mflags))
3556 flags |= SM_LDAP_SINGLEDN;
3557 # endif /* _FFR_LDAP_SINGLEDN */
3559 /* Create an rpool for search related memory usage */
3560 rpool = sm_rpool_new_x(NULL);
3562 p = NULL;
3563 *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
3564 rpool, &p, &plen, &psize, NULL);
3565 save_errno = errno;
3567 /* Copy result so rpool can be freed */
3568 if (*statp == EX_OK && p != NULL)
3569 vp = newstr(p);
3570 sm_rpool_free(rpool);
3572 /* need to restart LDAP connection? */
3573 if (*statp == EX_RESTART)
3575 *statp = EX_TEMPFAIL;
3576 ldapmap_close(map);
3579 errno = save_errno;
3580 if (*statp != EX_OK && *statp != EX_NOTFOUND)
3582 if (!bitset(MF_OPTIONAL, map->map_mflags))
3584 if (bitset(MF_NODEFER, map->map_mflags))
3585 syserr("Error getting LDAP results in map %s",
3586 map->map_mname);
3587 else
3588 syserr("451 4.3.5 Error getting LDAP results in map %s",
3589 map->map_mname);
3591 errno = save_errno;
3592 return NULL;
3595 /* Did we match anything? */
3596 if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
3597 return NULL;
3599 if (*statp == EX_OK)
3601 if (LogLevel > 9)
3602 sm_syslog(LOG_INFO, CurEnv->e_id,
3603 "ldap %.100s => %s", name,
3604 vp == NULL ? "<NULL>" : vp);
3605 if (bitset(MF_MATCHONLY, map->map_mflags))
3606 result = map_rewrite(map, name, strlen(name), NULL);
3607 else
3609 /* vp != NULL according to test above */
3610 result = map_rewrite(map, vp, strlen(vp), av);
3612 if (vp != NULL)
3613 sm_free(vp); /* XXX */
3615 return result;
3619 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server
3621 ** Cache LDAP connections based on the host, port, bind DN,
3622 ** secret, and PID so we don't have multiple connections open to
3623 ** the same server for different maps. Need a separate connection
3624 ** per PID since a parent process may close the map before the
3625 ** child is done with it.
3627 ** Parameters:
3628 ** lmap -- LDAP map information
3630 ** Returns:
3631 ** Symbol table entry for the LDAP connection.
3634 static STAB *
3635 ldapmap_findconn(lmap)
3636 SM_LDAP_STRUCT *lmap;
3638 char *format;
3639 char *nbuf;
3640 char *id;
3641 STAB *SM_NONVOLATILE s = NULL;
3643 if (lmap->ldap_host != NULL)
3644 id = lmap->ldap_host;
3645 else if (lmap->ldap_uri != NULL)
3646 id = lmap->ldap_uri;
3647 else
3648 id = "localhost";
3650 format = "%s%c%d%c%d%c%s%c%s%d";
3651 nbuf = sm_stringf_x(format,
3653 CONDELSE,
3654 lmap->ldap_port,
3655 CONDELSE,
3656 lmap->ldap_version,
3657 CONDELSE,
3658 (lmap->ldap_binddn == NULL ? ""
3659 : lmap->ldap_binddn),
3660 CONDELSE,
3661 (lmap->ldap_secret == NULL ? ""
3662 : lmap->ldap_secret),
3663 (int) CurrentPid);
3664 SM_TRY
3665 s = stab(nbuf, ST_LMAP, ST_ENTER);
3666 SM_FINALLY
3667 sm_free(nbuf);
3668 SM_END_TRY
3669 return s;
3672 ** LDAPMAP_PARSEARGS -- parse ldap map definition args.
3675 static struct lamvalues LDAPAuthMethods[] =
3677 { "none", LDAP_AUTH_NONE },
3678 { "simple", LDAP_AUTH_SIMPLE },
3679 # ifdef LDAP_AUTH_KRBV4
3680 { "krbv4", LDAP_AUTH_KRBV4 },
3681 # endif /* LDAP_AUTH_KRBV4 */
3682 { NULL, 0 }
3685 static struct ladvalues LDAPAliasDereference[] =
3687 { "never", LDAP_DEREF_NEVER },
3688 { "always", LDAP_DEREF_ALWAYS },
3689 { "search", LDAP_DEREF_SEARCHING },
3690 { "find", LDAP_DEREF_FINDING },
3691 { NULL, 0 }
3694 static struct lssvalues LDAPSearchScope[] =
3696 { "base", LDAP_SCOPE_BASE },
3697 { "one", LDAP_SCOPE_ONELEVEL },
3698 { "sub", LDAP_SCOPE_SUBTREE },
3699 { NULL, 0 }
3702 bool
3703 ldapmap_parseargs(map, args)
3704 MAP *map;
3705 char *args;
3707 bool secretread = true;
3708 bool attrssetup = false;
3709 int i;
3710 register char *p = args;
3711 SM_LDAP_STRUCT *lmap;
3712 struct lamvalues *lam;
3713 struct ladvalues *lad;
3714 struct lssvalues *lss;
3715 char ldapfilt[MAXLINE];
3716 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
3718 /* Get ldap struct pointer from map */
3719 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3721 /* Check if setting the initial LDAP defaults */
3722 if (lmap == NULL || lmap != LDAPDefaults)
3724 /* We need to alloc an SM_LDAP_STRUCT struct */
3725 lmap = (SM_LDAP_STRUCT *) xalloc(sizeof *lmap);
3726 if (LDAPDefaults == NULL)
3727 sm_ldap_clear(lmap);
3728 else
3729 STRUCTCOPY(*LDAPDefaults, *lmap);
3732 /* there is no check whether there is really an argument */
3733 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
3734 map->map_spacesub = SpaceSub; /* default value */
3736 /* Check if setting up an alias or file class LDAP map */
3737 if (bitset(MF_ALIAS, map->map_mflags))
3739 /* Comma separate if used as an alias file */
3740 map->map_coldelim = ',';
3741 if (*args == '\0')
3743 int n;
3744 char *lc;
3745 char jbuf[MAXHOSTNAMELEN];
3746 char lcbuf[MAXLINE];
3748 /* Get $j */
3749 expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
3750 if (jbuf[0] == '\0')
3752 (void) sm_strlcpy(jbuf, "localhost",
3753 sizeof jbuf);
3756 lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
3757 if (lc == NULL)
3758 lc = "";
3759 else
3761 expand(lc, lcbuf, sizeof lcbuf, CurEnv);
3762 lc = lcbuf;
3765 n = sm_snprintf(ldapfilt, sizeof ldapfilt,
3766 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
3767 lc, jbuf);
3768 if (n >= sizeof ldapfilt)
3770 syserr("%s: Default LDAP string too long",
3771 map->map_mname);
3772 return false;
3775 /* default args for an alias LDAP entry */
3776 lmap->ldap_filter = ldapfilt;
3777 lmap->ldap_attr[0] = "objectClass";
3778 lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
3779 lmap->ldap_attr_needobjclass[0] = NULL;
3780 lmap->ldap_attr[1] = "sendmailMTAAliasValue";
3781 lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
3782 lmap->ldap_attr_needobjclass[1] = NULL;
3783 lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
3784 lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
3785 lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
3786 lmap->ldap_attr[3] = "sendmailMTAAliasURL";
3787 lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
3788 lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
3789 lmap->ldap_attr[4] = NULL;
3790 lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
3791 lmap->ldap_attr_needobjclass[4] = NULL;
3792 attrssetup = true;
3795 else if (bitset(MF_FILECLASS, map->map_mflags))
3797 /* Space separate if used as a file class file */
3798 map->map_coldelim = ' ';
3801 for (;;)
3803 while (isascii(*p) && isspace(*p))
3804 p++;
3805 if (*p != '-')
3806 break;
3807 switch (*++p)
3809 case 'N':
3810 map->map_mflags |= MF_INCLNULL;
3811 map->map_mflags &= ~MF_TRY0NULL;
3812 break;
3814 case 'O':
3815 map->map_mflags &= ~MF_TRY1NULL;
3816 break;
3818 case 'o':
3819 map->map_mflags |= MF_OPTIONAL;
3820 break;
3822 case 'f':
3823 map->map_mflags |= MF_NOFOLDCASE;
3824 break;
3826 case 'm':
3827 map->map_mflags |= MF_MATCHONLY;
3828 break;
3830 case 'A':
3831 map->map_mflags |= MF_APPEND;
3832 break;
3834 case 'q':
3835 map->map_mflags |= MF_KEEPQUOTES;
3836 break;
3838 case 'a':
3839 map->map_app = ++p;
3840 break;
3842 case 'T':
3843 map->map_tapp = ++p;
3844 break;
3846 case 't':
3847 map->map_mflags |= MF_NODEFER;
3848 break;
3850 case 'S':
3851 map->map_spacesub = *++p;
3852 break;
3854 case 'D':
3855 map->map_mflags |= MF_DEFER;
3856 break;
3858 case 'z':
3859 if (*++p != '\\')
3860 map->map_coldelim = *p;
3861 else
3863 switch (*++p)
3865 case 'n':
3866 map->map_coldelim = '\n';
3867 break;
3869 case 't':
3870 map->map_coldelim = '\t';
3871 break;
3873 default:
3874 map->map_coldelim = '\\';
3877 break;
3879 /* Start of ldapmap specific args */
3880 case 'V':
3881 if (*++p != '\\')
3882 lmap->ldap_attrsep = *p;
3883 else
3885 switch (*++p)
3887 case 'n':
3888 lmap->ldap_attrsep = '\n';
3889 break;
3891 case 't':
3892 lmap->ldap_attrsep = '\t';
3893 break;
3895 default:
3896 lmap->ldap_attrsep = '\\';
3899 break;
3901 case 'k': /* search field */
3902 while (isascii(*++p) && isspace(*p))
3903 continue;
3904 lmap->ldap_filter = p;
3905 break;
3907 case 'v': /* attr to return */
3908 while (isascii(*++p) && isspace(*p))
3909 continue;
3910 lmap->ldap_attr[0] = p;
3911 lmap->ldap_attr[1] = NULL;
3912 break;
3914 case '1':
3915 map->map_mflags |= MF_SINGLEMATCH;
3916 break;
3918 # if _FFR_LDAP_SINGLEDN
3919 case '2':
3920 map->map_mflags |= MF_SINGLEDN;
3921 break;
3922 # endif /* _FFR_LDAP_SINGLEDN */
3924 /* args stolen from ldapsearch.c */
3925 case 'R': /* don't auto chase referrals */
3926 # ifdef LDAP_REFERRALS
3927 lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
3928 # else /* LDAP_REFERRALS */
3929 syserr("compile with -DLDAP_REFERRALS for referral support");
3930 # endif /* LDAP_REFERRALS */
3931 break;
3933 case 'n': /* retrieve attribute names only */
3934 lmap->ldap_attrsonly = LDAPMAP_TRUE;
3935 break;
3937 case 'r': /* alias dereferencing */
3938 while (isascii(*++p) && isspace(*p))
3939 continue;
3941 if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
3942 p += 11;
3944 for (lad = LDAPAliasDereference;
3945 lad != NULL && lad->lad_name != NULL; lad++)
3947 if (sm_strncasecmp(p, lad->lad_name,
3948 strlen(lad->lad_name)) == 0)
3949 break;
3951 if (lad->lad_name != NULL)
3952 lmap->ldap_deref = lad->lad_code;
3953 else
3955 /* bad config line */
3956 if (!bitset(MCF_OPTFILE,
3957 map->map_class->map_cflags))
3959 char *ptr;
3961 if ((ptr = strchr(p, ' ')) != NULL)
3962 *ptr = '\0';
3963 syserr("Deref must be [never|always|search|find] (not %s) in map %s",
3964 p, map->map_mname);
3965 if (ptr != NULL)
3966 *ptr = ' ';
3967 return false;
3970 break;
3972 case 's': /* search scope */
3973 while (isascii(*++p) && isspace(*p))
3974 continue;
3976 if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
3977 p += 11;
3979 for (lss = LDAPSearchScope;
3980 lss != NULL && lss->lss_name != NULL; lss++)
3982 if (sm_strncasecmp(p, lss->lss_name,
3983 strlen(lss->lss_name)) == 0)
3984 break;
3986 if (lss->lss_name != NULL)
3987 lmap->ldap_scope = lss->lss_code;
3988 else
3990 /* bad config line */
3991 if (!bitset(MCF_OPTFILE,
3992 map->map_class->map_cflags))
3994 char *ptr;
3996 if ((ptr = strchr(p, ' ')) != NULL)
3997 *ptr = '\0';
3998 syserr("Scope must be [base|one|sub] (not %s) in map %s",
3999 p, map->map_mname);
4000 if (ptr != NULL)
4001 *ptr = ' ';
4002 return false;
4005 break;
4007 case 'h': /* ldap host */
4008 while (isascii(*++p) && isspace(*p))
4009 continue;
4010 if (lmap->ldap_uri != NULL)
4012 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4013 map->map_mname);
4014 return false;
4016 lmap->ldap_host = p;
4017 break;
4019 case 'b': /* search base */
4020 while (isascii(*++p) && isspace(*p))
4021 continue;
4022 lmap->ldap_base = p;
4023 break;
4025 case 'p': /* ldap port */
4026 while (isascii(*++p) && isspace(*p))
4027 continue;
4028 lmap->ldap_port = atoi(p);
4029 break;
4031 case 'l': /* time limit */
4032 while (isascii(*++p) && isspace(*p))
4033 continue;
4034 lmap->ldap_timelimit = atoi(p);
4035 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
4036 break;
4038 case 'Z':
4039 while (isascii(*++p) && isspace(*p))
4040 continue;
4041 lmap->ldap_sizelimit = atoi(p);
4042 break;
4044 case 'd': /* Dn to bind to server as */
4045 while (isascii(*++p) && isspace(*p))
4046 continue;
4047 lmap->ldap_binddn = p;
4048 break;
4050 case 'M': /* Method for binding */
4051 while (isascii(*++p) && isspace(*p))
4052 continue;
4054 if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
4055 p += 10;
4057 for (lam = LDAPAuthMethods;
4058 lam != NULL && lam->lam_name != NULL; lam++)
4060 if (sm_strncasecmp(p, lam->lam_name,
4061 strlen(lam->lam_name)) == 0)
4062 break;
4064 if (lam->lam_name != NULL)
4065 lmap->ldap_method = lam->lam_code;
4066 else
4068 /* bad config line */
4069 if (!bitset(MCF_OPTFILE,
4070 map->map_class->map_cflags))
4072 char *ptr;
4074 if ((ptr = strchr(p, ' ')) != NULL)
4075 *ptr = '\0';
4076 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4077 p, map->map_mname);
4078 if (ptr != NULL)
4079 *ptr = ' ';
4080 return false;
4084 break;
4087 ** This is a string that is dependent on the
4088 ** method used defined above.
4091 case 'P': /* Secret password for binding */
4092 while (isascii(*++p) && isspace(*p))
4093 continue;
4094 lmap->ldap_secret = p;
4095 secretread = false;
4096 break;
4098 case 'H': /* Use LDAP URI */
4099 # if !USE_LDAP_INIT
4100 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4101 map->map_mname);
4102 return false;
4103 # else /* !USE_LDAP_INIT */
4104 if (lmap->ldap_host != NULL)
4106 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4107 map->map_mname);
4108 return false;
4110 while (isascii(*++p) && isspace(*p))
4111 continue;
4112 lmap->ldap_uri = p;
4113 break;
4114 # endif /* !USE_LDAP_INIT */
4116 case 'w':
4117 /* -w should be for passwd, -P should be for version */
4118 while (isascii(*++p) && isspace(*p))
4119 continue;
4120 lmap->ldap_version = atoi(p);
4121 # ifdef LDAP_VERSION_MAX
4122 if (lmap->ldap_version > LDAP_VERSION_MAX)
4124 syserr("LDAP version %d exceeds max of %d in map %s",
4125 lmap->ldap_version, LDAP_VERSION_MAX,
4126 map->map_mname);
4127 return false;
4129 # endif /* LDAP_VERSION_MAX */
4130 # ifdef LDAP_VERSION_MIN
4131 if (lmap->ldap_version < LDAP_VERSION_MIN)
4133 syserr("LDAP version %d is lower than min of %d in map %s",
4134 lmap->ldap_version, LDAP_VERSION_MIN,
4135 map->map_mname);
4136 return false;
4138 # endif /* LDAP_VERSION_MIN */
4139 break;
4141 default:
4142 syserr("Illegal option %c map %s", *p, map->map_mname);
4143 break;
4146 /* need to account for quoted strings here */
4147 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4149 if (*p == '"')
4151 while (*++p != '"' && *p != '\0')
4152 continue;
4153 if (*p != '\0')
4154 p++;
4156 else
4157 p++;
4160 if (*p != '\0')
4161 *p++ = '\0';
4164 if (map->map_app != NULL)
4165 map->map_app = newstr(ldapmap_dequote(map->map_app));
4166 if (map->map_tapp != NULL)
4167 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
4170 ** We need to swallow up all the stuff into a struct
4171 ** and dump it into map->map_dbptr1
4174 if (lmap->ldap_host != NULL &&
4175 (LDAPDefaults == NULL ||
4176 LDAPDefaults == lmap ||
4177 LDAPDefaults->ldap_host != lmap->ldap_host))
4178 lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
4179 map->map_domain = lmap->ldap_host;
4181 if (lmap->ldap_uri != NULL &&
4182 (LDAPDefaults == NULL ||
4183 LDAPDefaults == lmap ||
4184 LDAPDefaults->ldap_uri != lmap->ldap_uri))
4185 lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
4186 map->map_domain = lmap->ldap_uri;
4188 if (lmap->ldap_binddn != NULL &&
4189 (LDAPDefaults == NULL ||
4190 LDAPDefaults == lmap ||
4191 LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
4192 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
4194 if (lmap->ldap_secret != NULL &&
4195 (LDAPDefaults == NULL ||
4196 LDAPDefaults == lmap ||
4197 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4199 SM_FILE_T *sfd;
4200 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
4202 if (DontLockReadFiles)
4203 sff |= SFF_NOLOCK;
4205 /* need to use method to map secret to passwd string */
4206 switch (lmap->ldap_method)
4208 case LDAP_AUTH_NONE:
4209 /* Do nothing */
4210 break;
4212 case LDAP_AUTH_SIMPLE:
4215 ** Secret is the name of a file with
4216 ** the first line as the password.
4219 /* Already read in the secret? */
4220 if (secretread)
4221 break;
4223 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
4224 O_RDONLY, 0, sff);
4225 if (sfd == NULL)
4227 syserr("LDAP map: cannot open secret %s",
4228 ldapmap_dequote(lmap->ldap_secret));
4229 return false;
4231 lmap->ldap_secret = sfgets(m_tmp, sizeof m_tmp,
4232 sfd, TimeOuts.to_fileopen,
4233 "ldapmap_parseargs");
4234 (void) sm_io_close(sfd, SM_TIME_DEFAULT);
4235 if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
4237 syserr("LDAP map: secret in %s too long",
4238 ldapmap_dequote(lmap->ldap_secret));
4239 return false;
4241 if (lmap->ldap_secret != NULL &&
4242 strlen(m_tmp) > 0)
4244 /* chomp newline */
4245 if (m_tmp[strlen(m_tmp) - 1] == '\n')
4246 m_tmp[strlen(m_tmp) - 1] = '\0';
4248 lmap->ldap_secret = m_tmp;
4250 break;
4252 # ifdef LDAP_AUTH_KRBV4
4253 case LDAP_AUTH_KRBV4:
4256 ** Secret is where the ticket file is
4257 ** stashed
4260 (void) sm_snprintf(m_tmp, sizeof m_tmp,
4261 "KRBTKFILE=%s",
4262 ldapmap_dequote(lmap->ldap_secret));
4263 lmap->ldap_secret = m_tmp;
4264 break;
4265 # endif /* LDAP_AUTH_KRBV4 */
4267 default: /* Should NEVER get here */
4268 syserr("LDAP map: Illegal value in lmap method");
4269 return false;
4270 /* NOTREACHED */
4271 break;
4275 if (lmap->ldap_secret != NULL &&
4276 (LDAPDefaults == NULL ||
4277 LDAPDefaults == lmap ||
4278 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4279 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
4281 if (lmap->ldap_base != NULL &&
4282 (LDAPDefaults == NULL ||
4283 LDAPDefaults == lmap ||
4284 LDAPDefaults->ldap_base != lmap->ldap_base))
4285 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
4288 ** Save the server from extra work. If request is for a single
4289 ** match, tell the server to only return enough records to
4290 ** determine if there is a single match or not. This can not
4291 ** be one since the server would only return one and we wouldn't
4292 ** know if there were others available.
4295 if (bitset(MF_SINGLEMATCH, map->map_mflags))
4296 lmap->ldap_sizelimit = 2;
4298 /* If setting defaults, don't process ldap_filter and ldap_attr */
4299 if (lmap == LDAPDefaults)
4300 return true;
4302 if (lmap->ldap_filter != NULL)
4303 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
4304 else
4306 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
4308 syserr("No filter given in map %s", map->map_mname);
4309 return false;
4313 if (!attrssetup && lmap->ldap_attr[0] != NULL)
4315 bool recurse = false;
4316 bool normalseen = false;
4318 i = 0;
4319 p = ldapmap_dequote(lmap->ldap_attr[0]);
4320 lmap->ldap_attr[0] = NULL;
4322 /* Prime the attr list with the objectClass attribute */
4323 lmap->ldap_attr[i] = "objectClass";
4324 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
4325 lmap->ldap_attr_needobjclass[i] = NULL;
4326 i++;
4328 while (p != NULL)
4330 char *v;
4332 while (isascii(*p) && isspace(*p))
4333 p++;
4334 if (*p == '\0')
4335 break;
4336 v = p;
4337 p = strchr(v, ',');
4338 if (p != NULL)
4339 *p++ = '\0';
4341 if (i >= LDAPMAP_MAX_ATTR)
4343 syserr("Too many return attributes in %s (max %d)",
4344 map->map_mname, LDAPMAP_MAX_ATTR);
4345 return false;
4347 if (*v != '\0')
4349 int j;
4350 int use;
4351 char *type;
4352 char *needobjclass;
4354 type = strchr(v, ':');
4355 if (type != NULL)
4357 *type++ = '\0';
4358 needobjclass = strchr(type, ':');
4359 if (needobjclass != NULL)
4360 *needobjclass++ = '\0';
4362 else
4364 needobjclass = NULL;
4367 use = i;
4369 /* allow override on "objectClass" type */
4370 if (sm_strcasecmp(v, "objectClass") == 0 &&
4371 lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
4373 use = 0;
4375 else
4378 ** Don't add something to attribute
4379 ** list twice.
4382 for (j = 1; j < i; j++)
4384 if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
4386 syserr("Duplicate attribute (%s) in %s",
4387 v, map->map_mname);
4388 return false;
4392 lmap->ldap_attr[use] = newstr(v);
4393 if (needobjclass != NULL &&
4394 *needobjclass != '\0' &&
4395 *needobjclass != '*')
4397 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
4399 else
4401 lmap->ldap_attr_needobjclass[use] = NULL;
4406 if (type != NULL && *type != '\0')
4408 if (sm_strcasecmp(type, "dn") == 0)
4410 recurse = true;
4411 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
4413 else if (sm_strcasecmp(type, "filter") == 0)
4415 recurse = true;
4416 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
4418 else if (sm_strcasecmp(type, "url") == 0)
4420 recurse = true;
4421 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
4423 else if (sm_strcasecmp(type, "normal") == 0)
4425 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4426 normalseen = true;
4428 else
4430 syserr("Unknown attribute type (%s) in %s",
4431 type, map->map_mname);
4432 return false;
4435 else
4437 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4438 normalseen = true;
4440 i++;
4443 lmap->ldap_attr[i] = NULL;
4445 /* Set in case needed in future code */
4446 attrssetup = true;
4448 if (recurse && !normalseen)
4450 syserr("LDAP recursion requested in %s but no returnable attribute given",
4451 map->map_mname);
4452 return false;
4454 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
4456 syserr("LDAP recursion requested in %s can not be used with -n",
4457 map->map_mname);
4458 return false;
4461 map->map_db1 = (ARBPTR_T) lmap;
4462 return true;
4466 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4468 ** Parameters:
4469 ** spec -- map argument string from LDAPDefaults option
4471 ** Returns:
4472 ** None.
4475 void
4476 ldapmap_set_defaults(spec)
4477 char *spec;
4479 STAB *class;
4480 MAP map;
4482 /* Allocate and set the default values */
4483 if (LDAPDefaults == NULL)
4484 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof *LDAPDefaults);
4485 sm_ldap_clear(LDAPDefaults);
4487 memset(&map, '\0', sizeof map);
4489 /* look up the class */
4490 class = stab("ldap", ST_MAPCLASS, ST_FIND);
4491 if (class == NULL)
4493 syserr("readcf: LDAPDefaultSpec: class ldap not available");
4494 return;
4496 map.map_class = &class->s_mapclass;
4497 map.map_db1 = (ARBPTR_T) LDAPDefaults;
4498 map.map_mname = "O LDAPDefaultSpec";
4500 (void) ldapmap_parseargs(&map, spec);
4502 /* These should never be set in LDAPDefaults */
4503 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
4504 map.map_spacesub != SpaceSub ||
4505 map.map_app != NULL ||
4506 map.map_tapp != NULL)
4508 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4509 SM_FREE_CLR(map.map_app);
4510 SM_FREE_CLR(map.map_tapp);
4513 if (LDAPDefaults->ldap_filter != NULL)
4515 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4517 /* don't free, it isn't malloc'ed in parseargs */
4518 LDAPDefaults->ldap_filter = NULL;
4521 if (LDAPDefaults->ldap_attr[0] != NULL)
4523 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4524 /* don't free, they aren't malloc'ed in parseargs */
4525 LDAPDefaults->ldap_attr[0] = NULL;
4528 #endif /* LDAPMAP */
4530 ** PH map
4533 #if PH_MAP
4536 ** Support for the CCSO Nameserver (ph/qi).
4537 ** This code is intended to replace the so-called "ph mailer".
4538 ** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support.
4541 /* what version of the ph map code we're running */
4542 static char phmap_id[128];
4544 /* sendmail version for phmap id string */
4545 extern const char Version[];
4547 /* assume we're using nph-1.2.x if not specified */
4548 # ifndef NPH_VERSION
4549 # define NPH_VERSION 10200
4550 # endif
4552 /* compatibility for versions older than nph-1.2.0 */
4553 # if NPH_VERSION < 10200
4554 # define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
4555 # define PH_OPEN_DONTID PH_DONTID
4556 # define PH_CLOSE_FAST PH_FASTCLOSE
4557 # define PH_ERR_DATAERR PH_DATAERR
4558 # define PH_ERR_NOMATCH PH_NOMATCH
4559 # endif /* NPH_VERSION < 10200 */
4562 ** PH_MAP_PARSEARGS -- parse ph map definition args.
4565 bool
4566 ph_map_parseargs(map, args)
4567 MAP *map;
4568 char *args;
4570 register bool done;
4571 register char *p = args;
4572 PH_MAP_STRUCT *pmap = NULL;
4574 /* initialize version string */
4575 (void) sm_snprintf(phmap_id, sizeof phmap_id,
4576 "sendmail-%s phmap-20010529 libphclient-%s",
4577 Version, libphclient_version);
4579 pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap);
4581 /* defaults */
4582 pmap->ph_servers = NULL;
4583 pmap->ph_field_list = NULL;
4584 pmap->ph = NULL;
4585 pmap->ph_timeout = 0;
4586 pmap->ph_fastclose = 0;
4588 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4589 for (;;)
4591 while (isascii(*p) && isspace(*p))
4592 p++;
4593 if (*p != '-')
4594 break;
4595 switch (*++p)
4597 case 'N':
4598 map->map_mflags |= MF_INCLNULL;
4599 map->map_mflags &= ~MF_TRY0NULL;
4600 break;
4602 case 'O':
4603 map->map_mflags &= ~MF_TRY1NULL;
4604 break;
4606 case 'o':
4607 map->map_mflags |= MF_OPTIONAL;
4608 break;
4610 case 'f':
4611 map->map_mflags |= MF_NOFOLDCASE;
4612 break;
4614 case 'm':
4615 map->map_mflags |= MF_MATCHONLY;
4616 break;
4618 case 'A':
4619 map->map_mflags |= MF_APPEND;
4620 break;
4622 case 'q':
4623 map->map_mflags |= MF_KEEPQUOTES;
4624 break;
4626 case 't':
4627 map->map_mflags |= MF_NODEFER;
4628 break;
4630 case 'a':
4631 map->map_app = ++p;
4632 break;
4634 case 'T':
4635 map->map_tapp = ++p;
4636 break;
4638 case 'l':
4639 while (isascii(*++p) && isspace(*p))
4640 continue;
4641 pmap->ph_timeout = atoi(p);
4642 break;
4644 case 'S':
4645 map->map_spacesub = *++p;
4646 break;
4648 case 'D':
4649 map->map_mflags |= MF_DEFER;
4650 break;
4652 case 'h': /* PH server list */
4653 while (isascii(*++p) && isspace(*p))
4654 continue;
4655 pmap->ph_servers = p;
4656 break;
4658 case 'k': /* fields to search for */
4659 while (isascii(*++p) && isspace(*p))
4660 continue;
4661 pmap->ph_field_list = p;
4662 break;
4664 default:
4665 syserr("ph_map_parseargs: unknown option -%c", *p);
4668 /* try to account for quoted strings */
4669 done = isascii(*p) && isspace(*p);
4670 while (*p != '\0' && !done)
4672 if (*p == '"')
4674 while (*++p != '"' && *p != '\0')
4675 continue;
4676 if (*p != '\0')
4677 p++;
4679 else
4680 p++;
4681 done = isascii(*p) && isspace(*p);
4684 if (*p != '\0')
4685 *p++ = '\0';
4688 if (map->map_app != NULL)
4689 map->map_app = newstr(ph_map_dequote(map->map_app));
4690 if (map->map_tapp != NULL)
4691 map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
4693 if (pmap->ph_field_list != NULL)
4694 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
4696 if (pmap->ph_servers != NULL)
4697 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
4698 else
4700 syserr("ph_map_parseargs: -h flag is required");
4701 return false;
4704 map->map_db1 = (ARBPTR_T) pmap;
4705 return true;
4709 ** PH_MAP_CLOSE -- close the connection to the ph server
4712 void
4713 ph_map_close(map)
4714 MAP *map;
4716 PH_MAP_STRUCT *pmap;
4718 pmap = (PH_MAP_STRUCT *)map->map_db1;
4719 if (tTd(38, 9))
4720 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
4721 map->map_mname, pmap->ph_fastclose);
4724 if (pmap->ph != NULL)
4726 ph_set_sendhook(pmap->ph, NULL);
4727 ph_set_recvhook(pmap->ph, NULL);
4728 ph_close(pmap->ph, pmap->ph_fastclose);
4731 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4734 static jmp_buf PHTimeout;
4736 /* ARGSUSED */
4737 static void
4738 ph_timeout(unused)
4739 int unused;
4742 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
4743 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
4744 ** DOING.
4747 errno = ETIMEDOUT;
4748 longjmp(PHTimeout, 1);
4751 static void
4752 #if NPH_VERSION >= 10200
4753 ph_map_send_debug(appdata, text)
4754 void *appdata;
4755 #else
4756 ph_map_send_debug(text)
4757 #endif
4758 char *text;
4760 if (LogLevel > 9)
4761 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4762 "ph_map_send_debug: ==> %s", text);
4763 if (tTd(38, 20))
4764 sm_dprintf("ph_map_send_debug: ==> %s\n", text);
4767 static void
4768 #if NPH_VERSION >= 10200
4769 ph_map_recv_debug(appdata, text)
4770 void *appdata;
4771 #else
4772 ph_map_recv_debug(text)
4773 #endif
4774 char *text;
4776 if (LogLevel > 10)
4777 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4778 "ph_map_recv_debug: <== %s", text);
4779 if (tTd(38, 21))
4780 sm_dprintf("ph_map_recv_debug: <== %s\n", text);
4784 ** PH_MAP_OPEN -- sub for opening PH map
4786 bool
4787 ph_map_open(map, mode)
4788 MAP *map;
4789 int mode;
4791 PH_MAP_STRUCT *pmap;
4792 register SM_EVENT *ev = NULL;
4793 int save_errno = 0;
4794 char *hostlist, *host;
4796 if (tTd(38, 2))
4797 sm_dprintf("ph_map_open(%s)\n", map->map_mname);
4799 mode &= O_ACCMODE;
4800 if (mode != O_RDONLY)
4802 /* issue a pseudo-error message */
4803 errno = SM_EMAPCANTWRITE;
4804 return false;
4807 if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
4808 bitset(MF_DEFER, map->map_mflags))
4810 if (tTd(9, 1))
4811 sm_dprintf("ph_map_open(%s) => DEFERRED\n",
4812 map->map_mname);
4815 ** Unset MF_DEFER here so that map_lookup() returns
4816 ** a temporary failure using the bogus map and
4817 ** map->map_tapp instead of the default permanent error.
4820 map->map_mflags &= ~MF_DEFER;
4821 return false;
4824 pmap = (PH_MAP_STRUCT *)map->map_db1;
4825 pmap->ph_fastclose = 0; /* refresh field for reopen */
4827 /* try each host in the list */
4828 hostlist = newstr(pmap->ph_servers);
4829 for (host = strtok(hostlist, " ");
4830 host != NULL;
4831 host = strtok(NULL, " "))
4833 /* set timeout */
4834 if (pmap->ph_timeout != 0)
4836 if (setjmp(PHTimeout) != 0)
4838 ev = NULL;
4839 if (LogLevel > 1)
4840 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4841 "timeout connecting to PH server %.100s",
4842 host);
4843 errno = ETIMEDOUT;
4844 goto ph_map_open_abort;
4846 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
4849 /* open connection to server */
4850 if (ph_open(&(pmap->ph), host,
4851 PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
4852 ph_map_send_debug, ph_map_recv_debug
4853 #if NPH_VERSION >= 10200
4854 , NULL
4855 #endif
4856 ) == 0
4857 && ph_id(pmap->ph, phmap_id) == 0)
4859 if (ev != NULL)
4860 sm_clrevent(ev);
4861 sm_free(hostlist); /* XXX */
4862 return true;
4865 ph_map_open_abort:
4866 save_errno = errno;
4867 if (ev != NULL)
4868 sm_clrevent(ev);
4869 pmap->ph_fastclose = PH_CLOSE_FAST;
4870 ph_map_close(map);
4871 errno = save_errno;
4874 if (bitset(MF_NODEFER, map->map_mflags))
4876 if (errno == 0)
4877 errno = EAGAIN;
4878 syserr("ph_map_open: %s: cannot connect to PH server",
4879 map->map_mname);
4881 else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
4882 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4883 "ph_map_open: %s: cannot connect to PH server",
4884 map->map_mname);
4885 sm_free(hostlist); /* XXX */
4886 return false;
4890 ** PH_MAP_LOOKUP -- look up key from ph server
4893 char *
4894 ph_map_lookup(map, key, args, pstat)
4895 MAP *map;
4896 char *key;
4897 char **args;
4898 int *pstat;
4900 int i, save_errno = 0;
4901 register SM_EVENT *ev = NULL;
4902 PH_MAP_STRUCT *pmap;
4903 char *value = NULL;
4905 pmap = (PH_MAP_STRUCT *)map->map_db1;
4907 *pstat = EX_OK;
4909 /* set timeout */
4910 if (pmap->ph_timeout != 0)
4912 if (setjmp(PHTimeout) != 0)
4914 ev = NULL;
4915 if (LogLevel > 1)
4916 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4917 "timeout during PH lookup of %.100s",
4918 key);
4919 errno = ETIMEDOUT;
4920 *pstat = EX_TEMPFAIL;
4921 goto ph_map_lookup_abort;
4923 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
4926 /* perform lookup */
4927 i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
4928 if (i == -1)
4929 *pstat = EX_TEMPFAIL;
4930 else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
4931 *pstat = EX_UNAVAILABLE;
4933 ph_map_lookup_abort:
4934 if (ev != NULL)
4935 sm_clrevent(ev);
4938 ** Close the connection if the timer popped
4939 ** or we got a temporary PH error
4942 if (*pstat == EX_TEMPFAIL)
4944 save_errno = errno;
4945 pmap->ph_fastclose = PH_CLOSE_FAST;
4946 ph_map_close(map);
4947 errno = save_errno;
4950 if (*pstat == EX_OK)
4952 if (tTd(38,20))
4953 sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
4955 if (bitset(MF_MATCHONLY, map->map_mflags))
4956 return map_rewrite(map, key, strlen(key), NULL);
4957 else
4958 return map_rewrite(map, value, strlen(value), args);
4961 return NULL;
4963 #endif /* PH_MAP */
4965 ** syslog map
4968 #define map_prio map_lockfd /* overload field */
4971 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
4974 bool
4975 syslog_map_parseargs(map, args)
4976 MAP *map;
4977 char *args;
4979 char *p = args;
4980 char *priority = NULL;
4982 /* there is no check whether there is really an argument */
4983 while (*p != '\0')
4985 while (isascii(*p) && isspace(*p))
4986 p++;
4987 if (*p != '-')
4988 break;
4989 ++p;
4990 if (*p == 'D')
4992 map->map_mflags |= MF_DEFER;
4993 ++p;
4995 else if (*p == 'S')
4997 map->map_spacesub = *++p;
4998 if (*p != '\0')
4999 p++;
5001 else if (*p == 'L')
5003 while (*++p != '\0' && isascii(*p) && isspace(*p))
5004 continue;
5005 if (*p == '\0')
5006 break;
5007 priority = p;
5008 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5009 p++;
5010 if (*p != '\0')
5011 *p++ = '\0';
5013 else
5015 syserr("Illegal option %c map syslog", *p);
5016 ++p;
5020 if (priority == NULL)
5021 map->map_prio = LOG_INFO;
5022 else
5024 if (sm_strncasecmp("LOG_", priority, 4) == 0)
5025 priority += 4;
5027 #ifdef LOG_EMERG
5028 if (sm_strcasecmp("EMERG", priority) == 0)
5029 map->map_prio = LOG_EMERG;
5030 else
5031 #endif /* LOG_EMERG */
5032 #ifdef LOG_ALERT
5033 if (sm_strcasecmp("ALERT", priority) == 0)
5034 map->map_prio = LOG_ALERT;
5035 else
5036 #endif /* LOG_ALERT */
5037 #ifdef LOG_CRIT
5038 if (sm_strcasecmp("CRIT", priority) == 0)
5039 map->map_prio = LOG_CRIT;
5040 else
5041 #endif /* LOG_CRIT */
5042 #ifdef LOG_ERR
5043 if (sm_strcasecmp("ERR", priority) == 0)
5044 map->map_prio = LOG_ERR;
5045 else
5046 #endif /* LOG_ERR */
5047 #ifdef LOG_WARNING
5048 if (sm_strcasecmp("WARNING", priority) == 0)
5049 map->map_prio = LOG_WARNING;
5050 else
5051 #endif /* LOG_WARNING */
5052 #ifdef LOG_NOTICE
5053 if (sm_strcasecmp("NOTICE", priority) == 0)
5054 map->map_prio = LOG_NOTICE;
5055 else
5056 #endif /* LOG_NOTICE */
5057 #ifdef LOG_INFO
5058 if (sm_strcasecmp("INFO", priority) == 0)
5059 map->map_prio = LOG_INFO;
5060 else
5061 #endif /* LOG_INFO */
5062 #ifdef LOG_DEBUG
5063 if (sm_strcasecmp("DEBUG", priority) == 0)
5064 map->map_prio = LOG_DEBUG;
5065 else
5066 #endif /* LOG_DEBUG */
5068 syserr("syslog_map_parseargs: Unknown priority %s",
5069 priority);
5070 return false;
5073 return true;
5077 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
5080 char *
5081 syslog_map_lookup(map, string, args, statp)
5082 MAP *map;
5083 char *string;
5084 char **args;
5085 int *statp;
5087 char *ptr = map_rewrite(map, string, strlen(string), args);
5089 if (ptr != NULL)
5091 if (tTd(38, 20))
5092 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5093 map->map_mname, map->map_prio, ptr);
5095 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
5098 *statp = EX_OK;
5099 return "";
5103 ** HESIOD Modules
5106 #if HESIOD
5108 bool
5109 hes_map_open(map, mode)
5110 MAP *map;
5111 int mode;
5113 if (tTd(38, 2))
5114 sm_dprintf("hes_map_open(%s, %s, %d)\n",
5115 map->map_mname, map->map_file, mode);
5117 if (mode != O_RDONLY)
5119 /* issue a pseudo-error message */
5120 errno = SM_EMAPCANTWRITE;
5121 return false;
5124 # ifdef HESIOD_INIT
5125 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
5126 return true;
5128 if (!bitset(MF_OPTIONAL, map->map_mflags))
5129 syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5130 sm_errstring(errno));
5131 return false;
5132 # else /* HESIOD_INIT */
5133 if (hes_error() == HES_ER_UNINIT)
5134 hes_init();
5135 switch (hes_error())
5137 case HES_ER_OK:
5138 case HES_ER_NOTFOUND:
5139 return true;
5142 if (!bitset(MF_OPTIONAL, map->map_mflags))
5143 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5145 return false;
5146 # endif /* HESIOD_INIT */
5149 char *
5150 hes_map_lookup(map, name, av, statp)
5151 MAP *map;
5152 char *name;
5153 char **av;
5154 int *statp;
5156 char **hp;
5158 if (tTd(38, 20))
5159 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
5161 if (name[0] == '\\')
5163 char *np;
5164 int nl;
5165 int save_errno;
5166 char nbuf[MAXNAME];
5168 nl = strlen(name);
5169 if (nl < sizeof nbuf - 1)
5170 np = nbuf;
5171 else
5172 np = xalloc(strlen(name) + 2);
5173 np[0] = '\\';
5174 (void) sm_strlcpy(&np[1], name, (sizeof nbuf) - 1);
5175 # ifdef HESIOD_INIT
5176 hp = hesiod_resolve(HesiodContext, np, map->map_file);
5177 # else /* HESIOD_INIT */
5178 hp = hes_resolve(np, map->map_file);
5179 # endif /* HESIOD_INIT */
5180 save_errno = errno;
5181 if (np != nbuf)
5182 sm_free(np); /* XXX */
5183 errno = save_errno;
5185 else
5187 # ifdef HESIOD_INIT
5188 hp = hesiod_resolve(HesiodContext, name, map->map_file);
5189 # else /* HESIOD_INIT */
5190 hp = hes_resolve(name, map->map_file);
5191 # endif /* HESIOD_INIT */
5193 # ifdef HESIOD_INIT
5194 if (hp == NULL || *hp == NULL)
5196 switch (errno)
5198 case ENOENT:
5199 *statp = EX_NOTFOUND;
5200 break;
5201 case ECONNREFUSED:
5202 *statp = EX_TEMPFAIL;
5203 break;
5204 case EMSGSIZE:
5205 case ENOMEM:
5206 default:
5207 *statp = EX_UNAVAILABLE;
5208 break;
5210 if (hp != NULL)
5211 hesiod_free_list(HesiodContext, hp);
5212 return NULL;
5214 # else /* HESIOD_INIT */
5215 if (hp == NULL || hp[0] == NULL)
5217 switch (hes_error())
5219 case HES_ER_OK:
5220 *statp = EX_OK;
5221 break;
5223 case HES_ER_NOTFOUND:
5224 *statp = EX_NOTFOUND;
5225 break;
5227 case HES_ER_CONFIG:
5228 *statp = EX_UNAVAILABLE;
5229 break;
5231 case HES_ER_NET:
5232 *statp = EX_TEMPFAIL;
5233 break;
5235 return NULL;
5237 # endif /* HESIOD_INIT */
5239 if (bitset(MF_MATCHONLY, map->map_mflags))
5240 return map_rewrite(map, name, strlen(name), NULL);
5241 else
5242 return map_rewrite(map, hp[0], strlen(hp[0]), av);
5246 ** HES_MAP_CLOSE -- free the Hesiod context
5249 void
5250 hes_map_close(map)
5251 MAP *map;
5253 if (tTd(38, 20))
5254 sm_dprintf("hes_map_close(%s)\n", map->map_file);
5256 # ifdef HESIOD_INIT
5257 /* Free the hesiod context */
5258 if (HesiodContext != NULL)
5260 hesiod_end(HesiodContext);
5261 HesiodContext = NULL;
5263 # endif /* HESIOD_INIT */
5266 #endif /* HESIOD */
5268 ** NeXT NETINFO Modules
5271 #if NETINFO
5273 # define NETINFO_DEFAULT_DIR "/aliases"
5274 # define NETINFO_DEFAULT_PROPERTY "members"
5277 ** NI_MAP_OPEN -- open NetInfo Aliases
5280 bool
5281 ni_map_open(map, mode)
5282 MAP *map;
5283 int mode;
5285 if (tTd(38, 2))
5286 sm_dprintf("ni_map_open(%s, %s, %d)\n",
5287 map->map_mname, map->map_file, mode);
5288 mode &= O_ACCMODE;
5290 if (*map->map_file == '\0')
5291 map->map_file = NETINFO_DEFAULT_DIR;
5293 if (map->map_valcolnm == NULL)
5294 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
5296 if (map->map_coldelim == '\0')
5298 if (bitset(MF_ALIAS, map->map_mflags))
5299 map->map_coldelim = ',';
5300 else if (bitset(MF_FILECLASS, map->map_mflags))
5301 map->map_coldelim = ' ';
5303 return true;
5308 ** NI_MAP_LOOKUP -- look up a datum in NetInfo
5311 char *
5312 ni_map_lookup(map, name, av, statp)
5313 MAP *map;
5314 char *name;
5315 char **av;
5316 int *statp;
5318 char *res;
5319 char *propval;
5321 if (tTd(38, 20))
5322 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
5324 propval = ni_propval(map->map_file, map->map_keycolnm, name,
5325 map->map_valcolnm, map->map_coldelim);
5327 if (propval == NULL)
5328 return NULL;
5330 SM_TRY
5331 if (bitset(MF_MATCHONLY, map->map_mflags))
5332 res = map_rewrite(map, name, strlen(name), NULL);
5333 else
5334 res = map_rewrite(map, propval, strlen(propval), av);
5335 SM_FINALLY
5336 sm_free(propval);
5337 SM_END_TRY
5338 return res;
5342 static bool
5343 ni_getcanonname(name, hbsize, statp)
5344 char *name;
5345 int hbsize;
5346 int *statp;
5348 char *vptr;
5349 char *ptr;
5350 char nbuf[MAXNAME + 1];
5352 if (tTd(38, 20))
5353 sm_dprintf("ni_getcanonname(%s)\n", name);
5355 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5357 *statp = EX_UNAVAILABLE;
5358 return false;
5360 (void) shorten_hostname(nbuf);
5362 /* we only accept single token search key */
5363 if (strchr(nbuf, '.'))
5365 *statp = EX_NOHOST;
5366 return false;
5369 /* Do the search */
5370 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
5372 if (vptr == NULL)
5374 *statp = EX_NOHOST;
5375 return false;
5378 /* Only want the first machine name */
5379 if ((ptr = strchr(vptr, '\n')) != NULL)
5380 *ptr = '\0';
5382 if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
5384 sm_free(vptr);
5385 *statp = EX_UNAVAILABLE;
5386 return true;
5388 sm_free(vptr);
5389 *statp = EX_OK;
5390 return false;
5392 #endif /* NETINFO */
5394 ** TEXT (unindexed text file) Modules
5396 ** This code donated by Sun Microsystems.
5399 #define map_sff map_lockfd /* overload field */
5403 ** TEXT_MAP_OPEN -- open text table
5406 bool
5407 text_map_open(map, mode)
5408 MAP *map;
5409 int mode;
5411 long sff;
5412 int i;
5414 if (tTd(38, 2))
5415 sm_dprintf("text_map_open(%s, %s, %d)\n",
5416 map->map_mname, map->map_file, mode);
5418 mode &= O_ACCMODE;
5419 if (mode != O_RDONLY)
5421 errno = EPERM;
5422 return false;
5425 if (*map->map_file == '\0')
5427 syserr("text map \"%s\": file name required",
5428 map->map_mname);
5429 return false;
5432 if (map->map_file[0] != '/')
5434 syserr("text map \"%s\": file name must be fully qualified",
5435 map->map_mname);
5436 return false;
5439 sff = SFF_ROOTOK|SFF_REGONLY;
5440 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5441 sff |= SFF_NOWLINK;
5442 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5443 sff |= SFF_SAFEDIRPATH;
5444 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
5445 sff, S_IRUSR, NULL)) != 0)
5447 int save_errno = errno;
5449 /* cannot open this map */
5450 if (tTd(38, 2))
5451 sm_dprintf("\tunsafe map file: %d\n", i);
5452 errno = save_errno;
5453 if (!bitset(MF_OPTIONAL, map->map_mflags))
5454 syserr("text map \"%s\": unsafe map file %s",
5455 map->map_mname, map->map_file);
5456 return false;
5459 if (map->map_keycolnm == NULL)
5460 map->map_keycolno = 0;
5461 else
5463 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
5465 syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5466 map->map_mname, map->map_file,
5467 map->map_keycolnm);
5468 return false;
5470 map->map_keycolno = atoi(map->map_keycolnm);
5473 if (map->map_valcolnm == NULL)
5474 map->map_valcolno = 0;
5475 else
5477 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
5479 syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5480 map->map_mname, map->map_file,
5481 map->map_valcolnm);
5482 return false;
5484 map->map_valcolno = atoi(map->map_valcolnm);
5487 if (tTd(38, 2))
5489 sm_dprintf("text_map_open(%s, %s): delimiter = ",
5490 map->map_mname, map->map_file);
5491 if (map->map_coldelim == '\0')
5492 sm_dprintf("(white space)\n");
5493 else
5494 sm_dprintf("%c\n", map->map_coldelim);
5497 map->map_sff = sff;
5498 return true;
5503 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5506 char *
5507 text_map_lookup(map, name, av, statp)
5508 MAP *map;
5509 char *name;
5510 char **av;
5511 int *statp;
5513 char *vp;
5514 auto int vsize;
5515 int buflen;
5516 SM_FILE_T *f;
5517 char delim;
5518 int key_idx;
5519 bool found_it;
5520 long sff = map->map_sff;
5521 char search_key[MAXNAME + 1];
5522 char linebuf[MAXLINE];
5523 char buf[MAXNAME + 1];
5525 found_it = false;
5526 if (tTd(38, 20))
5527 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
5529 buflen = strlen(name);
5530 if (buflen > sizeof search_key - 1)
5531 buflen = sizeof search_key - 1; /* XXX just cut if off? */
5532 memmove(search_key, name, buflen);
5533 search_key[buflen] = '\0';
5534 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
5535 makelower(search_key);
5537 f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
5538 if (f == NULL)
5540 map->map_mflags &= ~(MF_VALID|MF_OPEN);
5541 *statp = EX_UNAVAILABLE;
5542 return NULL;
5544 key_idx = map->map_keycolno;
5545 delim = map->map_coldelim;
5546 while (sm_io_fgets(f, SM_TIME_DEFAULT,
5547 linebuf, sizeof linebuf) != NULL)
5549 char *p;
5551 /* skip comment line */
5552 if (linebuf[0] == '#')
5553 continue;
5554 p = strchr(linebuf, '\n');
5555 if (p != NULL)
5556 *p = '\0';
5557 p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
5558 if (p != NULL && sm_strcasecmp(search_key, p) == 0)
5560 found_it = true;
5561 break;
5564 (void) sm_io_close(f, SM_TIME_DEFAULT);
5565 if (!found_it)
5567 *statp = EX_NOTFOUND;
5568 return NULL;
5570 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
5571 if (vp == NULL)
5573 *statp = EX_NOTFOUND;
5574 return NULL;
5576 vsize = strlen(vp);
5577 *statp = EX_OK;
5578 if (bitset(MF_MATCHONLY, map->map_mflags))
5579 return map_rewrite(map, name, strlen(name), NULL);
5580 else
5581 return map_rewrite(map, vp, vsize, av);
5585 ** TEXT_GETCANONNAME -- look up canonical name in hosts file
5588 static bool
5589 text_getcanonname(name, hbsize, statp)
5590 char *name;
5591 int hbsize;
5592 int *statp;
5594 bool found;
5595 char *dot;
5596 SM_FILE_T *f;
5597 char linebuf[MAXLINE];
5598 char cbuf[MAXNAME + 1];
5599 char nbuf[MAXNAME + 1];
5601 if (tTd(38, 20))
5602 sm_dprintf("text_getcanonname(%s)\n", name);
5604 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf)
5606 *statp = EX_UNAVAILABLE;
5607 return false;
5609 dot = shorten_hostname(nbuf);
5611 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
5612 NULL);
5613 if (f == NULL)
5615 *statp = EX_UNAVAILABLE;
5616 return false;
5618 found = false;
5619 while (!found &&
5620 sm_io_fgets(f, SM_TIME_DEFAULT,
5621 linebuf, sizeof linebuf) != NULL)
5623 char *p = strpbrk(linebuf, "#\n");
5625 if (p != NULL)
5626 *p = '\0';
5627 if (linebuf[0] != '\0')
5628 found = extract_canonname(nbuf, dot, linebuf,
5629 cbuf, sizeof cbuf);
5631 (void) sm_io_close(f, SM_TIME_DEFAULT);
5632 if (!found)
5634 *statp = EX_NOHOST;
5635 return false;
5638 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
5640 *statp = EX_UNAVAILABLE;
5641 return false;
5643 *statp = EX_OK;
5644 return true;
5647 ** STAB (Symbol Table) Modules
5652 ** STAB_MAP_LOOKUP -- look up alias in symbol table
5655 /* ARGSUSED2 */
5656 char *
5657 stab_map_lookup(map, name, av, pstat)
5658 register MAP *map;
5659 char *name;
5660 char **av;
5661 int *pstat;
5663 register STAB *s;
5665 if (tTd(38, 20))
5666 sm_dprintf("stab_lookup(%s, %s)\n",
5667 map->map_mname, name);
5669 s = stab(name, ST_ALIAS, ST_FIND);
5670 if (s == NULL)
5671 return NULL;
5672 if (bitset(MF_MATCHONLY, map->map_mflags))
5673 return map_rewrite(map, name, strlen(name), NULL);
5674 else
5675 return map_rewrite(map, s->s_alias, strlen(s->s_alias), av);
5679 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
5682 void
5683 stab_map_store(map, lhs, rhs)
5684 register MAP *map;
5685 char *lhs;
5686 char *rhs;
5688 register STAB *s;
5690 s = stab(lhs, ST_ALIAS, ST_ENTER);
5691 s->s_alias = newstr(rhs);
5696 ** STAB_MAP_OPEN -- initialize (reads data file)
5698 ** This is a wierd case -- it is only intended as a fallback for
5699 ** aliases. For this reason, opens for write (only during a
5700 ** "newaliases") always fails, and opens for read open the
5701 ** actual underlying text file instead of the database.
5704 bool
5705 stab_map_open(map, mode)
5706 register MAP *map;
5707 int mode;
5709 SM_FILE_T *af;
5710 long sff;
5711 struct stat st;
5713 if (tTd(38, 2))
5714 sm_dprintf("stab_map_open(%s, %s, %d)\n",
5715 map->map_mname, map->map_file, mode);
5717 mode &= O_ACCMODE;
5718 if (mode != O_RDONLY)
5720 errno = EPERM;
5721 return false;
5724 sff = SFF_ROOTOK|SFF_REGONLY;
5725 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5726 sff |= SFF_NOWLINK;
5727 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5728 sff |= SFF_SAFEDIRPATH;
5729 af = safefopen(map->map_file, O_RDONLY, 0444, sff);
5730 if (af == NULL)
5731 return false;
5732 readaliases(map, af, false, false);
5734 if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
5735 map->map_mtime = st.st_mtime;
5736 (void) sm_io_close(af, SM_TIME_DEFAULT);
5738 return true;
5741 ** Implicit Modules
5743 ** Tries several types. For back compatibility of aliases.
5748 ** IMPL_MAP_LOOKUP -- lookup in best open database
5751 char *
5752 impl_map_lookup(map, name, av, pstat)
5753 MAP *map;
5754 char *name;
5755 char **av;
5756 int *pstat;
5758 if (tTd(38, 20))
5759 sm_dprintf("impl_map_lookup(%s, %s)\n",
5760 map->map_mname, name);
5762 #if NEWDB
5763 if (bitset(MF_IMPL_HASH, map->map_mflags))
5764 return db_map_lookup(map, name, av, pstat);
5765 #endif /* NEWDB */
5766 #if NDBM
5767 if (bitset(MF_IMPL_NDBM, map->map_mflags))
5768 return ndbm_map_lookup(map, name, av, pstat);
5769 #endif /* NDBM */
5770 return stab_map_lookup(map, name, av, pstat);
5774 ** IMPL_MAP_STORE -- store in open databases
5777 void
5778 impl_map_store(map, lhs, rhs)
5779 MAP *map;
5780 char *lhs;
5781 char *rhs;
5783 if (tTd(38, 12))
5784 sm_dprintf("impl_map_store(%s, %s, %s)\n",
5785 map->map_mname, lhs, rhs);
5786 #if NEWDB
5787 if (bitset(MF_IMPL_HASH, map->map_mflags))
5788 db_map_store(map, lhs, rhs);
5789 #endif /* NEWDB */
5790 #if NDBM
5791 if (bitset(MF_IMPL_NDBM, map->map_mflags))
5792 ndbm_map_store(map, lhs, rhs);
5793 #endif /* NDBM */
5794 stab_map_store(map, lhs, rhs);
5798 ** IMPL_MAP_OPEN -- implicit database open
5801 bool
5802 impl_map_open(map, mode)
5803 MAP *map;
5804 int mode;
5806 if (tTd(38, 2))
5807 sm_dprintf("impl_map_open(%s, %s, %d)\n",
5808 map->map_mname, map->map_file, mode);
5810 mode &= O_ACCMODE;
5811 #if NEWDB
5812 map->map_mflags |= MF_IMPL_HASH;
5813 if (hash_map_open(map, mode))
5815 # ifdef NDBM_YP_COMPAT
5816 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
5817 # endif /* NDBM_YP_COMPAT */
5818 return true;
5820 else
5821 map->map_mflags &= ~MF_IMPL_HASH;
5822 #endif /* NEWDB */
5823 #if NDBM
5824 map->map_mflags |= MF_IMPL_NDBM;
5825 if (ndbm_map_open(map, mode))
5827 return true;
5829 else
5830 map->map_mflags &= ~MF_IMPL_NDBM;
5831 #endif /* NDBM */
5833 #if defined(NEWDB) || defined(NDBM)
5834 if (Verbose)
5835 message("WARNING: cannot open alias database %s%s",
5836 map->map_file,
5837 mode == O_RDONLY ? "; reading text version" : "");
5838 #else /* defined(NEWDB) || defined(NDBM) */
5839 if (mode != O_RDONLY)
5840 usrerr("Cannot rebuild aliases: no database format defined");
5841 #endif /* defined(NEWDB) || defined(NDBM) */
5843 if (mode == O_RDONLY)
5844 return stab_map_open(map, mode);
5845 else
5846 return false;
5851 ** IMPL_MAP_CLOSE -- close any open database(s)
5854 void
5855 impl_map_close(map)
5856 MAP *map;
5858 if (tTd(38, 9))
5859 sm_dprintf("impl_map_close(%s, %s, %lx)\n",
5860 map->map_mname, map->map_file, map->map_mflags);
5861 #if NEWDB
5862 if (bitset(MF_IMPL_HASH, map->map_mflags))
5864 db_map_close(map);
5865 map->map_mflags &= ~MF_IMPL_HASH;
5867 #endif /* NEWDB */
5869 #if NDBM
5870 if (bitset(MF_IMPL_NDBM, map->map_mflags))
5872 ndbm_map_close(map);
5873 map->map_mflags &= ~MF_IMPL_NDBM;
5875 #endif /* NDBM */
5878 ** User map class.
5880 ** Provides access to the system password file.
5884 ** USER_MAP_OPEN -- open user map
5886 ** Really just binds field names to field numbers.
5889 bool
5890 user_map_open(map, mode)
5891 MAP *map;
5892 int mode;
5894 if (tTd(38, 2))
5895 sm_dprintf("user_map_open(%s, %d)\n",
5896 map->map_mname, mode);
5898 mode &= O_ACCMODE;
5899 if (mode != O_RDONLY)
5901 /* issue a pseudo-error message */
5902 errno = SM_EMAPCANTWRITE;
5903 return false;
5905 if (map->map_valcolnm == NULL)
5906 /* EMPTY */
5907 /* nothing */ ;
5908 else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
5909 map->map_valcolno = 1;
5910 else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
5911 map->map_valcolno = 2;
5912 else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
5913 map->map_valcolno = 3;
5914 else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
5915 map->map_valcolno = 4;
5916 else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
5917 map->map_valcolno = 5;
5918 else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
5919 map->map_valcolno = 6;
5920 else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
5921 map->map_valcolno = 7;
5922 else
5924 syserr("User map %s: unknown column name %s",
5925 map->map_mname, map->map_valcolnm);
5926 return false;
5928 return true;
5933 ** USER_MAP_LOOKUP -- look up a user in the passwd file.
5936 /* ARGSUSED3 */
5937 char *
5938 user_map_lookup(map, key, av, statp)
5939 MAP *map;
5940 char *key;
5941 char **av;
5942 int *statp;
5944 auto bool fuzzy;
5945 SM_MBDB_T user;
5947 if (tTd(38, 20))
5948 sm_dprintf("user_map_lookup(%s, %s)\n",
5949 map->map_mname, key);
5951 *statp = finduser(key, &fuzzy, &user);
5952 if (*statp != EX_OK)
5953 return NULL;
5954 if (bitset(MF_MATCHONLY, map->map_mflags))
5955 return map_rewrite(map, key, strlen(key), NULL);
5956 else
5958 char *rwval = NULL;
5959 char buf[30];
5961 switch (map->map_valcolno)
5963 case 0:
5964 case 1:
5965 rwval = user.mbdb_name;
5966 break;
5968 case 2:
5969 rwval = "x"; /* passwd no longer supported */
5970 break;
5972 case 3:
5973 (void) sm_snprintf(buf, sizeof buf, "%d",
5974 (int) user.mbdb_uid);
5975 rwval = buf;
5976 break;
5978 case 4:
5979 (void) sm_snprintf(buf, sizeof buf, "%d",
5980 (int) user.mbdb_gid);
5981 rwval = buf;
5982 break;
5984 case 5:
5985 rwval = user.mbdb_fullname;
5986 break;
5988 case 6:
5989 rwval = user.mbdb_homedir;
5990 break;
5992 case 7:
5993 rwval = user.mbdb_shell;
5994 break;
5995 default:
5996 syserr("user_map %s: bogus field %d",
5997 map->map_mname, map->map_valcolno);
5998 return NULL;
6000 return map_rewrite(map, rwval, strlen(rwval), av);
6004 ** Program map type.
6006 ** This provides access to arbitrary programs. It should be used
6007 ** only very sparingly, since there is no way to bound the cost
6008 ** of invoking an arbitrary program.
6011 char *
6012 prog_map_lookup(map, name, av, statp)
6013 MAP *map;
6014 char *name;
6015 char **av;
6016 int *statp;
6018 int i;
6019 int save_errno;
6020 int fd;
6021 int status;
6022 auto pid_t pid;
6023 register char *p;
6024 char *rval;
6025 char *argv[MAXPV + 1];
6026 char buf[MAXLINE];
6028 if (tTd(38, 20))
6029 sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6030 map->map_mname, name, map->map_file);
6032 i = 0;
6033 argv[i++] = map->map_file;
6034 if (map->map_rebuild != NULL)
6036 (void) sm_strlcpy(buf, map->map_rebuild, sizeof buf);
6037 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
6039 if (i >= MAXPV - 1)
6040 break;
6041 argv[i++] = p;
6044 argv[i++] = name;
6045 argv[i] = NULL;
6046 if (tTd(38, 21))
6048 sm_dprintf("prog_open:");
6049 for (i = 0; argv[i] != NULL; i++)
6050 sm_dprintf(" %s", argv[i]);
6051 sm_dprintf("\n");
6053 (void) sm_blocksignal(SIGCHLD);
6054 pid = prog_open(argv, &fd, CurEnv);
6055 if (pid < 0)
6057 if (!bitset(MF_OPTIONAL, map->map_mflags))
6058 syserr("prog_map_lookup(%s) failed (%s) -- closing",
6059 map->map_mname, sm_errstring(errno));
6060 else if (tTd(38, 9))
6061 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6062 map->map_mname, sm_errstring(errno));
6063 map->map_mflags &= ~(MF_VALID|MF_OPEN);
6064 *statp = EX_OSFILE;
6065 return NULL;
6067 i = read(fd, buf, sizeof buf - 1);
6068 if (i < 0)
6070 syserr("prog_map_lookup(%s): read error %s",
6071 map->map_mname, sm_errstring(errno));
6072 rval = NULL;
6074 else if (i == 0)
6076 if (tTd(38, 20))
6077 sm_dprintf("prog_map_lookup(%s): empty answer\n",
6078 map->map_mname);
6079 rval = NULL;
6081 else
6083 buf[i] = '\0';
6084 p = strchr(buf, '\n');
6085 if (p != NULL)
6086 *p = '\0';
6088 /* collect the return value */
6089 if (bitset(MF_MATCHONLY, map->map_mflags))
6090 rval = map_rewrite(map, name, strlen(name), NULL);
6091 else
6092 rval = map_rewrite(map, buf, strlen(buf), av);
6094 /* now flush any additional output */
6095 while ((i = read(fd, buf, sizeof buf)) > 0)
6096 continue;
6099 /* wait for the process to terminate */
6100 (void) close(fd);
6101 status = waitfor(pid);
6102 save_errno = errno;
6103 (void) sm_releasesignal(SIGCHLD);
6104 errno = save_errno;
6106 if (status == -1)
6108 syserr("prog_map_lookup(%s): wait error %s",
6109 map->map_mname, sm_errstring(errno));
6110 *statp = EX_SOFTWARE;
6111 rval = NULL;
6113 else if (WIFEXITED(status))
6115 if ((*statp = WEXITSTATUS(status)) != EX_OK)
6116 rval = NULL;
6118 else
6120 syserr("prog_map_lookup(%s): child died on signal %d",
6121 map->map_mname, status);
6122 *statp = EX_UNAVAILABLE;
6123 rval = NULL;
6125 return rval;
6128 ** Sequenced map type.
6130 ** Tries each map in order until something matches, much like
6131 ** implicit. Stores go to the first map in the list that can
6132 ** support storing.
6134 ** This is slightly unusual in that there are two interfaces.
6135 ** The "sequence" interface lets you stack maps arbitrarily.
6136 ** The "switch" interface builds a sequence map by looking
6137 ** at a system-dependent configuration file such as
6138 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6140 ** We don't need an explicit open, since all maps are
6141 ** opened on demand.
6145 ** SEQ_MAP_PARSE -- Sequenced map parsing
6148 bool
6149 seq_map_parse(map, ap)
6150 MAP *map;
6151 char *ap;
6153 int maxmap;
6155 if (tTd(38, 2))
6156 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
6157 maxmap = 0;
6158 while (*ap != '\0')
6160 register char *p;
6161 STAB *s;
6163 /* find beginning of map name */
6164 while (isascii(*ap) && isspace(*ap))
6165 ap++;
6166 for (p = ap;
6167 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
6168 p++)
6169 continue;
6170 if (*p != '\0')
6171 *p++ = '\0';
6172 while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
6173 p++;
6174 if (*ap == '\0')
6176 ap = p;
6177 continue;
6179 s = stab(ap, ST_MAP, ST_FIND);
6180 if (s == NULL)
6182 syserr("Sequence map %s: unknown member map %s",
6183 map->map_mname, ap);
6185 else if (maxmap >= MAXMAPSTACK)
6187 syserr("Sequence map %s: too many member maps (%d max)",
6188 map->map_mname, MAXMAPSTACK);
6189 maxmap++;
6191 else if (maxmap < MAXMAPSTACK)
6193 map->map_stack[maxmap++] = &s->s_map;
6195 ap = p;
6197 return true;
6201 ** SWITCH_MAP_OPEN -- open a switched map
6203 ** This looks at the system-dependent configuration and builds
6204 ** a sequence map that does the same thing.
6206 ** Every system must define a switch_map_find routine in conf.c
6207 ** that will return the list of service types associated with a
6208 ** given service class.
6211 bool
6212 switch_map_open(map, mode)
6213 MAP *map;
6214 int mode;
6216 int mapno;
6217 int nmaps;
6218 char *maptype[MAXMAPSTACK];
6220 if (tTd(38, 2))
6221 sm_dprintf("switch_map_open(%s, %s, %d)\n",
6222 map->map_mname, map->map_file, mode);
6224 mode &= O_ACCMODE;
6225 nmaps = switch_map_find(map->map_file, maptype, map->map_return);
6226 if (tTd(38, 19))
6228 sm_dprintf("\tswitch_map_find => %d\n", nmaps);
6229 for (mapno = 0; mapno < nmaps; mapno++)
6230 sm_dprintf("\t\t%s\n", maptype[mapno]);
6232 if (nmaps <= 0 || nmaps > MAXMAPSTACK)
6233 return false;
6235 for (mapno = 0; mapno < nmaps; mapno++)
6237 register STAB *s;
6238 char nbuf[MAXNAME + 1];
6240 if (maptype[mapno] == NULL)
6241 continue;
6242 (void) sm_strlcpyn(nbuf, sizeof nbuf, 3,
6243 map->map_mname, ".", maptype[mapno]);
6244 s = stab(nbuf, ST_MAP, ST_FIND);
6245 if (s == NULL)
6247 syserr("Switch map %s: unknown member map %s",
6248 map->map_mname, nbuf);
6250 else
6252 map->map_stack[mapno] = &s->s_map;
6253 if (tTd(38, 4))
6254 sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6255 mapno,
6256 s->s_map.map_class->map_cname,
6257 nbuf);
6260 return true;
6263 #if 0
6265 ** SEQ_MAP_CLOSE -- close all underlying maps
6268 void
6269 seq_map_close(map)
6270 MAP *map;
6272 int mapno;
6274 if (tTd(38, 9))
6275 sm_dprintf("seq_map_close(%s)\n", map->map_mname);
6277 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6279 MAP *mm = map->map_stack[mapno];
6281 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
6282 continue;
6283 mm->map_mflags |= MF_CLOSING;
6284 mm->map_class->map_close(mm);
6285 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
6288 #endif /* 0 */
6291 ** SEQ_MAP_LOOKUP -- sequenced map lookup
6294 char *
6295 seq_map_lookup(map, key, args, pstat)
6296 MAP *map;
6297 char *key;
6298 char **args;
6299 int *pstat;
6301 int mapno;
6302 int mapbit = 0x01;
6303 bool tempfail = false;
6305 if (tTd(38, 20))
6306 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
6308 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
6310 MAP *mm = map->map_stack[mapno];
6311 char *rv;
6313 if (mm == NULL)
6314 continue;
6315 if (!bitset(MF_OPEN, mm->map_mflags) &&
6316 !openmap(mm))
6318 if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
6320 *pstat = EX_UNAVAILABLE;
6321 return NULL;
6323 continue;
6325 *pstat = EX_OK;
6326 rv = mm->map_class->map_lookup(mm, key, args, pstat);
6327 if (rv != NULL)
6328 return rv;
6329 if (*pstat == EX_TEMPFAIL)
6331 if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
6332 return NULL;
6333 tempfail = true;
6335 else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
6336 break;
6338 if (tempfail)
6339 *pstat = EX_TEMPFAIL;
6340 else if (*pstat == EX_OK)
6341 *pstat = EX_NOTFOUND;
6342 return NULL;
6346 ** SEQ_MAP_STORE -- sequenced map store
6349 void
6350 seq_map_store(map, key, val)
6351 MAP *map;
6352 char *key;
6353 char *val;
6355 int mapno;
6357 if (tTd(38, 12))
6358 sm_dprintf("seq_map_store(%s, %s, %s)\n",
6359 map->map_mname, key, val);
6361 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6363 MAP *mm = map->map_stack[mapno];
6365 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
6366 continue;
6368 mm->map_class->map_store(mm, key, val);
6369 return;
6371 syserr("seq_map_store(%s, %s, %s): no writable map",
6372 map->map_mname, key, val);
6375 ** NULL stubs
6378 /* ARGSUSED */
6379 bool
6380 null_map_open(map, mode)
6381 MAP *map;
6382 int mode;
6384 return true;
6387 /* ARGSUSED */
6388 void
6389 null_map_close(map)
6390 MAP *map;
6392 return;
6395 char *
6396 null_map_lookup(map, key, args, pstat)
6397 MAP *map;
6398 char *key;
6399 char **args;
6400 int *pstat;
6402 *pstat = EX_NOTFOUND;
6403 return NULL;
6406 /* ARGSUSED */
6407 void
6408 null_map_store(map, key, val)
6409 MAP *map;
6410 char *key;
6411 char *val;
6413 return;
6417 ** BOGUS stubs
6420 char *
6421 bogus_map_lookup(map, key, args, pstat)
6422 MAP *map;
6423 char *key;
6424 char **args;
6425 int *pstat;
6427 *pstat = EX_TEMPFAIL;
6428 return NULL;
6431 MAPCLASS BogusMapClass =
6433 "bogus-map", NULL, 0,
6434 NULL, bogus_map_lookup, null_map_store,
6435 null_map_open, null_map_close,
6438 ** MACRO modules
6441 char *
6442 macro_map_lookup(map, name, av, statp)
6443 MAP *map;
6444 char *name;
6445 char **av;
6446 int *statp;
6448 int mid;
6450 if (tTd(38, 20))
6451 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
6452 name == NULL ? "NULL" : name);
6454 if (name == NULL ||
6455 *name == '\0' ||
6456 (mid = macid(name)) == 0)
6458 *statp = EX_CONFIG;
6459 return NULL;
6462 if (av[1] == NULL)
6463 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
6464 else
6465 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
6467 *statp = EX_OK;
6468 return "";
6471 ** REGEX modules
6474 #if MAP_REGEX
6476 # include <regex.h>
6478 # define DEFAULT_DELIM CONDELSE
6479 # define END_OF_FIELDS -1
6480 # define ERRBUF_SIZE 80
6481 # define MAX_MATCH 32
6483 # define xnalloc(s) memset(xalloc(s), '\0', s);
6485 struct regex_map
6487 regex_t *regex_pattern_buf; /* xalloc it */
6488 int *regex_subfields; /* move to type MAP */
6489 char *regex_delim; /* move to type MAP */
6492 static int parse_fields __P((char *, int *, int, int));
6493 static char *regex_map_rewrite __P((MAP *, const char*, size_t, char **));
6495 static int
6496 parse_fields(s, ibuf, blen, nr_substrings)
6497 char *s;
6498 int *ibuf; /* array */
6499 int blen; /* number of elements in ibuf */
6500 int nr_substrings; /* number of substrings in the pattern */
6502 register char *cp;
6503 int i = 0;
6504 bool lastone = false;
6506 blen--; /* for terminating END_OF_FIELDS */
6507 cp = s;
6510 for (;; cp++)
6512 if (*cp == ',')
6514 *cp = '\0';
6515 break;
6517 if (*cp == '\0')
6519 lastone = true;
6520 break;
6523 if (i < blen)
6525 int val = atoi(s);
6527 if (val < 0 || val >= nr_substrings)
6529 syserr("field (%d) out of range, only %d substrings in pattern",
6530 val, nr_substrings);
6531 return -1;
6533 ibuf[i++] = val;
6535 else
6537 syserr("too many fields, %d max", blen);
6538 return -1;
6540 s = ++cp;
6541 } while (!lastone);
6542 ibuf[i] = END_OF_FIELDS;
6543 return i;
6546 bool
6547 regex_map_init(map, ap)
6548 MAP *map;
6549 char *ap;
6551 int regerr;
6552 struct regex_map *map_p;
6553 register char *p;
6554 char *sub_param = NULL;
6555 int pflags;
6556 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
6558 if (tTd(38, 2))
6559 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6560 map->map_mname, ap);
6562 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
6563 p = ap;
6564 map_p = (struct regex_map *) xnalloc(sizeof *map_p);
6565 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
6567 for (;;)
6569 while (isascii(*p) && isspace(*p))
6570 p++;
6571 if (*p != '-')
6572 break;
6573 switch (*++p)
6575 case 'n': /* not */
6576 map->map_mflags |= MF_REGEX_NOT;
6577 break;
6579 case 'f': /* case sensitive */
6580 map->map_mflags |= MF_NOFOLDCASE;
6581 pflags &= ~REG_ICASE;
6582 break;
6584 case 'b': /* basic regular expressions */
6585 pflags &= ~REG_EXTENDED;
6586 break;
6588 case 's': /* substring match () syntax */
6589 sub_param = ++p;
6590 pflags &= ~REG_NOSUB;
6591 break;
6593 case 'd': /* delimiter */
6594 map_p->regex_delim = ++p;
6595 break;
6597 case 'a': /* map append */
6598 map->map_app = ++p;
6599 break;
6601 case 'm': /* matchonly */
6602 map->map_mflags |= MF_MATCHONLY;
6603 break;
6605 case 'q':
6606 map->map_mflags |= MF_KEEPQUOTES;
6607 break;
6609 case 'S':
6610 map->map_spacesub = *++p;
6611 break;
6613 case 'D':
6614 map->map_mflags |= MF_DEFER;
6615 break;
6618 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
6619 p++;
6620 if (*p != '\0')
6621 *p++ = '\0';
6623 if (tTd(38, 3))
6624 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
6626 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
6628 /* Errorhandling */
6629 char errbuf[ERRBUF_SIZE];
6631 (void) regerror(regerr, map_p->regex_pattern_buf,
6632 errbuf, sizeof errbuf);
6633 syserr("pattern-compile-error: %s", errbuf);
6634 sm_free(map_p->regex_pattern_buf); /* XXX */
6635 sm_free(map_p); /* XXX */
6636 return false;
6639 if (map->map_app != NULL)
6640 map->map_app = newstr(map->map_app);
6641 if (map_p->regex_delim != NULL)
6642 map_p->regex_delim = newstr(map_p->regex_delim);
6643 else
6644 map_p->regex_delim = defdstr;
6646 if (!bitset(REG_NOSUB, pflags))
6648 /* substring matching */
6649 int substrings;
6650 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
6652 substrings = map_p->regex_pattern_buf->re_nsub + 1;
6654 if (tTd(38, 3))
6655 sm_dprintf("regex_map_init: nr of substrings %d\n",
6656 substrings);
6658 if (substrings >= MAX_MATCH)
6660 syserr("too many substrings, %d max", MAX_MATCH);
6661 sm_free(map_p->regex_pattern_buf); /* XXX */
6662 sm_free(map_p); /* XXX */
6663 return false;
6665 if (sub_param != NULL && sub_param[0] != '\0')
6667 /* optional parameter -sfields */
6668 if (parse_fields(sub_param, fields,
6669 MAX_MATCH + 1, substrings) == -1)
6670 return false;
6672 else
6674 int i;
6676 /* set default fields */
6677 for (i = 0; i < substrings; i++)
6678 fields[i] = i;
6679 fields[i] = END_OF_FIELDS;
6681 map_p->regex_subfields = fields;
6682 if (tTd(38, 3))
6684 int *ip;
6686 sm_dprintf("regex_map_init: subfields");
6687 for (ip = fields; *ip != END_OF_FIELDS; ip++)
6688 sm_dprintf(" %d", *ip);
6689 sm_dprintf("\n");
6692 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */
6693 return true;
6696 static char *
6697 regex_map_rewrite(map, s, slen, av)
6698 MAP *map;
6699 const char *s;
6700 size_t slen;
6701 char **av;
6703 if (bitset(MF_MATCHONLY, map->map_mflags))
6704 return map_rewrite(map, av[0], strlen(av[0]), NULL);
6705 else
6706 return map_rewrite(map, s, slen, av);
6709 char *
6710 regex_map_lookup(map, name, av, statp)
6711 MAP *map;
6712 char *name;
6713 char **av;
6714 int *statp;
6716 int reg_res;
6717 struct regex_map *map_p;
6718 regmatch_t pmatch[MAX_MATCH];
6720 if (tTd(38, 20))
6722 char **cpp;
6724 sm_dprintf("regex_map_lookup: key '%s'\n", name);
6725 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
6726 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
6729 map_p = (struct regex_map *)(map->map_db1);
6730 reg_res = regexec(map_p->regex_pattern_buf,
6731 name, MAX_MATCH, pmatch, 0);
6733 if (bitset(MF_REGEX_NOT, map->map_mflags))
6735 /* option -n */
6736 if (reg_res == REG_NOMATCH)
6737 return regex_map_rewrite(map, "", (size_t) 0, av);
6738 else
6739 return NULL;
6741 if (reg_res == REG_NOMATCH)
6742 return NULL;
6744 if (map_p->regex_subfields != NULL)
6746 /* option -s */
6747 static char retbuf[MAXNAME];
6748 int fields[MAX_MATCH + 1];
6749 bool first = true;
6750 int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
6751 bool quotemode = false, bslashmode = false;
6752 register char *dp, *sp;
6753 char *endp, *ldp;
6754 int *ip;
6756 dp = retbuf;
6757 ldp = retbuf + sizeof(retbuf) - 1;
6759 if (av[1] != NULL)
6761 if (parse_fields(av[1], fields, MAX_MATCH + 1,
6762 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
6764 *statp = EX_CONFIG;
6765 return NULL;
6767 ip = fields;
6769 else
6770 ip = map_p->regex_subfields;
6772 for ( ; *ip != END_OF_FIELDS; ip++)
6774 if (!first)
6776 for (sp = map_p->regex_delim; *sp; sp++)
6778 if (dp < ldp)
6779 *dp++ = *sp;
6782 else
6783 first = false;
6785 if (*ip >= MAX_MATCH ||
6786 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
6787 continue;
6789 sp = name + pmatch[*ip].rm_so;
6790 endp = name + pmatch[*ip].rm_eo;
6791 for (; endp > sp; sp++)
6793 if (dp < ldp)
6795 if (bslashmode)
6797 *dp++ = *sp;
6798 bslashmode = false;
6800 else if (quotemode && *sp != '"' &&
6801 *sp != '\\')
6803 *dp++ = *sp;
6805 else switch (*dp++ = *sp)
6807 case '\\':
6808 bslashmode = true;
6809 break;
6811 case '(':
6812 cmntcnt++;
6813 break;
6815 case ')':
6816 cmntcnt--;
6817 break;
6819 case '<':
6820 anglecnt++;
6821 break;
6823 case '>':
6824 anglecnt--;
6825 break;
6827 case ' ':
6828 spacecnt++;
6829 break;
6831 case '"':
6832 quotemode = !quotemode;
6833 break;
6838 if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
6839 bslashmode || spacecnt != 0)
6841 sm_syslog(LOG_WARNING, NOQID,
6842 "Warning: regex may cause prescan() failure map=%s lookup=%s",
6843 map->map_mname, name);
6844 return NULL;
6847 *dp = '\0';
6849 return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
6851 return regex_map_rewrite(map, "", (size_t)0, av);
6853 #endif /* MAP_REGEX */
6855 ** NSD modules
6857 #if MAP_NSD
6859 # include <ndbm.h>
6860 # define _DATUM_DEFINED
6861 # include <ns_api.h>
6863 typedef struct ns_map_list
6865 ns_map_t *map; /* XXX ns_ ? */
6866 char *mapname;
6867 struct ns_map_list *next;
6868 } ns_map_list_t;
6870 static ns_map_t *
6871 ns_map_t_find(mapname)
6872 char *mapname;
6874 static ns_map_list_t *ns_maps = NULL;
6875 ns_map_list_t *ns_map;
6877 /* walk the list of maps looking for the correctly named map */
6878 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
6880 if (strcmp(ns_map->mapname, mapname) == 0)
6881 break;
6884 /* if we are looking at a NULL ns_map_list_t, then create a new one */
6885 if (ns_map == NULL)
6887 ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map);
6888 ns_map->mapname = newstr(mapname);
6889 ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map);
6890 memset(ns_map->map, '\0', sizeof *ns_map->map);
6891 ns_map->next = ns_maps;
6892 ns_maps = ns_map;
6894 return ns_map->map;
6897 char *
6898 nsd_map_lookup(map, name, av, statp)
6899 MAP *map;
6900 char *name;
6901 char **av;
6902 int *statp;
6904 int buflen, r;
6905 char *p;
6906 ns_map_t *ns_map;
6907 char keybuf[MAXNAME + 1];
6908 char buf[MAXLINE];
6910 if (tTd(38, 20))
6911 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
6913 buflen = strlen(name);
6914 if (buflen > sizeof keybuf - 1)
6915 buflen = sizeof keybuf - 1; /* XXX simply cut off? */
6916 memmove(keybuf, name, buflen);
6917 keybuf[buflen] = '\0';
6918 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
6919 makelower(keybuf);
6921 ns_map = ns_map_t_find(map->map_file);
6922 if (ns_map == NULL)
6924 if (tTd(38, 20))
6925 sm_dprintf("nsd_map_t_find failed\n");
6926 *statp = EX_UNAVAILABLE;
6927 return NULL;
6929 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
6930 buf, sizeof buf);
6931 if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
6933 *statp = EX_TEMPFAIL;
6934 return NULL;
6936 if (r == NS_BADREQ
6937 # ifdef NS_NOPERM
6938 || r == NS_NOPERM
6939 # endif /* NS_NOPERM */
6942 *statp = EX_CONFIG;
6943 return NULL;
6945 if (r != NS_SUCCESS)
6947 *statp = EX_NOTFOUND;
6948 return NULL;
6951 *statp = EX_OK;
6953 /* Null out trailing \n */
6954 if ((p = strchr(buf, '\n')) != NULL)
6955 *p = '\0';
6957 return map_rewrite(map, buf, strlen(buf), av);
6959 #endif /* MAP_NSD */
6961 char *
6962 arith_map_lookup(map, name, av, statp)
6963 MAP *map;
6964 char *name;
6965 char **av;
6966 int *statp;
6968 long r;
6969 long v[2];
6970 bool res = false;
6971 bool boolres;
6972 static char result[16];
6973 char **cpp;
6975 if (tTd(38, 2))
6977 sm_dprintf("arith_map_lookup: key '%s'\n", name);
6978 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
6979 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
6981 r = 0;
6982 boolres = false;
6983 cpp = av;
6984 *statp = EX_OK;
6987 ** read arguments for arith map
6988 ** - no check is made whether they are really numbers
6989 ** - just ignores args after the second
6992 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
6993 v[r++] = strtol(*cpp, NULL, 0);
6995 /* operator and (at least) two operands given? */
6996 if (name != NULL && r == 2)
6998 switch (*name)
7000 case '|':
7001 r = v[0] | v[1];
7002 break;
7004 case '&':
7005 r = v[0] & v[1];
7006 break;
7008 case '%':
7009 if (v[1] == 0)
7010 return NULL;
7011 r = v[0] % v[1];
7012 break;
7013 case '+':
7014 r = v[0] + v[1];
7015 break;
7017 case '-':
7018 r = v[0] - v[1];
7019 break;
7021 case '*':
7022 r = v[0] * v[1];
7023 break;
7025 case '/':
7026 if (v[1] == 0)
7027 return NULL;
7028 r = v[0] / v[1];
7029 break;
7031 case 'l':
7032 res = v[0] < v[1];
7033 boolres = true;
7034 break;
7036 case '=':
7037 res = v[0] == v[1];
7038 boolres = true;
7039 break;
7041 default:
7042 /* XXX */
7043 *statp = EX_CONFIG;
7044 if (LogLevel > 10)
7045 sm_syslog(LOG_WARNING, NOQID,
7046 "arith_map: unknown operator %c",
7047 isprint(*name) ? *name : '?');
7048 return NULL;
7050 if (boolres)
7051 (void) sm_snprintf(result, sizeof result,
7052 res ? "TRUE" : "FALSE");
7053 else
7054 (void) sm_snprintf(result, sizeof result, "%ld", r);
7055 return result;
7057 *statp = EX_CONFIG;
7058 return NULL;
7061 #if SOCKETMAP
7063 # if NETINET || NETINET6
7064 # include <arpa/inet.h>
7065 # endif /* NETINET || NETINET6 */
7067 # define socket_map_next map_stack[0]
7070 ** SOCKET_MAP_OPEN -- open socket table
7073 bool
7074 socket_map_open(map, mode)
7075 MAP *map;
7076 int mode;
7078 STAB *s;
7079 int sock = 0;
7080 SOCKADDR_LEN_T addrlen = 0;
7081 int addrno = 0;
7082 int save_errno;
7083 char *p;
7084 char *colon;
7085 char *at;
7086 struct hostent *hp = NULL;
7087 SOCKADDR addr;
7089 if (tTd(38, 2))
7090 sm_dprintf("socket_map_open(%s, %s, %d)\n",
7091 map->map_mname, map->map_file, mode);
7093 mode &= O_ACCMODE;
7095 /* sendmail doesn't have the ability to write to SOCKET (yet) */
7096 if (mode != O_RDONLY)
7098 /* issue a pseudo-error message */
7099 errno = SM_EMAPCANTWRITE;
7100 return false;
7103 if (*map->map_file == '\0')
7105 syserr("socket map \"%s\": empty or missing socket information",
7106 map->map_mname);
7107 return false;
7110 s = socket_map_findconn(map->map_file);
7111 if (s->s_socketmap != NULL)
7113 /* Copy open connection */
7114 map->map_db1 = s->s_socketmap->map_db1;
7116 /* Add this map as head of linked list */
7117 map->socket_map_next = s->s_socketmap;
7118 s->s_socketmap = map;
7120 if (tTd(38, 2))
7121 sm_dprintf("using cached connection\n");
7122 return true;
7125 if (tTd(38, 2))
7126 sm_dprintf("opening new connection\n");
7128 /* following code is ripped from milter.c */
7129 /* XXX It should be put in a library... */
7131 /* protocol:filename or protocol:port@host */
7132 memset(&addr, '\0', sizeof addr);
7133 p = map->map_file;
7134 colon = strchr(p, ':');
7135 if (colon != NULL)
7137 *colon = '\0';
7139 if (*p == '\0')
7141 # if NETUNIX
7142 /* default to AF_UNIX */
7143 addr.sa.sa_family = AF_UNIX;
7144 # else /* NETUNIX */
7145 # if NETINET
7146 /* default to AF_INET */
7147 addr.sa.sa_family = AF_INET;
7148 # else /* NETINET */
7149 # if NETINET6
7150 /* default to AF_INET6 */
7151 addr.sa.sa_family = AF_INET6;
7152 # else /* NETINET6 */
7153 /* no protocols available */
7154 syserr("socket map \"%s\": no valid socket protocols available",
7155 map->map_mname);
7156 return false;
7157 # endif /* NETINET6 */
7158 # endif /* NETINET */
7159 # endif /* NETUNIX */
7161 # if NETUNIX
7162 else if (sm_strcasecmp(p, "unix") == 0 ||
7163 sm_strcasecmp(p, "local") == 0)
7164 addr.sa.sa_family = AF_UNIX;
7165 # endif /* NETUNIX */
7166 # if NETINET
7167 else if (sm_strcasecmp(p, "inet") == 0)
7168 addr.sa.sa_family = AF_INET;
7169 # endif /* NETINET */
7170 # if NETINET6
7171 else if (sm_strcasecmp(p, "inet6") == 0)
7172 addr.sa.sa_family = AF_INET6;
7173 # endif /* NETINET6 */
7174 else
7176 # ifdef EPROTONOSUPPORT
7177 errno = EPROTONOSUPPORT;
7178 # else /* EPROTONOSUPPORT */
7179 errno = EINVAL;
7180 # endif /* EPROTONOSUPPORT */
7181 syserr("socket map \"%s\": unknown socket type %s",
7182 map->map_mname, p);
7183 return false;
7185 *colon++ = ':';
7187 else
7189 colon = p;
7190 #if NETUNIX
7191 /* default to AF_UNIX */
7192 addr.sa.sa_family = AF_UNIX;
7193 #else /* NETUNIX */
7194 # if NETINET
7195 /* default to AF_INET */
7196 addr.sa.sa_family = AF_INET;
7197 # else /* NETINET */
7198 # if NETINET6
7199 /* default to AF_INET6 */
7200 addr.sa.sa_family = AF_INET6;
7201 # else /* NETINET6 */
7202 syserr("socket map \"%s\": unknown socket type %s",
7203 map->map_mname, p);
7204 return false;
7205 # endif /* NETINET6 */
7206 # endif /* NETINET */
7207 #endif /* NETUNIX */
7210 # if NETUNIX
7211 if (addr.sa.sa_family == AF_UNIX)
7213 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7215 at = colon;
7216 if (strlen(colon) >= sizeof addr.sunix.sun_path)
7218 syserr("socket map \"%s\": local socket name %s too long",
7219 map->map_mname, colon);
7220 return false;
7222 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7223 S_IRUSR|S_IWUSR, NULL);
7225 if (errno != 0)
7227 /* if not safe, don't create */
7228 syserr("socket map \"%s\": local socket name %s unsafe",
7229 map->map_mname, colon);
7230 return false;
7233 (void) sm_strlcpy(addr.sunix.sun_path, colon,
7234 sizeof addr.sunix.sun_path);
7235 addrlen = sizeof (struct sockaddr_un);
7237 else
7238 # endif /* NETUNIX */
7239 # if NETINET || NETINET6
7240 if (false
7241 # if NETINET
7242 || addr.sa.sa_family == AF_INET
7243 # endif /* NETINET */
7244 # if NETINET6
7245 || addr.sa.sa_family == AF_INET6
7246 # endif /* NETINET6 */
7249 unsigned short port;
7251 /* Parse port@host */
7252 at = strchr(colon, '@');
7253 if (at == NULL)
7255 syserr("socket map \"%s\": bad address %s (expected port@host)",
7256 map->map_mname, colon);
7257 return false;
7259 *at = '\0';
7260 if (isascii(*colon) && isdigit(*colon))
7261 port = htons((unsigned short) atoi(colon));
7262 else
7264 # ifdef NO_GETSERVBYNAME
7265 syserr("socket map \"%s\": invalid port number %s",
7266 map->map_mname, colon);
7267 return false;
7268 # else /* NO_GETSERVBYNAME */
7269 register struct servent *sp;
7271 sp = getservbyname(colon, "tcp");
7272 if (sp == NULL)
7274 syserr("socket map \"%s\": unknown port name %s",
7275 map->map_mname, colon);
7276 return false;
7278 port = sp->s_port;
7279 # endif /* NO_GETSERVBYNAME */
7281 *at++ = '@';
7282 if (*at == '[')
7284 char *end;
7286 end = strchr(at, ']');
7287 if (end != NULL)
7289 bool found = false;
7290 # if NETINET
7291 unsigned long hid = INADDR_NONE;
7292 # endif /* NETINET */
7293 # if NETINET6
7294 struct sockaddr_in6 hid6;
7295 # endif /* NETINET6 */
7297 *end = '\0';
7298 # if NETINET
7299 if (addr.sa.sa_family == AF_INET &&
7300 (hid = inet_addr(&at[1])) != INADDR_NONE)
7302 addr.sin.sin_addr.s_addr = hid;
7303 addr.sin.sin_port = port;
7304 found = true;
7306 # endif /* NETINET */
7307 # if NETINET6
7308 (void) memset(&hid6, '\0', sizeof hid6);
7309 if (addr.sa.sa_family == AF_INET6 &&
7310 anynet_pton(AF_INET6, &at[1],
7311 &hid6.sin6_addr) == 1)
7313 addr.sin6.sin6_addr = hid6.sin6_addr;
7314 addr.sin6.sin6_port = port;
7315 found = true;
7317 # endif /* NETINET6 */
7318 *end = ']';
7319 if (!found)
7321 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7322 map->map_mname, at);
7323 return false;
7326 else
7328 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7329 map->map_mname, at);
7330 return false;
7333 else
7335 hp = sm_gethostbyname(at, addr.sa.sa_family);
7336 if (hp == NULL)
7338 syserr("socket map \"%s\": Unknown host name %s",
7339 map->map_mname, at);
7340 return false;
7342 addr.sa.sa_family = hp->h_addrtype;
7343 switch (hp->h_addrtype)
7345 # if NETINET
7346 case AF_INET:
7347 memmove(&addr.sin.sin_addr,
7348 hp->h_addr, INADDRSZ);
7349 addr.sin.sin_port = port;
7350 addrlen = sizeof (struct sockaddr_in);
7351 addrno = 1;
7352 break;
7353 # endif /* NETINET */
7355 # if NETINET6
7356 case AF_INET6:
7357 memmove(&addr.sin6.sin6_addr,
7358 hp->h_addr, IN6ADDRSZ);
7359 addr.sin6.sin6_port = port;
7360 addrlen = sizeof (struct sockaddr_in6);
7361 addrno = 1;
7362 break;
7363 # endif /* NETINET6 */
7365 default:
7366 syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7367 map->map_mname, at, hp->h_addrtype);
7368 # if NETINET6
7369 freehostent(hp);
7370 # endif /* NETINET6 */
7371 return false;
7375 else
7376 # endif /* NETINET || NETINET6 */
7378 syserr("socket map \"%s\": unknown socket protocol",
7379 map->map_mname);
7380 return false;
7383 /* nope, actually connecting */
7384 for (;;)
7386 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
7387 if (sock < 0)
7389 save_errno = errno;
7390 if (tTd(38, 5))
7391 sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7392 map->map_mname,
7393 sm_errstring(save_errno));
7394 # if NETINET6
7395 if (hp != NULL)
7396 freehostent(hp);
7397 # endif /* NETINET6 */
7398 return false;
7401 if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
7402 break;
7404 /* couldn't connect.... try next address */
7405 save_errno = errno;
7406 p = CurHostName;
7407 CurHostName = at;
7408 if (tTd(38, 5))
7409 sm_dprintf("socket_open (%s): open %s failed: %s\n",
7410 map->map_mname, at, sm_errstring(save_errno));
7411 CurHostName = p;
7412 (void) close(sock);
7414 /* try next address */
7415 if (hp != NULL && hp->h_addr_list[addrno] != NULL)
7417 switch (addr.sa.sa_family)
7419 # if NETINET
7420 case AF_INET:
7421 memmove(&addr.sin.sin_addr,
7422 hp->h_addr_list[addrno++],
7423 INADDRSZ);
7424 break;
7425 # endif /* NETINET */
7427 # if NETINET6
7428 case AF_INET6:
7429 memmove(&addr.sin6.sin6_addr,
7430 hp->h_addr_list[addrno++],
7431 IN6ADDRSZ);
7432 break;
7433 # endif /* NETINET6 */
7435 default:
7436 if (tTd(38, 5))
7437 sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7438 map->map_mname, at,
7439 hp->h_addrtype);
7440 # if NETINET6
7441 freehostent(hp);
7442 # endif /* NETINET6 */
7443 return false;
7445 continue;
7447 p = CurHostName;
7448 CurHostName = at;
7449 if (tTd(38, 5))
7450 sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7451 map->map_mname, sm_errstring(save_errno));
7452 CurHostName = p;
7453 # if NETINET6
7454 if (hp != NULL)
7455 freehostent(hp);
7456 # endif /* NETINET6 */
7457 return false;
7459 # if NETINET6
7460 if (hp != NULL)
7462 freehostent(hp);
7463 hp = NULL;
7465 # endif /* NETINET6 */
7466 if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
7467 SM_TIME_DEFAULT,
7468 (void *) &sock,
7469 SM_IO_RDWR,
7470 NULL)) == NULL)
7472 close(sock);
7473 if (tTd(38, 2))
7474 sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7475 map->map_mname, sm_errstring(errno));
7476 return false;
7479 /* Save connection for reuse */
7480 s->s_socketmap = map;
7481 return true;
7485 ** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7487 ** Cache SOCKET connections based on the connection specifier
7488 ** and PID so we don't have multiple connections open to
7489 ** the same server for different maps. Need a separate connection
7490 ** per PID since a parent process may close the map before the
7491 ** child is done with it.
7493 ** Parameters:
7494 ** conn -- SOCKET map connection specifier
7496 ** Returns:
7497 ** Symbol table entry for the SOCKET connection.
7500 static STAB *
7501 socket_map_findconn(conn)
7502 const char *conn;
7504 char *nbuf;
7505 STAB *SM_NONVOLATILE s = NULL;
7507 nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
7508 SM_TRY
7509 s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
7510 SM_FINALLY
7511 sm_free(nbuf);
7512 SM_END_TRY
7513 return s;
7517 ** SOCKET_MAP_CLOSE -- close the socket
7520 void
7521 socket_map_close(map)
7522 MAP *map;
7524 STAB *s;
7525 MAP *smap;
7527 if (tTd(38, 20))
7528 sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
7529 (long) CurrentPid);
7531 /* Check if already closed */
7532 if (map->map_db1 == NULL)
7534 if (tTd(38, 20))
7535 sm_dprintf("socket_map_close(%s) already closed\n",
7536 map->map_file);
7537 return;
7539 sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
7541 /* Mark all the maps that share the connection as closed */
7542 s = socket_map_findconn(map->map_file);
7543 smap = s->s_socketmap;
7544 while (smap != NULL)
7546 MAP *next;
7548 if (tTd(38, 2) && smap != map)
7549 sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7550 map->map_mname, smap->map_mname);
7552 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
7553 smap->map_db1 = NULL;
7554 next = smap->socket_map_next;
7555 smap->socket_map_next = NULL;
7556 smap = next;
7558 s->s_socketmap = NULL;
7562 ** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7565 char *
7566 socket_map_lookup(map, name, av, statp)
7567 MAP *map;
7568 char *name;
7569 char **av;
7570 int *statp;
7572 unsigned int nettolen, replylen, recvlen;
7573 char *replybuf, *rval, *value, *status, *key;
7574 SM_FILE_T *f;
7575 char keybuf[MAXNAME + 1];
7577 replybuf = NULL;
7578 rval = NULL;
7579 f = (SM_FILE_T *)map->map_db1;
7580 if (tTd(38, 20))
7581 sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7582 map->map_mname, name, map->map_file);
7584 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7586 nettolen = strlen(name);
7587 if (nettolen > sizeof keybuf - 1)
7588 nettolen = sizeof keybuf - 1;
7589 memmove(keybuf, name, nettolen);
7590 keybuf[nettolen] = '\0';
7591 makelower(keybuf);
7592 key = keybuf;
7594 else
7595 key = name;
7597 nettolen = strlen(map->map_mname) + 1 + strlen(key);
7598 SM_ASSERT(nettolen > strlen(map->map_mname));
7599 SM_ASSERT(nettolen > strlen(key));
7600 if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
7601 nettolen, map->map_mname, key) == SM_IO_EOF) ||
7602 (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
7603 (sm_io_error(f)))
7605 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
7606 map->map_mname);
7607 *statp = EX_TEMPFAIL;
7608 goto errcl;
7611 if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
7613 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
7614 map->map_mname);
7615 *statp = EX_TEMPFAIL;
7616 goto errcl;
7618 if (replylen > SOCKETMAP_MAXL)
7620 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
7621 map->map_mname, replylen);
7622 *statp = EX_TEMPFAIL;
7623 goto errcl;
7625 if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
7627 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
7628 map->map_mname);
7629 *statp = EX_TEMPFAIL;
7630 goto error;
7633 replybuf = (char *) sm_malloc(replylen + 1);
7634 if (replybuf == NULL)
7636 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
7637 map->map_mname, replylen + 1);
7638 *statp = EX_OSERR;
7639 goto error;
7642 recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
7643 if (recvlen < replylen)
7645 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
7646 map->map_mname, recvlen, replylen);
7647 *statp = EX_TEMPFAIL;
7648 goto errcl;
7650 if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
7652 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
7653 map->map_mname);
7654 *statp = EX_TEMPFAIL;
7655 goto errcl;
7657 status = replybuf;
7658 replybuf[recvlen] = '\0';
7659 value = strchr(replybuf, ' ');
7660 if (value != NULL)
7662 *value = '\0';
7663 value++;
7665 if (strcmp(status, "OK") == 0)
7667 *statp = EX_OK;
7669 /* collect the return value */
7670 if (bitset(MF_MATCHONLY, map->map_mflags))
7671 rval = map_rewrite(map, key, strlen(key), NULL);
7672 else
7673 rval = map_rewrite(map, value, strlen(value), av);
7675 else if (strcmp(status, "NOTFOUND") == 0)
7677 *statp = EX_NOTFOUND;
7678 if (tTd(38, 20))
7679 sm_dprintf("socket_map_lookup(%s): %s not found\n",
7680 map->map_mname, key);
7682 else
7684 if (tTd(38, 5))
7685 sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
7686 map->map_mname, key, status,
7687 value ? value : "");
7688 if ((strcmp(status, "TEMP") == 0) ||
7689 (strcmp(status, "TIMEOUT") == 0))
7690 *statp = EX_TEMPFAIL;
7691 else if(strcmp(status, "PERM") == 0)
7692 *statp = EX_UNAVAILABLE;
7693 else
7694 *statp = EX_PROTOCOL;
7697 if (replybuf != NULL)
7698 sm_free(replybuf);
7699 return rval;
7701 errcl:
7702 socket_map_close(map);
7703 error:
7704 if (replybuf != NULL)
7705 sm_free(replybuf);
7706 return rval;
7708 #endif /* SOCKETMAP */