2 * Copyright (c) 1998-2008 Proofpoint, Inc. and its suppliers.
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.
16 SM_RCSID("@(#)$Id: map.c,v 8.713 2013-11-22 20:51:55 ca Exp $")
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.
35 struct dom_binding; /* forward reference needed on IRIX */
36 # include <rpcsvc/ypclnt.h>
38 # define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
45 # if DB_VERSION_MAJOR < 2
46 static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
47 # endif /* DB_VERSION_MAJOR < 2 */
48 # if DB_VERSION_MAJOR == 2
49 static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
50 # endif /* DB_VERSION_MAJOR == 2 */
51 # if DB_VERSION_MAJOR > 2
52 static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **));
53 # endif /* DB_VERSION_MAJOR > 2 */
55 static bool extract_canonname __P((char *, char *, char *, char[], int));
56 static void map_close __P((STAB *, int));
57 static void map_init __P((STAB *, int));
59 static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *));
62 static bool nisplus_getcanonname __P((char *, int, int *));
65 static bool nis_getcanonname __P((char *, int, int *));
68 static bool ni_getcanonname __P((char *, int, int *));
70 static bool text_getcanonname __P((char *, int, int *));
72 static STAB *socket_map_findconn __P((const char*));
74 /* XXX arbitrary limit for sanity */
75 # define SOCKETMAP_MAXL 1000000
76 #endif /* SOCKETMAP */
78 /* default error message for trying to open a map in write mode */
80 # define SM_EMAPCANTWRITE ENOSYS
83 # define SM_EMAPCANTWRITE EFTYPE
85 # define SM_EMAPCANTWRITE ENXIO
90 ** MAP.C -- implementations for various map classes.
92 ** Each map class implements a series of functions:
94 ** bool map_parse(MAP *map, char *args)
95 ** Parse the arguments from the config file. Return true
96 ** if they were ok, false otherwise. Fill in map with the
99 ** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
100 ** Look up the key in the given map. If found, do any
101 ** rewriting the map wants (including "args" if desired)
102 ** and return the value. Set *pstat to the appropriate status
103 ** on error and return NULL. Args will be NULL if called
104 ** from the alias routines, although this should probably
105 ** not be relied upon. It is suggested you call map_rewrite
106 ** to return the results -- it takes care of null termination
107 ** and uses a dynamically expanded buffer as needed.
109 ** void map_store(MAP *map, char *key, char *value)
110 ** Store the key:value pair in the map.
112 ** bool map_open(MAP *map, int mode)
113 ** Open the map for the indicated mode. Mode should
114 ** be either O_RDONLY or O_RDWR. Return true if it
115 ** was opened successfully, false otherwise. If the open
116 ** failed and the MF_OPTIONAL flag is not set, it should
117 ** also print an error. If the MF_ALIAS bit is set
118 ** and this map class understands the @:@ convention, it
119 ** should call aliaswait() before returning.
121 ** void map_close(MAP *map)
124 ** This file also includes the implementation for getcanonname.
125 ** It is currently implemented in a pretty ad-hoc manner; it ought
126 ** to be more properly integrated into the map structure.
129 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
130 # define LOCK_ON_OPEN 1 /* we can open/create a locked file */
131 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
132 # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
133 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
136 ** MAP_PARSEARGS -- parse config line arguments for database lookup
138 ** This is a generic version of the map_parse method.
141 ** map -- the map being initialized.
142 ** ap -- a pointer to the args on the config line.
145 ** true -- if everything parsed OK.
146 ** false -- otherwise.
149 ** null terminates the filename; stores it in map
153 map_parseargs(map, ap)
157 register char *p = ap;
160 ** There is no check whether there is really an argument,
161 ** but that's not important enough to warrant extra code.
164 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
165 map->map_spacesub = SpaceSub; /* default value */
168 while (isascii(*p) && isspace(*p))
175 map->map_mflags |= MF_INCLNULL;
176 map->map_mflags &= ~MF_TRY0NULL;
180 map->map_mflags &= ~MF_TRY1NULL;
184 map->map_mflags |= MF_OPTIONAL;
188 map->map_mflags |= MF_NOFOLDCASE;
192 map->map_mflags |= MF_MATCHONLY;
196 map->map_mflags |= MF_APPEND;
200 map->map_mflags |= MF_KEEPQUOTES;
215 map->map_timeout = convtime(p, 's');
226 while (isascii(*++p) && isspace(*p))
228 map->map_keycolnm = p;
232 while (isascii(*++p) && isspace(*p))
234 map->map_valcolnm = p;
239 map->map_coldelim = *p;
245 map->map_coldelim = '\n';
249 map->map_coldelim = '\t';
253 map->map_coldelim = '\\';
259 map->map_mflags |= MF_NODEFER;
264 map->map_spacesub = *++p;
268 map->map_mflags |= MF_DEFER;
272 syserr("Illegal option %c map %s", *p, map->map_mname);
275 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
280 if (map->map_app != NULL)
281 map->map_app = newstr(map->map_app);
282 if (map->map_tapp != NULL)
283 map->map_tapp = newstr(map->map_tapp);
284 if (map->map_keycolnm != NULL)
285 map->map_keycolnm = newstr(map->map_keycolnm);
286 if (map->map_valcolnm != NULL)
287 map->map_valcolnm = newstr(map->map_valcolnm);
292 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
296 map->map_file = newstr(map->map_file);
299 while (*p != '\0' && isascii(*p) && isspace(*p))
302 map->map_rebuild = newstr(p);
304 if (map->map_file == NULL &&
305 !bitset(MCF_OPTFILE, map->map_class->map_cflags))
307 syserr("No file name for %s map %s",
308 map->map_class->map_cname, map->map_mname);
314 ** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
316 ** It also adds the map_app string. It can be used as a utility
317 ** in the map_lookup method.
320 ** map -- the map that causes this.
321 ** s -- the string to rewrite, NOT necessarily null terminated.
322 ** slen -- the length of s.
323 ** av -- arguments to interpolate into buf.
326 ** Pointer to rewritten result. This is static data that
327 ** should be copied if it is to be saved!
331 map_rewrite(map, s, slen, av)
333 register const char *s;
343 static size_t buflen = 0;
344 static char *buf = NULL;
348 sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
350 sm_dprintf(" (nullv)");
353 for (avp = av; *avp != NULL; avp++)
354 sm_dprintf("\n\t%s", *avp);
359 /* count expected size of output (can safely overestimate) */
365 while (l-- > 0 && (c = *sp++) != '\0')
372 if (!(isascii(c) && isdigit(c)))
374 for (avp = av; --c >= '0' && *avp != NULL; avp++)
381 if (map->map_app != NULL)
382 len += strlen(map->map_app);
385 /* need to malloc additional space */
389 buf = sm_pmalloc_x(buflen);
395 memmove(bp, s, slen);
398 /* assert(len > slen); */
403 while (slen-- > 0 && (c = *s++) != '\0')
413 if (slen-- <= 0 || (c = *s++) == '\0')
417 if (!(isascii(c) && isdigit(c)))
424 for (avp = av; --c >= '0' && *avp != NULL; avp++)
429 /* transliterate argument into output string */
430 for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
434 if (map->map_app != NULL && len > 0)
435 (void) sm_strlcpy(bp, map->map_app, len);
439 sm_dprintf("map_rewrite => %s\n", buf);
443 ** INITMAPS -- rebuild alias maps
456 checkfd012("entering initmaps");
458 stabapply(map_init, 0);
460 checkfd012("exiting initmaps");
464 ** MAP_INIT -- rebuild a map
467 ** s -- STAB entry: if map: try to rebuild
468 ** unused -- unused variable
474 ** will close already open rebuildable map.
485 /* has to be a map */
486 if (s->s_symtype != ST_MAP)
490 if (!bitset(MF_VALID, map->map_mflags))
494 sm_dprintf("map_init(%s:%s, %s)\n",
495 map->map_class->map_cname == NULL ? "NULL" :
496 map->map_class->map_cname,
497 map->map_mname == NULL ? "NULL" : map->map_mname,
498 map->map_file == NULL ? "NULL" : map->map_file);
500 if (!bitset(MF_ALIAS, map->map_mflags) ||
501 !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
504 sm_dprintf("\tnot rebuildable\n");
508 /* if already open, close it (for nested open) */
509 if (bitset(MF_OPEN, map->map_mflags))
511 map->map_mflags |= MF_CLOSING;
512 map->map_class->map_close(map);
513 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
516 (void) rebuildaliases(map, false);
520 ** OPENMAP -- open a map
523 ** map -- map to open (it must not be open).
526 ** whether open succeeded.
533 bool restore = false;
534 bool savehold = HoldErrs;
535 bool savequick = QuickAbort;
536 int saveerrors = Errors;
538 if (!bitset(MF_VALID, map->map_mflags))
541 /* better safe than sorry... */
542 if (bitset(MF_OPEN, map->map_mflags))
545 /* Don't send a map open error out via SMTP */
546 if ((OnlyOneError || QuickAbort) &&
547 (OpMode == MD_SMTP || OpMode == MD_DAEMON))
555 if (map->map_class->map_open(map, O_RDONLY))
558 sm_dprintf("openmap()\t%s:%s %s: valid\n",
559 map->map_class->map_cname == NULL ? "NULL" :
560 map->map_class->map_cname,
561 map->map_mname == NULL ? "NULL" :
563 map->map_file == NULL ? "NULL" :
565 map->map_mflags |= MF_OPEN;
566 map->map_pid = CurrentPid;
571 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
572 map->map_class->map_cname == NULL ? "NULL" :
573 map->map_class->map_cname,
574 map->map_mname == NULL ? "NULL" :
576 map->map_file == NULL ? "NULL" :
578 errno == 0 ? "" : ": ",
579 errno == 0 ? "" : sm_errstring(errno));
580 if (!bitset(MF_OPTIONAL, map->map_mflags))
582 extern MAPCLASS BogusMapClass;
584 map->map_orgclass = map->map_class;
585 map->map_class = &BogusMapClass;
586 map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
587 map->map_pid = CurrentPid;
591 /* don't try again */
592 map->map_mflags &= ~MF_VALID;
600 QuickAbort = savequick;
603 return bitset(MF_OPEN, map->map_mflags);
606 ** CLOSEMAPS -- close all open maps opened by the current pid.
609 ** bogus -- only close bogus maps.
619 stabapply(map_close, bogus);
622 ** MAP_CLOSE -- close a map opened by the current pid.
625 ** s -- STAB entry: if map: try to close
626 ** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
636 int bogus; /* int because of stabapply(), used as bool */
639 extern MAPCLASS BogusMapClass;
641 if (s->s_symtype != ST_MAP)
647 ** close the map iff:
648 ** it is valid and open and opened by this process
649 ** and (!bogus or it's a bogus map or it is not persistent)
650 ** negate this: return iff
651 ** it is not valid or it is not open or not opened by this process
652 ** or (bogus and it's not a bogus map and it's not not-persistent)
655 if (!bitset(MF_VALID, map->map_mflags) ||
656 !bitset(MF_OPEN, map->map_mflags) ||
657 bitset(MF_CLOSING, map->map_mflags) ||
658 map->map_pid != CurrentPid ||
659 (bogus && map->map_class != &BogusMapClass &&
660 !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
663 if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
664 map->map_orgclass != &BogusMapClass)
665 map->map_class = map->map_orgclass;
667 sm_dprintf("closemaps: closing %s (%s)\n",
668 map->map_mname == NULL ? "NULL" : map->map_mname,
669 map->map_file == NULL ? "NULL" : map->map_file);
671 if (!bitset(MF_OPENBOGUS, map->map_mflags))
673 map->map_mflags |= MF_CLOSING;
674 map->map_class->map_close(map);
676 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
679 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
680 extern int getdomainname();
682 /* this is mainly for backward compatibility in Sun environment */
687 ** Get the domain name from the kernel.
688 ** If it does not start with a leading dot, then remove
689 ** the first component. Since leading dots are funny Unix
690 ** files, we treat a leading "+" the same as a leading dot.
691 ** Finally, force there to be at least one dot in the domain name
692 ** (i.e. top-level domains are not allowed, like "com", must be
693 ** something like "sun.com").
697 char *period, *autodomain;
699 if (getdomainname(buf, sizeof buf) < 0)
706 printf("domainname = %s\n", buf);
710 period = strchr(buf, '.');
714 autodomain = period + 1;
715 if (strchr(autodomain, '.') == NULL)
718 return newstr(autodomain);
720 #endif /* SUN_EXTENSIONS && SUN_INIT_DOMAIN */
723 ** GETCANONNAME -- look up name using service switch
726 ** host -- the host name to look up.
727 ** hbsize -- the size of the host buffer.
728 ** trymx -- if set, try MX records.
729 ** pttl -- pointer to return TTL (can be NULL).
732 ** true -- if the host was found.
733 ** false -- otherwise.
737 getcanonname(host, hbsize, trymx, pttl)
746 bool got_tempfail = false;
747 auto int status = EX_UNAVAILABLE;
748 char *maptype[MAXMAPSTACK];
749 short mapreturn[MAXMAPACTIONS];
750 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
751 bool should_try_nis_domain = false;
752 static char *nis_domain = NULL;
755 nmaps = switch_map_find("hosts", maptype, mapreturn);
757 *pttl = SM_DEFAULT_TTL;
758 for (mapno = 0; mapno < nmaps; mapno++)
763 sm_dprintf("getcanonname(%s), trying %s\n",
764 host, maptype[mapno]);
765 if (strcmp("files", maptype[mapno]) == 0)
767 found = text_getcanonname(host, hbsize, &status);
770 else if (strcmp("nis", maptype[mapno]) == 0)
772 found = nis_getcanonname(host, hbsize, &status);
773 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
774 if (nis_domain == NULL)
775 nis_domain = sun_init_domain();
776 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
780 else if (strcmp("nisplus", maptype[mapno]) == 0)
782 found = nisplus_getcanonname(host, hbsize, &status);
783 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
784 if (nis_domain == NULL)
785 nis_domain = sun_init_domain();
786 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
790 else if (strcmp("dns", maptype[mapno]) == 0)
792 found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
794 #endif /* NAMED_BIND */
796 else if (strcmp("netinfo", maptype[mapno]) == 0)
798 found = ni_getcanonname(host, hbsize, &status);
804 status = EX_UNAVAILABLE;
808 ** Heuristic: if $m is not set, we are running during system
809 ** startup. In this case, when a name is apparently found
810 ** but has no dot, treat is as not found. This avoids
811 ** problems if /etc/hosts has no FQDN but is listed first
812 ** in the service switch.
816 (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
819 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
821 should_try_nis_domain = true;
822 /* but don't break, as we need to try all methods first */
823 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
825 /* see if we should continue */
826 if (status == EX_TEMPFAIL)
831 else if (status == EX_NOTFOUND)
835 if (bitset(1 << mapno, mapreturn[i]))
844 sm_dprintf("getcanonname(%s), found\n", host);
847 ** If returned name is still single token, compensate
848 ** by tagging on $m. This is because some sites set
849 ** up their DNS or NIS databases wrong.
852 if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
854 d = macvalue('m', CurEnv);
856 hbsize > (int) (strlen(host) + strlen(d) + 1))
858 if (host[strlen(host) - 1] != '.')
859 (void) sm_strlcat2(host, ".", d,
862 (void) sm_strlcat(host, d, hbsize);
866 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
867 if (VendorCode == VENDOR_SUN &&
868 should_try_nis_domain)
872 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
879 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
880 if (VendorCode == VENDOR_SUN && should_try_nis_domain)
883 if (nis_domain != NULL &&
884 strlen(nis_domain) + strlen(host) + 1 < hbsize)
886 (void) sm_strlcat2(host, ".", nis_domain, hbsize);
890 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
893 sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
897 SM_SET_H_ERRNO(TRY_AGAIN);
899 SM_SET_H_ERRNO(HOST_NOT_FOUND);
904 ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
907 ** name -- the name against which to match.
908 ** dot -- where to reinsert '.' to get FQDN
909 ** line -- the /etc/hosts line.
910 ** cbuf -- the location to store the result.
911 ** cbuflen -- the size of cbuf.
914 ** true -- if the line matched the desired name.
915 ** false -- otherwise.
919 extract_canonname(name, dot, line, cbuf, cbuflen)
936 char nbuf[MAXNAME + 1];
938 p = get_column(line, i, '\0', nbuf, sizeof(nbuf));
943 if (cbuf[0] == '\0' ||
944 (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
946 (void) sm_strlcpy(cbuf, p, cbuflen);
948 if (sm_strcasecmp(name, p) == 0)
950 else if (dot != NULL)
952 /* try looking for the FQDN as well */
954 if (sm_strcasecmp(name, p) == 0)
959 if (found && strchr(cbuf, '.') == NULL)
961 /* try to add a domain on the end of the name */
962 char *domain = macvalue('m', CurEnv);
964 if (domain != NULL &&
965 strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
969 (void) sm_strlcpy(p, domain, cbuflen - i - 1);
982 # include "sm_resolve.h"
983 # if NETINET || NETINET6
984 # include <arpa/inet.h>
985 # endif /* NETINET || NETINET6 */
988 ** DNS_MAP_OPEN -- stub to check proper value for dns map type
992 dns_map_open(map, mode)
997 sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
1000 if (mode != O_RDONLY)
1002 /* issue a pseudo-error message */
1003 errno = SM_EMAPCANTWRITE;
1010 ** DNS_MAP_PARSEARGS -- parse dns map definition args.
1013 ** map -- pointer to MAP
1014 ** args -- pointer to the args on the config line.
1017 ** true -- if everything parsed OK.
1018 ** false -- otherwise.
1021 #define map_sizelimit map_lockfd /* overload field */
1029 dns_map_parseargs(map,args)
1033 register char *p = args;
1034 struct dns_map *map_p;
1036 map_p = (struct dns_map *) xalloc(sizeof(*map_p));
1037 map_p->dns_m_type = -1;
1038 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
1042 while (isascii(*p) && isspace(*p))
1049 map->map_mflags |= MF_INCLNULL;
1050 map->map_mflags &= ~MF_TRY0NULL;
1054 map->map_mflags &= ~MF_TRY1NULL;
1058 map->map_mflags |= MF_OPTIONAL;
1062 map->map_mflags |= MF_NOFOLDCASE;
1066 map->map_mflags |= MF_MATCHONLY;
1070 map->map_mflags |= MF_APPEND;
1074 map->map_mflags |= MF_KEEPQUOTES;
1078 map->map_mflags |= MF_NODEFER;
1086 map->map_tapp = ++p;
1097 map->map_timeout = convtime(p, 's');
1104 while (isascii(*++p) && isspace(*p))
1106 map->map_retry = atoi(p);
1111 map->map_coldelim = *p;
1117 map->map_coldelim = '\n';
1121 map->map_coldelim = '\t';
1125 map->map_coldelim = '\\';
1131 while (isascii(*++p) && isspace(*p))
1133 map->map_sizelimit = atoi(p);
1136 /* Start of dns_map specific args */
1137 case 'R': /* search field */
1141 while (isascii(*++p) && isspace(*p))
1146 map_p->dns_m_type = dns_string_to_type(p);
1149 if (map_p->dns_m_type < 0)
1150 syserr("dns map %s: wrong type %s",
1155 case 'B': /* base domain */
1159 while (isascii(*++p) && isspace(*p))
1166 ** slight abuse of map->map_file; it isn't
1167 ** used otherwise in this map type.
1170 map->map_file = newstr(p);
1176 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1181 if (map_p->dns_m_type < 0)
1182 syserr("dns map %s: missing -R type", map->map_mname);
1183 if (map->map_app != NULL)
1184 map->map_app = newstr(map->map_app);
1185 if (map->map_tapp != NULL)
1186 map->map_tapp = newstr(map->map_tapp);
1189 ** Assumption: assert(sizeof(int) <= sizeof(ARBPTR_T));
1190 ** Even if this assumption is wrong, we use only one byte,
1191 ** so it doesn't really matter.
1194 map->map_db1 = (ARBPTR_T) map_p;
1199 ** DNS_MAP_LOOKUP -- perform dns map lookup.
1202 ** map -- pointer to MAP
1203 ** name -- name to lookup
1204 ** av -- arguments to interpolate into buf.
1205 ** statp -- pointer to status (EX_)
1208 ** result of lookup if succeeded.
1209 ** NULL -- otherwise.
1213 dns_map_lookup(map, name, av, statp)
1220 char *vp = NULL, *result = NULL;
1222 struct dns_map *map_p;
1223 RESOURCE_RECORD_T *rr = NULL;
1224 DNS_REPLY_T *r = NULL;
1226 static char buf6[INET6_ADDRSTRLEN];
1227 # endif /* NETINET6 */
1230 sm_dprintf("dns_map_lookup(%s, %s)\n",
1231 map->map_mname, name);
1233 map_p = (struct dns_map *)(map->map_db1);
1234 if (map->map_file != NULL && *map->map_file != '\0')
1239 len = strlen(map->map_file) + strlen(name) + 2;
1240 appdomain = (char *) sm_malloc(len);
1241 if (appdomain == NULL)
1243 *statp = EX_UNAVAILABLE;
1246 (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
1247 r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
1248 map->map_timeout, map->map_retry);
1253 r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
1254 map->map_timeout, map->map_retry);
1260 if (h_errno == TRY_AGAIN || transienterror(errno))
1261 *statp = EX_TEMPFAIL;
1263 *statp = EX_NOTFOUND;
1267 for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
1272 switch (rr->rr_type)
1276 value = rr->rr_u.rr_txt;
1280 value = rr->rr_u.rr_txt;
1284 value = rr->rr_u.rr_mx->mx_r_domain;
1288 value = rr->rr_u.rr_srv->srv_r_target;
1292 value = rr->rr_u.rr_txt;
1296 value = rr->rr_u.rr_txt;
1300 value = rr->rr_u.rr_mx->mx_r_domain;
1305 value = inet_ntoa(*(rr->rr_u.rr_a));
1307 # endif /* NETINET */
1311 value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
1314 # endif /* NETINET6 */
1317 (void) strreplnonprt(value, 'X');
1318 if (map_p->dns_m_type != rr->rr_type)
1321 sm_dprintf("\tskipping type %s (%d) value %s\n",
1322 type != NULL ? type : "<UNKNOWN>",
1324 value != NULL ? value : "<NO VALUE>");
1329 if (rr->rr_type == T_AAAA && value == NULL)
1332 *statp = EX_DATAERR;
1334 sm_dprintf("\tbad T_AAAA conversion\n");
1337 # endif /* NETINET6 */
1339 sm_dprintf("\tfound type %s (%d) value %s\n",
1340 type != NULL ? type : "<UNKNOWN>",
1342 value != NULL ? value : "<NO VALUE>");
1343 if (value != NULL &&
1344 (map->map_coldelim == '\0' ||
1345 map->map_sizelimit == 1 ||
1346 bitset(MF_MATCHONLY, map->map_mflags)))
1348 /* Only care about the first match */
1352 else if (vp == NULL)
1359 /* concatenate the results */
1363 sz = strlen(vp) + strlen(value) + 2;
1365 (void) sm_snprintf(new, sz, "%s%c%s",
1366 vp, map->map_coldelim, value);
1369 if (map->map_sizelimit > 0 &&
1370 ++resnum >= map->map_sizelimit)
1377 *statp = EX_NOTFOUND;
1379 sm_dprintf("\tno match found\n");
1383 /* Cleanly truncate for rulesets */
1384 truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
1389 sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
1391 if (bitset(MF_MATCHONLY, map->map_mflags))
1392 result = map_rewrite(map, name, strlen(name), NULL);
1394 result = map_rewrite(map, vp, vsize, av);
1403 # endif /* DNSMAP */
1404 #endif /* NAMED_BIND */
1413 ** NDBM_MAP_OPEN -- DBM-style map open
1417 ndbm_map_open(map, mode)
1427 int smode = S_IREAD;
1428 char dirfile[MAXPATHLEN];
1429 char pagfile[MAXPATHLEN];
1431 struct stat std, stp;
1434 sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1435 map->map_mname, map->map_file, mode);
1436 map->map_lockfd = -1;
1439 /* do initial file and directory checks */
1440 if (sm_strlcpyn(dirfile, sizeof(dirfile), 2,
1441 map->map_file, ".dir") >= sizeof(dirfile) ||
1442 sm_strlcpyn(pagfile, sizeof(pagfile), 2,
1443 map->map_file, ".pag") >= sizeof(pagfile))
1446 if (!bitset(MF_OPTIONAL, map->map_mflags))
1447 syserr("dbm map \"%s\": map file %s name too long",
1448 map->map_mname, map->map_file);
1451 sff = SFF_ROOTOK|SFF_REGONLY;
1455 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1457 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1463 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1466 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1467 sff |= SFF_SAFEDIRPATH;
1468 ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
1471 ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
1476 char *prob = "unsafe";
1478 /* cannot open this map */
1482 sm_dprintf("\t%s map file: %d\n", prob, ret);
1483 if (!bitset(MF_OPTIONAL, map->map_mflags))
1484 syserr("dbm map \"%s\": %s map file %s",
1485 map->map_mname, prob, map->map_file);
1488 if (std.st_mode == ST_MODE_NOFILE)
1489 mode |= O_CREAT|O_EXCL;
1492 if (mode == O_RDONLY)
1495 mode |= O_TRUNC|O_EXLOCK;
1496 # else /* LOCK_ON_OPEN */
1497 if ((mode & O_ACCMODE) == O_RDWR)
1501 ** Warning: race condition. Try to lock the file as
1502 ** quickly as possible after opening it.
1503 ** This may also have security problems on some systems,
1504 ** but there isn't anything we can do about it.
1508 # else /* NOFTRUNCATE */
1510 ** This ugly code opens the map without truncating it,
1511 ** locks the file, then truncates it. Necessary to
1512 ** avoid race conditions.
1517 long sff = SFF_CREAT|SFF_OPENASROOT;
1519 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1521 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1524 dirfd = safeopen(dirfile, mode, DBMMODE, sff);
1525 pagfd = safeopen(pagfile, mode, DBMMODE, sff);
1527 if (dirfd < 0 || pagfd < 0)
1531 (void) close(dirfd);
1533 (void) close(pagfd);
1535 syserr("ndbm_map_open: cannot create database %s",
1539 if (ftruncate(dirfd, (off_t) 0) < 0 ||
1540 ftruncate(pagfd, (off_t) 0) < 0)
1543 (void) close(dirfd);
1544 (void) close(pagfd);
1546 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1551 /* if new file, get "before" bits for later filechanged check */
1552 if (std.st_mode == ST_MODE_NOFILE &&
1553 (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
1556 (void) close(dirfd);
1557 (void) close(pagfd);
1559 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1564 /* have to save the lock for the duration (bletch) */
1565 map->map_lockfd = dirfd;
1566 (void) close(pagfd);
1568 /* twiddle bits for dbm_open */
1569 mode &= ~(O_CREAT|O_EXCL);
1570 # endif /* NOFTRUNCATE */
1572 # endif /* LOCK_ON_OPEN */
1574 /* open the database */
1575 dbm = dbm_open(map->map_file, mode, DBMMODE);
1579 if (bitset(MF_ALIAS, map->map_mflags) &&
1580 aliaswait(map, ".pag", false))
1582 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1583 if (map->map_lockfd >= 0)
1584 (void) close(map->map_lockfd);
1585 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1587 if (!bitset(MF_OPTIONAL, map->map_mflags))
1588 syserr("Cannot open DBM database %s", map->map_file);
1591 dfd = dbm_dirfno(dbm);
1592 pfd = dbm_pagfno(dbm);
1595 /* heuristic: if files are linked, this is actually gdbm */
1597 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1598 if (map->map_lockfd >= 0)
1599 (void) close(map->map_lockfd);
1600 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1602 syserr("dbm map \"%s\": cannot support GDBM",
1607 if (filechanged(dirfile, dfd, &std) ||
1608 filechanged(pagfile, pfd, &stp))
1612 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1613 if (map->map_lockfd >= 0)
1614 (void) close(map->map_lockfd);
1615 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1617 syserr("ndbm_map_open(%s): file changed after open",
1622 map->map_db1 = (ARBPTR_T) dbm;
1625 ** Need to set map_mtime before the call to aliaswait()
1626 ** as aliaswait() will call map_lookup() which requires
1627 ** map_mtime to be set
1630 if (fstat(pfd, &st) >= 0)
1631 map->map_mtime = st.st_mtime;
1633 if (mode == O_RDONLY)
1637 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1639 (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
1640 # endif /* LOCK_ON_OPEN */
1641 if (bitset(MF_ALIAS, map->map_mflags) &&
1642 !aliaswait(map, ".pag", true))
1647 map->map_mflags |= MF_LOCKED;
1648 if (geteuid() == 0 && TrustedUid != 0)
1651 if (fchown(dfd, TrustedUid, -1) < 0 ||
1652 fchown(pfd, TrustedUid, -1) < 0)
1656 sm_syslog(LOG_ALERT, NOQID,
1657 "ownership change on %s failed: %s",
1658 map->map_file, sm_errstring(err));
1659 message("050 ownership change on %s failed: %s",
1660 map->map_file, sm_errstring(err));
1662 # else /* HASFCHOWN */
1663 sm_syslog(LOG_ALERT, NOQID,
1664 "no fchown(): cannot change ownership on %s",
1666 message("050 no fchown(): cannot change ownership on %s",
1668 # endif /* HASFCHOWN */
1676 ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1680 ndbm_map_lookup(map, name, av, statp)
1688 char keybuf[MAXNAME + 1];
1692 sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1693 map->map_mname, name);
1696 key.dsize = strlen(name);
1697 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1699 if (key.dsize > sizeof(keybuf) - 1)
1700 key.dsize = sizeof(keybuf) - 1;
1701 memmove(keybuf, key.dptr, key.dsize);
1702 keybuf[key.dsize] = '\0';
1707 dfd = dbm_dirfno((DBM *) map->map_db1);
1708 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1709 (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
1710 pfd = dbm_pagfno((DBM *) map->map_db1);
1711 if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
1712 stbuf.st_mtime > map->map_mtime)
1714 /* Reopen the database to sync the cache */
1715 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1718 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1719 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1720 map->map_mflags |= MF_CLOSING;
1721 map->map_class->map_close(map);
1722 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1723 if (map->map_class->map_open(map, omode))
1725 map->map_mflags |= MF_OPEN;
1726 map->map_pid = CurrentPid;
1727 if ((omode & O_ACCMODE) == O_RDWR)
1728 map->map_mflags |= MF_WRITABLE;
1733 if (!bitset(MF_OPTIONAL, map->map_mflags))
1735 extern MAPCLASS BogusMapClass;
1737 *statp = EX_TEMPFAIL;
1738 map->map_orgclass = map->map_class;
1739 map->map_class = &BogusMapClass;
1740 map->map_mflags |= MF_OPEN;
1741 map->map_pid = CurrentPid;
1742 syserr("Cannot reopen NDBM database %s",
1749 if (bitset(MF_TRY0NULL, map->map_mflags))
1751 val = dbm_fetch((DBM *) map->map_db1, key);
1752 if (val.dptr != NULL)
1753 map->map_mflags &= ~MF_TRY1NULL;
1755 if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1758 val = dbm_fetch((DBM *) map->map_db1, key);
1759 if (val.dptr != NULL)
1760 map->map_mflags &= ~MF_TRY0NULL;
1762 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1763 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1764 if (val.dptr == NULL)
1766 if (bitset(MF_MATCHONLY, map->map_mflags))
1767 return map_rewrite(map, name, strlen(name), NULL);
1769 return map_rewrite(map, val.dptr, val.dsize, av);
1774 ** NDBM_MAP_STORE -- store a datum in the database
1778 ndbm_map_store(map, lhs, rhs)
1786 char keybuf[MAXNAME + 1];
1789 sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1790 map->map_mname, lhs, rhs);
1792 key.dsize = strlen(lhs);
1794 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1796 if (key.dsize > sizeof(keybuf) - 1)
1797 key.dsize = sizeof(keybuf) - 1;
1798 memmove(keybuf, key.dptr, key.dsize);
1799 keybuf[key.dsize] = '\0';
1804 data.dsize = strlen(rhs);
1807 if (bitset(MF_INCLNULL, map->map_mflags))
1813 status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1816 if (!bitset(MF_APPEND, map->map_mflags))
1817 message("050 Warning: duplicate alias name %s", lhs);
1820 static char *buf = NULL;
1821 static int bufsiz = 0;
1825 old.dptr = ndbm_map_lookup(map, key.dptr,
1826 (char **) NULL, &xstat);
1827 if (old.dptr != NULL && *(char *) old.dptr != '\0')
1829 old.dsize = strlen(old.dptr);
1830 if (data.dsize + old.dsize + 2 > bufsiz)
1833 (void) sm_free(buf);
1834 bufsiz = data.dsize + old.dsize + 2;
1835 buf = sm_pmalloc_x(bufsiz);
1837 (void) sm_strlcpyn(buf, bufsiz, 3,
1838 data.dptr, ",", old.dptr);
1839 data.dsize = data.dsize + old.dsize + 1;
1842 sm_dprintf("ndbm_map_store append=%s\n",
1846 status = dbm_store((DBM *) map->map_db1,
1847 key, data, DBM_REPLACE);
1850 syserr("readaliases: dbm put (%s): %d", lhs, status);
1855 ** NDBM_MAP_CLOSE -- close the database
1863 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1864 map->map_mname, map->map_file, map->map_mflags);
1866 if (bitset(MF_WRITABLE, map->map_mflags))
1868 # ifdef NDBM_YP_COMPAT
1870 char buf[MAXHOSTNAMELEN];
1872 inclnull = bitset(MF_INCLNULL, map->map_mflags);
1873 map->map_mflags &= ~MF_INCLNULL;
1875 if (strstr(map->map_file, "/yp/") != NULL)
1877 long save_mflags = map->map_mflags;
1879 map->map_mflags |= MF_NOFOLDCASE;
1881 (void) sm_snprintf(buf, sizeof(buf), "%010ld", curtime());
1882 ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1884 (void) gethostname(buf, sizeof(buf));
1885 ndbm_map_store(map, "YP_MASTER_NAME", buf);
1887 map->map_mflags = save_mflags;
1891 map->map_mflags |= MF_INCLNULL;
1892 # endif /* NDBM_YP_COMPAT */
1894 /* write out the distinguished alias */
1895 ndbm_map_store(map, "@", "@");
1897 dbm_close((DBM *) map->map_db1);
1899 /* release lock (if needed) */
1901 if (map->map_lockfd >= 0)
1902 (void) close(map->map_lockfd);
1903 # endif /* !LOCK_ON_OPEN */
1908 ** NEWDB (Hash and BTree) Modules
1914 ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1916 ** These do rather bizarre locking. If you can lock on open,
1917 ** do that to avoid the condition of opening a database that
1918 ** is being rebuilt. If you don't, we'll try to fake it, but
1919 ** there will be a race condition. If opening for read-only,
1920 ** we immediately release the lock to avoid freezing things up.
1921 ** We really ought to hold the lock, but guarantee that we won't
1922 ** be pokey about it. That's hard to do.
1925 /* these should be K line arguments */
1926 # if DB_VERSION_MAJOR < 2
1927 # define db_cachesize cachesize
1928 # define h_nelem nelem
1929 # ifndef DB_CACHE_SIZE
1930 # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
1931 # endif /* ! DB_CACHE_SIZE */
1932 # ifndef DB_HASH_NELEM
1933 # define DB_HASH_NELEM 4096 /* (starting) size of hash table */
1934 # endif /* ! DB_HASH_NELEM */
1935 # endif /* DB_VERSION_MAJOR < 2 */
1938 bt_map_open(map, mode)
1942 # if DB_VERSION_MAJOR < 2
1944 # endif /* DB_VERSION_MAJOR < 2 */
1945 # if DB_VERSION_MAJOR == 2
1947 # endif /* DB_VERSION_MAJOR == 2 */
1948 # if DB_VERSION_MAJOR > 2
1949 void *btinfo = NULL;
1950 # endif /* DB_VERSION_MAJOR > 2 */
1953 sm_dprintf("bt_map_open(%s, %s, %d)\n",
1954 map->map_mname, map->map_file, mode);
1956 # if DB_VERSION_MAJOR < 3
1957 memset(&btinfo, '\0', sizeof(btinfo));
1958 # ifdef DB_CACHE_SIZE
1959 btinfo.db_cachesize = DB_CACHE_SIZE;
1960 # endif /* DB_CACHE_SIZE */
1961 # endif /* DB_VERSION_MAJOR < 3 */
1963 return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1967 hash_map_open(map, mode)
1971 # if DB_VERSION_MAJOR < 2
1973 # endif /* DB_VERSION_MAJOR < 2 */
1974 # if DB_VERSION_MAJOR == 2
1976 # endif /* DB_VERSION_MAJOR == 2 */
1977 # if DB_VERSION_MAJOR > 2
1979 # endif /* DB_VERSION_MAJOR > 2 */
1982 sm_dprintf("hash_map_open(%s, %s, %d)\n",
1983 map->map_mname, map->map_file, mode);
1985 # if DB_VERSION_MAJOR < 3
1986 memset(&hinfo, '\0', sizeof(hinfo));
1987 # ifdef DB_HASH_NELEM
1988 hinfo.h_nelem = DB_HASH_NELEM;
1989 # endif /* DB_HASH_NELEM */
1990 # ifdef DB_CACHE_SIZE
1991 hinfo.db_cachesize = DB_CACHE_SIZE;
1992 # endif /* DB_CACHE_SIZE */
1993 # endif /* DB_VERSION_MAJOR < 3 */
1995 return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1999 db_map_open(map, mode, mapclassname, dbtype, openinfo)
2004 # if DB_VERSION_MAJOR < 2
2005 const void *openinfo;
2006 # endif /* DB_VERSION_MAJOR < 2 */
2007 # if DB_VERSION_MAJOR == 2
2009 # endif /* DB_VERSION_MAJOR == 2 */
2010 # if DB_VERSION_MAJOR > 2
2012 # endif /* DB_VERSION_MAJOR > 2 */
2017 int smode = S_IREAD;
2022 char buf[MAXPATHLEN];
2024 /* do initial file and directory checks */
2025 if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
2028 if (!bitset(MF_OPTIONAL, map->map_mflags))
2029 syserr("map \"%s\": map file %s name too long",
2030 map->map_mname, map->map_file);
2034 if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
2036 if (sm_strlcat(buf, ".db", sizeof(buf)) >= sizeof(buf))
2039 if (!bitset(MF_OPTIONAL, map->map_mflags))
2040 syserr("map \"%s\": map file %s name too long",
2041 map->map_mname, map->map_file);
2049 sff = SFF_ROOTOK|SFF_REGONLY;
2053 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
2055 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
2061 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
2064 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
2065 sff |= SFF_SAFEDIRPATH;
2066 i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
2070 char *prob = "unsafe";
2072 /* cannot open this map */
2076 sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
2078 if (!bitset(MF_OPTIONAL, map->map_mflags))
2079 syserr("%s map \"%s\": %s map file %s",
2080 mapclassname, map->map_mname, prob, buf);
2083 if (st.st_mode == ST_MODE_NOFILE)
2084 omode |= O_CREAT|O_EXCL;
2086 map->map_lockfd = -1;
2090 omode |= O_TRUNC|O_EXLOCK;
2093 # else /* LOCK_ON_OPEN */
2095 ** Pre-lock the file to avoid race conditions. In particular,
2096 ** since dbopen returns NULL if the file is zero length, we
2097 ** must have a locked instance around the dbopen.
2100 fd = open(buf, omode, DBMMODE);
2103 if (!bitset(MF_OPTIONAL, map->map_mflags))
2104 syserr("db_map_open: cannot pre-open database %s", buf);
2108 /* make sure no baddies slipped in just before the open... */
2109 if (filechanged(buf, fd, &st))
2114 syserr("db_map_open(%s): file changed after pre-open", buf);
2118 /* if new file, get the "before" bits for later filechanged check */
2119 if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
2124 syserr("db_map_open(%s): cannot fstat pre-opened file",
2129 /* actually lock the pre-opened file */
2130 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
2131 syserr("db_map_open: cannot lock %s", buf);
2133 /* set up mode bits for dbopen */
2136 omode &= ~(O_EXCL|O_CREAT);
2137 # endif /* LOCK_ON_OPEN */
2139 # if DB_VERSION_MAJOR < 2
2140 db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
2141 # else /* DB_VERSION_MAJOR < 2 */
2144 # if DB_VERSION_MAJOR > 2
2146 # endif /* DB_VERSION_MAJOR > 2 */
2148 if (mode == O_RDONLY)
2150 if (bitset(O_CREAT, omode))
2152 if (bitset(O_TRUNC, omode))
2153 flags |= DB_TRUNCATE;
2154 SM_DB_FLAG_ADD(flags);
2156 # if DB_VERSION_MAJOR > 2
2157 ret = db_create(&db, NULL, 0);
2158 # ifdef DB_CACHE_SIZE
2159 if (ret == 0 && db != NULL)
2161 ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
2164 (void) db->close(db, 0);
2168 # endif /* DB_CACHE_SIZE */
2169 # ifdef DB_HASH_NELEM
2170 if (dbtype == DB_HASH && ret == 0 && db != NULL)
2172 ret = db->set_h_nelem(db, DB_HASH_NELEM);
2175 (void) db->close(db, 0);
2179 # endif /* DB_HASH_NELEM */
2180 if (ret == 0 && db != NULL)
2183 DBTXN /* transaction for DB 4.1 */
2184 buf, NULL, dbtype, flags, DBMMODE);
2187 #ifdef DB_OLD_VERSION
2188 if (ret == DB_OLD_VERSION)
2190 #endif /* DB_OLD_VERSION */
2191 (void) db->close(db, 0);
2196 # else /* DB_VERSION_MAJOR > 2 */
2197 errno = db_open(buf, dbtype, flags, DBMMODE,
2198 NULL, openinfo, &db);
2199 # endif /* DB_VERSION_MAJOR > 2 */
2201 # endif /* DB_VERSION_MAJOR < 2 */
2206 map->map_lockfd = fd;
2209 # endif /* !LOCK_ON_OPEN */
2213 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2214 aliaswait(map, ".db", false))
2217 if (map->map_lockfd >= 0)
2218 (void) close(map->map_lockfd);
2219 # endif /* !LOCK_ON_OPEN */
2221 if (!bitset(MF_OPTIONAL, map->map_mflags))
2222 syserr("Cannot open %s database %s",
2227 # if DB_VERSION_MAJOR < 2
2229 # else /* DB_VERSION_MAJOR < 2 */
2231 errno = db->fd(db, &fd);
2232 # endif /* DB_VERSION_MAJOR < 2 */
2233 if (filechanged(buf, fd, &st))
2236 # if DB_VERSION_MAJOR < 2
2237 (void) db->close(db);
2238 # else /* DB_VERSION_MAJOR < 2 */
2239 errno = db->close(db, 0);
2240 # endif /* DB_VERSION_MAJOR < 2 */
2242 if (map->map_lockfd >= 0)
2243 (void) close(map->map_lockfd);
2244 # endif /* !LOCK_ON_OPEN */
2246 syserr("db_map_open(%s): file changed after open", buf);
2251 map->map_mflags |= MF_LOCKED;
2253 if (fd >= 0 && mode == O_RDONLY)
2255 (void) lockfile(fd, buf, NULL, LOCK_UN);
2257 # endif /* LOCK_ON_OPEN */
2259 /* try to make sure that at least the database header is on disk */
2262 (void) db->sync(db, 0);
2263 if (geteuid() == 0 && TrustedUid != 0)
2266 if (fchown(fd, TrustedUid, -1) < 0)
2270 sm_syslog(LOG_ALERT, NOQID,
2271 "ownership change on %s failed: %s",
2272 buf, sm_errstring(err));
2273 message("050 ownership change on %s failed: %s",
2274 buf, sm_errstring(err));
2276 # else /* HASFCHOWN */
2277 sm_syslog(LOG_ALERT, NOQID,
2278 "no fchown(): cannot change ownership on %s",
2280 message("050 no fchown(): cannot change ownership on %s",
2282 # endif /* HASFCHOWN */
2286 map->map_db2 = (ARBPTR_T) db;
2289 ** Need to set map_mtime before the call to aliaswait()
2290 ** as aliaswait() will call map_lookup() which requires
2291 ** map_mtime to be set
2294 if (fd >= 0 && fstat(fd, &st) >= 0)
2295 map->map_mtime = st.st_mtime;
2297 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2298 !aliaswait(map, ".db", true))
2305 ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2309 db_map_lookup(map, name, av, statp)
2316 register DB *db = (DB *) map->map_db2;
2322 char keybuf[MAXNAME + 1];
2323 char buf[MAXPATHLEN];
2325 memset(&key, '\0', sizeof(key));
2326 memset(&val, '\0', sizeof(val));
2329 sm_dprintf("db_map_lookup(%s, %s)\n",
2330 map->map_mname, name);
2332 if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
2335 if (!bitset(MF_OPTIONAL, map->map_mflags))
2336 syserr("map \"%s\": map file %s name too long",
2337 map->map_mname, map->map_file);
2341 if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
2344 key.size = strlen(name);
2345 if (key.size > sizeof(keybuf) - 1)
2346 key.size = sizeof(keybuf) - 1;
2348 memmove(keybuf, name, key.size);
2349 keybuf[key.size] = '\0';
2350 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2353 # if DB_VERSION_MAJOR < 2
2355 # else /* DB_VERSION_MAJOR < 2 */
2357 errno = db->fd(db, &fd);
2358 # endif /* DB_VERSION_MAJOR < 2 */
2359 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2360 (void) lockfile(fd, buf, ".db", LOCK_SH);
2361 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
2363 /* Reopen the database to sync the cache */
2364 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
2367 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2368 (void) lockfile(fd, buf, ".db", LOCK_UN);
2369 map->map_mflags |= MF_CLOSING;
2370 map->map_class->map_close(map);
2371 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
2372 if (map->map_class->map_open(map, omode))
2374 map->map_mflags |= MF_OPEN;
2375 map->map_pid = CurrentPid;
2376 if ((omode & O_ACCMODE) == O_RDWR)
2377 map->map_mflags |= MF_WRITABLE;
2378 db = (DB *) map->map_db2;
2383 if (!bitset(MF_OPTIONAL, map->map_mflags))
2385 extern MAPCLASS BogusMapClass;
2387 *statp = EX_TEMPFAIL;
2388 map->map_orgclass = map->map_class;
2389 map->map_class = &BogusMapClass;
2390 map->map_mflags |= MF_OPEN;
2391 map->map_pid = CurrentPid;
2392 syserr("Cannot reopen DB database %s",
2400 if (bitset(MF_TRY0NULL, map->map_mflags))
2402 # if DB_VERSION_MAJOR < 2
2403 st = db->get(db, &key, &val, 0);
2404 # else /* DB_VERSION_MAJOR < 2 */
2405 errno = db->get(db, NULL, &key, &val, 0);
2421 # endif /* DB_VERSION_MAJOR < 2 */
2423 map->map_mflags &= ~MF_TRY1NULL;
2425 if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
2428 # if DB_VERSION_MAJOR < 2
2429 st = db->get(db, &key, &val, 0);
2430 # else /* DB_VERSION_MAJOR < 2 */
2431 errno = db->get(db, NULL, &key, &val, 0);
2447 # endif /* DB_VERSION_MAJOR < 2 */
2449 map->map_mflags &= ~MF_TRY0NULL;
2452 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2453 (void) lockfile(fd, buf, ".db", LOCK_UN);
2458 syserr("db_map_lookup: get (%s)", name);
2461 if (bitset(MF_MATCHONLY, map->map_mflags))
2462 return map_rewrite(map, name, strlen(name), NULL);
2464 return map_rewrite(map, val.data, val.size, av);
2469 ** DB_MAP_STORE -- store a datum in the NEWDB database
2473 db_map_store(map, lhs, rhs)
2481 register DB *db = map->map_db2;
2482 char keybuf[MAXNAME + 1];
2484 memset(&key, '\0', sizeof(key));
2485 memset(&data, '\0', sizeof(data));
2488 sm_dprintf("db_map_store(%s, %s, %s)\n",
2489 map->map_mname, lhs, rhs);
2491 key.size = strlen(lhs);
2493 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2495 if (key.size > sizeof(keybuf) - 1)
2496 key.size = sizeof(keybuf) - 1;
2497 memmove(keybuf, key.data, key.size);
2498 keybuf[key.size] = '\0';
2503 data.size = strlen(rhs);
2506 if (bitset(MF_INCLNULL, map->map_mflags))
2512 # if DB_VERSION_MAJOR < 2
2513 status = db->put(db, &key, &data, R_NOOVERWRITE);
2514 # else /* DB_VERSION_MAJOR < 2 */
2515 errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
2530 # endif /* DB_VERSION_MAJOR < 2 */
2533 if (!bitset(MF_APPEND, map->map_mflags))
2534 message("050 Warning: duplicate alias name %s", lhs);
2537 static char *buf = NULL;
2538 static int bufsiz = 0;
2541 memset(&old, '\0', sizeof(old));
2543 old.data = db_map_lookup(map, key.data,
2544 (char **) NULL, &status);
2545 if (old.data != NULL)
2547 old.size = strlen(old.data);
2548 if (data.size + old.size + 2 > (size_t) bufsiz)
2552 bufsiz = data.size + old.size + 2;
2553 buf = sm_pmalloc_x(bufsiz);
2555 (void) sm_strlcpyn(buf, bufsiz, 3,
2556 (char *) data.data, ",",
2558 data.size = data.size + old.size + 1;
2561 sm_dprintf("db_map_store append=%s\n",
2562 (char *) data.data);
2565 # if DB_VERSION_MAJOR < 2
2566 status = db->put(db, &key, &data, 0);
2567 # else /* DB_VERSION_MAJOR < 2 */
2568 status = errno = db->put(db, NULL, &key, &data, 0);
2569 # endif /* DB_VERSION_MAJOR < 2 */
2572 syserr("readaliases: db put (%s)", lhs);
2577 ** DB_MAP_CLOSE -- add distinguished entries and close the database
2584 register DB *db = map->map_db2;
2587 sm_dprintf("db_map_close(%s, %s, %lx)\n",
2588 map->map_mname, map->map_file, map->map_mflags);
2590 if (bitset(MF_WRITABLE, map->map_mflags))
2592 /* write out the distinguished alias */
2593 db_map_store(map, "@", "@");
2596 (void) db->sync(db, 0);
2599 if (map->map_lockfd >= 0)
2600 (void) close(map->map_lockfd);
2601 # endif /* !LOCK_ON_OPEN */
2603 # if DB_VERSION_MAJOR < 2
2604 if (db->close(db) != 0)
2605 # else /* DB_VERSION_MAJOR < 2 */
2607 ** Berkeley DB can use internal shared memory
2608 ** locking for its memory pool. Closing a map
2609 ** opened by another process will interfere
2610 ** with the shared memory and locks of the parent
2611 ** process leaving things in a bad state.
2615 ** If this map was not opened by the current
2616 ** process, do not close the map but recover
2617 ** the file descriptor.
2620 if (map->map_pid != CurrentPid)
2624 errno = db->fd(db, &fd);
2630 if ((errno = db->close(db, 0)) != 0)
2631 # endif /* DB_VERSION_MAJOR < 2 */
2632 syserr("db_map_close(%s, %s, %lx): db close failure",
2633 map->map_mname, map->map_file, map->map_mflags);
2643 # define YPERR_BUSY 16
2644 # endif /* ! YPERR_BUSY */
2647 ** NIS_MAP_OPEN -- open DBM map
2651 nis_map_open(map, mode)
2661 sm_dprintf("nis_map_open(%s, %s, %d)\n",
2662 map->map_mname, map->map_file, mode);
2665 if (mode != O_RDONLY)
2667 /* issue a pseudo-error message */
2668 errno = SM_EMAPCANTWRITE;
2672 p = strchr(map->map_file, '@');
2677 map->map_domain = p;
2680 if (*map->map_file == '\0')
2681 map->map_file = "mail.aliases";
2683 if (map->map_domain == NULL)
2685 yperr = yp_get_default_domain(&map->map_domain);
2688 if (!bitset(MF_OPTIONAL, map->map_mflags))
2689 syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2695 /* check to see if this map actually exists */
2697 yperr = yp_match(map->map_domain, map->map_file, "@", 1,
2700 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2701 map->map_domain, map->map_file, yperr_string(yperr));
2705 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
2708 ** We ought to be calling aliaswait() here if this is an
2709 ** alias file, but powerful HP-UX NIS servers apparently
2710 ** don't insert the @:@ token into the alias map when it
2711 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
2715 if (!bitset(MF_ALIAS, map->map_mflags) ||
2716 aliaswait(map, NULL, true))
2721 if (!bitset(MF_OPTIONAL, map->map_mflags))
2723 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2724 map->map_file, map->map_domain, yperr_string(yperr));
2732 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map
2737 nis_map_lookup(map, name, av, statp)
2747 char keybuf[MAXNAME + 1];
2748 char *SM_NONVOLATILE result = NULL;
2751 sm_dprintf("nis_map_lookup(%s, %s)\n",
2752 map->map_mname, name);
2754 buflen = strlen(name);
2755 if (buflen > sizeof(keybuf) - 1)
2756 buflen = sizeof(keybuf) - 1;
2757 memmove(keybuf, name, buflen);
2758 keybuf[buflen] = '\0';
2759 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2763 if (bitset(MF_TRY0NULL, map->map_mflags))
2765 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2768 map->map_mflags &= ~MF_TRY1NULL;
2770 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2774 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2777 map->map_mflags &= ~MF_TRY0NULL;
2781 if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2782 map->map_mflags &= ~(MF_VALID|MF_OPEN);
2788 if (bitset(MF_MATCHONLY, map->map_mflags))
2789 result = map_rewrite(map, name, strlen(name), NULL);
2791 result = map_rewrite(map, vp, vsize, av);
2801 ** NIS_GETCANONNAME -- look up canonical name in NIS
2805 nis_getcanonname(name, hbsize, statp)
2814 static bool try0null = true;
2815 static bool try1null = true;
2816 static char *yp_domain = NULL;
2817 char host_record[MAXLINE];
2819 char nbuf[MAXNAME + 1];
2822 sm_dprintf("nis_getcanonname(%s)\n", name);
2824 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
2826 *statp = EX_UNAVAILABLE;
2829 (void) shorten_hostname(nbuf);
2830 keylen = strlen(nbuf);
2832 if (yp_domain == NULL)
2833 (void) yp_get_default_domain(&yp_domain);
2839 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2844 if (yperr == YPERR_KEY && try1null)
2848 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2855 if (yperr == YPERR_KEY)
2857 else if (yperr == YPERR_BUSY)
2858 *statp = EX_TEMPFAIL;
2860 *statp = EX_UNAVAILABLE;
2865 (void) sm_strlcpy(host_record, vp, sizeof(host_record));
2868 sm_dprintf("got record `%s'\n", host_record);
2869 vp = strpbrk(host_record, "#\n");
2872 if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof(cbuf)))
2874 /* this should not happen, but.... */
2878 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
2880 *statp = EX_UNAVAILABLE;
2891 ** This code donated by Sun Microsystems.
2896 # undef NIS /* symbol conflict in nis.h */
2897 # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2898 # include <rpcsvc/nis.h>
2899 # include <rpcsvc/nislib.h>
2900 # ifndef NIS_TABLE_OBJ
2901 # define NIS_TABLE_OBJ TABLE_OBJ
2902 # endif /* NIS_TABLE_OBJ */
2904 # define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2905 # define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2906 # define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2907 # define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
2910 ** NISPLUS_MAP_OPEN -- open nisplus table
2914 nisplus_map_open(map, mode)
2918 nis_result *res = NULL;
2919 int retry_cnt, max_col, i;
2920 char qbuf[MAXLINE + NIS_MAXNAMELEN];
2923 sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2924 map->map_mname, map->map_file, mode);
2927 if (mode != O_RDONLY)
2933 if (*map->map_file == '\0')
2934 map->map_file = "mail_aliases.org_dir";
2936 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2938 /* set default NISPLUS Domain to $m */
2939 map->map_domain = newstr(nisplus_default_domain());
2941 sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2942 map->map_file, map->map_domain);
2944 if (!PARTIAL_NAME(map->map_file))
2946 map->map_domain = newstr("");
2947 (void) sm_strlcpy(qbuf, map->map_file, sizeof(qbuf));
2951 /* check to see if this map actually exists */
2952 (void) sm_strlcpyn(qbuf, sizeof(qbuf), 3,
2953 map->map_file, ".", map->map_domain);
2957 while (res == NULL || res->status != NIS_SUCCESS)
2959 res = nis_lookup(qbuf, FOLLOW_LINKS);
2960 switch (res->status)
2967 case NIS_NAMEUNREACHABLE:
2968 if (retry_cnt++ > 4)
2973 /* try not to overwhelm hosed server */
2977 default: /* all other nisplus errors */
2979 if (!bitset(MF_OPTIONAL, map->map_mflags))
2980 syserr("451 4.3.5 Cannot find table %s.%s: %s",
2981 map->map_file, map->map_domain,
2982 nis_sperrno(res->status));
2989 if (NIS_RES_NUMOBJ(res) != 1 ||
2990 (NIS_RES_OBJECT(res)->zo_data.zo_type != NIS_TABLE_OBJ))
2993 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
2995 if (!bitset(MF_OPTIONAL, map->map_mflags))
2996 syserr("451 4.3.5 %s.%s: %s is not a table",
2997 map->map_file, map->map_domain,
2998 nis_sperrno(res->status));
3003 /* default key column is column 0 */
3004 if (map->map_keycolnm == NULL)
3005 map->map_keycolnm = newstr(COL_NAME(res,0));
3007 max_col = COL_MAX(res);
3009 /* verify the key column exist */
3010 for (i = 0; i < max_col; i++)
3012 if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
3018 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
3019 map->map_file, map->map_keycolnm);
3024 /* default value column is the last column */
3025 if (map->map_valcolnm == NULL)
3027 map->map_valcolno = max_col - 1;
3031 for (i = 0; i< max_col; i++)
3033 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
3035 map->map_valcolno = i;
3041 sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
3042 map->map_file, map->map_keycolnm);
3049 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
3053 nisplus_map_lookup(map, name, av, statp)
3063 char search_key[MAXNAME + 4];
3064 char qbuf[MAXLINE + NIS_MAXNAMELEN];
3068 sm_dprintf("nisplus_map_lookup(%s, %s)\n",
3069 map->map_mname, name);
3071 if (!bitset(MF_OPEN, map->map_mflags))
3073 if (nisplus_map_open(map, O_RDONLY))
3075 map->map_mflags |= MF_OPEN;
3076 map->map_pid = CurrentPid;
3080 *statp = EX_UNAVAILABLE;
3086 ** Copy the name to the key buffer, escaping double quote characters
3087 ** by doubling them and quoting "]" and "," to avoid having the
3088 ** NIS+ parser choke on them.
3091 skleft = sizeof(search_key) - 4;
3093 for (p = name; *p != '\0' && skleft > 0; p++)
3099 /* quote the character */
3107 /* double the quote */
3119 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3120 makelower(search_key);
3122 /* construct the query */
3123 if (PARTIAL_NAME(map->map_file))
3124 (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s.%s",
3125 map->map_keycolnm, search_key, map->map_file,
3128 (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s",
3129 map->map_keycolnm, search_key, map->map_file);
3132 sm_dprintf("qbuf=%s\n", qbuf);
3133 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
3134 if (result->status == NIS_SUCCESS)
3139 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3142 sm_syslog(LOG_WARNING, CurEnv->e_id,
3143 "%s: lookup error, expected 1 entry, got %d",
3144 map->map_file, count);
3146 /* ignore second entry */
3148 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3152 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
3153 /* set the length of the result */
3158 sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3160 if (bitset(MF_MATCHONLY, map->map_mflags))
3161 str = map_rewrite(map, name, strlen(name), NULL);
3163 str = map_rewrite(map, p, vsize, av);
3164 nis_freeresult(result);
3170 if (result->status == NIS_NOTFOUND)
3171 *statp = EX_NOTFOUND;
3172 else if (result->status == NIS_TRYAGAIN)
3173 *statp = EX_TEMPFAIL;
3176 *statp = EX_UNAVAILABLE;
3177 map->map_mflags &= ~(MF_VALID|MF_OPEN);
3181 sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
3182 nis_freeresult(result);
3189 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3193 nisplus_getcanonname(name, hbsize, statp)
3202 char nbuf[MAXNAME + 1];
3203 char qbuf[MAXLINE + NIS_MAXNAMELEN];
3205 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
3207 *statp = EX_UNAVAILABLE;
3210 (void) shorten_hostname(nbuf);
3212 p = strchr(nbuf, '.');
3216 (void) sm_snprintf(qbuf, sizeof(qbuf),
3217 "[name=%s],hosts.org_dir", nbuf);
3219 else if (p[1] != '\0')
3221 /* multi token -- take only first token in nbuf */
3223 (void) sm_snprintf(qbuf, sizeof(qbuf),
3224 "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
3233 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3236 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
3239 if (result->status == NIS_SUCCESS)
3244 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3247 sm_syslog(LOG_WARNING, CurEnv->e_id,
3248 "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3251 /* ignore second entry */
3253 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3258 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3259 name, (NIS_RES_OBJECT(result))->zo_domain);
3262 vp = ((NIS_RES_OBJECT(result))->EN_col(0));
3265 sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3267 if (strchr(vp, '.') != NULL)
3273 domain = macvalue('m', CurEnv);
3277 if (hbsize > vsize + (int) strlen(domain) + 1)
3279 if (domain[0] == '\0')
3280 (void) sm_strlcpy(name, vp, hbsize);
3282 (void) sm_snprintf(name, hbsize,
3283 "%s.%s", vp, domain);
3288 nis_freeresult(result);
3293 if (result->status == NIS_NOTFOUND)
3295 else if (result->status == NIS_TRYAGAIN)
3296 *statp = EX_TEMPFAIL;
3298 *statp = EX_UNAVAILABLE;
3301 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3302 name, result->status, *statp);
3303 nis_freeresult(result);
3308 nisplus_default_domain()
3310 static char default_domain[MAXNAME + 1] = "";
3313 if (default_domain[0] != '\0')
3314 return default_domain;
3316 p = nis_local_directory();
3317 (void) sm_strlcpy(default_domain, p, sizeof(default_domain));
3318 return default_domain;
3321 #endif /* NISPLUS */
3327 ** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3330 #if defined(LDAPMAP) || defined(PH_MAP)
3333 # define ph_map_dequote ldapmap_dequote
3334 # endif /* PH_MAP */
3336 static char *ldapmap_dequote __P((char *));
3339 ldapmap_dequote(str)
3351 /* Should probably swallow initial whitespace here */
3356 while (*p != '"' && *p != '\0')
3362 #endif /* defined(LDAPMAP) || defined(PH_MAP) */
3366 static SM_LDAP_STRUCT *LDAPDefaults = NULL;
3369 ** LDAPMAP_OPEN -- open LDAP map
3371 ** Connect to the LDAP server. Re-use existing connections since a
3372 ** single server connection to a host (with the same host, port,
3373 ** bind DN, and secret) can answer queries for multiple maps.
3377 ldapmap_open(map, mode)
3381 SM_LDAP_STRUCT *lmap;
3386 sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
3388 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
3389 HASLDAPGETALIASBYNAME
3390 if (VendorCode == VENDOR_SUN &&
3391 strcmp(map->map_mname, "aliases.ldap") == 0)
3395 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
3399 /* sendmail doesn't have the ability to write to LDAP (yet) */
3400 if (mode != O_RDONLY)
3402 /* issue a pseudo-error message */
3403 errno = SM_EMAPCANTWRITE;
3407 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3409 s = ldapmap_findconn(lmap);
3410 if (s->s_lmap != NULL)
3412 /* Already have a connection open to this LDAP server */
3413 lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
3414 lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
3416 /* Add this map as head of linked list */
3417 lmap->ldap_next = s->s_lmap;
3421 sm_dprintf("using cached connection\n");
3426 sm_dprintf("opening new connection\n");
3428 if (lmap->ldap_host != NULL)
3429 id = lmap->ldap_host;
3430 else if (lmap->ldap_uri != NULL)
3431 id = lmap->ldap_uri;
3437 extern MAPCLASS NullMapClass;
3439 /* debug mode: don't actually open an LDAP connection */
3440 map->map_orgclass = map->map_class;
3441 map->map_class = &NullMapClass;
3442 map->map_mflags |= MF_OPEN;
3443 map->map_pid = CurrentPid;
3447 /* No connection yet, connect */
3448 if (!sm_ldap_start(map->map_mname, lmap))
3450 if (errno == ETIMEDOUT)
3453 sm_syslog(LOG_NOTICE, CurEnv->e_id,
3454 "timeout connecting to LDAP server %.100s",
3458 if (!bitset(MF_OPTIONAL, map->map_mflags))
3460 if (bitset(MF_NODEFER, map->map_mflags))
3462 syserr("%s failed to %s in map %s",
3464 "ldap_init/ldap_bind",
3465 # else /* USE_LDAP_INIT */
3467 # endif /* USE_LDAP_INIT */
3468 id, map->map_mname);
3472 syserr("451 4.3.5 %s failed to %s in map %s",
3474 "ldap_init/ldap_bind",
3475 # else /* USE_LDAP_INIT */
3477 # endif /* USE_LDAP_INIT */
3478 id, map->map_mname);
3484 /* Save connection for reuse */
3490 ** LDAPMAP_CLOSE -- close ldap map
3497 SM_LDAP_STRUCT *lmap;
3501 sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
3503 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3505 /* Check if already closed */
3506 if (lmap->ldap_ld == NULL)
3509 /* Close the LDAP connection */
3510 sm_ldap_close(lmap);
3512 /* Mark all the maps that share the connection as closed */
3513 s = ldapmap_findconn(lmap);
3515 while (s->s_lmap != NULL)
3517 MAP *smap = s->s_lmap;
3519 if (tTd(38, 2) && smap != map)
3520 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3521 map->map_mname, smap->map_mname);
3522 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
3523 lmap = (SM_LDAP_STRUCT *) smap->map_db1;
3524 lmap->ldap_ld = NULL;
3525 s->s_lmap = lmap->ldap_next;
3526 lmap->ldap_next = NULL;
3532 ** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3533 ** This only makes sense at Stanford University.
3546 if (isascii(*p) && (islower(*p) || isdigit(*p)))
3551 else if (isascii(*p) && isupper(*p))
3553 *p_last = tolower(*p);
3558 if (*p_last != '\0')
3562 # define SM_CONVERT_ID(str) sunet_id_hash(str)
3563 # else /* SUNET_ID */
3564 # define SM_CONVERT_ID(str) makelower(str)
3565 # endif /* SUNET_ID */
3568 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3572 ldapmap_lookup(map, name, av, statp)
3585 char *result = NULL;
3587 SM_LDAP_STRUCT *lmap = NULL;
3588 char *argv[SM_LDAP_ARGS];
3589 char keybuf[MAXKEY];
3590 #if SM_LDAP_ARGS != MAX_MAP_ARGS
3591 # ERROR _SM_LDAP_ARGS must be the same as _MAX_MAP_ARGS
3592 #endif /* SM_LDAP_ARGS != MAX_MAP_ARGS */
3594 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
3595 HASLDAPGETALIASBYNAME
3596 if (VendorCode == VENDOR_SUN &&
3597 strcmp(map->map_mname, "aliases.ldap") == 0)
3600 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
3601 extern char *__getldapaliasbyname();
3604 answer = __getldapaliasbyname(name, &rc);
3606 char answer[MAXNAME + 1];
3608 rc = __getldapaliasbyname(name, answer, sizeof(answer));
3613 sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n",
3615 *statp = EX_NOTFOUND;
3620 sm_dprintf("getldapaliasbyname(%.100s) => %s\n", name,
3622 if (bitset(MF_MATCHONLY, map->map_mflags))
3623 result = map_rewrite(map, name, strlen(name), NULL);
3625 result = map_rewrite(map, answer, strlen(answer), av);
3626 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
3631 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
3633 /* Get ldap struct pointer from map */
3634 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3635 sm_ldap_setopts(lmap->ldap_ld, lmap);
3637 if (lmap->ldap_multi_args)
3639 SM_REQUIRE(av != NULL);
3640 memset(argv, '\0', sizeof(argv));
3641 for (i = 0; i < SM_LDAP_ARGS && av[i] != NULL; i++)
3643 argv[i] = sm_strdup(av[i]);
3644 if (argv[i] == NULL)
3649 for (j = 0; j < i && argv[j] != NULL; j++)
3651 *statp = EX_TEMPFAIL;
3656 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3657 SM_CONVERT_ID(av[i]);
3662 (void) sm_strlcpy(keybuf, name, sizeof(keybuf));
3664 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3665 SM_CONVERT_ID(keybuf);
3670 if (lmap->ldap_multi_args)
3672 sm_dprintf("ldapmap_lookup(%s, argv)\n",
3674 for (i = 0; i < SM_LDAP_ARGS; i++)
3676 sm_dprintf(" argv[%d] = %s\n", i,
3677 argv[i] == NULL ? "NULL" : argv[i]);
3682 sm_dprintf("ldapmap_lookup(%s, %s)\n",
3683 map->map_mname, name);
3687 if (lmap->ldap_multi_args)
3689 msgid = sm_ldap_search_m(lmap, argv);
3691 /* free the argv array and its content, no longer needed */
3692 for (i = 0; i < SM_LDAP_ARGS && argv[i] != NULL; i++)
3696 msgid = sm_ldap_search(lmap, keybuf);
3697 if (msgid == SM_LDAP_ERR)
3699 errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
3701 if (!bitset(MF_OPTIONAL, map->map_mflags))
3704 ** Do not include keybuf as this error may be shown
3708 if (bitset(MF_NODEFER, map->map_mflags))
3709 syserr("Error in ldap_search in map %s",
3712 syserr("451 4.3.5 Error in ldap_search in map %s",
3715 *statp = EX_TEMPFAIL;
3716 switch (save_errno - E_LDAPBASE)
3718 # ifdef LDAP_SERVER_DOWN
3719 case LDAP_SERVER_DOWN:
3720 # endif /* LDAP_SERVER_DOWN */
3722 case LDAP_UNAVAILABLE:
3723 /* server disappeared, try reopen on next search */
3730 #if SM_LDAP_ERROR_ON_MISSING_ARGS
3731 else if (msgid == SM_LDAP_ERR_ARG_MISS)
3733 if (bitset(MF_NODEFER, map->map_mflags))
3734 syserr("Error in ldap_search in map %s, too few arguments",
3737 syserr("554 5.3.5 Error in ldap_search in map %s, too few arguments",
3742 #endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
3744 *statp = EX_NOTFOUND;
3748 if (bitset(MF_SINGLEMATCH, map->map_mflags))
3749 flags |= SM_LDAP_SINGLEMATCH;
3750 if (bitset(MF_MATCHONLY, map->map_mflags))
3751 flags |= SM_LDAP_MATCHONLY;
3752 # if _FFR_LDAP_SINGLEDN
3753 if (bitset(MF_SINGLEDN, map->map_mflags))
3754 flags |= SM_LDAP_SINGLEDN;
3755 # endif /* _FFR_LDAP_SINGLEDN */
3757 /* Create an rpool for search related memory usage */
3758 rpool = sm_rpool_new_x(NULL);
3761 *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
3762 rpool, &p, &plen, &psize, NULL);
3765 /* Copy result so rpool can be freed */
3766 if (*statp == EX_OK && p != NULL)
3768 sm_rpool_free(rpool);
3770 /* need to restart LDAP connection? */
3771 if (*statp == EX_RESTART)
3773 *statp = EX_TEMPFAIL;
3778 if (*statp != EX_OK && *statp != EX_NOTFOUND)
3780 if (!bitset(MF_OPTIONAL, map->map_mflags))
3782 if (bitset(MF_NODEFER, map->map_mflags))
3783 syserr("Error getting LDAP results, map=%s, name=%s",
3784 map->map_mname, name);
3786 syserr("451 4.3.5 Error getting LDAP results, map=%s, name=%s",
3787 map->map_mname, name);
3793 /* Did we match anything? */
3794 if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
3797 if (*statp == EX_OK)
3800 sm_syslog(LOG_INFO, CurEnv->e_id,
3801 "ldap=%s, %.100s=>%s", map->map_mname, name,
3802 vp == NULL ? "<NULL>" : vp);
3803 if (bitset(MF_MATCHONLY, map->map_mflags))
3804 result = map_rewrite(map, name, strlen(name), NULL);
3807 /* vp != NULL according to test above */
3808 result = map_rewrite(map, vp, strlen(vp), av);
3811 sm_free(vp); /* XXX */
3817 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server
3819 ** Cache LDAP connections based on the host, port, bind DN,
3820 ** secret, and PID so we don't have multiple connections open to
3821 ** the same server for different maps. Need a separate connection
3822 ** per PID since a parent process may close the map before the
3823 ** child is done with it.
3826 ** lmap -- LDAP map information
3829 ** Symbol table entry for the LDAP connection.
3833 ldapmap_findconn(lmap)
3834 SM_LDAP_STRUCT *lmap;
3839 STAB *SM_NONVOLATILE s = NULL;
3841 if (lmap->ldap_host != NULL)
3842 id = lmap->ldap_host;
3843 else if (lmap->ldap_uri != NULL)
3844 id = lmap->ldap_uri;
3848 format = "%s%c%d%c%d%c%s%c%s%d";
3849 nbuf = sm_stringf_x(format,
3856 (lmap->ldap_binddn == NULL ? ""
3857 : lmap->ldap_binddn),
3859 (lmap->ldap_secret == NULL ? ""
3860 : lmap->ldap_secret),
3863 s = stab(nbuf, ST_LMAP, ST_ENTER);
3870 ** LDAPMAP_PARSEARGS -- parse ldap map definition args.
3873 static struct lamvalues LDAPAuthMethods[] =
3875 { "none", LDAP_AUTH_NONE },
3876 { "simple", LDAP_AUTH_SIMPLE },
3877 # ifdef LDAP_AUTH_KRBV4
3878 { "krbv4", LDAP_AUTH_KRBV4 },
3879 # endif /* LDAP_AUTH_KRBV4 */
3883 static struct ladvalues LDAPAliasDereference[] =
3885 { "never", LDAP_DEREF_NEVER },
3886 { "always", LDAP_DEREF_ALWAYS },
3887 { "search", LDAP_DEREF_SEARCHING },
3888 { "find", LDAP_DEREF_FINDING },
3892 static struct lssvalues LDAPSearchScope[] =
3894 { "base", LDAP_SCOPE_BASE },
3895 { "one", LDAP_SCOPE_ONELEVEL },
3896 { "sub", LDAP_SCOPE_SUBTREE },
3901 ldapmap_parseargs(map, args)
3905 bool secretread = true;
3906 bool attrssetup = false;
3908 register char *p = args;
3909 SM_LDAP_STRUCT *lmap;
3910 struct lamvalues *lam;
3911 struct ladvalues *lad;
3912 struct lssvalues *lss;
3913 char ldapfilt[MAXLINE];
3914 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
3916 /* Get ldap struct pointer from map */
3917 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3919 /* Check if setting the initial LDAP defaults */
3920 if (lmap == NULL || lmap != LDAPDefaults)
3922 /* We need to alloc an SM_LDAP_STRUCT struct */
3923 lmap = (SM_LDAP_STRUCT *) xalloc(sizeof(*lmap));
3924 if (LDAPDefaults == NULL)
3925 sm_ldap_clear(lmap);
3927 STRUCTCOPY(*LDAPDefaults, *lmap);
3930 /* there is no check whether there is really an argument */
3931 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
3932 map->map_spacesub = SpaceSub; /* default value */
3934 /* Check if setting up an alias or file class LDAP map */
3935 if (bitset(MF_ALIAS, map->map_mflags))
3937 /* Comma separate if used as an alias file */
3938 map->map_coldelim = ',';
3943 char jbuf[MAXHOSTNAMELEN];
3944 char lcbuf[MAXLINE];
3947 expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
3948 if (jbuf[0] == '\0')
3950 (void) sm_strlcpy(jbuf, "localhost",
3954 lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
3959 expand(lc, lcbuf, sizeof(lcbuf), CurEnv);
3963 n = sm_snprintf(ldapfilt, sizeof(ldapfilt),
3964 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
3966 if (n >= sizeof(ldapfilt))
3968 syserr("%s: Default LDAP string too long",
3973 /* default args for an alias LDAP entry */
3974 lmap->ldap_filter = ldapfilt;
3975 lmap->ldap_attr[0] = "objectClass";
3976 lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
3977 lmap->ldap_attr_needobjclass[0] = NULL;
3978 lmap->ldap_attr[1] = "sendmailMTAAliasValue";
3979 lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
3980 lmap->ldap_attr_needobjclass[1] = NULL;
3981 lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
3982 lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
3983 lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
3984 lmap->ldap_attr[3] = "sendmailMTAAliasURL";
3985 lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
3986 lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
3987 lmap->ldap_attr[4] = NULL;
3988 lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
3989 lmap->ldap_attr_needobjclass[4] = NULL;
3993 else if (bitset(MF_FILECLASS, map->map_mflags))
3995 /* Space separate if used as a file class file */
3996 map->map_coldelim = ' ';
3999 # if _FFR_LDAP_NETWORK_TIMEOUT
4000 lmap->ldap_networktmo = 120;
4001 # endif /* _FFR_LDAP_NETWORK_TIMEOUT */
4005 while (isascii(*p) && isspace(*p))
4012 map->map_mflags |= MF_APPEND;
4020 map->map_mflags |= MF_DEFER;
4024 map->map_mflags |= MF_NOFOLDCASE;
4028 map->map_mflags |= MF_MATCHONLY;
4032 map->map_mflags |= MF_INCLNULL;
4033 map->map_mflags &= ~MF_TRY0NULL;
4037 map->map_mflags &= ~MF_TRY1NULL;
4041 map->map_mflags |= MF_OPTIONAL;
4045 map->map_mflags |= MF_KEEPQUOTES;
4049 map->map_spacesub = *++p;
4053 map->map_tapp = ++p;
4057 map->map_mflags |= MF_NODEFER;
4062 map->map_coldelim = *p;
4068 map->map_coldelim = '\n';
4072 map->map_coldelim = '\t';
4076 map->map_coldelim = '\\';
4081 /* Start of ldapmap specific args */
4083 map->map_mflags |= MF_SINGLEMATCH;
4086 # if _FFR_LDAP_SINGLEDN
4088 map->map_mflags |= MF_SINGLEDN;
4090 # endif /* _FFR_LDAP_SINGLEDN */
4092 case 'b': /* search base */
4093 while (isascii(*++p) && isspace(*p))
4095 lmap->ldap_base = p;
4098 # if _FFR_LDAP_NETWORK_TIMEOUT
4099 case 'c': /* network (connect) timeout */
4100 while (isascii(*++p) && isspace(*p))
4102 lmap->ldap_networktmo = atoi(p);
4104 # endif /* _FFR_LDAP_NETWORK_TIMEOUT */
4106 case 'd': /* Dn to bind to server as */
4107 while (isascii(*++p) && isspace(*p))
4109 lmap->ldap_binddn = p;
4112 case 'H': /* Use LDAP URI */
4114 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4117 # else /* !USE_LDAP_INIT */
4118 if (lmap->ldap_host != NULL)
4120 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4124 while (isascii(*++p) && isspace(*p))
4128 # endif /* !USE_LDAP_INIT */
4130 case 'h': /* ldap host */
4131 while (isascii(*++p) && isspace(*p))
4133 if (lmap->ldap_uri != NULL)
4135 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4139 lmap->ldap_host = p;
4143 lmap->ldap_multi_args = true;
4146 case 'k': /* search field */
4147 while (isascii(*++p) && isspace(*p))
4149 lmap->ldap_filter = p;
4152 case 'l': /* time limit */
4153 while (isascii(*++p) && isspace(*p))
4155 lmap->ldap_timelimit = atoi(p);
4156 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
4159 case 'M': /* Method for binding */
4160 while (isascii(*++p) && isspace(*p))
4163 if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
4166 for (lam = LDAPAuthMethods;
4167 lam != NULL && lam->lam_name != NULL; lam++)
4169 if (sm_strncasecmp(p, lam->lam_name,
4170 strlen(lam->lam_name)) == 0)
4173 if (lam->lam_name != NULL)
4174 lmap->ldap_method = lam->lam_code;
4177 /* bad config line */
4178 if (!bitset(MCF_OPTFILE,
4179 map->map_class->map_cflags))
4183 if ((ptr = strchr(p, ' ')) != NULL)
4185 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4194 case 'n': /* retrieve attribute names only */
4195 lmap->ldap_attrsonly = LDAPMAP_TRUE;
4199 ** This is a string that is dependent on the
4200 ** method used defined by 'M'.
4203 case 'P': /* Secret password for binding */
4204 while (isascii(*++p) && isspace(*p))
4206 lmap->ldap_secret = p;
4210 case 'p': /* ldap port */
4211 while (isascii(*++p) && isspace(*p))
4213 lmap->ldap_port = atoi(p);
4216 /* args stolen from ldapsearch.c */
4217 case 'R': /* don't auto chase referrals */
4218 # ifdef LDAP_REFERRALS
4219 lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
4220 # else /* LDAP_REFERRALS */
4221 syserr("compile with -DLDAP_REFERRALS for referral support");
4222 # endif /* LDAP_REFERRALS */
4225 case 'r': /* alias dereferencing */
4226 while (isascii(*++p) && isspace(*p))
4229 if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
4232 for (lad = LDAPAliasDereference;
4233 lad != NULL && lad->lad_name != NULL; lad++)
4235 if (sm_strncasecmp(p, lad->lad_name,
4236 strlen(lad->lad_name)) == 0)
4239 if (lad->lad_name != NULL)
4240 lmap->ldap_deref = lad->lad_code;
4243 /* bad config line */
4244 if (!bitset(MCF_OPTFILE,
4245 map->map_class->map_cflags))
4249 if ((ptr = strchr(p, ' ')) != NULL)
4251 syserr("Deref must be [never|always|search|find] (not %s) in map %s",
4260 case 's': /* search scope */
4261 while (isascii(*++p) && isspace(*p))
4264 if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
4267 for (lss = LDAPSearchScope;
4268 lss != NULL && lss->lss_name != NULL; lss++)
4270 if (sm_strncasecmp(p, lss->lss_name,
4271 strlen(lss->lss_name)) == 0)
4274 if (lss->lss_name != NULL)
4275 lmap->ldap_scope = lss->lss_code;
4278 /* bad config line */
4279 if (!bitset(MCF_OPTFILE,
4280 map->map_class->map_cflags))
4284 if ((ptr = strchr(p, ' ')) != NULL)
4286 syserr("Scope must be [base|one|sub] (not %s) in map %s",
4297 lmap->ldap_attrsep = *p;
4303 lmap->ldap_attrsep = '\n';
4307 lmap->ldap_attrsep = '\t';
4311 lmap->ldap_attrsep = '\\';
4316 case 'v': /* attr to return */
4317 while (isascii(*++p) && isspace(*p))
4319 lmap->ldap_attr[0] = p;
4320 lmap->ldap_attr[1] = NULL;
4324 /* -w should be for passwd, -P should be for version */
4325 while (isascii(*++p) && isspace(*p))
4327 lmap->ldap_version = atoi(p);
4328 # ifdef LDAP_VERSION_MAX
4329 if (lmap->ldap_version > LDAP_VERSION_MAX)
4331 syserr("LDAP version %d exceeds max of %d in map %s",
4332 lmap->ldap_version, LDAP_VERSION_MAX,
4336 # endif /* LDAP_VERSION_MAX */
4337 # ifdef LDAP_VERSION_MIN
4338 if (lmap->ldap_version < LDAP_VERSION_MIN)
4340 syserr("LDAP version %d is lower than min of %d in map %s",
4341 lmap->ldap_version, LDAP_VERSION_MIN,
4345 # endif /* LDAP_VERSION_MIN */
4349 while (isascii(*++p) && isspace(*p))
4351 lmap->ldap_sizelimit = atoi(p);
4355 syserr("Illegal option %c map %s", *p, map->map_mname);
4359 /* need to account for quoted strings here */
4360 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4364 while (*++p != '"' && *p != '\0')
4377 if (map->map_app != NULL)
4378 map->map_app = newstr(ldapmap_dequote(map->map_app));
4379 if (map->map_tapp != NULL)
4380 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
4383 ** We need to swallow up all the stuff into a struct
4384 ** and dump it into map->map_dbptr1
4387 if (lmap->ldap_host != NULL &&
4388 (LDAPDefaults == NULL ||
4389 LDAPDefaults == lmap ||
4390 LDAPDefaults->ldap_host != lmap->ldap_host))
4391 lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
4392 map->map_domain = lmap->ldap_host;
4394 if (lmap->ldap_uri != NULL &&
4395 (LDAPDefaults == NULL ||
4396 LDAPDefaults == lmap ||
4397 LDAPDefaults->ldap_uri != lmap->ldap_uri))
4398 lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
4399 map->map_domain = lmap->ldap_uri;
4401 if (lmap->ldap_binddn != NULL &&
4402 (LDAPDefaults == NULL ||
4403 LDAPDefaults == lmap ||
4404 LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
4405 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
4407 if (lmap->ldap_secret != NULL &&
4408 (LDAPDefaults == NULL ||
4409 LDAPDefaults == lmap ||
4410 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4413 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
4415 if (DontLockReadFiles)
4418 /* need to use method to map secret to passwd string */
4419 switch (lmap->ldap_method)
4421 case LDAP_AUTH_NONE:
4425 case LDAP_AUTH_SIMPLE:
4428 ** Secret is the name of a file with
4429 ** the first line as the password.
4432 /* Already read in the secret? */
4436 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
4440 syserr("LDAP map: cannot open secret %s",
4441 ldapmap_dequote(lmap->ldap_secret));
4444 lmap->ldap_secret = sfgets(m_tmp, sizeof(m_tmp),
4445 sfd, TimeOuts.to_fileopen,
4446 "ldapmap_parseargs");
4447 (void) sm_io_close(sfd, SM_TIME_DEFAULT);
4448 if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
4450 syserr("LDAP map: secret in %s too long",
4451 ldapmap_dequote(lmap->ldap_secret));
4454 if (lmap->ldap_secret != NULL &&
4458 if (m_tmp[strlen(m_tmp) - 1] == '\n')
4459 m_tmp[strlen(m_tmp) - 1] = '\0';
4461 lmap->ldap_secret = m_tmp;
4465 # ifdef LDAP_AUTH_KRBV4
4466 case LDAP_AUTH_KRBV4:
4469 ** Secret is where the ticket file is
4473 (void) sm_snprintf(m_tmp, sizeof(m_tmp),
4475 ldapmap_dequote(lmap->ldap_secret));
4476 lmap->ldap_secret = m_tmp;
4478 # endif /* LDAP_AUTH_KRBV4 */
4480 default: /* Should NEVER get here */
4481 syserr("LDAP map: Illegal value in lmap method");
4488 if (lmap->ldap_secret != NULL &&
4489 (LDAPDefaults == NULL ||
4490 LDAPDefaults == lmap ||
4491 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4492 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
4494 if (lmap->ldap_base != NULL &&
4495 (LDAPDefaults == NULL ||
4496 LDAPDefaults == lmap ||
4497 LDAPDefaults->ldap_base != lmap->ldap_base))
4498 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
4501 ** Save the server from extra work. If request is for a single
4502 ** match, tell the server to only return enough records to
4503 ** determine if there is a single match or not. This can not
4504 ** be one since the server would only return one and we wouldn't
4505 ** know if there were others available.
4508 if (bitset(MF_SINGLEMATCH, map->map_mflags))
4509 lmap->ldap_sizelimit = 2;
4511 /* If setting defaults, don't process ldap_filter and ldap_attr */
4512 if (lmap == LDAPDefaults)
4515 if (lmap->ldap_filter != NULL)
4516 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
4519 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
4521 syserr("No filter given in map %s", map->map_mname);
4526 if (!attrssetup && lmap->ldap_attr[0] != NULL)
4528 bool recurse = false;
4529 bool normalseen = false;
4532 p = ldapmap_dequote(lmap->ldap_attr[0]);
4533 lmap->ldap_attr[0] = NULL;
4535 /* Prime the attr list with the objectClass attribute */
4536 lmap->ldap_attr[i] = "objectClass";
4537 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
4538 lmap->ldap_attr_needobjclass[i] = NULL;
4545 while (isascii(*p) && isspace(*p))
4554 if (i >= LDAPMAP_MAX_ATTR)
4556 syserr("Too many return attributes in %s (max %d)",
4557 map->map_mname, LDAPMAP_MAX_ATTR);
4567 type = strchr(v, ':');
4571 needobjclass = strchr(type, ':');
4572 if (needobjclass != NULL)
4573 *needobjclass++ = '\0';
4577 needobjclass = NULL;
4582 /* allow override on "objectClass" type */
4583 if (sm_strcasecmp(v, "objectClass") == 0 &&
4584 lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
4591 ** Don't add something to attribute
4595 for (j = 1; j < i; j++)
4597 if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
4599 syserr("Duplicate attribute (%s) in %s",
4605 lmap->ldap_attr[use] = newstr(v);
4606 if (needobjclass != NULL &&
4607 *needobjclass != '\0' &&
4608 *needobjclass != '*')
4610 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
4614 lmap->ldap_attr_needobjclass[use] = NULL;
4619 if (type != NULL && *type != '\0')
4621 if (sm_strcasecmp(type, "dn") == 0)
4624 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
4626 else if (sm_strcasecmp(type, "filter") == 0)
4629 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
4631 else if (sm_strcasecmp(type, "url") == 0)
4634 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
4636 else if (sm_strcasecmp(type, "normal") == 0)
4638 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4643 syserr("Unknown attribute type (%s) in %s",
4644 type, map->map_mname);
4650 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4656 lmap->ldap_attr[i] = NULL;
4658 /* Set in case needed in future code */
4661 if (recurse && !normalseen)
4663 syserr("LDAP recursion requested in %s but no returnable attribute given",
4667 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
4669 syserr("LDAP recursion requested in %s can not be used with -n",
4674 map->map_db1 = (ARBPTR_T) lmap;
4679 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4682 ** spec -- map argument string from LDAPDefaults option
4689 ldapmap_set_defaults(spec)
4695 /* Allocate and set the default values */
4696 if (LDAPDefaults == NULL)
4697 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof(*LDAPDefaults));
4698 sm_ldap_clear(LDAPDefaults);
4700 memset(&map, '\0', sizeof(map));
4702 /* look up the class */
4703 class = stab("ldap", ST_MAPCLASS, ST_FIND);
4706 syserr("readcf: LDAPDefaultSpec: class ldap not available");
4709 map.map_class = &class->s_mapclass;
4710 map.map_db1 = (ARBPTR_T) LDAPDefaults;
4711 map.map_mname = "O LDAPDefaultSpec";
4713 (void) ldapmap_parseargs(&map, spec);
4715 /* These should never be set in LDAPDefaults */
4716 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
4717 map.map_spacesub != SpaceSub ||
4718 map.map_app != NULL ||
4719 map.map_tapp != NULL)
4721 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4722 SM_FREE_CLR(map.map_app);
4723 SM_FREE_CLR(map.map_tapp);
4726 if (LDAPDefaults->ldap_filter != NULL)
4728 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4730 /* don't free, it isn't malloc'ed in parseargs */
4731 LDAPDefaults->ldap_filter = NULL;
4734 if (LDAPDefaults->ldap_attr[0] != NULL)
4736 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4737 /* don't free, they aren't malloc'ed in parseargs */
4738 LDAPDefaults->ldap_attr[0] = NULL;
4741 #endif /* LDAPMAP */
4749 ** Support for the CCSO Nameserver (ph/qi).
4750 ** This code is intended to replace the so-called "ph mailer".
4751 ** Contributed by Mark D. Roth. Contact him for support.
4754 /* what version of the ph map code we're running */
4755 static char phmap_id[128];
4757 /* sendmail version for phmap id string */
4758 extern const char Version[];
4760 /* assume we're using nph-1.2.x if not specified */
4761 # ifndef NPH_VERSION
4762 # define NPH_VERSION 10200
4765 /* compatibility for versions older than nph-1.2.0 */
4766 # if NPH_VERSION < 10200
4767 # define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
4768 # define PH_OPEN_DONTID PH_DONTID
4769 # define PH_CLOSE_FAST PH_FASTCLOSE
4770 # define PH_ERR_DATAERR PH_DATAERR
4771 # define PH_ERR_NOMATCH PH_NOMATCH
4772 # endif /* NPH_VERSION < 10200 */
4775 ** PH_MAP_PARSEARGS -- parse ph map definition args.
4779 ph_map_parseargs(map, args)
4784 register char *p = args;
4785 PH_MAP_STRUCT *pmap = NULL;
4787 /* initialize version string */
4788 (void) sm_snprintf(phmap_id, sizeof(phmap_id),
4789 "sendmail-%s phmap-20010529 libphclient-%s",
4790 Version, libphclient_version);
4792 pmap = (PH_MAP_STRUCT *) xalloc(sizeof(*pmap));
4795 pmap->ph_servers = NULL;
4796 pmap->ph_field_list = NULL;
4798 pmap->ph_timeout = 0;
4799 pmap->ph_fastclose = 0;
4801 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4804 while (isascii(*p) && isspace(*p))
4811 map->map_mflags |= MF_INCLNULL;
4812 map->map_mflags &= ~MF_TRY0NULL;
4816 map->map_mflags &= ~MF_TRY1NULL;
4820 map->map_mflags |= MF_OPTIONAL;
4824 map->map_mflags |= MF_NOFOLDCASE;
4828 map->map_mflags |= MF_MATCHONLY;
4832 map->map_mflags |= MF_APPEND;
4836 map->map_mflags |= MF_KEEPQUOTES;
4840 map->map_mflags |= MF_NODEFER;
4848 map->map_tapp = ++p;
4852 while (isascii(*++p) && isspace(*p))
4854 pmap->ph_timeout = atoi(p);
4858 map->map_spacesub = *++p;
4862 map->map_mflags |= MF_DEFER;
4865 case 'h': /* PH server list */
4866 while (isascii(*++p) && isspace(*p))
4868 pmap->ph_servers = p;
4871 case 'k': /* fields to search for */
4872 while (isascii(*++p) && isspace(*p))
4874 pmap->ph_field_list = p;
4878 syserr("ph_map_parseargs: unknown option -%c", *p);
4881 /* try to account for quoted strings */
4882 done = isascii(*p) && isspace(*p);
4883 while (*p != '\0' && !done)
4887 while (*++p != '"' && *p != '\0')
4894 done = isascii(*p) && isspace(*p);
4901 if (map->map_app != NULL)
4902 map->map_app = newstr(ph_map_dequote(map->map_app));
4903 if (map->map_tapp != NULL)
4904 map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
4906 if (pmap->ph_field_list != NULL)
4907 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
4909 if (pmap->ph_servers != NULL)
4910 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
4913 syserr("ph_map_parseargs: -h flag is required");
4917 map->map_db1 = (ARBPTR_T) pmap;
4922 ** PH_MAP_CLOSE -- close the connection to the ph server
4929 PH_MAP_STRUCT *pmap;
4931 pmap = (PH_MAP_STRUCT *)map->map_db1;
4933 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
4934 map->map_mname, pmap->ph_fastclose);
4937 if (pmap->ph != NULL)
4939 ph_set_sendhook(pmap->ph, NULL);
4940 ph_set_recvhook(pmap->ph, NULL);
4941 ph_close(pmap->ph, pmap->ph_fastclose);
4944 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4947 static jmp_buf PHTimeout;
4955 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
4956 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
4961 longjmp(PHTimeout, 1);
4965 #if NPH_VERSION >= 10200
4966 ph_map_send_debug(appdata, text)
4969 ph_map_send_debug(text)
4974 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4975 "ph_map_send_debug: ==> %s", text);
4977 sm_dprintf("ph_map_send_debug: ==> %s\n", text);
4981 #if NPH_VERSION >= 10200
4982 ph_map_recv_debug(appdata, text)
4985 ph_map_recv_debug(text)
4990 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4991 "ph_map_recv_debug: <== %s", text);
4993 sm_dprintf("ph_map_recv_debug: <== %s\n", text);
4997 ** PH_MAP_OPEN -- sub for opening PH map
5000 ph_map_open(map, mode)
5004 PH_MAP_STRUCT *pmap;
5005 register SM_EVENT *ev = NULL;
5007 char *hostlist, *host;
5010 sm_dprintf("ph_map_open(%s)\n", map->map_mname);
5013 if (mode != O_RDONLY)
5015 /* issue a pseudo-error message */
5016 errno = SM_EMAPCANTWRITE;
5020 if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
5021 bitset(MF_DEFER, map->map_mflags))
5024 sm_dprintf("ph_map_open(%s) => DEFERRED\n",
5028 ** Unset MF_DEFER here so that map_lookup() returns
5029 ** a temporary failure using the bogus map and
5030 ** map->map_tapp instead of the default permanent error.
5033 map->map_mflags &= ~MF_DEFER;
5037 pmap = (PH_MAP_STRUCT *)map->map_db1;
5038 pmap->ph_fastclose = 0; /* refresh field for reopen */
5040 /* try each host in the list */
5041 hostlist = newstr(pmap->ph_servers);
5042 for (host = strtok(hostlist, " ");
5044 host = strtok(NULL, " "))
5047 if (pmap->ph_timeout != 0)
5049 if (setjmp(PHTimeout) != 0)
5053 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5054 "timeout connecting to PH server %.100s",
5057 goto ph_map_open_abort;
5059 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
5062 /* open connection to server */
5063 if (ph_open(&(pmap->ph), host,
5064 PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
5065 ph_map_send_debug, ph_map_recv_debug
5066 #if NPH_VERSION >= 10200
5070 && ph_id(pmap->ph, phmap_id) == 0)
5074 sm_free(hostlist); /* XXX */
5082 pmap->ph_fastclose = PH_CLOSE_FAST;
5087 if (bitset(MF_NODEFER, map->map_mflags))
5091 syserr("ph_map_open: %s: cannot connect to PH server",
5094 else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
5095 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5096 "ph_map_open: %s: cannot connect to PH server",
5098 sm_free(hostlist); /* XXX */
5103 ** PH_MAP_LOOKUP -- look up key from ph server
5107 ph_map_lookup(map, key, args, pstat)
5113 int i, save_errno = 0;
5114 register SM_EVENT *ev = NULL;
5115 PH_MAP_STRUCT *pmap;
5118 pmap = (PH_MAP_STRUCT *)map->map_db1;
5123 if (pmap->ph_timeout != 0)
5125 if (setjmp(PHTimeout) != 0)
5129 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5130 "timeout during PH lookup of %.100s",
5133 *pstat = EX_TEMPFAIL;
5134 goto ph_map_lookup_abort;
5136 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
5139 /* perform lookup */
5140 i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
5142 *pstat = EX_TEMPFAIL;
5143 else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
5144 *pstat = EX_UNAVAILABLE;
5146 ph_map_lookup_abort:
5151 ** Close the connection if the timer popped
5152 ** or we got a temporary PH error
5155 if (*pstat == EX_TEMPFAIL)
5158 pmap->ph_fastclose = PH_CLOSE_FAST;
5163 if (*pstat == EX_OK)
5166 sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
5168 if (bitset(MF_MATCHONLY, map->map_mflags))
5169 return map_rewrite(map, key, strlen(key), NULL);
5171 return map_rewrite(map, value, strlen(value), args);
5182 #define map_prio map_lockfd /* overload field */
5185 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
5189 syslog_map_parseargs(map, args)
5194 char *priority = NULL;
5196 /* there is no check whether there is really an argument */
5199 while (isascii(*p) && isspace(*p))
5206 map->map_mflags |= MF_DEFER;
5211 map->map_spacesub = *++p;
5217 while (*++p != '\0' && isascii(*p) && isspace(*p))
5222 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5229 syserr("Illegal option %c map syslog", *p);
5234 if (priority == NULL)
5235 map->map_prio = LOG_INFO;
5238 if (sm_strncasecmp("LOG_", priority, 4) == 0)
5242 if (sm_strcasecmp("EMERG", priority) == 0)
5243 map->map_prio = LOG_EMERG;
5245 #endif /* LOG_EMERG */
5247 if (sm_strcasecmp("ALERT", priority) == 0)
5248 map->map_prio = LOG_ALERT;
5250 #endif /* LOG_ALERT */
5252 if (sm_strcasecmp("CRIT", priority) == 0)
5253 map->map_prio = LOG_CRIT;
5255 #endif /* LOG_CRIT */
5257 if (sm_strcasecmp("ERR", priority) == 0)
5258 map->map_prio = LOG_ERR;
5260 #endif /* LOG_ERR */
5262 if (sm_strcasecmp("WARNING", priority) == 0)
5263 map->map_prio = LOG_WARNING;
5265 #endif /* LOG_WARNING */
5267 if (sm_strcasecmp("NOTICE", priority) == 0)
5268 map->map_prio = LOG_NOTICE;
5270 #endif /* LOG_NOTICE */
5272 if (sm_strcasecmp("INFO", priority) == 0)
5273 map->map_prio = LOG_INFO;
5275 #endif /* LOG_INFO */
5277 if (sm_strcasecmp("DEBUG", priority) == 0)
5278 map->map_prio = LOG_DEBUG;
5280 #endif /* LOG_DEBUG */
5282 syserr("syslog_map_parseargs: Unknown priority %s",
5291 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
5295 syslog_map_lookup(map, string, args, statp)
5301 char *ptr = map_rewrite(map, string, strlen(string), args);
5306 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5307 map->map_mname, map->map_prio, ptr);
5309 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
5316 #if _FFR_DPRINTF_MAP
5321 #define map_dbg_level map_lockfd /* overload field */
5324 ** DPRINTF_MAP_PARSEARGS -- check for priority level to dprintf messages.
5328 dprintf_map_parseargs(map, args)
5333 char *dbg_level = NULL;
5335 /* there is no check whether there is really an argument */
5338 while (isascii(*p) && isspace(*p))
5345 map->map_mflags |= MF_DEFER;
5350 map->map_spacesub = *++p;
5356 while (*++p != '\0' && isascii(*p) && isspace(*p))
5361 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5368 syserr("Illegal option %c map dprintf", *p);
5373 if (dbg_level == NULL)
5374 map->map_dbg_level = 0;
5377 if (!(isascii(*dbg_level) && isdigit(*dbg_level)))
5379 syserr("dprintf map \"%s\", file %s: -d should specify a number, not %s",
5380 map->map_mname, map->map_file,
5384 map->map_dbg_level = atoi(dbg_level);
5390 ** DPRINTF_MAP_LOOKUP -- rewrite and print message. Always return empty string
5394 dprintf_map_lookup(map, string, args, statp)
5400 char *ptr = map_rewrite(map, string, strlen(string), args);
5402 if (ptr != NULL && tTd(85, map->map_dbg_level))
5403 sm_dprintf("%s\n", ptr);
5407 #endif /* _FFR_DPRINTF_MAP */
5416 hes_map_open(map, mode)
5421 sm_dprintf("hes_map_open(%s, %s, %d)\n",
5422 map->map_mname, map->map_file, mode);
5424 if (mode != O_RDONLY)
5426 /* issue a pseudo-error message */
5427 errno = SM_EMAPCANTWRITE;
5432 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
5435 if (!bitset(MF_OPTIONAL, map->map_mflags))
5436 syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5437 sm_errstring(errno));
5439 # else /* HESIOD_INIT */
5440 if (hes_error() == HES_ER_UNINIT)
5442 switch (hes_error())
5445 case HES_ER_NOTFOUND:
5449 if (!bitset(MF_OPTIONAL, map->map_mflags))
5450 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5453 # endif /* HESIOD_INIT */
5457 hes_map_lookup(map, name, av, statp)
5466 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
5468 if (name[0] == '\\')
5476 if (nl < sizeof(nbuf) - 1)
5479 np = xalloc(strlen(name) + 2);
5481 (void) sm_strlcpy(&np[1], name, (sizeof(nbuf)) - 1);
5483 hp = hesiod_resolve(HesiodContext, np, map->map_file);
5484 # else /* HESIOD_INIT */
5485 hp = hes_resolve(np, map->map_file);
5486 # endif /* HESIOD_INIT */
5489 sm_free(np); /* XXX */
5495 hp = hesiod_resolve(HesiodContext, name, map->map_file);
5496 # else /* HESIOD_INIT */
5497 hp = hes_resolve(name, map->map_file);
5498 # endif /* HESIOD_INIT */
5501 if (hp == NULL || *hp == NULL)
5506 *statp = EX_NOTFOUND;
5509 *statp = EX_TEMPFAIL;
5514 *statp = EX_UNAVAILABLE;
5518 hesiod_free_list(HesiodContext, hp);
5521 # else /* HESIOD_INIT */
5522 if (hp == NULL || hp[0] == NULL)
5524 switch (hes_error())
5530 case HES_ER_NOTFOUND:
5531 *statp = EX_NOTFOUND;
5535 *statp = EX_UNAVAILABLE;
5539 *statp = EX_TEMPFAIL;
5544 # endif /* HESIOD_INIT */
5546 if (bitset(MF_MATCHONLY, map->map_mflags))
5547 return map_rewrite(map, name, strlen(name), NULL);
5549 return map_rewrite(map, hp[0], strlen(hp[0]), av);
5553 ** HES_MAP_CLOSE -- free the Hesiod context
5561 sm_dprintf("hes_map_close(%s)\n", map->map_file);
5564 /* Free the hesiod context */
5565 if (HesiodContext != NULL)
5567 hesiod_end(HesiodContext);
5568 HesiodContext = NULL;
5570 # endif /* HESIOD_INIT */
5575 ** NeXT NETINFO Modules
5580 # define NETINFO_DEFAULT_DIR "/aliases"
5581 # define NETINFO_DEFAULT_PROPERTY "members"
5584 ** NI_MAP_OPEN -- open NetInfo Aliases
5588 ni_map_open(map, mode)
5593 sm_dprintf("ni_map_open(%s, %s, %d)\n",
5594 map->map_mname, map->map_file, mode);
5597 if (*map->map_file == '\0')
5598 map->map_file = NETINFO_DEFAULT_DIR;
5600 if (map->map_valcolnm == NULL)
5601 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
5603 if (map->map_coldelim == '\0')
5605 if (bitset(MF_ALIAS, map->map_mflags))
5606 map->map_coldelim = ',';
5607 else if (bitset(MF_FILECLASS, map->map_mflags))
5608 map->map_coldelim = ' ';
5615 ** NI_MAP_LOOKUP -- look up a datum in NetInfo
5619 ni_map_lookup(map, name, av, statp)
5629 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
5631 propval = ni_propval(map->map_file, map->map_keycolnm, name,
5632 map->map_valcolnm, map->map_coldelim);
5634 if (propval == NULL)
5638 if (bitset(MF_MATCHONLY, map->map_mflags))
5639 res = map_rewrite(map, name, strlen(name), NULL);
5641 res = map_rewrite(map, propval, strlen(propval), av);
5650 ni_getcanonname(name, hbsize, statp)
5657 char nbuf[MAXNAME + 1];
5660 sm_dprintf("ni_getcanonname(%s)\n", name);
5662 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
5664 *statp = EX_UNAVAILABLE;
5667 (void) shorten_hostname(nbuf);
5669 /* we only accept single token search key */
5670 if (strchr(nbuf, '.'))
5677 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
5685 /* Only want the first machine name */
5686 if ((ptr = strchr(vptr, '\n')) != NULL)
5689 if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
5692 *statp = EX_UNAVAILABLE;
5699 #endif /* NETINFO */
5701 ** TEXT (unindexed text file) Modules
5703 ** This code donated by Sun Microsystems.
5706 #define map_sff map_lockfd /* overload field */
5710 ** TEXT_MAP_OPEN -- open text table
5714 text_map_open(map, mode)
5722 sm_dprintf("text_map_open(%s, %s, %d)\n",
5723 map->map_mname, map->map_file, mode);
5726 if (mode != O_RDONLY)
5732 if (*map->map_file == '\0')
5734 syserr("text map \"%s\": file name required",
5739 if (map->map_file[0] != '/')
5741 syserr("text map \"%s\": file name must be fully qualified",
5746 sff = SFF_ROOTOK|SFF_REGONLY;
5747 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5749 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5750 sff |= SFF_SAFEDIRPATH;
5751 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
5752 sff, S_IRUSR, NULL)) != 0)
5754 int save_errno = errno;
5756 /* cannot open this map */
5758 sm_dprintf("\tunsafe map file: %d\n", i);
5760 if (!bitset(MF_OPTIONAL, map->map_mflags))
5761 syserr("text map \"%s\": unsafe map file %s",
5762 map->map_mname, map->map_file);
5766 if (map->map_keycolnm == NULL)
5767 map->map_keycolno = 0;
5770 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
5772 syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5773 map->map_mname, map->map_file,
5777 map->map_keycolno = atoi(map->map_keycolnm);
5780 if (map->map_valcolnm == NULL)
5781 map->map_valcolno = 0;
5784 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
5786 syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5787 map->map_mname, map->map_file,
5791 map->map_valcolno = atoi(map->map_valcolnm);
5796 sm_dprintf("text_map_open(%s, %s): delimiter = ",
5797 map->map_mname, map->map_file);
5798 if (map->map_coldelim == '\0')
5799 sm_dprintf("(white space)\n");
5801 sm_dprintf("%c\n", map->map_coldelim);
5810 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5814 text_map_lookup(map, name, av, statp)
5827 long sff = map->map_sff;
5828 char search_key[MAXNAME + 1];
5829 char linebuf[MAXLINE];
5830 char buf[MAXNAME + 1];
5834 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
5836 buflen = strlen(name);
5837 if (buflen > sizeof(search_key) - 1)
5838 buflen = sizeof(search_key) - 1; /* XXX just cut if off? */
5839 memmove(search_key, name, buflen);
5840 search_key[buflen] = '\0';
5841 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
5842 makelower(search_key);
5844 f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
5847 map->map_mflags &= ~(MF_VALID|MF_OPEN);
5848 *statp = EX_UNAVAILABLE;
5851 key_idx = map->map_keycolno;
5852 delim = map->map_coldelim;
5853 while (sm_io_fgets(f, SM_TIME_DEFAULT,
5854 linebuf, sizeof(linebuf)) >= 0)
5858 /* skip comment line */
5859 if (linebuf[0] == '#')
5861 p = strchr(linebuf, '\n');
5864 p = get_column(linebuf, key_idx, delim, buf, sizeof(buf));
5865 if (p != NULL && sm_strcasecmp(search_key, p) == 0)
5871 (void) sm_io_close(f, SM_TIME_DEFAULT);
5874 *statp = EX_NOTFOUND;
5877 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof(buf));
5880 *statp = EX_NOTFOUND;
5885 if (bitset(MF_MATCHONLY, map->map_mflags))
5886 return map_rewrite(map, name, strlen(name), NULL);
5888 return map_rewrite(map, vp, vsize, av);
5892 ** TEXT_GETCANONNAME -- look up canonical name in hosts file
5896 text_getcanonname(name, hbsize, statp)
5904 char linebuf[MAXLINE];
5905 char cbuf[MAXNAME + 1];
5906 char nbuf[MAXNAME + 1];
5909 sm_dprintf("text_getcanonname(%s)\n", name);
5911 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
5913 *statp = EX_UNAVAILABLE;
5916 dot = shorten_hostname(nbuf);
5918 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
5922 *statp = EX_UNAVAILABLE;
5927 sm_io_fgets(f, SM_TIME_DEFAULT,
5928 linebuf, sizeof(linebuf)) >= 0)
5930 char *p = strpbrk(linebuf, "#\n");
5934 if (linebuf[0] != '\0')
5935 found = extract_canonname(nbuf, dot, linebuf,
5936 cbuf, sizeof(cbuf));
5938 (void) sm_io_close(f, SM_TIME_DEFAULT);
5945 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
5947 *statp = EX_UNAVAILABLE;
5954 ** STAB (Symbol Table) Modules
5959 ** STAB_MAP_LOOKUP -- look up alias in symbol table
5964 stab_map_lookup(map, name, av, pstat)
5973 sm_dprintf("stab_lookup(%s, %s)\n",
5974 map->map_mname, name);
5976 s = stab(name, ST_ALIAS, ST_FIND);
5979 if (bitset(MF_MATCHONLY, map->map_mflags))
5980 return map_rewrite(map, name, strlen(name), NULL);
5982 return map_rewrite(map, s->s_alias, strlen(s->s_alias), av);
5986 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
5990 stab_map_store(map, lhs, rhs)
5997 s = stab(lhs, ST_ALIAS, ST_ENTER);
5998 s->s_alias = newstr(rhs);
6003 ** STAB_MAP_OPEN -- initialize (reads data file)
6005 ** This is a weird case -- it is only intended as a fallback for
6006 ** aliases. For this reason, opens for write (only during a
6007 ** "newaliases") always fails, and opens for read open the
6008 ** actual underlying text file instead of the database.
6012 stab_map_open(map, mode)
6021 sm_dprintf("stab_map_open(%s, %s, %d)\n",
6022 map->map_mname, map->map_file, mode);
6025 if (mode != O_RDONLY)
6031 sff = SFF_ROOTOK|SFF_REGONLY;
6032 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
6034 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
6035 sff |= SFF_SAFEDIRPATH;
6036 af = safefopen(map->map_file, O_RDONLY, 0444, sff);
6039 readaliases(map, af, false, false);
6041 if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
6042 map->map_mtime = st.st_mtime;
6043 (void) sm_io_close(af, SM_TIME_DEFAULT);
6050 ** Tries several types. For back compatibility of aliases.
6055 ** IMPL_MAP_LOOKUP -- lookup in best open database
6059 impl_map_lookup(map, name, av, pstat)
6066 sm_dprintf("impl_map_lookup(%s, %s)\n",
6067 map->map_mname, name);
6070 if (bitset(MF_IMPL_HASH, map->map_mflags))
6071 return db_map_lookup(map, name, av, pstat);
6074 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6075 return ndbm_map_lookup(map, name, av, pstat);
6077 return stab_map_lookup(map, name, av, pstat);
6081 ** IMPL_MAP_STORE -- store in open databases
6085 impl_map_store(map, lhs, rhs)
6091 sm_dprintf("impl_map_store(%s, %s, %s)\n",
6092 map->map_mname, lhs, rhs);
6094 if (bitset(MF_IMPL_HASH, map->map_mflags))
6095 db_map_store(map, lhs, rhs);
6098 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6099 ndbm_map_store(map, lhs, rhs);
6101 stab_map_store(map, lhs, rhs);
6105 ** IMPL_MAP_OPEN -- implicit database open
6109 impl_map_open(map, mode)
6114 sm_dprintf("impl_map_open(%s, %s, %d)\n",
6115 map->map_mname, map->map_file, mode);
6119 map->map_mflags |= MF_IMPL_HASH;
6120 if (hash_map_open(map, mode))
6122 # ifdef NDBM_YP_COMPAT
6123 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
6124 # endif /* NDBM_YP_COMPAT */
6128 map->map_mflags &= ~MF_IMPL_HASH;
6131 map->map_mflags |= MF_IMPL_NDBM;
6132 if (ndbm_map_open(map, mode))
6137 map->map_mflags &= ~MF_IMPL_NDBM;
6140 #if defined(NEWDB) || defined(NDBM)
6142 message("WARNING: cannot open alias database %s%s",
6144 mode == O_RDONLY ? "; reading text version" : "");
6145 #else /* defined(NEWDB) || defined(NDBM) */
6146 if (mode != O_RDONLY)
6147 usrerr("Cannot rebuild aliases: no database format defined");
6148 #endif /* defined(NEWDB) || defined(NDBM) */
6150 if (mode == O_RDONLY)
6151 return stab_map_open(map, mode);
6158 ** IMPL_MAP_CLOSE -- close any open database(s)
6166 sm_dprintf("impl_map_close(%s, %s, %lx)\n",
6167 map->map_mname, map->map_file, map->map_mflags);
6169 if (bitset(MF_IMPL_HASH, map->map_mflags))
6172 map->map_mflags &= ~MF_IMPL_HASH;
6177 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6179 ndbm_map_close(map);
6180 map->map_mflags &= ~MF_IMPL_NDBM;
6187 ** Provides access to the system password file.
6191 ** USER_MAP_OPEN -- open user map
6193 ** Really just binds field names to field numbers.
6197 user_map_open(map, mode)
6202 sm_dprintf("user_map_open(%s, %d)\n",
6203 map->map_mname, mode);
6206 if (mode != O_RDONLY)
6208 /* issue a pseudo-error message */
6209 errno = SM_EMAPCANTWRITE;
6212 if (map->map_valcolnm == NULL)
6215 else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
6216 map->map_valcolno = 1;
6217 else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
6218 map->map_valcolno = 2;
6219 else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
6220 map->map_valcolno = 3;
6221 else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
6222 map->map_valcolno = 4;
6223 else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
6224 map->map_valcolno = 5;
6225 else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
6226 map->map_valcolno = 6;
6227 else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
6228 map->map_valcolno = 7;
6231 syserr("User map %s: unknown column name %s",
6232 map->map_mname, map->map_valcolnm);
6240 ** USER_MAP_LOOKUP -- look up a user in the passwd file.
6245 user_map_lookup(map, key, av, statp)
6255 sm_dprintf("user_map_lookup(%s, %s)\n",
6256 map->map_mname, key);
6258 *statp = finduser(key, &fuzzy, &user);
6259 if (*statp != EX_OK)
6261 if (bitset(MF_MATCHONLY, map->map_mflags))
6262 return map_rewrite(map, key, strlen(key), NULL);
6268 switch (map->map_valcolno)
6272 rwval = user.mbdb_name;
6276 rwval = "x"; /* passwd no longer supported */
6280 (void) sm_snprintf(buf, sizeof(buf), "%d",
6281 (int) user.mbdb_uid);
6286 (void) sm_snprintf(buf, sizeof(buf), "%d",
6287 (int) user.mbdb_gid);
6292 rwval = user.mbdb_fullname;
6296 rwval = user.mbdb_homedir;
6300 rwval = user.mbdb_shell;
6303 syserr("user_map %s: bogus field %d",
6304 map->map_mname, map->map_valcolno);
6307 return map_rewrite(map, rwval, strlen(rwval), av);
6311 ** Program map type.
6313 ** This provides access to arbitrary programs. It should be used
6314 ** only very sparingly, since there is no way to bound the cost
6315 ** of invoking an arbitrary program.
6319 prog_map_lookup(map, name, av, statp)
6332 char *argv[MAXPV + 1];
6336 sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6337 map->map_mname, name, map->map_file);
6340 argv[i++] = map->map_file;
6341 if (map->map_rebuild != NULL)
6343 (void) sm_strlcpy(buf, map->map_rebuild, sizeof(buf));
6344 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
6355 sm_dprintf("prog_open:");
6356 for (i = 0; argv[i] != NULL; i++)
6357 sm_dprintf(" %s", argv[i]);
6360 (void) sm_blocksignal(SIGCHLD);
6361 pid = prog_open(argv, &fd, CurEnv);
6364 if (!bitset(MF_OPTIONAL, map->map_mflags))
6365 syserr("prog_map_lookup(%s) failed (%s) -- closing",
6366 map->map_mname, sm_errstring(errno));
6367 else if (tTd(38, 9))
6368 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6369 map->map_mname, sm_errstring(errno));
6370 map->map_mflags &= ~(MF_VALID|MF_OPEN);
6374 i = read(fd, buf, sizeof(buf) - 1);
6377 syserr("prog_map_lookup(%s): read error %s",
6378 map->map_mname, sm_errstring(errno));
6384 sm_dprintf("prog_map_lookup(%s): empty answer\n",
6391 p = strchr(buf, '\n');
6395 /* collect the return value */
6396 if (bitset(MF_MATCHONLY, map->map_mflags))
6397 rval = map_rewrite(map, name, strlen(name), NULL);
6399 rval = map_rewrite(map, buf, strlen(buf), av);
6401 /* now flush any additional output */
6402 while ((i = read(fd, buf, sizeof(buf))) > 0)
6406 /* wait for the process to terminate */
6408 status = waitfor(pid);
6410 (void) sm_releasesignal(SIGCHLD);
6415 syserr("prog_map_lookup(%s): wait error %s",
6416 map->map_mname, sm_errstring(errno));
6417 *statp = EX_SOFTWARE;
6420 else if (WIFEXITED(status))
6422 if ((*statp = WEXITSTATUS(status)) != EX_OK)
6427 syserr("prog_map_lookup(%s): child died on signal %d",
6428 map->map_mname, status);
6429 *statp = EX_UNAVAILABLE;
6435 ** Sequenced map type.
6437 ** Tries each map in order until something matches, much like
6438 ** implicit. Stores go to the first map in the list that can
6441 ** This is slightly unusual in that there are two interfaces.
6442 ** The "sequence" interface lets you stack maps arbitrarily.
6443 ** The "switch" interface builds a sequence map by looking
6444 ** at a system-dependent configuration file such as
6445 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6447 ** We don't need an explicit open, since all maps are
6448 ** opened on demand.
6452 ** SEQ_MAP_PARSE -- Sequenced map parsing
6456 seq_map_parse(map, ap)
6463 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
6470 /* find beginning of map name */
6471 while (isascii(*ap) && isspace(*ap))
6474 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
6479 while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
6486 s = stab(ap, ST_MAP, ST_FIND);
6489 syserr("Sequence map %s: unknown member map %s",
6490 map->map_mname, ap);
6492 else if (maxmap >= MAXMAPSTACK)
6494 syserr("Sequence map %s: too many member maps (%d max)",
6495 map->map_mname, MAXMAPSTACK);
6498 else if (maxmap < MAXMAPSTACK)
6500 map->map_stack[maxmap++] = &s->s_map;
6508 ** SWITCH_MAP_OPEN -- open a switched map
6510 ** This looks at the system-dependent configuration and builds
6511 ** a sequence map that does the same thing.
6513 ** Every system must define a switch_map_find routine in conf.c
6514 ** that will return the list of service types associated with a
6515 ** given service class.
6519 switch_map_open(map, mode)
6525 char *maptype[MAXMAPSTACK];
6528 sm_dprintf("switch_map_open(%s, %s, %d)\n",
6529 map->map_mname, map->map_file, mode);
6532 nmaps = switch_map_find(map->map_file, maptype, map->map_return);
6535 sm_dprintf("\tswitch_map_find => %d\n", nmaps);
6536 for (mapno = 0; mapno < nmaps; mapno++)
6537 sm_dprintf("\t\t%s\n", maptype[mapno]);
6539 if (nmaps <= 0 || nmaps > MAXMAPSTACK)
6542 for (mapno = 0; mapno < nmaps; mapno++)
6545 char nbuf[MAXNAME + 1];
6547 if (maptype[mapno] == NULL)
6549 (void) sm_strlcpyn(nbuf, sizeof(nbuf), 3,
6550 map->map_mname, ".", maptype[mapno]);
6551 s = stab(nbuf, ST_MAP, ST_FIND);
6554 syserr("Switch map %s: unknown member map %s",
6555 map->map_mname, nbuf);
6559 map->map_stack[mapno] = &s->s_map;
6561 sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6563 s->s_map.map_class->map_cname,
6572 ** SEQ_MAP_CLOSE -- close all underlying maps
6582 sm_dprintf("seq_map_close(%s)\n", map->map_mname);
6584 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6586 MAP *mm = map->map_stack[mapno];
6588 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
6590 mm->map_mflags |= MF_CLOSING;
6591 mm->map_class->map_close(mm);
6592 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
6598 ** SEQ_MAP_LOOKUP -- sequenced map lookup
6602 seq_map_lookup(map, key, args, pstat)
6610 bool tempfail = false;
6613 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
6615 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
6617 MAP *mm = map->map_stack[mapno];
6622 if (!bitset(MF_OPEN, mm->map_mflags) &&
6625 if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
6627 *pstat = EX_UNAVAILABLE;
6633 rv = mm->map_class->map_lookup(mm, key, args, pstat);
6636 if (*pstat == EX_TEMPFAIL)
6638 if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
6642 else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
6646 *pstat = EX_TEMPFAIL;
6647 else if (*pstat == EX_OK)
6648 *pstat = EX_NOTFOUND;
6653 ** SEQ_MAP_STORE -- sequenced map store
6657 seq_map_store(map, key, val)
6665 sm_dprintf("seq_map_store(%s, %s, %s)\n",
6666 map->map_mname, key, val);
6668 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6670 MAP *mm = map->map_stack[mapno];
6672 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
6675 mm->map_class->map_store(mm, key, val);
6678 syserr("seq_map_store(%s, %s, %s): no writable map",
6679 map->map_mname, key, val);
6687 null_map_open(map, mode)
6703 null_map_lookup(map, key, args, pstat)
6709 *pstat = EX_NOTFOUND;
6715 null_map_store(map, key, val)
6723 MAPCLASS NullMapClass =
6725 "null-map", NULL, 0,
6726 NULL, null_map_lookup, null_map_store,
6727 null_map_open, null_map_close,
6735 bogus_map_lookup(map, key, args, pstat)
6741 *pstat = EX_TEMPFAIL;
6745 MAPCLASS BogusMapClass =
6747 "bogus-map", NULL, 0,
6748 NULL, bogus_map_lookup, null_map_store,
6749 null_map_open, null_map_close,
6756 macro_map_lookup(map, name, av, statp)
6765 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
6766 name == NULL ? "NULL" : name);
6770 (mid = macid(name)) == 0)
6777 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
6779 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
6792 # define DEFAULT_DELIM CONDELSE
6793 # define END_OF_FIELDS -1
6794 # define ERRBUF_SIZE 80
6795 # define MAX_MATCH 32
6797 # define xnalloc(s) memset(xalloc(s), '\0', s);
6801 regex_t *regex_pattern_buf; /* xalloc it */
6802 int *regex_subfields; /* move to type MAP */
6803 char *regex_delim; /* move to type MAP */
6806 static int parse_fields __P((char *, int *, int, int));
6807 static char *regex_map_rewrite __P((MAP *, const char*, size_t, char **));
6810 parse_fields(s, ibuf, blen, nr_substrings)
6812 int *ibuf; /* array */
6813 int blen; /* number of elements in ibuf */
6814 int nr_substrings; /* number of substrings in the pattern */
6818 bool lastone = false;
6820 blen--; /* for terminating END_OF_FIELDS */
6841 if (val < 0 || val >= nr_substrings)
6843 syserr("field (%d) out of range, only %d substrings in pattern",
6844 val, nr_substrings);
6851 syserr("too many fields, %d max", blen);
6856 ibuf[i] = END_OF_FIELDS;
6861 regex_map_init(map, ap)
6866 struct regex_map *map_p;
6868 char *sub_param = NULL;
6870 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
6873 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6874 map->map_mname, ap);
6876 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
6878 map_p = (struct regex_map *) xnalloc(sizeof(*map_p));
6879 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
6883 while (isascii(*p) && isspace(*p))
6890 map->map_mflags |= MF_REGEX_NOT;
6893 case 'f': /* case sensitive */
6894 map->map_mflags |= MF_NOFOLDCASE;
6895 pflags &= ~REG_ICASE;
6898 case 'b': /* basic regular expressions */
6899 pflags &= ~REG_EXTENDED;
6902 case 's': /* substring match () syntax */
6904 pflags &= ~REG_NOSUB;
6907 case 'd': /* delimiter */
6908 map_p->regex_delim = ++p;
6911 case 'a': /* map append */
6915 case 'm': /* matchonly */
6916 map->map_mflags |= MF_MATCHONLY;
6920 map->map_mflags |= MF_KEEPQUOTES;
6924 map->map_spacesub = *++p;
6928 map->map_mflags |= MF_DEFER;
6932 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
6938 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
6940 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
6943 char errbuf[ERRBUF_SIZE];
6945 (void) regerror(regerr, map_p->regex_pattern_buf,
6946 errbuf, sizeof(errbuf));
6947 syserr("pattern-compile-error: %s", errbuf);
6948 sm_free(map_p->regex_pattern_buf); /* XXX */
6949 sm_free(map_p); /* XXX */
6953 if (map->map_app != NULL)
6954 map->map_app = newstr(map->map_app);
6955 if (map_p->regex_delim != NULL)
6956 map_p->regex_delim = newstr(map_p->regex_delim);
6958 map_p->regex_delim = defdstr;
6960 if (!bitset(REG_NOSUB, pflags))
6962 /* substring matching */
6964 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
6966 substrings = map_p->regex_pattern_buf->re_nsub + 1;
6969 sm_dprintf("regex_map_init: nr of substrings %d\n",
6972 if (substrings >= MAX_MATCH)
6974 syserr("too many substrings, %d max", MAX_MATCH);
6975 sm_free(map_p->regex_pattern_buf); /* XXX */
6976 sm_free(map_p); /* XXX */
6979 if (sub_param != NULL && sub_param[0] != '\0')
6981 /* optional parameter -sfields */
6982 if (parse_fields(sub_param, fields,
6983 MAX_MATCH + 1, substrings) == -1)
6990 /* set default fields */
6991 for (i = 0; i < substrings; i++)
6993 fields[i] = END_OF_FIELDS;
6995 map_p->regex_subfields = fields;
7000 sm_dprintf("regex_map_init: subfields");
7001 for (ip = fields; *ip != END_OF_FIELDS; ip++)
7002 sm_dprintf(" %d", *ip);
7006 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */
7011 regex_map_rewrite(map, s, slen, av)
7017 if (bitset(MF_MATCHONLY, map->map_mflags))
7018 return map_rewrite(map, av[0], strlen(av[0]), NULL);
7020 return map_rewrite(map, s, slen, av);
7024 regex_map_lookup(map, name, av, statp)
7031 struct regex_map *map_p;
7032 regmatch_t pmatch[MAX_MATCH];
7038 sm_dprintf("regex_map_lookup: key '%s'\n", name);
7039 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7040 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
7043 map_p = (struct regex_map *)(map->map_db1);
7044 reg_res = regexec(map_p->regex_pattern_buf,
7045 name, MAX_MATCH, pmatch, 0);
7047 if (bitset(MF_REGEX_NOT, map->map_mflags))
7050 if (reg_res == REG_NOMATCH)
7051 return regex_map_rewrite(map, "", (size_t) 0, av);
7055 if (reg_res == REG_NOMATCH)
7058 if (map_p->regex_subfields != NULL)
7061 static char retbuf[MAXNAME];
7062 int fields[MAX_MATCH + 1];
7064 int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
7065 bool quotemode = false, bslashmode = false;
7066 register char *dp, *sp;
7071 ldp = retbuf + sizeof(retbuf) - 1;
7075 if (parse_fields(av[1], fields, MAX_MATCH + 1,
7076 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
7084 ip = map_p->regex_subfields;
7086 for ( ; *ip != END_OF_FIELDS; ip++)
7090 for (sp = map_p->regex_delim; *sp; sp++)
7099 if (*ip >= MAX_MATCH ||
7100 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
7103 sp = name + pmatch[*ip].rm_so;
7104 endp = name + pmatch[*ip].rm_eo;
7105 for (; endp > sp; sp++)
7114 else if (quotemode && *sp != '"' &&
7119 else switch (*dp++ = *sp)
7146 quotemode = !quotemode;
7152 if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
7153 bslashmode || spacecnt != 0)
7155 sm_syslog(LOG_WARNING, NOQID,
7156 "Warning: regex may cause prescan() failure map=%s lookup=%s",
7157 map->map_mname, name);
7163 return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
7165 return regex_map_rewrite(map, "", (size_t)0, av);
7167 #endif /* MAP_REGEX */
7174 # define _DATUM_DEFINED
7175 # include <ns_api.h>
7177 typedef struct ns_map_list
7179 ns_map_t *map; /* XXX ns_ ? */
7181 struct ns_map_list *next;
7185 ns_map_t_find(mapname)
7188 static ns_map_list_t *ns_maps = NULL;
7189 ns_map_list_t *ns_map;
7191 /* walk the list of maps looking for the correctly named map */
7192 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
7194 if (strcmp(ns_map->mapname, mapname) == 0)
7198 /* if we are looking at a NULL ns_map_list_t, then create a new one */
7201 ns_map = (ns_map_list_t *) xalloc(sizeof(*ns_map));
7202 ns_map->mapname = newstr(mapname);
7203 ns_map->map = (ns_map_t *) xalloc(sizeof(*ns_map->map));
7204 memset(ns_map->map, '\0', sizeof(*ns_map->map));
7205 ns_map->next = ns_maps;
7212 nsd_map_lookup(map, name, av, statp)
7221 char keybuf[MAXNAME + 1];
7225 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
7227 buflen = strlen(name);
7228 if (buflen > sizeof(keybuf) - 1)
7229 buflen = sizeof(keybuf) - 1; /* XXX simply cut off? */
7230 memmove(keybuf, name, buflen);
7231 keybuf[buflen] = '\0';
7232 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7235 ns_map = ns_map_t_find(map->map_file);
7239 sm_dprintf("nsd_map_t_find failed\n");
7240 *statp = EX_UNAVAILABLE;
7243 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
7245 if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
7247 *statp = EX_TEMPFAIL;
7253 # endif /* NS_NOPERM */
7259 if (r != NS_SUCCESS)
7261 *statp = EX_NOTFOUND;
7267 /* Null out trailing \n */
7268 if ((p = strchr(buf, '\n')) != NULL)
7271 return map_rewrite(map, buf, strlen(buf), av);
7273 #endif /* MAP_NSD */
7276 arith_map_lookup(map, name, av, statp)
7286 static char result[16];
7291 sm_dprintf("arith_map_lookup: key '%s'\n", name);
7292 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7293 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
7301 ** read arguments for arith map
7302 ** - no check is made whether they are really numbers
7303 ** - just ignores args after the second
7306 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
7307 v[r++] = strtol(*cpp, NULL, 0);
7309 /* operator and (at least) two operands given? */
7310 if (name != NULL && r == 2)
7356 r = v[1] - v[0] + 1;
7359 r = get_random() % r + v[0];
7366 sm_syslog(LOG_WARNING, NOQID,
7367 "arith_map: unknown operator %c",
7368 (isascii(*name) && isprint(*name)) ?
7373 (void) sm_snprintf(result, sizeof(result),
7374 res ? "TRUE" : "FALSE");
7376 (void) sm_snprintf(result, sizeof(result), "%ld", r);
7384 arpa_map_lookup(map, name, av, statp)
7392 char result[128]; /* IPv6: 64 + 10 + 1 would be enough */
7395 sm_dprintf("arpa_map_lookup: key '%s'\n", name);
7396 *statp = EX_DATAERR;
7398 memset(result, '\0', sizeof(result));
7402 if (sm_strncasecmp(name, "IPv6:", 5) == 0)
7404 struct in6_addr in6_addr;
7406 r = anynet_pton(AF_INET6, name, &in6_addr);
7409 static char hex_digits[] =
7410 { '0', '1', '2', '3', '4', '5', '6', '7', '8',
7411 '9', 'a', 'b', 'c', 'd', 'e', 'f' };
7417 src = (unsigned char *) &in6_addr;
7419 for (i = 15; i >= 0; i--) {
7420 *dst++ = hex_digits[src[i] & 0x0f];
7422 *dst++ = hex_digits[(src[i] >> 4) & 0x0f];
7430 # endif /* NETINET6 */
7433 struct in_addr in_addr;
7435 r = inet_pton(AF_INET, name, &in_addr);
7440 src = (unsigned char *) &in_addr;
7441 (void) snprintf(result, sizeof(result),
7443 src[3], src[2], src[1], src[0]);
7447 # endif /* NETINET */
7449 *statp = EX_UNAVAILABLE;
7451 sm_dprintf("arpa_map_lookup: r=%d, result='%s'\n", r, result);
7452 if (*statp == EX_OK)
7454 if (bitset(MF_MATCHONLY, map->map_mflags))
7455 rval = map_rewrite(map, name, strlen(name), NULL);
7457 rval = map_rewrite(map, result, strlen(result), av);
7464 # if NETINET || NETINET6
7465 # include <arpa/inet.h>
7466 # endif /* NETINET || NETINET6 */
7468 # define socket_map_next map_stack[0]
7471 ** SOCKET_MAP_OPEN -- open socket table
7475 socket_map_open(map, mode)
7482 SOCKADDR_LEN_T addrlen = 0;
7488 struct hostent *hp = NULL;
7492 sm_dprintf("socket_map_open(%s, %s, %d)\n",
7493 map->map_mname, map->map_file, mode);
7497 /* sendmail doesn't have the ability to write to SOCKET (yet) */
7498 if (mode != O_RDONLY)
7500 /* issue a pseudo-error message */
7501 errno = SM_EMAPCANTWRITE;
7505 if (*map->map_file == '\0')
7507 syserr("socket map \"%s\": empty or missing socket information",
7512 s = socket_map_findconn(map->map_file);
7513 if (s->s_socketmap != NULL)
7515 /* Copy open connection */
7516 map->map_db1 = s->s_socketmap->map_db1;
7518 /* Add this map as head of linked list */
7519 map->socket_map_next = s->s_socketmap;
7520 s->s_socketmap = map;
7523 sm_dprintf("using cached connection\n");
7528 sm_dprintf("opening new connection\n");
7530 /* following code is ripped from milter.c */
7531 /* XXX It should be put in a library... */
7533 /* protocol:filename or protocol:port@host */
7534 memset(&addr, '\0', sizeof(addr));
7536 colon = strchr(p, ':');
7544 /* default to AF_UNIX */
7545 addr.sa.sa_family = AF_UNIX;
7546 # else /* NETUNIX */
7548 /* default to AF_INET */
7549 addr.sa.sa_family = AF_INET;
7550 # else /* NETINET */
7552 /* default to AF_INET6 */
7553 addr.sa.sa_family = AF_INET6;
7554 # else /* NETINET6 */
7555 /* no protocols available */
7556 syserr("socket map \"%s\": no valid socket protocols available",
7559 # endif /* NETINET6 */
7560 # endif /* NETINET */
7561 # endif /* NETUNIX */
7564 else if (sm_strcasecmp(p, "unix") == 0 ||
7565 sm_strcasecmp(p, "local") == 0)
7566 addr.sa.sa_family = AF_UNIX;
7567 # endif /* NETUNIX */
7569 else if (sm_strcasecmp(p, "inet") == 0)
7570 addr.sa.sa_family = AF_INET;
7571 # endif /* NETINET */
7573 else if (sm_strcasecmp(p, "inet6") == 0)
7574 addr.sa.sa_family = AF_INET6;
7575 # endif /* NETINET6 */
7578 # ifdef EPROTONOSUPPORT
7579 errno = EPROTONOSUPPORT;
7580 # else /* EPROTONOSUPPORT */
7582 # endif /* EPROTONOSUPPORT */
7583 syserr("socket map \"%s\": unknown socket type %s",
7593 /* default to AF_UNIX */
7594 addr.sa.sa_family = AF_UNIX;
7597 /* default to AF_INET */
7598 addr.sa.sa_family = AF_INET;
7599 # else /* NETINET */
7601 /* default to AF_INET6 */
7602 addr.sa.sa_family = AF_INET6;
7603 # else /* NETINET6 */
7604 syserr("socket map \"%s\": unknown socket type %s",
7607 # endif /* NETINET6 */
7608 # endif /* NETINET */
7609 #endif /* NETUNIX */
7613 if (addr.sa.sa_family == AF_UNIX)
7615 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7618 if (strlen(colon) >= sizeof(addr.sunix.sun_path))
7620 syserr("socket map \"%s\": local socket name %s too long",
7621 map->map_mname, colon);
7624 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7625 S_IRUSR|S_IWUSR, NULL);
7629 /* if not safe, don't create */
7630 syserr("socket map \"%s\": local socket name %s unsafe",
7631 map->map_mname, colon);
7635 (void) sm_strlcpy(addr.sunix.sun_path, colon,
7636 sizeof(addr.sunix.sun_path));
7637 addrlen = sizeof(struct sockaddr_un);
7640 # endif /* NETUNIX */
7641 # if NETINET || NETINET6
7644 || addr.sa.sa_family == AF_INET
7645 # endif /* NETINET */
7647 || addr.sa.sa_family == AF_INET6
7648 # endif /* NETINET6 */
7651 unsigned short port;
7653 /* Parse port@host */
7654 at = strchr(colon, '@');
7657 syserr("socket map \"%s\": bad address %s (expected port@host)",
7658 map->map_mname, colon);
7662 if (isascii(*colon) && isdigit(*colon))
7663 port = htons((unsigned short) atoi(colon));
7666 # ifdef NO_GETSERVBYNAME
7667 syserr("socket map \"%s\": invalid port number %s",
7668 map->map_mname, colon);
7670 # else /* NO_GETSERVBYNAME */
7671 register struct servent *sp;
7673 sp = getservbyname(colon, "tcp");
7676 syserr("socket map \"%s\": unknown port name %s",
7677 map->map_mname, colon);
7681 # endif /* NO_GETSERVBYNAME */
7688 end = strchr(at, ']');
7693 unsigned long hid = INADDR_NONE;
7694 # endif /* NETINET */
7696 struct sockaddr_in6 hid6;
7697 # endif /* NETINET6 */
7701 if (addr.sa.sa_family == AF_INET &&
7702 (hid = inet_addr(&at[1])) != INADDR_NONE)
7704 addr.sin.sin_addr.s_addr = hid;
7705 addr.sin.sin_port = port;
7708 # endif /* NETINET */
7710 (void) memset(&hid6, '\0', sizeof(hid6));
7711 if (addr.sa.sa_family == AF_INET6 &&
7712 anynet_pton(AF_INET6, &at[1],
7713 &hid6.sin6_addr) == 1)
7715 addr.sin6.sin6_addr = hid6.sin6_addr;
7716 addr.sin6.sin6_port = port;
7719 # endif /* NETINET6 */
7723 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7724 map->map_mname, at);
7730 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7731 map->map_mname, at);
7737 hp = sm_gethostbyname(at, addr.sa.sa_family);
7740 syserr("socket map \"%s\": Unknown host name %s",
7741 map->map_mname, at);
7744 addr.sa.sa_family = hp->h_addrtype;
7745 switch (hp->h_addrtype)
7749 memmove(&addr.sin.sin_addr,
7750 hp->h_addr, INADDRSZ);
7751 addr.sin.sin_port = port;
7752 addrlen = sizeof(struct sockaddr_in);
7755 # endif /* NETINET */
7759 memmove(&addr.sin6.sin6_addr,
7760 hp->h_addr, IN6ADDRSZ);
7761 addr.sin6.sin6_port = port;
7762 addrlen = sizeof(struct sockaddr_in6);
7765 # endif /* NETINET6 */
7768 syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7769 map->map_mname, at, hp->h_addrtype);
7772 # endif /* NETINET6 */
7778 # endif /* NETINET || NETINET6 */
7780 syserr("socket map \"%s\": unknown socket protocol",
7785 /* nope, actually connecting */
7788 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
7793 sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7795 sm_errstring(save_errno));
7799 # endif /* NETINET6 */
7803 if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
7806 /* couldn't connect.... try next address */
7811 sm_dprintf("socket_open (%s): open %s failed: %s\n",
7812 map->map_mname, at, sm_errstring(save_errno));
7816 /* try next address */
7817 if (hp != NULL && hp->h_addr_list[addrno] != NULL)
7819 switch (addr.sa.sa_family)
7823 memmove(&addr.sin.sin_addr,
7824 hp->h_addr_list[addrno++],
7827 # endif /* NETINET */
7831 memmove(&addr.sin6.sin6_addr,
7832 hp->h_addr_list[addrno++],
7835 # endif /* NETINET6 */
7839 sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7844 # endif /* NETINET6 */
7852 sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7853 map->map_mname, sm_errstring(save_errno));
7858 # endif /* NETINET6 */
7867 # endif /* NETINET6 */
7868 if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
7876 sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7877 map->map_mname, sm_errstring(errno));
7881 tmo = map->map_timeout;
7883 tmo = 30000; /* default: 30s */
7885 tmo *= 1000; /* s -> ms */
7886 sm_io_setinfo(map->map_db1, SM_IO_WHAT_TIMEOUT, &tmo);
7888 /* Save connection for reuse */
7889 s->s_socketmap = map;
7894 ** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7896 ** Cache SOCKET connections based on the connection specifier
7897 ** and PID so we don't have multiple connections open to
7898 ** the same server for different maps. Need a separate connection
7899 ** per PID since a parent process may close the map before the
7900 ** child is done with it.
7903 ** conn -- SOCKET map connection specifier
7906 ** Symbol table entry for the SOCKET connection.
7910 socket_map_findconn(conn)
7914 STAB *SM_NONVOLATILE s = NULL;
7916 nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
7918 s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
7926 ** SOCKET_MAP_CLOSE -- close the socket
7930 socket_map_close(map)
7937 sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
7940 /* Check if already closed */
7941 if (map->map_db1 == NULL)
7944 sm_dprintf("socket_map_close(%s) already closed\n",
7948 sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
7950 /* Mark all the maps that share the connection as closed */
7951 s = socket_map_findconn(map->map_file);
7952 smap = s->s_socketmap;
7953 while (smap != NULL)
7957 if (tTd(38, 2) && smap != map)
7958 sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7959 map->map_mname, smap->map_mname);
7961 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
7962 smap->map_db1 = NULL;
7963 next = smap->socket_map_next;
7964 smap->socket_map_next = NULL;
7967 s->s_socketmap = NULL;
7971 ** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7975 socket_map_lookup(map, name, av, statp)
7981 unsigned int nettolen, replylen, recvlen;
7982 char *replybuf, *rval, *value, *status, *key;
7984 char keybuf[MAXNAME + 1];
7988 f = (SM_FILE_T *)map->map_db1;
7990 sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7991 map->map_mname, name, map->map_file);
7993 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7995 nettolen = strlen(name);
7996 if (nettolen > sizeof(keybuf) - 1)
7997 nettolen = sizeof(keybuf) - 1;
7998 memmove(keybuf, name, nettolen);
7999 keybuf[nettolen] = '\0';
8006 nettolen = strlen(map->map_mname) + 1 + strlen(key);
8007 SM_ASSERT(nettolen > strlen(map->map_mname));
8008 SM_ASSERT(nettolen > strlen(key));
8009 if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
8010 nettolen, map->map_mname, key) == SM_IO_EOF) ||
8011 (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
8014 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
8016 *statp = EX_TEMPFAIL;
8020 if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
8022 if (errno == EAGAIN)
8024 syserr("451 4.3.0 socket_map_lookup(%s): read timeout",
8029 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply %d",
8030 map->map_mname, errno);
8032 *statp = EX_TEMPFAIL;
8035 if (replylen > SOCKETMAP_MAXL)
8037 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
8038 map->map_mname, replylen);
8039 *statp = EX_TEMPFAIL;
8042 if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
8044 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
8046 *statp = EX_TEMPFAIL;
8050 replybuf = (char *) sm_malloc(replylen + 1);
8051 if (replybuf == NULL)
8053 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
8054 map->map_mname, replylen + 1);
8059 recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
8060 if (recvlen < replylen)
8062 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
8063 map->map_mname, recvlen, replylen);
8064 *statp = EX_TEMPFAIL;
8067 if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
8069 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
8071 *statp = EX_TEMPFAIL;
8075 replybuf[recvlen] = '\0';
8076 value = strchr(replybuf, ' ');
8082 if (strcmp(status, "OK") == 0)
8086 /* collect the return value */
8087 if (bitset(MF_MATCHONLY, map->map_mflags))
8088 rval = map_rewrite(map, key, strlen(key), NULL);
8090 rval = map_rewrite(map, value, strlen(value), av);
8092 else if (strcmp(status, "NOTFOUND") == 0)
8094 *statp = EX_NOTFOUND;
8096 sm_dprintf("socket_map_lookup(%s): %s not found\n",
8097 map->map_mname, key);
8102 sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
8103 map->map_mname, key, status,
8104 value ? value : "");
8105 if ((strcmp(status, "TEMP") == 0) ||
8106 (strcmp(status, "TIMEOUT") == 0))
8107 *statp = EX_TEMPFAIL;
8108 else if(strcmp(status, "PERM") == 0)
8109 *statp = EX_UNAVAILABLE;
8111 *statp = EX_PROTOCOL;
8114 if (replybuf != NULL)
8119 socket_map_close(map);
8121 if (replybuf != NULL)
8125 #endif /* SOCKETMAP */