2 * Copyright (c) 1998-2008 Sendmail, 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.705 2009/08/11 22:22:40 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;
212 while (isascii(*++p) && isspace(*p))
214 map->map_keycolnm = p;
218 while (isascii(*++p) && isspace(*p))
220 map->map_valcolnm = p;
225 map->map_coldelim = *p;
231 map->map_coldelim = '\n';
235 map->map_coldelim = '\t';
239 map->map_coldelim = '\\';
245 map->map_mflags |= MF_NODEFER;
250 map->map_spacesub = *++p;
254 map->map_mflags |= MF_DEFER;
258 syserr("Illegal option %c map %s", *p, map->map_mname);
261 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
266 if (map->map_app != NULL)
267 map->map_app = newstr(map->map_app);
268 if (map->map_tapp != NULL)
269 map->map_tapp = newstr(map->map_tapp);
270 if (map->map_keycolnm != NULL)
271 map->map_keycolnm = newstr(map->map_keycolnm);
272 if (map->map_valcolnm != NULL)
273 map->map_valcolnm = newstr(map->map_valcolnm);
278 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
282 map->map_file = newstr(map->map_file);
285 while (*p != '\0' && isascii(*p) && isspace(*p))
288 map->map_rebuild = newstr(p);
290 if (map->map_file == NULL &&
291 !bitset(MCF_OPTFILE, map->map_class->map_cflags))
293 syserr("No file name for %s map %s",
294 map->map_class->map_cname, map->map_mname);
300 ** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
302 ** It also adds the map_app string. It can be used as a utility
303 ** in the map_lookup method.
306 ** map -- the map that causes this.
307 ** s -- the string to rewrite, NOT necessarily null terminated.
308 ** slen -- the length of s.
309 ** av -- arguments to interpolate into buf.
312 ** Pointer to rewritten result. This is static data that
313 ** should be copied if it is to be saved!
317 map_rewrite(map, s, slen, av)
319 register const char *s;
329 static size_t buflen = 0;
330 static char *buf = NULL;
334 sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
336 sm_dprintf(" (nullv)");
339 for (avp = av; *avp != NULL; avp++)
340 sm_dprintf("\n\t%s", *avp);
345 /* count expected size of output (can safely overestimate) */
351 while (l-- > 0 && (c = *sp++) != '\0')
358 if (!(isascii(c) && isdigit(c)))
360 for (avp = av; --c >= '0' && *avp != NULL; avp++)
367 if (map->map_app != NULL)
368 len += strlen(map->map_app);
371 /* need to malloc additional space */
375 buf = sm_pmalloc_x(buflen);
381 memmove(bp, s, slen);
384 /* assert(len > slen); */
389 while (slen-- > 0 && (c = *s++) != '\0')
399 if (slen-- <= 0 || (c = *s++) == '\0')
403 if (!(isascii(c) && isdigit(c)))
410 for (avp = av; --c >= '0' && *avp != NULL; avp++)
415 /* transliterate argument into output string */
416 for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
420 if (map->map_app != NULL && len > 0)
421 (void) sm_strlcpy(bp, map->map_app, len);
425 sm_dprintf("map_rewrite => %s\n", buf);
429 ** INITMAPS -- rebuild alias maps
442 checkfd012("entering initmaps");
444 stabapply(map_init, 0);
446 checkfd012("exiting initmaps");
450 ** MAP_INIT -- rebuild a map
453 ** s -- STAB entry: if map: try to rebuild
454 ** unused -- unused variable
460 ** will close already open rebuildable map.
471 /* has to be a map */
472 if (s->s_symtype != ST_MAP)
476 if (!bitset(MF_VALID, map->map_mflags))
480 sm_dprintf("map_init(%s:%s, %s)\n",
481 map->map_class->map_cname == NULL ? "NULL" :
482 map->map_class->map_cname,
483 map->map_mname == NULL ? "NULL" : map->map_mname,
484 map->map_file == NULL ? "NULL" : map->map_file);
486 if (!bitset(MF_ALIAS, map->map_mflags) ||
487 !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
490 sm_dprintf("\tnot rebuildable\n");
494 /* if already open, close it (for nested open) */
495 if (bitset(MF_OPEN, map->map_mflags))
497 map->map_mflags |= MF_CLOSING;
498 map->map_class->map_close(map);
499 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
502 (void) rebuildaliases(map, false);
506 ** OPENMAP -- open a map
509 ** map -- map to open (it must not be open).
512 ** whether open succeeded.
519 bool restore = false;
520 bool savehold = HoldErrs;
521 bool savequick = QuickAbort;
522 int saveerrors = Errors;
524 if (!bitset(MF_VALID, map->map_mflags))
527 /* better safe than sorry... */
528 if (bitset(MF_OPEN, map->map_mflags))
531 /* Don't send a map open error out via SMTP */
532 if ((OnlyOneError || QuickAbort) &&
533 (OpMode == MD_SMTP || OpMode == MD_DAEMON))
541 if (map->map_class->map_open(map, O_RDONLY))
544 sm_dprintf("openmap()\t%s:%s %s: valid\n",
545 map->map_class->map_cname == NULL ? "NULL" :
546 map->map_class->map_cname,
547 map->map_mname == NULL ? "NULL" :
549 map->map_file == NULL ? "NULL" :
551 map->map_mflags |= MF_OPEN;
552 map->map_pid = CurrentPid;
557 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
558 map->map_class->map_cname == NULL ? "NULL" :
559 map->map_class->map_cname,
560 map->map_mname == NULL ? "NULL" :
562 map->map_file == NULL ? "NULL" :
564 errno == 0 ? "" : ": ",
565 errno == 0 ? "" : sm_errstring(errno));
566 if (!bitset(MF_OPTIONAL, map->map_mflags))
568 extern MAPCLASS BogusMapClass;
570 map->map_orgclass = map->map_class;
571 map->map_class = &BogusMapClass;
572 map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
573 map->map_pid = CurrentPid;
577 /* don't try again */
578 map->map_mflags &= ~MF_VALID;
586 QuickAbort = savequick;
589 return bitset(MF_OPEN, map->map_mflags);
592 ** CLOSEMAPS -- close all open maps opened by the current pid.
595 ** bogus -- only close bogus maps.
605 stabapply(map_close, bogus);
608 ** MAP_CLOSE -- close a map opened by the current pid.
611 ** s -- STAB entry: if map: try to close
612 ** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
622 int bogus; /* int because of stabapply(), used as bool */
625 extern MAPCLASS BogusMapClass;
627 if (s->s_symtype != ST_MAP)
633 ** close the map iff:
634 ** it is valid and open and opened by this process
635 ** and (!bogus or it's a bogus map or it is not persistent)
636 ** negate this: return iff
637 ** it is not valid or it is not open or not opened by this process
638 ** or (bogus and it's not a bogus map and it's not not-persistent)
641 if (!bitset(MF_VALID, map->map_mflags) ||
642 !bitset(MF_OPEN, map->map_mflags) ||
643 bitset(MF_CLOSING, map->map_mflags) ||
644 map->map_pid != CurrentPid ||
645 (bogus && map->map_class != &BogusMapClass &&
646 !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
649 if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
650 map->map_orgclass != &BogusMapClass)
651 map->map_class = map->map_orgclass;
653 sm_dprintf("closemaps: closing %s (%s)\n",
654 map->map_mname == NULL ? "NULL" : map->map_mname,
655 map->map_file == NULL ? "NULL" : map->map_file);
657 if (!bitset(MF_OPENBOGUS, map->map_mflags))
659 map->map_mflags |= MF_CLOSING;
660 map->map_class->map_close(map);
662 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
665 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
666 extern int getdomainname();
668 /* this is mainly for backward compatibility in Sun environment */
673 ** Get the domain name from the kernel.
674 ** If it does not start with a leading dot, then remove
675 ** the first component. Since leading dots are funny Unix
676 ** files, we treat a leading "+" the same as a leading dot.
677 ** Finally, force there to be at least one dot in the domain name
678 ** (i.e. top-level domains are not allowed, like "com", must be
679 ** something like "sun.com").
683 char *period, *autodomain;
685 if (getdomainname(buf, sizeof buf) < 0)
692 printf("domainname = %s\n", buf);
696 period = strchr(buf, '.');
700 autodomain = period + 1;
701 if (strchr(autodomain, '.') == NULL)
704 return newstr(autodomain);
706 #endif /* SUN_EXTENSIONS && SUN_INIT_DOMAIN */
709 ** GETCANONNAME -- look up name using service switch
712 ** host -- the host name to look up.
713 ** hbsize -- the size of the host buffer.
714 ** trymx -- if set, try MX records.
715 ** pttl -- pointer to return TTL (can be NULL).
718 ** true -- if the host was found.
719 ** false -- otherwise.
723 getcanonname(host, hbsize, trymx, pttl)
732 bool got_tempfail = false;
733 auto int status = EX_UNAVAILABLE;
734 char *maptype[MAXMAPSTACK];
735 short mapreturn[MAXMAPACTIONS];
736 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
737 bool should_try_nis_domain = false;
738 static char *nis_domain = NULL;
741 nmaps = switch_map_find("hosts", maptype, mapreturn);
743 *pttl = SM_DEFAULT_TTL;
744 for (mapno = 0; mapno < nmaps; mapno++)
749 sm_dprintf("getcanonname(%s), trying %s\n",
750 host, maptype[mapno]);
751 if (strcmp("files", maptype[mapno]) == 0)
753 found = text_getcanonname(host, hbsize, &status);
756 else if (strcmp("nis", maptype[mapno]) == 0)
758 found = nis_getcanonname(host, hbsize, &status);
759 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
760 if (nis_domain == NULL)
761 nis_domain = sun_init_domain();
762 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
766 else if (strcmp("nisplus", maptype[mapno]) == 0)
768 found = nisplus_getcanonname(host, hbsize, &status);
769 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
770 if (nis_domain == NULL)
771 nis_domain = sun_init_domain();
772 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
776 else if (strcmp("dns", maptype[mapno]) == 0)
778 found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
780 #endif /* NAMED_BIND */
782 else if (strcmp("netinfo", maptype[mapno]) == 0)
784 found = ni_getcanonname(host, hbsize, &status);
790 status = EX_UNAVAILABLE;
794 ** Heuristic: if $m is not set, we are running during system
795 ** startup. In this case, when a name is apparently found
796 ** but has no dot, treat is as not found. This avoids
797 ** problems if /etc/hosts has no FQDN but is listed first
798 ** in the service switch.
802 (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
805 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
807 should_try_nis_domain = true;
808 /* but don't break, as we need to try all methods first */
809 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
811 /* see if we should continue */
812 if (status == EX_TEMPFAIL)
817 else if (status == EX_NOTFOUND)
821 if (bitset(1 << mapno, mapreturn[i]))
830 sm_dprintf("getcanonname(%s), found\n", host);
833 ** If returned name is still single token, compensate
834 ** by tagging on $m. This is because some sites set
835 ** up their DNS or NIS databases wrong.
838 if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
840 d = macvalue('m', CurEnv);
842 hbsize > (int) (strlen(host) + strlen(d) + 1))
844 if (host[strlen(host) - 1] != '.')
845 (void) sm_strlcat2(host, ".", d,
848 (void) sm_strlcat(host, d, hbsize);
852 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
853 if (VendorCode == VENDOR_SUN &&
854 should_try_nis_domain)
858 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
865 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
866 if (VendorCode == VENDOR_SUN && should_try_nis_domain)
869 if (nis_domain != NULL &&
870 strlen(nis_domain) + strlen(host) + 1 < hbsize)
872 (void) sm_strlcat2(host, ".", nis_domain, hbsize);
876 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
879 sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
883 SM_SET_H_ERRNO(TRY_AGAIN);
885 SM_SET_H_ERRNO(HOST_NOT_FOUND);
890 ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
893 ** name -- the name against which to match.
894 ** dot -- where to reinsert '.' to get FQDN
895 ** line -- the /etc/hosts line.
896 ** cbuf -- the location to store the result.
897 ** cbuflen -- the size of cbuf.
900 ** true -- if the line matched the desired name.
901 ** false -- otherwise.
905 extract_canonname(name, dot, line, cbuf, cbuflen)
922 char nbuf[MAXNAME + 1];
924 p = get_column(line, i, '\0', nbuf, sizeof(nbuf));
929 if (cbuf[0] == '\0' ||
930 (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
932 (void) sm_strlcpy(cbuf, p, cbuflen);
934 if (sm_strcasecmp(name, p) == 0)
936 else if (dot != NULL)
938 /* try looking for the FQDN as well */
940 if (sm_strcasecmp(name, p) == 0)
945 if (found && strchr(cbuf, '.') == NULL)
947 /* try to add a domain on the end of the name */
948 char *domain = macvalue('m', CurEnv);
950 if (domain != NULL &&
951 strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
955 (void) sm_strlcpy(p, domain, cbuflen - i - 1);
968 # include "sm_resolve.h"
969 # if NETINET || NETINET6
970 # include <arpa/inet.h>
971 # endif /* NETINET || NETINET6 */
974 ** DNS_MAP_OPEN -- stub to check proper value for dns map type
978 dns_map_open(map, mode)
983 sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
986 if (mode != O_RDONLY)
988 /* issue a pseudo-error message */
989 errno = SM_EMAPCANTWRITE;
996 ** DNS_MAP_PARSEARGS -- parse dns map definition args.
999 ** map -- pointer to MAP
1000 ** args -- pointer to the args on the config line.
1003 ** true -- if everything parsed OK.
1004 ** false -- otherwise.
1007 #define map_sizelimit map_lockfd /* overload field */
1015 dns_map_parseargs(map,args)
1019 register char *p = args;
1020 struct dns_map *map_p;
1022 map_p = (struct dns_map *) xalloc(sizeof(*map_p));
1023 map_p->dns_m_type = -1;
1024 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
1028 while (isascii(*p) && isspace(*p))
1035 map->map_mflags |= MF_INCLNULL;
1036 map->map_mflags &= ~MF_TRY0NULL;
1040 map->map_mflags &= ~MF_TRY1NULL;
1044 map->map_mflags |= MF_OPTIONAL;
1048 map->map_mflags |= MF_NOFOLDCASE;
1052 map->map_mflags |= MF_MATCHONLY;
1056 map->map_mflags |= MF_APPEND;
1060 map->map_mflags |= MF_KEEPQUOTES;
1064 map->map_mflags |= MF_NODEFER;
1072 map->map_tapp = ++p;
1083 map->map_timeout = convtime(p, 's');
1090 while (isascii(*++p) && isspace(*p))
1092 map->map_retry = atoi(p);
1097 map->map_coldelim = *p;
1103 map->map_coldelim = '\n';
1107 map->map_coldelim = '\t';
1111 map->map_coldelim = '\\';
1117 while (isascii(*++p) && isspace(*p))
1119 map->map_sizelimit = atoi(p);
1122 /* Start of dns_map specific args */
1123 case 'R': /* search field */
1127 while (isascii(*++p) && isspace(*p))
1132 map_p->dns_m_type = dns_string_to_type(p);
1135 if (map_p->dns_m_type < 0)
1136 syserr("dns map %s: wrong type %s",
1141 case 'B': /* base domain */
1145 while (isascii(*++p) && isspace(*p))
1152 ** slight abuse of map->map_file; it isn't
1153 ** used otherwise in this map type.
1156 map->map_file = newstr(p);
1162 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1167 if (map_p->dns_m_type < 0)
1168 syserr("dns map %s: missing -R type", map->map_mname);
1169 if (map->map_app != NULL)
1170 map->map_app = newstr(map->map_app);
1171 if (map->map_tapp != NULL)
1172 map->map_tapp = newstr(map->map_tapp);
1175 ** Assumption: assert(sizeof(int) <= sizeof(ARBPTR_T));
1176 ** Even if this assumption is wrong, we use only one byte,
1177 ** so it doesn't really matter.
1180 map->map_db1 = (ARBPTR_T) map_p;
1185 ** DNS_MAP_LOOKUP -- perform dns map lookup.
1188 ** map -- pointer to MAP
1189 ** name -- name to lookup
1190 ** av -- arguments to interpolate into buf.
1191 ** statp -- pointer to status (EX_)
1194 ** result of lookup if succeeded.
1195 ** NULL -- otherwise.
1199 dns_map_lookup(map, name, av, statp)
1206 char *vp = NULL, *result = NULL;
1208 struct dns_map *map_p;
1209 RESOURCE_RECORD_T *rr = NULL;
1210 DNS_REPLY_T *r = NULL;
1212 static char buf6[INET6_ADDRSTRLEN];
1213 # endif /* NETINET6 */
1216 sm_dprintf("dns_map_lookup(%s, %s)\n",
1217 map->map_mname, name);
1219 map_p = (struct dns_map *)(map->map_db1);
1220 if (map->map_file != NULL && *map->map_file != '\0')
1225 len = strlen(map->map_file) + strlen(name) + 2;
1226 appdomain = (char *) sm_malloc(len);
1227 if (appdomain == NULL)
1229 *statp = EX_UNAVAILABLE;
1232 (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
1233 r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
1234 map->map_timeout, map->map_retry);
1239 r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
1240 map->map_timeout, map->map_retry);
1246 if (h_errno == TRY_AGAIN || transienterror(errno))
1247 *statp = EX_TEMPFAIL;
1249 *statp = EX_NOTFOUND;
1253 for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
1258 switch (rr->rr_type)
1262 value = rr->rr_u.rr_txt;
1266 value = rr->rr_u.rr_txt;
1270 value = rr->rr_u.rr_mx->mx_r_domain;
1274 value = rr->rr_u.rr_srv->srv_r_target;
1278 value = rr->rr_u.rr_txt;
1282 value = rr->rr_u.rr_txt;
1286 value = rr->rr_u.rr_mx->mx_r_domain;
1291 value = inet_ntoa(*(rr->rr_u.rr_a));
1293 # endif /* NETINET */
1297 value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
1300 # endif /* NETINET6 */
1303 (void) strreplnonprt(value, 'X');
1304 if (map_p->dns_m_type != rr->rr_type)
1307 sm_dprintf("\tskipping type %s (%d) value %s\n",
1308 type != NULL ? type : "<UNKNOWN>",
1310 value != NULL ? value : "<NO VALUE>");
1315 if (rr->rr_type == T_AAAA && value == NULL)
1318 *statp = EX_DATAERR;
1320 sm_dprintf("\tbad T_AAAA conversion\n");
1323 # endif /* NETINET6 */
1325 sm_dprintf("\tfound type %s (%d) value %s\n",
1326 type != NULL ? type : "<UNKNOWN>",
1328 value != NULL ? value : "<NO VALUE>");
1329 if (value != NULL &&
1330 (map->map_coldelim == '\0' ||
1331 map->map_sizelimit == 1 ||
1332 bitset(MF_MATCHONLY, map->map_mflags)))
1334 /* Only care about the first match */
1338 else if (vp == NULL)
1345 /* concatenate the results */
1349 sz = strlen(vp) + strlen(value) + 2;
1351 (void) sm_snprintf(new, sz, "%s%c%s",
1352 vp, map->map_coldelim, value);
1355 if (map->map_sizelimit > 0 &&
1356 ++resnum >= map->map_sizelimit)
1363 *statp = EX_NOTFOUND;
1365 sm_dprintf("\tno match found\n");
1369 /* Cleanly truncate for rulesets */
1370 truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
1375 sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
1377 if (bitset(MF_MATCHONLY, map->map_mflags))
1378 result = map_rewrite(map, name, strlen(name), NULL);
1380 result = map_rewrite(map, vp, vsize, av);
1389 # endif /* DNSMAP */
1390 #endif /* NAMED_BIND */
1399 ** NDBM_MAP_OPEN -- DBM-style map open
1403 ndbm_map_open(map, mode)
1413 int smode = S_IREAD;
1414 char dirfile[MAXPATHLEN];
1415 char pagfile[MAXPATHLEN];
1417 struct stat std, stp;
1420 sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1421 map->map_mname, map->map_file, mode);
1422 map->map_lockfd = -1;
1425 /* do initial file and directory checks */
1426 if (sm_strlcpyn(dirfile, sizeof(dirfile), 2,
1427 map->map_file, ".dir") >= sizeof(dirfile) ||
1428 sm_strlcpyn(pagfile, sizeof(pagfile), 2,
1429 map->map_file, ".pag") >= sizeof(pagfile))
1432 if (!bitset(MF_OPTIONAL, map->map_mflags))
1433 syserr("dbm map \"%s\": map file %s name too long",
1434 map->map_mname, map->map_file);
1437 sff = SFF_ROOTOK|SFF_REGONLY;
1441 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1443 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1449 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1452 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1453 sff |= SFF_SAFEDIRPATH;
1454 ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
1457 ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
1462 char *prob = "unsafe";
1464 /* cannot open this map */
1468 sm_dprintf("\t%s map file: %d\n", prob, ret);
1469 if (!bitset(MF_OPTIONAL, map->map_mflags))
1470 syserr("dbm map \"%s\": %s map file %s",
1471 map->map_mname, prob, map->map_file);
1474 if (std.st_mode == ST_MODE_NOFILE)
1475 mode |= O_CREAT|O_EXCL;
1478 if (mode == O_RDONLY)
1481 mode |= O_TRUNC|O_EXLOCK;
1482 # else /* LOCK_ON_OPEN */
1483 if ((mode & O_ACCMODE) == O_RDWR)
1487 ** Warning: race condition. Try to lock the file as
1488 ** quickly as possible after opening it.
1489 ** This may also have security problems on some systems,
1490 ** but there isn't anything we can do about it.
1494 # else /* NOFTRUNCATE */
1496 ** This ugly code opens the map without truncating it,
1497 ** locks the file, then truncates it. Necessary to
1498 ** avoid race conditions.
1503 long sff = SFF_CREAT|SFF_OPENASROOT;
1505 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1507 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1510 dirfd = safeopen(dirfile, mode, DBMMODE, sff);
1511 pagfd = safeopen(pagfile, mode, DBMMODE, sff);
1513 if (dirfd < 0 || pagfd < 0)
1517 (void) close(dirfd);
1519 (void) close(pagfd);
1521 syserr("ndbm_map_open: cannot create database %s",
1525 if (ftruncate(dirfd, (off_t) 0) < 0 ||
1526 ftruncate(pagfd, (off_t) 0) < 0)
1529 (void) close(dirfd);
1530 (void) close(pagfd);
1532 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1537 /* if new file, get "before" bits for later filechanged check */
1538 if (std.st_mode == ST_MODE_NOFILE &&
1539 (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
1542 (void) close(dirfd);
1543 (void) close(pagfd);
1545 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1550 /* have to save the lock for the duration (bletch) */
1551 map->map_lockfd = dirfd;
1552 (void) close(pagfd);
1554 /* twiddle bits for dbm_open */
1555 mode &= ~(O_CREAT|O_EXCL);
1556 # endif /* NOFTRUNCATE */
1558 # endif /* LOCK_ON_OPEN */
1560 /* open the database */
1561 dbm = dbm_open(map->map_file, mode, DBMMODE);
1565 if (bitset(MF_ALIAS, map->map_mflags) &&
1566 aliaswait(map, ".pag", false))
1568 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1569 if (map->map_lockfd >= 0)
1570 (void) close(map->map_lockfd);
1571 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1573 if (!bitset(MF_OPTIONAL, map->map_mflags))
1574 syserr("Cannot open DBM database %s", map->map_file);
1577 dfd = dbm_dirfno(dbm);
1578 pfd = dbm_pagfno(dbm);
1581 /* heuristic: if files are linked, this is actually gdbm */
1583 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1584 if (map->map_lockfd >= 0)
1585 (void) close(map->map_lockfd);
1586 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1588 syserr("dbm map \"%s\": cannot support GDBM",
1593 if (filechanged(dirfile, dfd, &std) ||
1594 filechanged(pagfile, pfd, &stp))
1598 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1599 if (map->map_lockfd >= 0)
1600 (void) close(map->map_lockfd);
1601 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1603 syserr("ndbm_map_open(%s): file changed after open",
1608 map->map_db1 = (ARBPTR_T) dbm;
1611 ** Need to set map_mtime before the call to aliaswait()
1612 ** as aliaswait() will call map_lookup() which requires
1613 ** map_mtime to be set
1616 if (fstat(pfd, &st) >= 0)
1617 map->map_mtime = st.st_mtime;
1619 if (mode == O_RDONLY)
1623 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1625 (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
1626 # endif /* LOCK_ON_OPEN */
1627 if (bitset(MF_ALIAS, map->map_mflags) &&
1628 !aliaswait(map, ".pag", true))
1633 map->map_mflags |= MF_LOCKED;
1634 if (geteuid() == 0 && TrustedUid != 0)
1637 if (fchown(dfd, TrustedUid, -1) < 0 ||
1638 fchown(pfd, TrustedUid, -1) < 0)
1642 sm_syslog(LOG_ALERT, NOQID,
1643 "ownership change on %s failed: %s",
1644 map->map_file, sm_errstring(err));
1645 message("050 ownership change on %s failed: %s",
1646 map->map_file, sm_errstring(err));
1648 # else /* HASFCHOWN */
1649 sm_syslog(LOG_ALERT, NOQID,
1650 "no fchown(): cannot change ownership on %s",
1652 message("050 no fchown(): cannot change ownership on %s",
1654 # endif /* HASFCHOWN */
1662 ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1666 ndbm_map_lookup(map, name, av, statp)
1674 char keybuf[MAXNAME + 1];
1678 sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1679 map->map_mname, name);
1682 key.dsize = strlen(name);
1683 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1685 if (key.dsize > sizeof(keybuf) - 1)
1686 key.dsize = sizeof(keybuf) - 1;
1687 memmove(keybuf, key.dptr, key.dsize);
1688 keybuf[key.dsize] = '\0';
1693 dfd = dbm_dirfno((DBM *) map->map_db1);
1694 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1695 (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
1696 pfd = dbm_pagfno((DBM *) map->map_db1);
1697 if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
1698 stbuf.st_mtime > map->map_mtime)
1700 /* Reopen the database to sync the cache */
1701 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1704 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1705 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1706 map->map_mflags |= MF_CLOSING;
1707 map->map_class->map_close(map);
1708 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1709 if (map->map_class->map_open(map, omode))
1711 map->map_mflags |= MF_OPEN;
1712 map->map_pid = CurrentPid;
1713 if ((omode & O_ACCMODE) == O_RDWR)
1714 map->map_mflags |= MF_WRITABLE;
1719 if (!bitset(MF_OPTIONAL, map->map_mflags))
1721 extern MAPCLASS BogusMapClass;
1723 *statp = EX_TEMPFAIL;
1724 map->map_orgclass = map->map_class;
1725 map->map_class = &BogusMapClass;
1726 map->map_mflags |= MF_OPEN;
1727 map->map_pid = CurrentPid;
1728 syserr("Cannot reopen NDBM database %s",
1735 if (bitset(MF_TRY0NULL, map->map_mflags))
1737 val = dbm_fetch((DBM *) map->map_db1, key);
1738 if (val.dptr != NULL)
1739 map->map_mflags &= ~MF_TRY1NULL;
1741 if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1744 val = dbm_fetch((DBM *) map->map_db1, key);
1745 if (val.dptr != NULL)
1746 map->map_mflags &= ~MF_TRY0NULL;
1748 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1749 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1750 if (val.dptr == NULL)
1752 if (bitset(MF_MATCHONLY, map->map_mflags))
1753 return map_rewrite(map, name, strlen(name), NULL);
1755 return map_rewrite(map, val.dptr, val.dsize, av);
1760 ** NDBM_MAP_STORE -- store a datum in the database
1764 ndbm_map_store(map, lhs, rhs)
1772 char keybuf[MAXNAME + 1];
1775 sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1776 map->map_mname, lhs, rhs);
1778 key.dsize = strlen(lhs);
1780 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1782 if (key.dsize > sizeof(keybuf) - 1)
1783 key.dsize = sizeof(keybuf) - 1;
1784 memmove(keybuf, key.dptr, key.dsize);
1785 keybuf[key.dsize] = '\0';
1790 data.dsize = strlen(rhs);
1793 if (bitset(MF_INCLNULL, map->map_mflags))
1799 status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1802 if (!bitset(MF_APPEND, map->map_mflags))
1803 message("050 Warning: duplicate alias name %s", lhs);
1806 static char *buf = NULL;
1807 static int bufsiz = 0;
1811 old.dptr = ndbm_map_lookup(map, key.dptr,
1812 (char **) NULL, &xstat);
1813 if (old.dptr != NULL && *(char *) old.dptr != '\0')
1815 old.dsize = strlen(old.dptr);
1816 if (data.dsize + old.dsize + 2 > bufsiz)
1819 (void) sm_free(buf);
1820 bufsiz = data.dsize + old.dsize + 2;
1821 buf = sm_pmalloc_x(bufsiz);
1823 (void) sm_strlcpyn(buf, bufsiz, 3,
1824 data.dptr, ",", old.dptr);
1825 data.dsize = data.dsize + old.dsize + 1;
1828 sm_dprintf("ndbm_map_store append=%s\n",
1832 status = dbm_store((DBM *) map->map_db1,
1833 key, data, DBM_REPLACE);
1836 syserr("readaliases: dbm put (%s): %d", lhs, status);
1841 ** NDBM_MAP_CLOSE -- close the database
1849 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1850 map->map_mname, map->map_file, map->map_mflags);
1852 if (bitset(MF_WRITABLE, map->map_mflags))
1854 # ifdef NDBM_YP_COMPAT
1856 char buf[MAXHOSTNAMELEN];
1858 inclnull = bitset(MF_INCLNULL, map->map_mflags);
1859 map->map_mflags &= ~MF_INCLNULL;
1861 if (strstr(map->map_file, "/yp/") != NULL)
1863 long save_mflags = map->map_mflags;
1865 map->map_mflags |= MF_NOFOLDCASE;
1867 (void) sm_snprintf(buf, sizeof(buf), "%010ld", curtime());
1868 ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1870 (void) gethostname(buf, sizeof(buf));
1871 ndbm_map_store(map, "YP_MASTER_NAME", buf);
1873 map->map_mflags = save_mflags;
1877 map->map_mflags |= MF_INCLNULL;
1878 # endif /* NDBM_YP_COMPAT */
1880 /* write out the distinguished alias */
1881 ndbm_map_store(map, "@", "@");
1883 dbm_close((DBM *) map->map_db1);
1885 /* release lock (if needed) */
1887 if (map->map_lockfd >= 0)
1888 (void) close(map->map_lockfd);
1889 # endif /* !LOCK_ON_OPEN */
1894 ** NEWDB (Hash and BTree) Modules
1900 ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1902 ** These do rather bizarre locking. If you can lock on open,
1903 ** do that to avoid the condition of opening a database that
1904 ** is being rebuilt. If you don't, we'll try to fake it, but
1905 ** there will be a race condition. If opening for read-only,
1906 ** we immediately release the lock to avoid freezing things up.
1907 ** We really ought to hold the lock, but guarantee that we won't
1908 ** be pokey about it. That's hard to do.
1911 /* these should be K line arguments */
1912 # if DB_VERSION_MAJOR < 2
1913 # define db_cachesize cachesize
1914 # define h_nelem nelem
1915 # ifndef DB_CACHE_SIZE
1916 # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
1917 # endif /* ! DB_CACHE_SIZE */
1918 # ifndef DB_HASH_NELEM
1919 # define DB_HASH_NELEM 4096 /* (starting) size of hash table */
1920 # endif /* ! DB_HASH_NELEM */
1921 # endif /* DB_VERSION_MAJOR < 2 */
1924 bt_map_open(map, mode)
1928 # if DB_VERSION_MAJOR < 2
1930 # endif /* DB_VERSION_MAJOR < 2 */
1931 # if DB_VERSION_MAJOR == 2
1933 # endif /* DB_VERSION_MAJOR == 2 */
1934 # if DB_VERSION_MAJOR > 2
1935 void *btinfo = NULL;
1936 # endif /* DB_VERSION_MAJOR > 2 */
1939 sm_dprintf("bt_map_open(%s, %s, %d)\n",
1940 map->map_mname, map->map_file, mode);
1942 # if DB_VERSION_MAJOR < 3
1943 memset(&btinfo, '\0', sizeof(btinfo));
1944 # ifdef DB_CACHE_SIZE
1945 btinfo.db_cachesize = DB_CACHE_SIZE;
1946 # endif /* DB_CACHE_SIZE */
1947 # endif /* DB_VERSION_MAJOR < 3 */
1949 return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1953 hash_map_open(map, mode)
1957 # if DB_VERSION_MAJOR < 2
1959 # endif /* DB_VERSION_MAJOR < 2 */
1960 # if DB_VERSION_MAJOR == 2
1962 # endif /* DB_VERSION_MAJOR == 2 */
1963 # if DB_VERSION_MAJOR > 2
1965 # endif /* DB_VERSION_MAJOR > 2 */
1968 sm_dprintf("hash_map_open(%s, %s, %d)\n",
1969 map->map_mname, map->map_file, mode);
1971 # if DB_VERSION_MAJOR < 3
1972 memset(&hinfo, '\0', sizeof(hinfo));
1973 # ifdef DB_HASH_NELEM
1974 hinfo.h_nelem = DB_HASH_NELEM;
1975 # endif /* DB_HASH_NELEM */
1976 # ifdef DB_CACHE_SIZE
1977 hinfo.db_cachesize = DB_CACHE_SIZE;
1978 # endif /* DB_CACHE_SIZE */
1979 # endif /* DB_VERSION_MAJOR < 3 */
1981 return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1985 db_map_open(map, mode, mapclassname, dbtype, openinfo)
1990 # if DB_VERSION_MAJOR < 2
1991 const void *openinfo;
1992 # endif /* DB_VERSION_MAJOR < 2 */
1993 # if DB_VERSION_MAJOR == 2
1995 # endif /* DB_VERSION_MAJOR == 2 */
1996 # if DB_VERSION_MAJOR > 2
1998 # endif /* DB_VERSION_MAJOR > 2 */
2003 int smode = S_IREAD;
2008 char buf[MAXPATHLEN];
2010 /* do initial file and directory checks */
2011 if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
2014 if (!bitset(MF_OPTIONAL, map->map_mflags))
2015 syserr("map \"%s\": map file %s name too long",
2016 map->map_mname, map->map_file);
2020 if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
2022 if (sm_strlcat(buf, ".db", sizeof(buf)) >= sizeof(buf))
2025 if (!bitset(MF_OPTIONAL, map->map_mflags))
2026 syserr("map \"%s\": map file %s name too long",
2027 map->map_mname, map->map_file);
2035 sff = SFF_ROOTOK|SFF_REGONLY;
2039 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
2041 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
2047 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
2050 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
2051 sff |= SFF_SAFEDIRPATH;
2052 i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
2056 char *prob = "unsafe";
2058 /* cannot open this map */
2062 sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
2064 if (!bitset(MF_OPTIONAL, map->map_mflags))
2065 syserr("%s map \"%s\": %s map file %s",
2066 mapclassname, map->map_mname, prob, buf);
2069 if (st.st_mode == ST_MODE_NOFILE)
2070 omode |= O_CREAT|O_EXCL;
2072 map->map_lockfd = -1;
2076 omode |= O_TRUNC|O_EXLOCK;
2079 # else /* LOCK_ON_OPEN */
2081 ** Pre-lock the file to avoid race conditions. In particular,
2082 ** since dbopen returns NULL if the file is zero length, we
2083 ** must have a locked instance around the dbopen.
2086 fd = open(buf, omode, DBMMODE);
2089 if (!bitset(MF_OPTIONAL, map->map_mflags))
2090 syserr("db_map_open: cannot pre-open database %s", buf);
2094 /* make sure no baddies slipped in just before the open... */
2095 if (filechanged(buf, fd, &st))
2100 syserr("db_map_open(%s): file changed after pre-open", buf);
2104 /* if new file, get the "before" bits for later filechanged check */
2105 if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
2110 syserr("db_map_open(%s): cannot fstat pre-opened file",
2115 /* actually lock the pre-opened file */
2116 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
2117 syserr("db_map_open: cannot lock %s", buf);
2119 /* set up mode bits for dbopen */
2122 omode &= ~(O_EXCL|O_CREAT);
2123 # endif /* LOCK_ON_OPEN */
2125 # if DB_VERSION_MAJOR < 2
2126 db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
2127 # else /* DB_VERSION_MAJOR < 2 */
2130 # if DB_VERSION_MAJOR > 2
2132 # endif /* DB_VERSION_MAJOR > 2 */
2134 if (mode == O_RDONLY)
2136 if (bitset(O_CREAT, omode))
2138 if (bitset(O_TRUNC, omode))
2139 flags |= DB_TRUNCATE;
2140 SM_DB_FLAG_ADD(flags);
2142 # if DB_VERSION_MAJOR > 2
2143 ret = db_create(&db, NULL, 0);
2144 # ifdef DB_CACHE_SIZE
2145 if (ret == 0 && db != NULL)
2147 ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
2150 (void) db->close(db, 0);
2154 # endif /* DB_CACHE_SIZE */
2155 # ifdef DB_HASH_NELEM
2156 if (dbtype == DB_HASH && ret == 0 && db != NULL)
2158 ret = db->set_h_nelem(db, DB_HASH_NELEM);
2161 (void) db->close(db, 0);
2165 # endif /* DB_HASH_NELEM */
2166 if (ret == 0 && db != NULL)
2169 DBTXN /* transaction for DB 4.1 */
2170 buf, NULL, dbtype, flags, DBMMODE);
2173 #ifdef DB_OLD_VERSION
2174 if (ret == DB_OLD_VERSION)
2176 #endif /* DB_OLD_VERSION */
2177 (void) db->close(db, 0);
2182 # else /* DB_VERSION_MAJOR > 2 */
2183 errno = db_open(buf, dbtype, flags, DBMMODE,
2184 NULL, openinfo, &db);
2185 # endif /* DB_VERSION_MAJOR > 2 */
2187 # endif /* DB_VERSION_MAJOR < 2 */
2192 map->map_lockfd = fd;
2195 # endif /* !LOCK_ON_OPEN */
2199 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2200 aliaswait(map, ".db", false))
2203 if (map->map_lockfd >= 0)
2204 (void) close(map->map_lockfd);
2205 # endif /* !LOCK_ON_OPEN */
2207 if (!bitset(MF_OPTIONAL, map->map_mflags))
2208 syserr("Cannot open %s database %s",
2213 # if DB_VERSION_MAJOR < 2
2215 # else /* DB_VERSION_MAJOR < 2 */
2217 errno = db->fd(db, &fd);
2218 # endif /* DB_VERSION_MAJOR < 2 */
2219 if (filechanged(buf, fd, &st))
2222 # if DB_VERSION_MAJOR < 2
2223 (void) db->close(db);
2224 # else /* DB_VERSION_MAJOR < 2 */
2225 errno = db->close(db, 0);
2226 # endif /* DB_VERSION_MAJOR < 2 */
2228 if (map->map_lockfd >= 0)
2229 (void) close(map->map_lockfd);
2230 # endif /* !LOCK_ON_OPEN */
2232 syserr("db_map_open(%s): file changed after open", buf);
2237 map->map_mflags |= MF_LOCKED;
2239 if (fd >= 0 && mode == O_RDONLY)
2241 (void) lockfile(fd, buf, NULL, LOCK_UN);
2243 # endif /* LOCK_ON_OPEN */
2245 /* try to make sure that at least the database header is on disk */
2248 (void) db->sync(db, 0);
2249 if (geteuid() == 0 && TrustedUid != 0)
2252 if (fchown(fd, TrustedUid, -1) < 0)
2256 sm_syslog(LOG_ALERT, NOQID,
2257 "ownership change on %s failed: %s",
2258 buf, sm_errstring(err));
2259 message("050 ownership change on %s failed: %s",
2260 buf, sm_errstring(err));
2262 # else /* HASFCHOWN */
2263 sm_syslog(LOG_ALERT, NOQID,
2264 "no fchown(): cannot change ownership on %s",
2266 message("050 no fchown(): cannot change ownership on %s",
2268 # endif /* HASFCHOWN */
2272 map->map_db2 = (ARBPTR_T) db;
2275 ** Need to set map_mtime before the call to aliaswait()
2276 ** as aliaswait() will call map_lookup() which requires
2277 ** map_mtime to be set
2280 if (fd >= 0 && fstat(fd, &st) >= 0)
2281 map->map_mtime = st.st_mtime;
2283 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
2284 !aliaswait(map, ".db", true))
2291 ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2295 db_map_lookup(map, name, av, statp)
2302 register DB *db = (DB *) map->map_db2;
2308 char keybuf[MAXNAME + 1];
2309 char buf[MAXPATHLEN];
2311 memset(&key, '\0', sizeof(key));
2312 memset(&val, '\0', sizeof(val));
2315 sm_dprintf("db_map_lookup(%s, %s)\n",
2316 map->map_mname, name);
2318 if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
2321 if (!bitset(MF_OPTIONAL, map->map_mflags))
2322 syserr("map \"%s\": map file %s name too long",
2323 map->map_mname, map->map_file);
2327 if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
2330 key.size = strlen(name);
2331 if (key.size > sizeof(keybuf) - 1)
2332 key.size = sizeof(keybuf) - 1;
2334 memmove(keybuf, name, key.size);
2335 keybuf[key.size] = '\0';
2336 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2339 # if DB_VERSION_MAJOR < 2
2341 # else /* DB_VERSION_MAJOR < 2 */
2343 errno = db->fd(db, &fd);
2344 # endif /* DB_VERSION_MAJOR < 2 */
2345 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2346 (void) lockfile(fd, buf, ".db", LOCK_SH);
2347 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
2349 /* Reopen the database to sync the cache */
2350 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
2353 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2354 (void) lockfile(fd, buf, ".db", LOCK_UN);
2355 map->map_mflags |= MF_CLOSING;
2356 map->map_class->map_close(map);
2357 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
2358 if (map->map_class->map_open(map, omode))
2360 map->map_mflags |= MF_OPEN;
2361 map->map_pid = CurrentPid;
2362 if ((omode & O_ACCMODE) == O_RDWR)
2363 map->map_mflags |= MF_WRITABLE;
2364 db = (DB *) map->map_db2;
2369 if (!bitset(MF_OPTIONAL, map->map_mflags))
2371 extern MAPCLASS BogusMapClass;
2373 *statp = EX_TEMPFAIL;
2374 map->map_orgclass = map->map_class;
2375 map->map_class = &BogusMapClass;
2376 map->map_mflags |= MF_OPEN;
2377 map->map_pid = CurrentPid;
2378 syserr("Cannot reopen DB database %s",
2386 if (bitset(MF_TRY0NULL, map->map_mflags))
2388 # if DB_VERSION_MAJOR < 2
2389 st = db->get(db, &key, &val, 0);
2390 # else /* DB_VERSION_MAJOR < 2 */
2391 errno = db->get(db, NULL, &key, &val, 0);
2407 # endif /* DB_VERSION_MAJOR < 2 */
2409 map->map_mflags &= ~MF_TRY1NULL;
2411 if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
2414 # if DB_VERSION_MAJOR < 2
2415 st = db->get(db, &key, &val, 0);
2416 # else /* DB_VERSION_MAJOR < 2 */
2417 errno = db->get(db, NULL, &key, &val, 0);
2433 # endif /* DB_VERSION_MAJOR < 2 */
2435 map->map_mflags &= ~MF_TRY0NULL;
2438 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
2439 (void) lockfile(fd, buf, ".db", LOCK_UN);
2444 syserr("db_map_lookup: get (%s)", name);
2447 if (bitset(MF_MATCHONLY, map->map_mflags))
2448 return map_rewrite(map, name, strlen(name), NULL);
2450 return map_rewrite(map, val.data, val.size, av);
2455 ** DB_MAP_STORE -- store a datum in the NEWDB database
2459 db_map_store(map, lhs, rhs)
2467 register DB *db = map->map_db2;
2468 char keybuf[MAXNAME + 1];
2470 memset(&key, '\0', sizeof(key));
2471 memset(&data, '\0', sizeof(data));
2474 sm_dprintf("db_map_store(%s, %s, %s)\n",
2475 map->map_mname, lhs, rhs);
2477 key.size = strlen(lhs);
2479 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2481 if (key.size > sizeof(keybuf) - 1)
2482 key.size = sizeof(keybuf) - 1;
2483 memmove(keybuf, key.data, key.size);
2484 keybuf[key.size] = '\0';
2489 data.size = strlen(rhs);
2492 if (bitset(MF_INCLNULL, map->map_mflags))
2498 # if DB_VERSION_MAJOR < 2
2499 status = db->put(db, &key, &data, R_NOOVERWRITE);
2500 # else /* DB_VERSION_MAJOR < 2 */
2501 errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
2516 # endif /* DB_VERSION_MAJOR < 2 */
2519 if (!bitset(MF_APPEND, map->map_mflags))
2520 message("050 Warning: duplicate alias name %s", lhs);
2523 static char *buf = NULL;
2524 static int bufsiz = 0;
2527 memset(&old, '\0', sizeof(old));
2529 old.data = db_map_lookup(map, key.data,
2530 (char **) NULL, &status);
2531 if (old.data != NULL)
2533 old.size = strlen(old.data);
2534 if (data.size + old.size + 2 > (size_t) bufsiz)
2538 bufsiz = data.size + old.size + 2;
2539 buf = sm_pmalloc_x(bufsiz);
2541 (void) sm_strlcpyn(buf, bufsiz, 3,
2542 (char *) data.data, ",",
2544 data.size = data.size + old.size + 1;
2547 sm_dprintf("db_map_store append=%s\n",
2548 (char *) data.data);
2551 # if DB_VERSION_MAJOR < 2
2552 status = db->put(db, &key, &data, 0);
2553 # else /* DB_VERSION_MAJOR < 2 */
2554 status = errno = db->put(db, NULL, &key, &data, 0);
2555 # endif /* DB_VERSION_MAJOR < 2 */
2558 syserr("readaliases: db put (%s)", lhs);
2563 ** DB_MAP_CLOSE -- add distinguished entries and close the database
2570 register DB *db = map->map_db2;
2573 sm_dprintf("db_map_close(%s, %s, %lx)\n",
2574 map->map_mname, map->map_file, map->map_mflags);
2576 if (bitset(MF_WRITABLE, map->map_mflags))
2578 /* write out the distinguished alias */
2579 db_map_store(map, "@", "@");
2582 (void) db->sync(db, 0);
2585 if (map->map_lockfd >= 0)
2586 (void) close(map->map_lockfd);
2587 # endif /* !LOCK_ON_OPEN */
2589 # if DB_VERSION_MAJOR < 2
2590 if (db->close(db) != 0)
2591 # else /* DB_VERSION_MAJOR < 2 */
2593 ** Berkeley DB can use internal shared memory
2594 ** locking for its memory pool. Closing a map
2595 ** opened by another process will interfere
2596 ** with the shared memory and locks of the parent
2597 ** process leaving things in a bad state.
2601 ** If this map was not opened by the current
2602 ** process, do not close the map but recover
2603 ** the file descriptor.
2606 if (map->map_pid != CurrentPid)
2610 errno = db->fd(db, &fd);
2616 if ((errno = db->close(db, 0)) != 0)
2617 # endif /* DB_VERSION_MAJOR < 2 */
2618 syserr("db_map_close(%s, %s, %lx): db close failure",
2619 map->map_mname, map->map_file, map->map_mflags);
2629 # define YPERR_BUSY 16
2630 # endif /* ! YPERR_BUSY */
2633 ** NIS_MAP_OPEN -- open DBM map
2637 nis_map_open(map, mode)
2647 sm_dprintf("nis_map_open(%s, %s, %d)\n",
2648 map->map_mname, map->map_file, mode);
2651 if (mode != O_RDONLY)
2653 /* issue a pseudo-error message */
2654 errno = SM_EMAPCANTWRITE;
2658 p = strchr(map->map_file, '@');
2663 map->map_domain = p;
2666 if (*map->map_file == '\0')
2667 map->map_file = "mail.aliases";
2669 if (map->map_domain == NULL)
2671 yperr = yp_get_default_domain(&map->map_domain);
2674 if (!bitset(MF_OPTIONAL, map->map_mflags))
2675 syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2681 /* check to see if this map actually exists */
2683 yperr = yp_match(map->map_domain, map->map_file, "@", 1,
2686 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2687 map->map_domain, map->map_file, yperr_string(yperr));
2691 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
2694 ** We ought to be calling aliaswait() here if this is an
2695 ** alias file, but powerful HP-UX NIS servers apparently
2696 ** don't insert the @:@ token into the alias map when it
2697 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
2701 if (!bitset(MF_ALIAS, map->map_mflags) ||
2702 aliaswait(map, NULL, true))
2707 if (!bitset(MF_OPTIONAL, map->map_mflags))
2709 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2710 map->map_file, map->map_domain, yperr_string(yperr));
2718 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map
2723 nis_map_lookup(map, name, av, statp)
2733 char keybuf[MAXNAME + 1];
2734 char *SM_NONVOLATILE result = NULL;
2737 sm_dprintf("nis_map_lookup(%s, %s)\n",
2738 map->map_mname, name);
2740 buflen = strlen(name);
2741 if (buflen > sizeof(keybuf) - 1)
2742 buflen = sizeof(keybuf) - 1;
2743 memmove(keybuf, name, buflen);
2744 keybuf[buflen] = '\0';
2745 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
2749 if (bitset(MF_TRY0NULL, map->map_mflags))
2751 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2754 map->map_mflags &= ~MF_TRY1NULL;
2756 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
2760 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
2763 map->map_mflags &= ~MF_TRY0NULL;
2767 if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
2768 map->map_mflags &= ~(MF_VALID|MF_OPEN);
2774 if (bitset(MF_MATCHONLY, map->map_mflags))
2775 result = map_rewrite(map, name, strlen(name), NULL);
2777 result = map_rewrite(map, vp, vsize, av);
2787 ** NIS_GETCANONNAME -- look up canonical name in NIS
2791 nis_getcanonname(name, hbsize, statp)
2800 static bool try0null = true;
2801 static bool try1null = true;
2802 static char *yp_domain = NULL;
2803 char host_record[MAXLINE];
2805 char nbuf[MAXNAME + 1];
2808 sm_dprintf("nis_getcanonname(%s)\n", name);
2810 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
2812 *statp = EX_UNAVAILABLE;
2815 (void) shorten_hostname(nbuf);
2816 keylen = strlen(nbuf);
2818 if (yp_domain == NULL)
2819 (void) yp_get_default_domain(&yp_domain);
2825 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2830 if (yperr == YPERR_KEY && try1null)
2834 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
2841 if (yperr == YPERR_KEY)
2843 else if (yperr == YPERR_BUSY)
2844 *statp = EX_TEMPFAIL;
2846 *statp = EX_UNAVAILABLE;
2851 (void) sm_strlcpy(host_record, vp, sizeof(host_record));
2854 sm_dprintf("got record `%s'\n", host_record);
2855 vp = strpbrk(host_record, "#\n");
2858 if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof(cbuf)))
2860 /* this should not happen, but.... */
2864 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
2866 *statp = EX_UNAVAILABLE;
2877 ** This code donated by Sun Microsystems.
2882 # undef NIS /* symbol conflict in nis.h */
2883 # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2884 # include <rpcsvc/nis.h>
2885 # include <rpcsvc/nislib.h>
2887 # define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2888 # define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2889 # define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2890 # define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
2893 ** NISPLUS_MAP_OPEN -- open nisplus table
2897 nisplus_map_open(map, mode)
2901 nis_result *res = NULL;
2902 int retry_cnt, max_col, i;
2903 char qbuf[MAXLINE + NIS_MAXNAMELEN];
2906 sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2907 map->map_mname, map->map_file, mode);
2910 if (mode != O_RDONLY)
2916 if (*map->map_file == '\0')
2917 map->map_file = "mail_aliases.org_dir";
2919 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
2921 /* set default NISPLUS Domain to $m */
2922 map->map_domain = newstr(nisplus_default_domain());
2924 sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2925 map->map_file, map->map_domain);
2927 if (!PARTIAL_NAME(map->map_file))
2929 map->map_domain = newstr("");
2930 (void) sm_strlcpy(qbuf, map->map_file, sizeof(qbuf));
2934 /* check to see if this map actually exists */
2935 (void) sm_strlcpyn(qbuf, sizeof(qbuf), 3,
2936 map->map_file, ".", map->map_domain);
2940 while (res == NULL || res->status != NIS_SUCCESS)
2942 res = nis_lookup(qbuf, FOLLOW_LINKS);
2943 switch (res->status)
2950 case NIS_NAMEUNREACHABLE:
2951 if (retry_cnt++ > 4)
2956 /* try not to overwhelm hosed server */
2960 default: /* all other nisplus errors */
2962 if (!bitset(MF_OPTIONAL, map->map_mflags))
2963 syserr("451 4.3.5 Cannot find table %s.%s: %s",
2964 map->map_file, map->map_domain,
2965 nis_sperrno(res->status));
2972 if (NIS_RES_NUMOBJ(res) != 1 ||
2973 (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
2976 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
2978 if (!bitset(MF_OPTIONAL, map->map_mflags))
2979 syserr("451 4.3.5 %s.%s: %s is not a table",
2980 map->map_file, map->map_domain,
2981 nis_sperrno(res->status));
2986 /* default key column is column 0 */
2987 if (map->map_keycolnm == NULL)
2988 map->map_keycolnm = newstr(COL_NAME(res,0));
2990 max_col = COL_MAX(res);
2992 /* verify the key column exist */
2993 for (i = 0; i < max_col; i++)
2995 if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
3001 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
3002 map->map_file, map->map_keycolnm);
3007 /* default value column is the last column */
3008 if (map->map_valcolnm == NULL)
3010 map->map_valcolno = max_col - 1;
3014 for (i = 0; i< max_col; i++)
3016 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
3018 map->map_valcolno = i;
3024 sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
3025 map->map_file, map->map_keycolnm);
3032 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
3036 nisplus_map_lookup(map, name, av, statp)
3046 char search_key[MAXNAME + 4];
3047 char qbuf[MAXLINE + NIS_MAXNAMELEN];
3051 sm_dprintf("nisplus_map_lookup(%s, %s)\n",
3052 map->map_mname, name);
3054 if (!bitset(MF_OPEN, map->map_mflags))
3056 if (nisplus_map_open(map, O_RDONLY))
3058 map->map_mflags |= MF_OPEN;
3059 map->map_pid = CurrentPid;
3063 *statp = EX_UNAVAILABLE;
3069 ** Copy the name to the key buffer, escaping double quote characters
3070 ** by doubling them and quoting "]" and "," to avoid having the
3071 ** NIS+ parser choke on them.
3074 skleft = sizeof(search_key) - 4;
3076 for (p = name; *p != '\0' && skleft > 0; p++)
3082 /* quote the character */
3090 /* double the quote */
3102 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3103 makelower(search_key);
3105 /* construct the query */
3106 if (PARTIAL_NAME(map->map_file))
3107 (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s.%s",
3108 map->map_keycolnm, search_key, map->map_file,
3111 (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s",
3112 map->map_keycolnm, search_key, map->map_file);
3115 sm_dprintf("qbuf=%s\n", qbuf);
3116 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
3117 if (result->status == NIS_SUCCESS)
3122 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3125 sm_syslog(LOG_WARNING, CurEnv->e_id,
3126 "%s: lookup error, expected 1 entry, got %d",
3127 map->map_file, count);
3129 /* ignore second entry */
3131 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3135 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
3136 /* set the length of the result */
3141 sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3143 if (bitset(MF_MATCHONLY, map->map_mflags))
3144 str = map_rewrite(map, name, strlen(name), NULL);
3146 str = map_rewrite(map, p, vsize, av);
3147 nis_freeresult(result);
3153 if (result->status == NIS_NOTFOUND)
3154 *statp = EX_NOTFOUND;
3155 else if (result->status == NIS_TRYAGAIN)
3156 *statp = EX_TEMPFAIL;
3159 *statp = EX_UNAVAILABLE;
3160 map->map_mflags &= ~(MF_VALID|MF_OPEN);
3164 sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
3165 nis_freeresult(result);
3172 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3176 nisplus_getcanonname(name, hbsize, statp)
3185 char nbuf[MAXNAME + 1];
3186 char qbuf[MAXLINE + NIS_MAXNAMELEN];
3188 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
3190 *statp = EX_UNAVAILABLE;
3193 (void) shorten_hostname(nbuf);
3195 p = strchr(nbuf, '.');
3199 (void) sm_snprintf(qbuf, sizeof(qbuf),
3200 "[name=%s],hosts.org_dir", nbuf);
3202 else if (p[1] != '\0')
3204 /* multi token -- take only first token in nbuf */
3206 (void) sm_snprintf(qbuf, sizeof(qbuf),
3207 "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
3216 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3219 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
3222 if (result->status == NIS_SUCCESS)
3227 if ((count = NIS_RES_NUMOBJ(result)) != 1)
3230 sm_syslog(LOG_WARNING, CurEnv->e_id,
3231 "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3234 /* ignore second entry */
3236 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3241 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3242 name, (NIS_RES_OBJECT(result))->zo_domain);
3245 vp = ((NIS_RES_OBJECT(result))->EN_col(0));
3248 sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3250 if (strchr(vp, '.') != NULL)
3256 domain = macvalue('m', CurEnv);
3260 if (hbsize > vsize + (int) strlen(domain) + 1)
3262 if (domain[0] == '\0')
3263 (void) sm_strlcpy(name, vp, hbsize);
3265 (void) sm_snprintf(name, hbsize,
3266 "%s.%s", vp, domain);
3271 nis_freeresult(result);
3276 if (result->status == NIS_NOTFOUND)
3278 else if (result->status == NIS_TRYAGAIN)
3279 *statp = EX_TEMPFAIL;
3281 *statp = EX_UNAVAILABLE;
3284 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3285 name, result->status, *statp);
3286 nis_freeresult(result);
3291 nisplus_default_domain()
3293 static char default_domain[MAXNAME + 1] = "";
3296 if (default_domain[0] != '\0')
3297 return default_domain;
3299 p = nis_local_directory();
3300 (void) sm_strlcpy(default_domain, p, sizeof(default_domain));
3301 return default_domain;
3304 #endif /* NISPLUS */
3310 ** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3313 #if defined(LDAPMAP) || defined(PH_MAP)
3316 # define ph_map_dequote ldapmap_dequote
3317 # endif /* PH_MAP */
3319 static char *ldapmap_dequote __P((char *));
3322 ldapmap_dequote(str)
3334 /* Should probably swallow initial whitespace here */
3339 while (*p != '"' && *p != '\0')
3345 #endif /* defined(LDAPMAP) || defined(PH_MAP) */
3349 static SM_LDAP_STRUCT *LDAPDefaults = NULL;
3352 ** LDAPMAP_OPEN -- open LDAP map
3354 ** Connect to the LDAP server. Re-use existing connections since a
3355 ** single server connection to a host (with the same host, port,
3356 ** bind DN, and secret) can answer queries for multiple maps.
3360 ldapmap_open(map, mode)
3364 SM_LDAP_STRUCT *lmap;
3369 sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
3371 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
3372 HASLDAPGETALIASBYNAME
3373 if (VendorCode == VENDOR_SUN &&
3374 strcmp(map->map_mname, "aliases.ldap") == 0)
3378 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
3382 /* sendmail doesn't have the ability to write to LDAP (yet) */
3383 if (mode != O_RDONLY)
3385 /* issue a pseudo-error message */
3386 errno = SM_EMAPCANTWRITE;
3390 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3392 s = ldapmap_findconn(lmap);
3393 if (s->s_lmap != NULL)
3395 /* Already have a connection open to this LDAP server */
3396 lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
3397 lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
3399 /* Add this map as head of linked list */
3400 lmap->ldap_next = s->s_lmap;
3404 sm_dprintf("using cached connection\n");
3409 sm_dprintf("opening new connection\n");
3411 if (lmap->ldap_host != NULL)
3412 id = lmap->ldap_host;
3413 else if (lmap->ldap_uri != NULL)
3414 id = lmap->ldap_uri;
3420 extern MAPCLASS NullMapClass;
3422 /* debug mode: don't actually open an LDAP connection */
3423 map->map_orgclass = map->map_class;
3424 map->map_class = &NullMapClass;
3425 map->map_mflags |= MF_OPEN;
3426 map->map_pid = CurrentPid;
3430 /* No connection yet, connect */
3431 if (!sm_ldap_start(map->map_mname, lmap))
3433 if (errno == ETIMEDOUT)
3436 sm_syslog(LOG_NOTICE, CurEnv->e_id,
3437 "timeout conning to LDAP server %.100s",
3441 if (!bitset(MF_OPTIONAL, map->map_mflags))
3443 if (bitset(MF_NODEFER, map->map_mflags))
3445 syserr("%s failed to %s in map %s",
3447 "ldap_init/ldap_bind",
3448 # else /* USE_LDAP_INIT */
3450 # endif /* USE_LDAP_INIT */
3451 id, map->map_mname);
3455 syserr("451 4.3.5 %s failed to %s in map %s",
3457 "ldap_init/ldap_bind",
3458 # else /* USE_LDAP_INIT */
3460 # endif /* USE_LDAP_INIT */
3461 id, map->map_mname);
3467 /* Save connection for reuse */
3473 ** LDAPMAP_CLOSE -- close ldap map
3480 SM_LDAP_STRUCT *lmap;
3484 sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
3486 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3488 /* Check if already closed */
3489 if (lmap->ldap_ld == NULL)
3492 /* Close the LDAP connection */
3493 sm_ldap_close(lmap);
3495 /* Mark all the maps that share the connection as closed */
3496 s = ldapmap_findconn(lmap);
3498 while (s->s_lmap != NULL)
3500 MAP *smap = s->s_lmap;
3502 if (tTd(38, 2) && smap != map)
3503 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3504 map->map_mname, smap->map_mname);
3505 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
3506 lmap = (SM_LDAP_STRUCT *) smap->map_db1;
3507 lmap->ldap_ld = NULL;
3508 s->s_lmap = lmap->ldap_next;
3509 lmap->ldap_next = NULL;
3515 ** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3516 ** This only makes sense at Stanford University.
3529 if (isascii(*p) && (islower(*p) || isdigit(*p)))
3534 else if (isascii(*p) && isupper(*p))
3536 *p_last = tolower(*p);
3541 if (*p_last != '\0')
3545 # define SM_CONVERT_ID(str) sunet_id_hash(str)
3546 # else /* SUNET_ID */
3547 # define SM_CONVERT_ID(str) makelower(str)
3548 # endif /* SUNET_ID */
3551 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3555 ldapmap_lookup(map, name, av, statp)
3568 char *result = NULL;
3570 SM_LDAP_STRUCT *lmap = NULL;
3571 char *argv[SM_LDAP_ARGS];
3572 char keybuf[MAXKEY];
3573 #if SM_LDAP_ARGS != MAX_MAP_ARGS
3574 # ERROR _SM_LDAP_ARGS must be the same as _MAX_MAP_ARGS
3575 #endif /* SM_LDAP_ARGS != MAX_MAP_ARGS */
3577 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
3578 HASLDAPGETALIASBYNAME
3579 if (VendorCode == VENDOR_SUN &&
3580 strcmp(map->map_mname, "aliases.ldap") == 0)
3583 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
3584 extern char *__getldapaliasbyname();
3587 answer = __getldapaliasbyname(name, &rc);
3589 char answer[MAXNAME + 1];
3591 rc = __getldapaliasbyname(name, answer, sizeof(answer));
3596 sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n",
3598 *statp = EX_NOTFOUND;
3603 sm_dprintf("getldapaliasbyname(%.100s) => %s\n", name,
3605 if (bitset(MF_MATCHONLY, map->map_mflags))
3606 result = map_rewrite(map, name, strlen(name), NULL);
3608 result = map_rewrite(map, answer, strlen(answer), av);
3609 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
3614 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
3616 /* Get ldap struct pointer from map */
3617 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3618 sm_ldap_setopts(lmap->ldap_ld, lmap);
3620 if (lmap->ldap_multi_args)
3622 SM_REQUIRE(av != NULL);
3623 memset(argv, '\0', sizeof(argv));
3624 for (i = 0; i < SM_LDAP_ARGS && av[i] != NULL; i++)
3626 argv[i] = sm_strdup(av[i]);
3627 if (argv[i] == NULL)
3632 for (j = 0; j < i && argv[j] != NULL; j++)
3634 *statp = EX_TEMPFAIL;
3639 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3640 SM_CONVERT_ID(av[i]);
3645 (void) sm_strlcpy(keybuf, name, sizeof(keybuf));
3647 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
3648 SM_CONVERT_ID(keybuf);
3653 if (lmap->ldap_multi_args)
3655 sm_dprintf("ldapmap_lookup(%s, argv)\n",
3657 for (i = 0; i < SM_LDAP_ARGS; i++)
3659 sm_dprintf(" argv[%d] = %s\n", i,
3660 argv[i] == NULL ? "NULL" : argv[i]);
3665 sm_dprintf("ldapmap_lookup(%s, %s)\n",
3666 map->map_mname, name);
3670 if (lmap->ldap_multi_args)
3672 msgid = sm_ldap_search_m(lmap, argv);
3674 /* free the argv array and its content, no longer needed */
3675 for (i = 0; i < SM_LDAP_ARGS && argv[i] != NULL; i++)
3679 msgid = sm_ldap_search(lmap, keybuf);
3680 if (msgid == SM_LDAP_ERR)
3682 errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
3684 if (!bitset(MF_OPTIONAL, map->map_mflags))
3687 ** Do not include keybuf as this error may be shown
3691 if (bitset(MF_NODEFER, map->map_mflags))
3692 syserr("Error in ldap_search in map %s",
3695 syserr("451 4.3.5 Error in ldap_search in map %s",
3698 *statp = EX_TEMPFAIL;
3699 switch (save_errno - E_LDAPBASE)
3701 # ifdef LDAP_SERVER_DOWN
3702 case LDAP_SERVER_DOWN:
3703 # endif /* LDAP_SERVER_DOWN */
3705 case LDAP_UNAVAILABLE:
3706 /* server disappeared, try reopen on next search */
3713 #if SM_LDAP_ERROR_ON_MISSING_ARGS
3714 else if (msgid == SM_LDAP_ERR_ARG_MISS)
3716 if (bitset(MF_NODEFER, map->map_mflags))
3717 syserr("Error in ldap_search in map %s, too few arguments",
3720 syserr("554 5.3.5 Error in ldap_search in map %s, too few arguments",
3725 #endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
3727 *statp = EX_NOTFOUND;
3731 if (bitset(MF_SINGLEMATCH, map->map_mflags))
3732 flags |= SM_LDAP_SINGLEMATCH;
3733 if (bitset(MF_MATCHONLY, map->map_mflags))
3734 flags |= SM_LDAP_MATCHONLY;
3735 # if _FFR_LDAP_SINGLEDN
3736 if (bitset(MF_SINGLEDN, map->map_mflags))
3737 flags |= SM_LDAP_SINGLEDN;
3738 # endif /* _FFR_LDAP_SINGLEDN */
3740 /* Create an rpool for search related memory usage */
3741 rpool = sm_rpool_new_x(NULL);
3744 *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
3745 rpool, &p, &plen, &psize, NULL);
3748 /* Copy result so rpool can be freed */
3749 if (*statp == EX_OK && p != NULL)
3751 sm_rpool_free(rpool);
3753 /* need to restart LDAP connection? */
3754 if (*statp == EX_RESTART)
3756 *statp = EX_TEMPFAIL;
3761 if (*statp != EX_OK && *statp != EX_NOTFOUND)
3763 if (!bitset(MF_OPTIONAL, map->map_mflags))
3765 if (bitset(MF_NODEFER, map->map_mflags))
3766 syserr("Error getting LDAP results in map %s",
3769 syserr("451 4.3.5 Error getting LDAP results in map %s",
3776 /* Did we match anything? */
3777 if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
3780 if (*statp == EX_OK)
3783 sm_syslog(LOG_INFO, CurEnv->e_id,
3784 "ldap %.100s => %s", name,
3785 vp == NULL ? "<NULL>" : vp);
3786 if (bitset(MF_MATCHONLY, map->map_mflags))
3787 result = map_rewrite(map, name, strlen(name), NULL);
3790 /* vp != NULL according to test above */
3791 result = map_rewrite(map, vp, strlen(vp), av);
3794 sm_free(vp); /* XXX */
3800 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server
3802 ** Cache LDAP connections based on the host, port, bind DN,
3803 ** secret, and PID so we don't have multiple connections open to
3804 ** the same server for different maps. Need a separate connection
3805 ** per PID since a parent process may close the map before the
3806 ** child is done with it.
3809 ** lmap -- LDAP map information
3812 ** Symbol table entry for the LDAP connection.
3816 ldapmap_findconn(lmap)
3817 SM_LDAP_STRUCT *lmap;
3822 STAB *SM_NONVOLATILE s = NULL;
3824 if (lmap->ldap_host != NULL)
3825 id = lmap->ldap_host;
3826 else if (lmap->ldap_uri != NULL)
3827 id = lmap->ldap_uri;
3831 format = "%s%c%d%c%d%c%s%c%s%d";
3832 nbuf = sm_stringf_x(format,
3839 (lmap->ldap_binddn == NULL ? ""
3840 : lmap->ldap_binddn),
3842 (lmap->ldap_secret == NULL ? ""
3843 : lmap->ldap_secret),
3846 s = stab(nbuf, ST_LMAP, ST_ENTER);
3853 ** LDAPMAP_PARSEARGS -- parse ldap map definition args.
3856 static struct lamvalues LDAPAuthMethods[] =
3858 { "none", LDAP_AUTH_NONE },
3859 { "simple", LDAP_AUTH_SIMPLE },
3860 # ifdef LDAP_AUTH_KRBV4
3861 { "krbv4", LDAP_AUTH_KRBV4 },
3862 # endif /* LDAP_AUTH_KRBV4 */
3866 static struct ladvalues LDAPAliasDereference[] =
3868 { "never", LDAP_DEREF_NEVER },
3869 { "always", LDAP_DEREF_ALWAYS },
3870 { "search", LDAP_DEREF_SEARCHING },
3871 { "find", LDAP_DEREF_FINDING },
3875 static struct lssvalues LDAPSearchScope[] =
3877 { "base", LDAP_SCOPE_BASE },
3878 { "one", LDAP_SCOPE_ONELEVEL },
3879 { "sub", LDAP_SCOPE_SUBTREE },
3884 ldapmap_parseargs(map, args)
3888 bool secretread = true;
3889 bool attrssetup = false;
3891 register char *p = args;
3892 SM_LDAP_STRUCT *lmap;
3893 struct lamvalues *lam;
3894 struct ladvalues *lad;
3895 struct lssvalues *lss;
3896 char ldapfilt[MAXLINE];
3897 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
3899 /* Get ldap struct pointer from map */
3900 lmap = (SM_LDAP_STRUCT *) map->map_db1;
3902 /* Check if setting the initial LDAP defaults */
3903 if (lmap == NULL || lmap != LDAPDefaults)
3905 /* We need to alloc an SM_LDAP_STRUCT struct */
3906 lmap = (SM_LDAP_STRUCT *) xalloc(sizeof(*lmap));
3907 if (LDAPDefaults == NULL)
3908 sm_ldap_clear(lmap);
3910 STRUCTCOPY(*LDAPDefaults, *lmap);
3913 /* there is no check whether there is really an argument */
3914 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
3915 map->map_spacesub = SpaceSub; /* default value */
3917 /* Check if setting up an alias or file class LDAP map */
3918 if (bitset(MF_ALIAS, map->map_mflags))
3920 /* Comma separate if used as an alias file */
3921 map->map_coldelim = ',';
3926 char jbuf[MAXHOSTNAMELEN];
3927 char lcbuf[MAXLINE];
3930 expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
3931 if (jbuf[0] == '\0')
3933 (void) sm_strlcpy(jbuf, "localhost",
3937 lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
3942 expand(lc, lcbuf, sizeof(lcbuf), CurEnv);
3946 n = sm_snprintf(ldapfilt, sizeof(ldapfilt),
3947 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
3949 if (n >= sizeof(ldapfilt))
3951 syserr("%s: Default LDAP string too long",
3956 /* default args for an alias LDAP entry */
3957 lmap->ldap_filter = ldapfilt;
3958 lmap->ldap_attr[0] = "objectClass";
3959 lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
3960 lmap->ldap_attr_needobjclass[0] = NULL;
3961 lmap->ldap_attr[1] = "sendmailMTAAliasValue";
3962 lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
3963 lmap->ldap_attr_needobjclass[1] = NULL;
3964 lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
3965 lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
3966 lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
3967 lmap->ldap_attr[3] = "sendmailMTAAliasURL";
3968 lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
3969 lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
3970 lmap->ldap_attr[4] = NULL;
3971 lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
3972 lmap->ldap_attr_needobjclass[4] = NULL;
3976 else if (bitset(MF_FILECLASS, map->map_mflags))
3978 /* Space separate if used as a file class file */
3979 map->map_coldelim = ' ';
3982 # if _FFR_LDAP_NETWORK_TIMEOUT
3983 lmap->ldap_networktmo = 120;
3984 # endif /* _FFR_LDAP_NETWORK_TIMEOUT */
3988 while (isascii(*p) && isspace(*p))
3995 map->map_mflags |= MF_APPEND;
4003 map->map_mflags |= MF_DEFER;
4007 map->map_mflags |= MF_NOFOLDCASE;
4011 map->map_mflags |= MF_MATCHONLY;
4015 map->map_mflags |= MF_INCLNULL;
4016 map->map_mflags &= ~MF_TRY0NULL;
4020 map->map_mflags &= ~MF_TRY1NULL;
4024 map->map_mflags |= MF_OPTIONAL;
4028 map->map_mflags |= MF_KEEPQUOTES;
4032 map->map_spacesub = *++p;
4036 map->map_tapp = ++p;
4040 map->map_mflags |= MF_NODEFER;
4045 map->map_coldelim = *p;
4051 map->map_coldelim = '\n';
4055 map->map_coldelim = '\t';
4059 map->map_coldelim = '\\';
4064 /* Start of ldapmap specific args */
4066 map->map_mflags |= MF_SINGLEMATCH;
4069 # if _FFR_LDAP_SINGLEDN
4071 map->map_mflags |= MF_SINGLEDN;
4073 # endif /* _FFR_LDAP_SINGLEDN */
4075 case 'b': /* search base */
4076 while (isascii(*++p) && isspace(*p))
4078 lmap->ldap_base = p;
4081 # if _FFR_LDAP_NETWORK_TIMEOUT
4082 case 'c': /* network (connect) timeout */
4083 while (isascii(*++p) && isspace(*p))
4085 lmap->ldap_networktmo = atoi(p);
4087 # endif /* _FFR_LDAP_NETWORK_TIMEOUT */
4089 case 'd': /* Dn to bind to server as */
4090 while (isascii(*++p) && isspace(*p))
4092 lmap->ldap_binddn = p;
4095 case 'H': /* Use LDAP URI */
4097 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4100 # else /* !USE_LDAP_INIT */
4101 if (lmap->ldap_host != NULL)
4103 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4107 while (isascii(*++p) && isspace(*p))
4111 # endif /* !USE_LDAP_INIT */
4113 case 'h': /* ldap host */
4114 while (isascii(*++p) && isspace(*p))
4116 if (lmap->ldap_uri != NULL)
4118 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4122 lmap->ldap_host = p;
4126 lmap->ldap_multi_args = true;
4129 case 'k': /* search field */
4130 while (isascii(*++p) && isspace(*p))
4132 lmap->ldap_filter = p;
4135 case 'l': /* time limit */
4136 while (isascii(*++p) && isspace(*p))
4138 lmap->ldap_timelimit = atoi(p);
4139 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
4142 case 'M': /* Method for binding */
4143 while (isascii(*++p) && isspace(*p))
4146 if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
4149 for (lam = LDAPAuthMethods;
4150 lam != NULL && lam->lam_name != NULL; lam++)
4152 if (sm_strncasecmp(p, lam->lam_name,
4153 strlen(lam->lam_name)) == 0)
4156 if (lam->lam_name != NULL)
4157 lmap->ldap_method = lam->lam_code;
4160 /* bad config line */
4161 if (!bitset(MCF_OPTFILE,
4162 map->map_class->map_cflags))
4166 if ((ptr = strchr(p, ' ')) != NULL)
4168 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4177 case 'n': /* retrieve attribute names only */
4178 lmap->ldap_attrsonly = LDAPMAP_TRUE;
4182 ** This is a string that is dependent on the
4183 ** method used defined by 'M'.
4186 case 'P': /* Secret password for binding */
4187 while (isascii(*++p) && isspace(*p))
4189 lmap->ldap_secret = p;
4193 case 'p': /* ldap port */
4194 while (isascii(*++p) && isspace(*p))
4196 lmap->ldap_port = atoi(p);
4199 /* args stolen from ldapsearch.c */
4200 case 'R': /* don't auto chase referrals */
4201 # ifdef LDAP_REFERRALS
4202 lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
4203 # else /* LDAP_REFERRALS */
4204 syserr("compile with -DLDAP_REFERRALS for referral support");
4205 # endif /* LDAP_REFERRALS */
4208 case 'r': /* alias dereferencing */
4209 while (isascii(*++p) && isspace(*p))
4212 if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
4215 for (lad = LDAPAliasDereference;
4216 lad != NULL && lad->lad_name != NULL; lad++)
4218 if (sm_strncasecmp(p, lad->lad_name,
4219 strlen(lad->lad_name)) == 0)
4222 if (lad->lad_name != NULL)
4223 lmap->ldap_deref = lad->lad_code;
4226 /* bad config line */
4227 if (!bitset(MCF_OPTFILE,
4228 map->map_class->map_cflags))
4232 if ((ptr = strchr(p, ' ')) != NULL)
4234 syserr("Deref must be [never|always|search|find] (not %s) in map %s",
4243 case 's': /* search scope */
4244 while (isascii(*++p) && isspace(*p))
4247 if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
4250 for (lss = LDAPSearchScope;
4251 lss != NULL && lss->lss_name != NULL; lss++)
4253 if (sm_strncasecmp(p, lss->lss_name,
4254 strlen(lss->lss_name)) == 0)
4257 if (lss->lss_name != NULL)
4258 lmap->ldap_scope = lss->lss_code;
4261 /* bad config line */
4262 if (!bitset(MCF_OPTFILE,
4263 map->map_class->map_cflags))
4267 if ((ptr = strchr(p, ' ')) != NULL)
4269 syserr("Scope must be [base|one|sub] (not %s) in map %s",
4280 lmap->ldap_attrsep = *p;
4286 lmap->ldap_attrsep = '\n';
4290 lmap->ldap_attrsep = '\t';
4294 lmap->ldap_attrsep = '\\';
4299 case 'v': /* attr to return */
4300 while (isascii(*++p) && isspace(*p))
4302 lmap->ldap_attr[0] = p;
4303 lmap->ldap_attr[1] = NULL;
4307 /* -w should be for passwd, -P should be for version */
4308 while (isascii(*++p) && isspace(*p))
4310 lmap->ldap_version = atoi(p);
4311 # ifdef LDAP_VERSION_MAX
4312 if (lmap->ldap_version > LDAP_VERSION_MAX)
4314 syserr("LDAP version %d exceeds max of %d in map %s",
4315 lmap->ldap_version, LDAP_VERSION_MAX,
4319 # endif /* LDAP_VERSION_MAX */
4320 # ifdef LDAP_VERSION_MIN
4321 if (lmap->ldap_version < LDAP_VERSION_MIN)
4323 syserr("LDAP version %d is lower than min of %d in map %s",
4324 lmap->ldap_version, LDAP_VERSION_MIN,
4328 # endif /* LDAP_VERSION_MIN */
4332 while (isascii(*++p) && isspace(*p))
4334 lmap->ldap_sizelimit = atoi(p);
4338 syserr("Illegal option %c map %s", *p, map->map_mname);
4342 /* need to account for quoted strings here */
4343 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4347 while (*++p != '"' && *p != '\0')
4360 if (map->map_app != NULL)
4361 map->map_app = newstr(ldapmap_dequote(map->map_app));
4362 if (map->map_tapp != NULL)
4363 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
4366 ** We need to swallow up all the stuff into a struct
4367 ** and dump it into map->map_dbptr1
4370 if (lmap->ldap_host != NULL &&
4371 (LDAPDefaults == NULL ||
4372 LDAPDefaults == lmap ||
4373 LDAPDefaults->ldap_host != lmap->ldap_host))
4374 lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
4375 map->map_domain = lmap->ldap_host;
4377 if (lmap->ldap_uri != NULL &&
4378 (LDAPDefaults == NULL ||
4379 LDAPDefaults == lmap ||
4380 LDAPDefaults->ldap_uri != lmap->ldap_uri))
4381 lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
4382 map->map_domain = lmap->ldap_uri;
4384 if (lmap->ldap_binddn != NULL &&
4385 (LDAPDefaults == NULL ||
4386 LDAPDefaults == lmap ||
4387 LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
4388 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
4390 if (lmap->ldap_secret != NULL &&
4391 (LDAPDefaults == NULL ||
4392 LDAPDefaults == lmap ||
4393 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4396 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
4398 if (DontLockReadFiles)
4401 /* need to use method to map secret to passwd string */
4402 switch (lmap->ldap_method)
4404 case LDAP_AUTH_NONE:
4408 case LDAP_AUTH_SIMPLE:
4411 ** Secret is the name of a file with
4412 ** the first line as the password.
4415 /* Already read in the secret? */
4419 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
4423 syserr("LDAP map: cannot open secret %s",
4424 ldapmap_dequote(lmap->ldap_secret));
4427 lmap->ldap_secret = sfgets(m_tmp, sizeof(m_tmp),
4428 sfd, TimeOuts.to_fileopen,
4429 "ldapmap_parseargs");
4430 (void) sm_io_close(sfd, SM_TIME_DEFAULT);
4431 if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
4433 syserr("LDAP map: secret in %s too long",
4434 ldapmap_dequote(lmap->ldap_secret));
4437 if (lmap->ldap_secret != NULL &&
4441 if (m_tmp[strlen(m_tmp) - 1] == '\n')
4442 m_tmp[strlen(m_tmp) - 1] = '\0';
4444 lmap->ldap_secret = m_tmp;
4448 # ifdef LDAP_AUTH_KRBV4
4449 case LDAP_AUTH_KRBV4:
4452 ** Secret is where the ticket file is
4456 (void) sm_snprintf(m_tmp, sizeof(m_tmp),
4458 ldapmap_dequote(lmap->ldap_secret));
4459 lmap->ldap_secret = m_tmp;
4461 # endif /* LDAP_AUTH_KRBV4 */
4463 default: /* Should NEVER get here */
4464 syserr("LDAP map: Illegal value in lmap method");
4471 if (lmap->ldap_secret != NULL &&
4472 (LDAPDefaults == NULL ||
4473 LDAPDefaults == lmap ||
4474 LDAPDefaults->ldap_secret != lmap->ldap_secret))
4475 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
4477 if (lmap->ldap_base != NULL &&
4478 (LDAPDefaults == NULL ||
4479 LDAPDefaults == lmap ||
4480 LDAPDefaults->ldap_base != lmap->ldap_base))
4481 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
4484 ** Save the server from extra work. If request is for a single
4485 ** match, tell the server to only return enough records to
4486 ** determine if there is a single match or not. This can not
4487 ** be one since the server would only return one and we wouldn't
4488 ** know if there were others available.
4491 if (bitset(MF_SINGLEMATCH, map->map_mflags))
4492 lmap->ldap_sizelimit = 2;
4494 /* If setting defaults, don't process ldap_filter and ldap_attr */
4495 if (lmap == LDAPDefaults)
4498 if (lmap->ldap_filter != NULL)
4499 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
4502 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
4504 syserr("No filter given in map %s", map->map_mname);
4509 if (!attrssetup && lmap->ldap_attr[0] != NULL)
4511 bool recurse = false;
4512 bool normalseen = false;
4515 p = ldapmap_dequote(lmap->ldap_attr[0]);
4516 lmap->ldap_attr[0] = NULL;
4518 /* Prime the attr list with the objectClass attribute */
4519 lmap->ldap_attr[i] = "objectClass";
4520 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
4521 lmap->ldap_attr_needobjclass[i] = NULL;
4528 while (isascii(*p) && isspace(*p))
4537 if (i >= LDAPMAP_MAX_ATTR)
4539 syserr("Too many return attributes in %s (max %d)",
4540 map->map_mname, LDAPMAP_MAX_ATTR);
4550 type = strchr(v, ':');
4554 needobjclass = strchr(type, ':');
4555 if (needobjclass != NULL)
4556 *needobjclass++ = '\0';
4560 needobjclass = NULL;
4565 /* allow override on "objectClass" type */
4566 if (sm_strcasecmp(v, "objectClass") == 0 &&
4567 lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
4574 ** Don't add something to attribute
4578 for (j = 1; j < i; j++)
4580 if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
4582 syserr("Duplicate attribute (%s) in %s",
4588 lmap->ldap_attr[use] = newstr(v);
4589 if (needobjclass != NULL &&
4590 *needobjclass != '\0' &&
4591 *needobjclass != '*')
4593 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
4597 lmap->ldap_attr_needobjclass[use] = NULL;
4602 if (type != NULL && *type != '\0')
4604 if (sm_strcasecmp(type, "dn") == 0)
4607 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
4609 else if (sm_strcasecmp(type, "filter") == 0)
4612 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
4614 else if (sm_strcasecmp(type, "url") == 0)
4617 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
4619 else if (sm_strcasecmp(type, "normal") == 0)
4621 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4626 syserr("Unknown attribute type (%s) in %s",
4627 type, map->map_mname);
4633 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
4639 lmap->ldap_attr[i] = NULL;
4641 /* Set in case needed in future code */
4644 if (recurse && !normalseen)
4646 syserr("LDAP recursion requested in %s but no returnable attribute given",
4650 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
4652 syserr("LDAP recursion requested in %s can not be used with -n",
4657 map->map_db1 = (ARBPTR_T) lmap;
4662 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4665 ** spec -- map argument string from LDAPDefaults option
4672 ldapmap_set_defaults(spec)
4678 /* Allocate and set the default values */
4679 if (LDAPDefaults == NULL)
4680 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof(*LDAPDefaults));
4681 sm_ldap_clear(LDAPDefaults);
4683 memset(&map, '\0', sizeof(map));
4685 /* look up the class */
4686 class = stab("ldap", ST_MAPCLASS, ST_FIND);
4689 syserr("readcf: LDAPDefaultSpec: class ldap not available");
4692 map.map_class = &class->s_mapclass;
4693 map.map_db1 = (ARBPTR_T) LDAPDefaults;
4694 map.map_mname = "O LDAPDefaultSpec";
4696 (void) ldapmap_parseargs(&map, spec);
4698 /* These should never be set in LDAPDefaults */
4699 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
4700 map.map_spacesub != SpaceSub ||
4701 map.map_app != NULL ||
4702 map.map_tapp != NULL)
4704 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4705 SM_FREE_CLR(map.map_app);
4706 SM_FREE_CLR(map.map_tapp);
4709 if (LDAPDefaults->ldap_filter != NULL)
4711 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4713 /* don't free, it isn't malloc'ed in parseargs */
4714 LDAPDefaults->ldap_filter = NULL;
4717 if (LDAPDefaults->ldap_attr[0] != NULL)
4719 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4720 /* don't free, they aren't malloc'ed in parseargs */
4721 LDAPDefaults->ldap_attr[0] = NULL;
4724 #endif /* LDAPMAP */
4732 ** Support for the CCSO Nameserver (ph/qi).
4733 ** This code is intended to replace the so-called "ph mailer".
4734 ** Contributed by Mark D. Roth. Contact him for support.
4737 /* what version of the ph map code we're running */
4738 static char phmap_id[128];
4740 /* sendmail version for phmap id string */
4741 extern const char Version[];
4743 /* assume we're using nph-1.2.x if not specified */
4744 # ifndef NPH_VERSION
4745 # define NPH_VERSION 10200
4748 /* compatibility for versions older than nph-1.2.0 */
4749 # if NPH_VERSION < 10200
4750 # define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
4751 # define PH_OPEN_DONTID PH_DONTID
4752 # define PH_CLOSE_FAST PH_FASTCLOSE
4753 # define PH_ERR_DATAERR PH_DATAERR
4754 # define PH_ERR_NOMATCH PH_NOMATCH
4755 # endif /* NPH_VERSION < 10200 */
4758 ** PH_MAP_PARSEARGS -- parse ph map definition args.
4762 ph_map_parseargs(map, args)
4767 register char *p = args;
4768 PH_MAP_STRUCT *pmap = NULL;
4770 /* initialize version string */
4771 (void) sm_snprintf(phmap_id, sizeof(phmap_id),
4772 "sendmail-%s phmap-20010529 libphclient-%s",
4773 Version, libphclient_version);
4775 pmap = (PH_MAP_STRUCT *) xalloc(sizeof(*pmap));
4778 pmap->ph_servers = NULL;
4779 pmap->ph_field_list = NULL;
4781 pmap->ph_timeout = 0;
4782 pmap->ph_fastclose = 0;
4784 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
4787 while (isascii(*p) && isspace(*p))
4794 map->map_mflags |= MF_INCLNULL;
4795 map->map_mflags &= ~MF_TRY0NULL;
4799 map->map_mflags &= ~MF_TRY1NULL;
4803 map->map_mflags |= MF_OPTIONAL;
4807 map->map_mflags |= MF_NOFOLDCASE;
4811 map->map_mflags |= MF_MATCHONLY;
4815 map->map_mflags |= MF_APPEND;
4819 map->map_mflags |= MF_KEEPQUOTES;
4823 map->map_mflags |= MF_NODEFER;
4831 map->map_tapp = ++p;
4835 while (isascii(*++p) && isspace(*p))
4837 pmap->ph_timeout = atoi(p);
4841 map->map_spacesub = *++p;
4845 map->map_mflags |= MF_DEFER;
4848 case 'h': /* PH server list */
4849 while (isascii(*++p) && isspace(*p))
4851 pmap->ph_servers = p;
4854 case 'k': /* fields to search for */
4855 while (isascii(*++p) && isspace(*p))
4857 pmap->ph_field_list = p;
4861 syserr("ph_map_parseargs: unknown option -%c", *p);
4864 /* try to account for quoted strings */
4865 done = isascii(*p) && isspace(*p);
4866 while (*p != '\0' && !done)
4870 while (*++p != '"' && *p != '\0')
4877 done = isascii(*p) && isspace(*p);
4884 if (map->map_app != NULL)
4885 map->map_app = newstr(ph_map_dequote(map->map_app));
4886 if (map->map_tapp != NULL)
4887 map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
4889 if (pmap->ph_field_list != NULL)
4890 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
4892 if (pmap->ph_servers != NULL)
4893 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
4896 syserr("ph_map_parseargs: -h flag is required");
4900 map->map_db1 = (ARBPTR_T) pmap;
4905 ** PH_MAP_CLOSE -- close the connection to the ph server
4912 PH_MAP_STRUCT *pmap;
4914 pmap = (PH_MAP_STRUCT *)map->map_db1;
4916 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
4917 map->map_mname, pmap->ph_fastclose);
4920 if (pmap->ph != NULL)
4922 ph_set_sendhook(pmap->ph, NULL);
4923 ph_set_recvhook(pmap->ph, NULL);
4924 ph_close(pmap->ph, pmap->ph_fastclose);
4927 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
4930 static jmp_buf PHTimeout;
4938 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
4939 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
4944 longjmp(PHTimeout, 1);
4948 #if NPH_VERSION >= 10200
4949 ph_map_send_debug(appdata, text)
4952 ph_map_send_debug(text)
4957 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4958 "ph_map_send_debug: ==> %s", text);
4960 sm_dprintf("ph_map_send_debug: ==> %s\n", text);
4964 #if NPH_VERSION >= 10200
4965 ph_map_recv_debug(appdata, text)
4968 ph_map_recv_debug(text)
4973 sm_syslog(LOG_NOTICE, CurEnv->e_id,
4974 "ph_map_recv_debug: <== %s", text);
4976 sm_dprintf("ph_map_recv_debug: <== %s\n", text);
4980 ** PH_MAP_OPEN -- sub for opening PH map
4983 ph_map_open(map, mode)
4987 PH_MAP_STRUCT *pmap;
4988 register SM_EVENT *ev = NULL;
4990 char *hostlist, *host;
4993 sm_dprintf("ph_map_open(%s)\n", map->map_mname);
4996 if (mode != O_RDONLY)
4998 /* issue a pseudo-error message */
4999 errno = SM_EMAPCANTWRITE;
5003 if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
5004 bitset(MF_DEFER, map->map_mflags))
5007 sm_dprintf("ph_map_open(%s) => DEFERRED\n",
5011 ** Unset MF_DEFER here so that map_lookup() returns
5012 ** a temporary failure using the bogus map and
5013 ** map->map_tapp instead of the default permanent error.
5016 map->map_mflags &= ~MF_DEFER;
5020 pmap = (PH_MAP_STRUCT *)map->map_db1;
5021 pmap->ph_fastclose = 0; /* refresh field for reopen */
5023 /* try each host in the list */
5024 hostlist = newstr(pmap->ph_servers);
5025 for (host = strtok(hostlist, " ");
5027 host = strtok(NULL, " "))
5030 if (pmap->ph_timeout != 0)
5032 if (setjmp(PHTimeout) != 0)
5036 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5037 "timeout connecting to PH server %.100s",
5040 goto ph_map_open_abort;
5042 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
5045 /* open connection to server */
5046 if (ph_open(&(pmap->ph), host,
5047 PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
5048 ph_map_send_debug, ph_map_recv_debug
5049 #if NPH_VERSION >= 10200
5053 && ph_id(pmap->ph, phmap_id) == 0)
5057 sm_free(hostlist); /* XXX */
5065 pmap->ph_fastclose = PH_CLOSE_FAST;
5070 if (bitset(MF_NODEFER, map->map_mflags))
5074 syserr("ph_map_open: %s: cannot connect to PH server",
5077 else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
5078 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5079 "ph_map_open: %s: cannot connect to PH server",
5081 sm_free(hostlist); /* XXX */
5086 ** PH_MAP_LOOKUP -- look up key from ph server
5090 ph_map_lookup(map, key, args, pstat)
5096 int i, save_errno = 0;
5097 register SM_EVENT *ev = NULL;
5098 PH_MAP_STRUCT *pmap;
5101 pmap = (PH_MAP_STRUCT *)map->map_db1;
5106 if (pmap->ph_timeout != 0)
5108 if (setjmp(PHTimeout) != 0)
5112 sm_syslog(LOG_NOTICE, CurEnv->e_id,
5113 "timeout during PH lookup of %.100s",
5116 *pstat = EX_TEMPFAIL;
5117 goto ph_map_lookup_abort;
5119 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
5122 /* perform lookup */
5123 i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
5125 *pstat = EX_TEMPFAIL;
5126 else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
5127 *pstat = EX_UNAVAILABLE;
5129 ph_map_lookup_abort:
5134 ** Close the connection if the timer popped
5135 ** or we got a temporary PH error
5138 if (*pstat == EX_TEMPFAIL)
5141 pmap->ph_fastclose = PH_CLOSE_FAST;
5146 if (*pstat == EX_OK)
5149 sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
5151 if (bitset(MF_MATCHONLY, map->map_mflags))
5152 return map_rewrite(map, key, strlen(key), NULL);
5154 return map_rewrite(map, value, strlen(value), args);
5165 #define map_prio map_lockfd /* overload field */
5168 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
5172 syslog_map_parseargs(map, args)
5177 char *priority = NULL;
5179 /* there is no check whether there is really an argument */
5182 while (isascii(*p) && isspace(*p))
5189 map->map_mflags |= MF_DEFER;
5194 map->map_spacesub = *++p;
5200 while (*++p != '\0' && isascii(*p) && isspace(*p))
5205 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5212 syserr("Illegal option %c map syslog", *p);
5217 if (priority == NULL)
5218 map->map_prio = LOG_INFO;
5221 if (sm_strncasecmp("LOG_", priority, 4) == 0)
5225 if (sm_strcasecmp("EMERG", priority) == 0)
5226 map->map_prio = LOG_EMERG;
5228 #endif /* LOG_EMERG */
5230 if (sm_strcasecmp("ALERT", priority) == 0)
5231 map->map_prio = LOG_ALERT;
5233 #endif /* LOG_ALERT */
5235 if (sm_strcasecmp("CRIT", priority) == 0)
5236 map->map_prio = LOG_CRIT;
5238 #endif /* LOG_CRIT */
5240 if (sm_strcasecmp("ERR", priority) == 0)
5241 map->map_prio = LOG_ERR;
5243 #endif /* LOG_ERR */
5245 if (sm_strcasecmp("WARNING", priority) == 0)
5246 map->map_prio = LOG_WARNING;
5248 #endif /* LOG_WARNING */
5250 if (sm_strcasecmp("NOTICE", priority) == 0)
5251 map->map_prio = LOG_NOTICE;
5253 #endif /* LOG_NOTICE */
5255 if (sm_strcasecmp("INFO", priority) == 0)
5256 map->map_prio = LOG_INFO;
5258 #endif /* LOG_INFO */
5260 if (sm_strcasecmp("DEBUG", priority) == 0)
5261 map->map_prio = LOG_DEBUG;
5263 #endif /* LOG_DEBUG */
5265 syserr("syslog_map_parseargs: Unknown priority %s",
5274 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
5278 syslog_map_lookup(map, string, args, statp)
5284 char *ptr = map_rewrite(map, string, strlen(string), args);
5289 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5290 map->map_mname, map->map_prio, ptr);
5292 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
5299 #if _FFR_DPRINTF_MAP
5304 #define map_dbg_level map_lockfd /* overload field */
5307 ** DPRINTF_MAP_PARSEARGS -- check for priority level to dprintf messages.
5311 dprintf_map_parseargs(map, args)
5316 char *dbg_level = NULL;
5318 /* there is no check whether there is really an argument */
5321 while (isascii(*p) && isspace(*p))
5328 map->map_mflags |= MF_DEFER;
5333 map->map_spacesub = *++p;
5339 while (*++p != '\0' && isascii(*p) && isspace(*p))
5344 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
5351 syserr("Illegal option %c map dprintf", *p);
5356 if (dbg_level == NULL)
5357 map->map_dbg_level = 0;
5360 if (!(isascii(*dbg_level) && isdigit(*dbg_level)))
5362 syserr("dprintf map \"%s\", file %s: -d should specify a number, not %s",
5363 map->map_mname, map->map_file,
5367 map->map_dbg_level = atoi(dbg_level);
5373 ** DPRINTF_MAP_LOOKUP -- rewrite and print message. Always return empty string
5377 dprintf_map_lookup(map, string, args, statp)
5383 char *ptr = map_rewrite(map, string, strlen(string), args);
5385 if (ptr != NULL && tTd(85, map->map_dbg_level))
5386 sm_dprintf("%s\n", ptr);
5390 #endif /* _FFR_DPRINTF_MAP */
5399 hes_map_open(map, mode)
5404 sm_dprintf("hes_map_open(%s, %s, %d)\n",
5405 map->map_mname, map->map_file, mode);
5407 if (mode != O_RDONLY)
5409 /* issue a pseudo-error message */
5410 errno = SM_EMAPCANTWRITE;
5415 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
5418 if (!bitset(MF_OPTIONAL, map->map_mflags))
5419 syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5420 sm_errstring(errno));
5422 # else /* HESIOD_INIT */
5423 if (hes_error() == HES_ER_UNINIT)
5425 switch (hes_error())
5428 case HES_ER_NOTFOUND:
5432 if (!bitset(MF_OPTIONAL, map->map_mflags))
5433 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5436 # endif /* HESIOD_INIT */
5440 hes_map_lookup(map, name, av, statp)
5449 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
5451 if (name[0] == '\\')
5459 if (nl < sizeof(nbuf) - 1)
5462 np = xalloc(strlen(name) + 2);
5464 (void) sm_strlcpy(&np[1], name, (sizeof(nbuf)) - 1);
5466 hp = hesiod_resolve(HesiodContext, np, map->map_file);
5467 # else /* HESIOD_INIT */
5468 hp = hes_resolve(np, map->map_file);
5469 # endif /* HESIOD_INIT */
5472 sm_free(np); /* XXX */
5478 hp = hesiod_resolve(HesiodContext, name, map->map_file);
5479 # else /* HESIOD_INIT */
5480 hp = hes_resolve(name, map->map_file);
5481 # endif /* HESIOD_INIT */
5484 if (hp == NULL || *hp == NULL)
5489 *statp = EX_NOTFOUND;
5492 *statp = EX_TEMPFAIL;
5497 *statp = EX_UNAVAILABLE;
5501 hesiod_free_list(HesiodContext, hp);
5504 # else /* HESIOD_INIT */
5505 if (hp == NULL || hp[0] == NULL)
5507 switch (hes_error())
5513 case HES_ER_NOTFOUND:
5514 *statp = EX_NOTFOUND;
5518 *statp = EX_UNAVAILABLE;
5522 *statp = EX_TEMPFAIL;
5527 # endif /* HESIOD_INIT */
5529 if (bitset(MF_MATCHONLY, map->map_mflags))
5530 return map_rewrite(map, name, strlen(name), NULL);
5532 return map_rewrite(map, hp[0], strlen(hp[0]), av);
5536 ** HES_MAP_CLOSE -- free the Hesiod context
5544 sm_dprintf("hes_map_close(%s)\n", map->map_file);
5547 /* Free the hesiod context */
5548 if (HesiodContext != NULL)
5550 hesiod_end(HesiodContext);
5551 HesiodContext = NULL;
5553 # endif /* HESIOD_INIT */
5558 ** NeXT NETINFO Modules
5563 # define NETINFO_DEFAULT_DIR "/aliases"
5564 # define NETINFO_DEFAULT_PROPERTY "members"
5567 ** NI_MAP_OPEN -- open NetInfo Aliases
5571 ni_map_open(map, mode)
5576 sm_dprintf("ni_map_open(%s, %s, %d)\n",
5577 map->map_mname, map->map_file, mode);
5580 if (*map->map_file == '\0')
5581 map->map_file = NETINFO_DEFAULT_DIR;
5583 if (map->map_valcolnm == NULL)
5584 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
5586 if (map->map_coldelim == '\0')
5588 if (bitset(MF_ALIAS, map->map_mflags))
5589 map->map_coldelim = ',';
5590 else if (bitset(MF_FILECLASS, map->map_mflags))
5591 map->map_coldelim = ' ';
5598 ** NI_MAP_LOOKUP -- look up a datum in NetInfo
5602 ni_map_lookup(map, name, av, statp)
5612 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
5614 propval = ni_propval(map->map_file, map->map_keycolnm, name,
5615 map->map_valcolnm, map->map_coldelim);
5617 if (propval == NULL)
5621 if (bitset(MF_MATCHONLY, map->map_mflags))
5622 res = map_rewrite(map, name, strlen(name), NULL);
5624 res = map_rewrite(map, propval, strlen(propval), av);
5633 ni_getcanonname(name, hbsize, statp)
5640 char nbuf[MAXNAME + 1];
5643 sm_dprintf("ni_getcanonname(%s)\n", name);
5645 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
5647 *statp = EX_UNAVAILABLE;
5650 (void) shorten_hostname(nbuf);
5652 /* we only accept single token search key */
5653 if (strchr(nbuf, '.'))
5660 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
5668 /* Only want the first machine name */
5669 if ((ptr = strchr(vptr, '\n')) != NULL)
5672 if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
5675 *statp = EX_UNAVAILABLE;
5682 #endif /* NETINFO */
5684 ** TEXT (unindexed text file) Modules
5686 ** This code donated by Sun Microsystems.
5689 #define map_sff map_lockfd /* overload field */
5693 ** TEXT_MAP_OPEN -- open text table
5697 text_map_open(map, mode)
5705 sm_dprintf("text_map_open(%s, %s, %d)\n",
5706 map->map_mname, map->map_file, mode);
5709 if (mode != O_RDONLY)
5715 if (*map->map_file == '\0')
5717 syserr("text map \"%s\": file name required",
5722 if (map->map_file[0] != '/')
5724 syserr("text map \"%s\": file name must be fully qualified",
5729 sff = SFF_ROOTOK|SFF_REGONLY;
5730 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
5732 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
5733 sff |= SFF_SAFEDIRPATH;
5734 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
5735 sff, S_IRUSR, NULL)) != 0)
5737 int save_errno = errno;
5739 /* cannot open this map */
5741 sm_dprintf("\tunsafe map file: %d\n", i);
5743 if (!bitset(MF_OPTIONAL, map->map_mflags))
5744 syserr("text map \"%s\": unsafe map file %s",
5745 map->map_mname, map->map_file);
5749 if (map->map_keycolnm == NULL)
5750 map->map_keycolno = 0;
5753 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
5755 syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5756 map->map_mname, map->map_file,
5760 map->map_keycolno = atoi(map->map_keycolnm);
5763 if (map->map_valcolnm == NULL)
5764 map->map_valcolno = 0;
5767 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
5769 syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5770 map->map_mname, map->map_file,
5774 map->map_valcolno = atoi(map->map_valcolnm);
5779 sm_dprintf("text_map_open(%s, %s): delimiter = ",
5780 map->map_mname, map->map_file);
5781 if (map->map_coldelim == '\0')
5782 sm_dprintf("(white space)\n");
5784 sm_dprintf("%c\n", map->map_coldelim);
5793 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5797 text_map_lookup(map, name, av, statp)
5810 long sff = map->map_sff;
5811 char search_key[MAXNAME + 1];
5812 char linebuf[MAXLINE];
5813 char buf[MAXNAME + 1];
5817 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
5819 buflen = strlen(name);
5820 if (buflen > sizeof(search_key) - 1)
5821 buflen = sizeof(search_key) - 1; /* XXX just cut if off? */
5822 memmove(search_key, name, buflen);
5823 search_key[buflen] = '\0';
5824 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
5825 makelower(search_key);
5827 f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
5830 map->map_mflags &= ~(MF_VALID|MF_OPEN);
5831 *statp = EX_UNAVAILABLE;
5834 key_idx = map->map_keycolno;
5835 delim = map->map_coldelim;
5836 while (sm_io_fgets(f, SM_TIME_DEFAULT,
5837 linebuf, sizeof(linebuf)) != NULL)
5841 /* skip comment line */
5842 if (linebuf[0] == '#')
5844 p = strchr(linebuf, '\n');
5847 p = get_column(linebuf, key_idx, delim, buf, sizeof(buf));
5848 if (p != NULL && sm_strcasecmp(search_key, p) == 0)
5854 (void) sm_io_close(f, SM_TIME_DEFAULT);
5857 *statp = EX_NOTFOUND;
5860 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof(buf));
5863 *statp = EX_NOTFOUND;
5868 if (bitset(MF_MATCHONLY, map->map_mflags))
5869 return map_rewrite(map, name, strlen(name), NULL);
5871 return map_rewrite(map, vp, vsize, av);
5875 ** TEXT_GETCANONNAME -- look up canonical name in hosts file
5879 text_getcanonname(name, hbsize, statp)
5887 char linebuf[MAXLINE];
5888 char cbuf[MAXNAME + 1];
5889 char nbuf[MAXNAME + 1];
5892 sm_dprintf("text_getcanonname(%s)\n", name);
5894 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
5896 *statp = EX_UNAVAILABLE;
5899 dot = shorten_hostname(nbuf);
5901 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
5905 *statp = EX_UNAVAILABLE;
5910 sm_io_fgets(f, SM_TIME_DEFAULT,
5911 linebuf, sizeof(linebuf)) != NULL)
5913 char *p = strpbrk(linebuf, "#\n");
5917 if (linebuf[0] != '\0')
5918 found = extract_canonname(nbuf, dot, linebuf,
5919 cbuf, sizeof(cbuf));
5921 (void) sm_io_close(f, SM_TIME_DEFAULT);
5928 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
5930 *statp = EX_UNAVAILABLE;
5937 ** STAB (Symbol Table) Modules
5942 ** STAB_MAP_LOOKUP -- look up alias in symbol table
5947 stab_map_lookup(map, name, av, pstat)
5956 sm_dprintf("stab_lookup(%s, %s)\n",
5957 map->map_mname, name);
5959 s = stab(name, ST_ALIAS, ST_FIND);
5962 if (bitset(MF_MATCHONLY, map->map_mflags))
5963 return map_rewrite(map, name, strlen(name), NULL);
5965 return map_rewrite(map, s->s_alias, strlen(s->s_alias), av);
5969 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
5973 stab_map_store(map, lhs, rhs)
5980 s = stab(lhs, ST_ALIAS, ST_ENTER);
5981 s->s_alias = newstr(rhs);
5986 ** STAB_MAP_OPEN -- initialize (reads data file)
5988 ** This is a wierd case -- it is only intended as a fallback for
5989 ** aliases. For this reason, opens for write (only during a
5990 ** "newaliases") always fails, and opens for read open the
5991 ** actual underlying text file instead of the database.
5995 stab_map_open(map, mode)
6004 sm_dprintf("stab_map_open(%s, %s, %d)\n",
6005 map->map_mname, map->map_file, mode);
6008 if (mode != O_RDONLY)
6014 sff = SFF_ROOTOK|SFF_REGONLY;
6015 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
6017 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
6018 sff |= SFF_SAFEDIRPATH;
6019 af = safefopen(map->map_file, O_RDONLY, 0444, sff);
6022 readaliases(map, af, false, false);
6024 if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
6025 map->map_mtime = st.st_mtime;
6026 (void) sm_io_close(af, SM_TIME_DEFAULT);
6033 ** Tries several types. For back compatibility of aliases.
6038 ** IMPL_MAP_LOOKUP -- lookup in best open database
6042 impl_map_lookup(map, name, av, pstat)
6049 sm_dprintf("impl_map_lookup(%s, %s)\n",
6050 map->map_mname, name);
6053 if (bitset(MF_IMPL_HASH, map->map_mflags))
6054 return db_map_lookup(map, name, av, pstat);
6057 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6058 return ndbm_map_lookup(map, name, av, pstat);
6060 return stab_map_lookup(map, name, av, pstat);
6064 ** IMPL_MAP_STORE -- store in open databases
6068 impl_map_store(map, lhs, rhs)
6074 sm_dprintf("impl_map_store(%s, %s, %s)\n",
6075 map->map_mname, lhs, rhs);
6077 if (bitset(MF_IMPL_HASH, map->map_mflags))
6078 db_map_store(map, lhs, rhs);
6081 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6082 ndbm_map_store(map, lhs, rhs);
6084 stab_map_store(map, lhs, rhs);
6088 ** IMPL_MAP_OPEN -- implicit database open
6092 impl_map_open(map, mode)
6097 sm_dprintf("impl_map_open(%s, %s, %d)\n",
6098 map->map_mname, map->map_file, mode);
6102 map->map_mflags |= MF_IMPL_HASH;
6103 if (hash_map_open(map, mode))
6105 # ifdef NDBM_YP_COMPAT
6106 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
6107 # endif /* NDBM_YP_COMPAT */
6111 map->map_mflags &= ~MF_IMPL_HASH;
6114 map->map_mflags |= MF_IMPL_NDBM;
6115 if (ndbm_map_open(map, mode))
6120 map->map_mflags &= ~MF_IMPL_NDBM;
6123 #if defined(NEWDB) || defined(NDBM)
6125 message("WARNING: cannot open alias database %s%s",
6127 mode == O_RDONLY ? "; reading text version" : "");
6128 #else /* defined(NEWDB) || defined(NDBM) */
6129 if (mode != O_RDONLY)
6130 usrerr("Cannot rebuild aliases: no database format defined");
6131 #endif /* defined(NEWDB) || defined(NDBM) */
6133 if (mode == O_RDONLY)
6134 return stab_map_open(map, mode);
6141 ** IMPL_MAP_CLOSE -- close any open database(s)
6149 sm_dprintf("impl_map_close(%s, %s, %lx)\n",
6150 map->map_mname, map->map_file, map->map_mflags);
6152 if (bitset(MF_IMPL_HASH, map->map_mflags))
6155 map->map_mflags &= ~MF_IMPL_HASH;
6160 if (bitset(MF_IMPL_NDBM, map->map_mflags))
6162 ndbm_map_close(map);
6163 map->map_mflags &= ~MF_IMPL_NDBM;
6170 ** Provides access to the system password file.
6174 ** USER_MAP_OPEN -- open user map
6176 ** Really just binds field names to field numbers.
6180 user_map_open(map, mode)
6185 sm_dprintf("user_map_open(%s, %d)\n",
6186 map->map_mname, mode);
6189 if (mode != O_RDONLY)
6191 /* issue a pseudo-error message */
6192 errno = SM_EMAPCANTWRITE;
6195 if (map->map_valcolnm == NULL)
6198 else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
6199 map->map_valcolno = 1;
6200 else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
6201 map->map_valcolno = 2;
6202 else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
6203 map->map_valcolno = 3;
6204 else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
6205 map->map_valcolno = 4;
6206 else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
6207 map->map_valcolno = 5;
6208 else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
6209 map->map_valcolno = 6;
6210 else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
6211 map->map_valcolno = 7;
6214 syserr("User map %s: unknown column name %s",
6215 map->map_mname, map->map_valcolnm);
6223 ** USER_MAP_LOOKUP -- look up a user in the passwd file.
6228 user_map_lookup(map, key, av, statp)
6238 sm_dprintf("user_map_lookup(%s, %s)\n",
6239 map->map_mname, key);
6241 *statp = finduser(key, &fuzzy, &user);
6242 if (*statp != EX_OK)
6244 if (bitset(MF_MATCHONLY, map->map_mflags))
6245 return map_rewrite(map, key, strlen(key), NULL);
6251 switch (map->map_valcolno)
6255 rwval = user.mbdb_name;
6259 rwval = "x"; /* passwd no longer supported */
6263 (void) sm_snprintf(buf, sizeof(buf), "%d",
6264 (int) user.mbdb_uid);
6269 (void) sm_snprintf(buf, sizeof(buf), "%d",
6270 (int) user.mbdb_gid);
6275 rwval = user.mbdb_fullname;
6279 rwval = user.mbdb_homedir;
6283 rwval = user.mbdb_shell;
6286 syserr("user_map %s: bogus field %d",
6287 map->map_mname, map->map_valcolno);
6290 return map_rewrite(map, rwval, strlen(rwval), av);
6294 ** Program map type.
6296 ** This provides access to arbitrary programs. It should be used
6297 ** only very sparingly, since there is no way to bound the cost
6298 ** of invoking an arbitrary program.
6302 prog_map_lookup(map, name, av, statp)
6315 char *argv[MAXPV + 1];
6319 sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6320 map->map_mname, name, map->map_file);
6323 argv[i++] = map->map_file;
6324 if (map->map_rebuild != NULL)
6326 (void) sm_strlcpy(buf, map->map_rebuild, sizeof(buf));
6327 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
6338 sm_dprintf("prog_open:");
6339 for (i = 0; argv[i] != NULL; i++)
6340 sm_dprintf(" %s", argv[i]);
6343 (void) sm_blocksignal(SIGCHLD);
6344 pid = prog_open(argv, &fd, CurEnv);
6347 if (!bitset(MF_OPTIONAL, map->map_mflags))
6348 syserr("prog_map_lookup(%s) failed (%s) -- closing",
6349 map->map_mname, sm_errstring(errno));
6350 else if (tTd(38, 9))
6351 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6352 map->map_mname, sm_errstring(errno));
6353 map->map_mflags &= ~(MF_VALID|MF_OPEN);
6357 i = read(fd, buf, sizeof(buf) - 1);
6360 syserr("prog_map_lookup(%s): read error %s",
6361 map->map_mname, sm_errstring(errno));
6367 sm_dprintf("prog_map_lookup(%s): empty answer\n",
6374 p = strchr(buf, '\n');
6378 /* collect the return value */
6379 if (bitset(MF_MATCHONLY, map->map_mflags))
6380 rval = map_rewrite(map, name, strlen(name), NULL);
6382 rval = map_rewrite(map, buf, strlen(buf), av);
6384 /* now flush any additional output */
6385 while ((i = read(fd, buf, sizeof(buf))) > 0)
6389 /* wait for the process to terminate */
6391 status = waitfor(pid);
6393 (void) sm_releasesignal(SIGCHLD);
6398 syserr("prog_map_lookup(%s): wait error %s",
6399 map->map_mname, sm_errstring(errno));
6400 *statp = EX_SOFTWARE;
6403 else if (WIFEXITED(status))
6405 if ((*statp = WEXITSTATUS(status)) != EX_OK)
6410 syserr("prog_map_lookup(%s): child died on signal %d",
6411 map->map_mname, status);
6412 *statp = EX_UNAVAILABLE;
6418 ** Sequenced map type.
6420 ** Tries each map in order until something matches, much like
6421 ** implicit. Stores go to the first map in the list that can
6424 ** This is slightly unusual in that there are two interfaces.
6425 ** The "sequence" interface lets you stack maps arbitrarily.
6426 ** The "switch" interface builds a sequence map by looking
6427 ** at a system-dependent configuration file such as
6428 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6430 ** We don't need an explicit open, since all maps are
6431 ** opened on demand.
6435 ** SEQ_MAP_PARSE -- Sequenced map parsing
6439 seq_map_parse(map, ap)
6446 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
6453 /* find beginning of map name */
6454 while (isascii(*ap) && isspace(*ap))
6457 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
6462 while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
6469 s = stab(ap, ST_MAP, ST_FIND);
6472 syserr("Sequence map %s: unknown member map %s",
6473 map->map_mname, ap);
6475 else if (maxmap >= MAXMAPSTACK)
6477 syserr("Sequence map %s: too many member maps (%d max)",
6478 map->map_mname, MAXMAPSTACK);
6481 else if (maxmap < MAXMAPSTACK)
6483 map->map_stack[maxmap++] = &s->s_map;
6491 ** SWITCH_MAP_OPEN -- open a switched map
6493 ** This looks at the system-dependent configuration and builds
6494 ** a sequence map that does the same thing.
6496 ** Every system must define a switch_map_find routine in conf.c
6497 ** that will return the list of service types associated with a
6498 ** given service class.
6502 switch_map_open(map, mode)
6508 char *maptype[MAXMAPSTACK];
6511 sm_dprintf("switch_map_open(%s, %s, %d)\n",
6512 map->map_mname, map->map_file, mode);
6515 nmaps = switch_map_find(map->map_file, maptype, map->map_return);
6518 sm_dprintf("\tswitch_map_find => %d\n", nmaps);
6519 for (mapno = 0; mapno < nmaps; mapno++)
6520 sm_dprintf("\t\t%s\n", maptype[mapno]);
6522 if (nmaps <= 0 || nmaps > MAXMAPSTACK)
6525 for (mapno = 0; mapno < nmaps; mapno++)
6528 char nbuf[MAXNAME + 1];
6530 if (maptype[mapno] == NULL)
6532 (void) sm_strlcpyn(nbuf, sizeof(nbuf), 3,
6533 map->map_mname, ".", maptype[mapno]);
6534 s = stab(nbuf, ST_MAP, ST_FIND);
6537 syserr("Switch map %s: unknown member map %s",
6538 map->map_mname, nbuf);
6542 map->map_stack[mapno] = &s->s_map;
6544 sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6546 s->s_map.map_class->map_cname,
6555 ** SEQ_MAP_CLOSE -- close all underlying maps
6565 sm_dprintf("seq_map_close(%s)\n", map->map_mname);
6567 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6569 MAP *mm = map->map_stack[mapno];
6571 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
6573 mm->map_mflags |= MF_CLOSING;
6574 mm->map_class->map_close(mm);
6575 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
6581 ** SEQ_MAP_LOOKUP -- sequenced map lookup
6585 seq_map_lookup(map, key, args, pstat)
6593 bool tempfail = false;
6596 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
6598 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
6600 MAP *mm = map->map_stack[mapno];
6605 if (!bitset(MF_OPEN, mm->map_mflags) &&
6608 if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
6610 *pstat = EX_UNAVAILABLE;
6616 rv = mm->map_class->map_lookup(mm, key, args, pstat);
6619 if (*pstat == EX_TEMPFAIL)
6621 if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
6625 else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
6629 *pstat = EX_TEMPFAIL;
6630 else if (*pstat == EX_OK)
6631 *pstat = EX_NOTFOUND;
6636 ** SEQ_MAP_STORE -- sequenced map store
6640 seq_map_store(map, key, val)
6648 sm_dprintf("seq_map_store(%s, %s, %s)\n",
6649 map->map_mname, key, val);
6651 for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
6653 MAP *mm = map->map_stack[mapno];
6655 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
6658 mm->map_class->map_store(mm, key, val);
6661 syserr("seq_map_store(%s, %s, %s): no writable map",
6662 map->map_mname, key, val);
6670 null_map_open(map, mode)
6686 null_map_lookup(map, key, args, pstat)
6692 *pstat = EX_NOTFOUND;
6698 null_map_store(map, key, val)
6706 MAPCLASS NullMapClass =
6708 "null-map", NULL, 0,
6709 NULL, null_map_lookup, null_map_store,
6710 null_map_open, null_map_close,
6718 bogus_map_lookup(map, key, args, pstat)
6724 *pstat = EX_TEMPFAIL;
6728 MAPCLASS BogusMapClass =
6730 "bogus-map", NULL, 0,
6731 NULL, bogus_map_lookup, null_map_store,
6732 null_map_open, null_map_close,
6739 macro_map_lookup(map, name, av, statp)
6748 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
6749 name == NULL ? "NULL" : name);
6753 (mid = macid(name)) == 0)
6760 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
6762 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
6775 # define DEFAULT_DELIM CONDELSE
6776 # define END_OF_FIELDS -1
6777 # define ERRBUF_SIZE 80
6778 # define MAX_MATCH 32
6780 # define xnalloc(s) memset(xalloc(s), '\0', s);
6784 regex_t *regex_pattern_buf; /* xalloc it */
6785 int *regex_subfields; /* move to type MAP */
6786 char *regex_delim; /* move to type MAP */
6789 static int parse_fields __P((char *, int *, int, int));
6790 static char *regex_map_rewrite __P((MAP *, const char*, size_t, char **));
6793 parse_fields(s, ibuf, blen, nr_substrings)
6795 int *ibuf; /* array */
6796 int blen; /* number of elements in ibuf */
6797 int nr_substrings; /* number of substrings in the pattern */
6801 bool lastone = false;
6803 blen--; /* for terminating END_OF_FIELDS */
6824 if (val < 0 || val >= nr_substrings)
6826 syserr("field (%d) out of range, only %d substrings in pattern",
6827 val, nr_substrings);
6834 syserr("too many fields, %d max", blen);
6839 ibuf[i] = END_OF_FIELDS;
6844 regex_map_init(map, ap)
6849 struct regex_map *map_p;
6851 char *sub_param = NULL;
6853 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
6856 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6857 map->map_mname, ap);
6859 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
6861 map_p = (struct regex_map *) xnalloc(sizeof(*map_p));
6862 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
6866 while (isascii(*p) && isspace(*p))
6873 map->map_mflags |= MF_REGEX_NOT;
6876 case 'f': /* case sensitive */
6877 map->map_mflags |= MF_NOFOLDCASE;
6878 pflags &= ~REG_ICASE;
6881 case 'b': /* basic regular expressions */
6882 pflags &= ~REG_EXTENDED;
6885 case 's': /* substring match () syntax */
6887 pflags &= ~REG_NOSUB;
6890 case 'd': /* delimiter */
6891 map_p->regex_delim = ++p;
6894 case 'a': /* map append */
6898 case 'm': /* matchonly */
6899 map->map_mflags |= MF_MATCHONLY;
6903 map->map_mflags |= MF_KEEPQUOTES;
6907 map->map_spacesub = *++p;
6911 map->map_mflags |= MF_DEFER;
6915 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
6921 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
6923 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
6926 char errbuf[ERRBUF_SIZE];
6928 (void) regerror(regerr, map_p->regex_pattern_buf,
6929 errbuf, sizeof(errbuf));
6930 syserr("pattern-compile-error: %s", errbuf);
6931 sm_free(map_p->regex_pattern_buf); /* XXX */
6932 sm_free(map_p); /* XXX */
6936 if (map->map_app != NULL)
6937 map->map_app = newstr(map->map_app);
6938 if (map_p->regex_delim != NULL)
6939 map_p->regex_delim = newstr(map_p->regex_delim);
6941 map_p->regex_delim = defdstr;
6943 if (!bitset(REG_NOSUB, pflags))
6945 /* substring matching */
6947 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
6949 substrings = map_p->regex_pattern_buf->re_nsub + 1;
6952 sm_dprintf("regex_map_init: nr of substrings %d\n",
6955 if (substrings >= MAX_MATCH)
6957 syserr("too many substrings, %d max", MAX_MATCH);
6958 sm_free(map_p->regex_pattern_buf); /* XXX */
6959 sm_free(map_p); /* XXX */
6962 if (sub_param != NULL && sub_param[0] != '\0')
6964 /* optional parameter -sfields */
6965 if (parse_fields(sub_param, fields,
6966 MAX_MATCH + 1, substrings) == -1)
6973 /* set default fields */
6974 for (i = 0; i < substrings; i++)
6976 fields[i] = END_OF_FIELDS;
6978 map_p->regex_subfields = fields;
6983 sm_dprintf("regex_map_init: subfields");
6984 for (ip = fields; *ip != END_OF_FIELDS; ip++)
6985 sm_dprintf(" %d", *ip);
6989 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */
6994 regex_map_rewrite(map, s, slen, av)
7000 if (bitset(MF_MATCHONLY, map->map_mflags))
7001 return map_rewrite(map, av[0], strlen(av[0]), NULL);
7003 return map_rewrite(map, s, slen, av);
7007 regex_map_lookup(map, name, av, statp)
7014 struct regex_map *map_p;
7015 regmatch_t pmatch[MAX_MATCH];
7021 sm_dprintf("regex_map_lookup: key '%s'\n", name);
7022 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7023 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
7026 map_p = (struct regex_map *)(map->map_db1);
7027 reg_res = regexec(map_p->regex_pattern_buf,
7028 name, MAX_MATCH, pmatch, 0);
7030 if (bitset(MF_REGEX_NOT, map->map_mflags))
7033 if (reg_res == REG_NOMATCH)
7034 return regex_map_rewrite(map, "", (size_t) 0, av);
7038 if (reg_res == REG_NOMATCH)
7041 if (map_p->regex_subfields != NULL)
7044 static char retbuf[MAXNAME];
7045 int fields[MAX_MATCH + 1];
7047 int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
7048 bool quotemode = false, bslashmode = false;
7049 register char *dp, *sp;
7054 ldp = retbuf + sizeof(retbuf) - 1;
7058 if (parse_fields(av[1], fields, MAX_MATCH + 1,
7059 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
7067 ip = map_p->regex_subfields;
7069 for ( ; *ip != END_OF_FIELDS; ip++)
7073 for (sp = map_p->regex_delim; *sp; sp++)
7082 if (*ip >= MAX_MATCH ||
7083 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
7086 sp = name + pmatch[*ip].rm_so;
7087 endp = name + pmatch[*ip].rm_eo;
7088 for (; endp > sp; sp++)
7097 else if (quotemode && *sp != '"' &&
7102 else switch (*dp++ = *sp)
7129 quotemode = !quotemode;
7135 if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
7136 bslashmode || spacecnt != 0)
7138 sm_syslog(LOG_WARNING, NOQID,
7139 "Warning: regex may cause prescan() failure map=%s lookup=%s",
7140 map->map_mname, name);
7146 return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
7148 return regex_map_rewrite(map, "", (size_t)0, av);
7150 #endif /* MAP_REGEX */
7157 # define _DATUM_DEFINED
7158 # include <ns_api.h>
7160 typedef struct ns_map_list
7162 ns_map_t *map; /* XXX ns_ ? */
7164 struct ns_map_list *next;
7168 ns_map_t_find(mapname)
7171 static ns_map_list_t *ns_maps = NULL;
7172 ns_map_list_t *ns_map;
7174 /* walk the list of maps looking for the correctly named map */
7175 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
7177 if (strcmp(ns_map->mapname, mapname) == 0)
7181 /* if we are looking at a NULL ns_map_list_t, then create a new one */
7184 ns_map = (ns_map_list_t *) xalloc(sizeof(*ns_map));
7185 ns_map->mapname = newstr(mapname);
7186 ns_map->map = (ns_map_t *) xalloc(sizeof(*ns_map->map));
7187 memset(ns_map->map, '\0', sizeof(*ns_map->map));
7188 ns_map->next = ns_maps;
7195 nsd_map_lookup(map, name, av, statp)
7204 char keybuf[MAXNAME + 1];
7208 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
7210 buflen = strlen(name);
7211 if (buflen > sizeof(keybuf) - 1)
7212 buflen = sizeof(keybuf) - 1; /* XXX simply cut off? */
7213 memmove(keybuf, name, buflen);
7214 keybuf[buflen] = '\0';
7215 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7218 ns_map = ns_map_t_find(map->map_file);
7222 sm_dprintf("nsd_map_t_find failed\n");
7223 *statp = EX_UNAVAILABLE;
7226 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
7228 if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
7230 *statp = EX_TEMPFAIL;
7236 # endif /* NS_NOPERM */
7242 if (r != NS_SUCCESS)
7244 *statp = EX_NOTFOUND;
7250 /* Null out trailing \n */
7251 if ((p = strchr(buf, '\n')) != NULL)
7254 return map_rewrite(map, buf, strlen(buf), av);
7256 #endif /* MAP_NSD */
7259 arith_map_lookup(map, name, av, statp)
7269 static char result[16];
7274 sm_dprintf("arith_map_lookup: key '%s'\n", name);
7275 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
7276 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
7284 ** read arguments for arith map
7285 ** - no check is made whether they are really numbers
7286 ** - just ignores args after the second
7289 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
7290 v[r++] = strtol(*cpp, NULL, 0);
7292 /* operator and (at least) two operands given? */
7293 if (name != NULL && r == 2)
7339 r = v[1] - v[0] + 1;
7342 r = get_random() % r + v[0];
7349 sm_syslog(LOG_WARNING, NOQID,
7350 "arith_map: unknown operator %c",
7351 (isascii(*name) && isprint(*name)) ?
7356 (void) sm_snprintf(result, sizeof(result),
7357 res ? "TRUE" : "FALSE");
7359 (void) sm_snprintf(result, sizeof(result), "%ld", r);
7368 # if NETINET || NETINET6
7369 # include <arpa/inet.h>
7370 # endif /* NETINET || NETINET6 */
7372 # define socket_map_next map_stack[0]
7375 ** SOCKET_MAP_OPEN -- open socket table
7379 socket_map_open(map, mode)
7385 SOCKADDR_LEN_T addrlen = 0;
7391 struct hostent *hp = NULL;
7395 sm_dprintf("socket_map_open(%s, %s, %d)\n",
7396 map->map_mname, map->map_file, mode);
7400 /* sendmail doesn't have the ability to write to SOCKET (yet) */
7401 if (mode != O_RDONLY)
7403 /* issue a pseudo-error message */
7404 errno = SM_EMAPCANTWRITE;
7408 if (*map->map_file == '\0')
7410 syserr("socket map \"%s\": empty or missing socket information",
7415 s = socket_map_findconn(map->map_file);
7416 if (s->s_socketmap != NULL)
7418 /* Copy open connection */
7419 map->map_db1 = s->s_socketmap->map_db1;
7421 /* Add this map as head of linked list */
7422 map->socket_map_next = s->s_socketmap;
7423 s->s_socketmap = map;
7426 sm_dprintf("using cached connection\n");
7431 sm_dprintf("opening new connection\n");
7433 /* following code is ripped from milter.c */
7434 /* XXX It should be put in a library... */
7436 /* protocol:filename or protocol:port@host */
7437 memset(&addr, '\0', sizeof(addr));
7439 colon = strchr(p, ':');
7447 /* default to AF_UNIX */
7448 addr.sa.sa_family = AF_UNIX;
7449 # else /* NETUNIX */
7451 /* default to AF_INET */
7452 addr.sa.sa_family = AF_INET;
7453 # else /* NETINET */
7455 /* default to AF_INET6 */
7456 addr.sa.sa_family = AF_INET6;
7457 # else /* NETINET6 */
7458 /* no protocols available */
7459 syserr("socket map \"%s\": no valid socket protocols available",
7462 # endif /* NETINET6 */
7463 # endif /* NETINET */
7464 # endif /* NETUNIX */
7467 else if (sm_strcasecmp(p, "unix") == 0 ||
7468 sm_strcasecmp(p, "local") == 0)
7469 addr.sa.sa_family = AF_UNIX;
7470 # endif /* NETUNIX */
7472 else if (sm_strcasecmp(p, "inet") == 0)
7473 addr.sa.sa_family = AF_INET;
7474 # endif /* NETINET */
7476 else if (sm_strcasecmp(p, "inet6") == 0)
7477 addr.sa.sa_family = AF_INET6;
7478 # endif /* NETINET6 */
7481 # ifdef EPROTONOSUPPORT
7482 errno = EPROTONOSUPPORT;
7483 # else /* EPROTONOSUPPORT */
7485 # endif /* EPROTONOSUPPORT */
7486 syserr("socket map \"%s\": unknown socket type %s",
7496 /* default to AF_UNIX */
7497 addr.sa.sa_family = AF_UNIX;
7500 /* default to AF_INET */
7501 addr.sa.sa_family = AF_INET;
7502 # else /* NETINET */
7504 /* default to AF_INET6 */
7505 addr.sa.sa_family = AF_INET6;
7506 # else /* NETINET6 */
7507 syserr("socket map \"%s\": unknown socket type %s",
7510 # endif /* NETINET6 */
7511 # endif /* NETINET */
7512 #endif /* NETUNIX */
7516 if (addr.sa.sa_family == AF_UNIX)
7518 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
7521 if (strlen(colon) >= sizeof(addr.sunix.sun_path))
7523 syserr("socket map \"%s\": local socket name %s too long",
7524 map->map_mname, colon);
7527 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
7528 S_IRUSR|S_IWUSR, NULL);
7532 /* if not safe, don't create */
7533 syserr("socket map \"%s\": local socket name %s unsafe",
7534 map->map_mname, colon);
7538 (void) sm_strlcpy(addr.sunix.sun_path, colon,
7539 sizeof(addr.sunix.sun_path));
7540 addrlen = sizeof(struct sockaddr_un);
7543 # endif /* NETUNIX */
7544 # if NETINET || NETINET6
7547 || addr.sa.sa_family == AF_INET
7548 # endif /* NETINET */
7550 || addr.sa.sa_family == AF_INET6
7551 # endif /* NETINET6 */
7554 unsigned short port;
7556 /* Parse port@host */
7557 at = strchr(colon, '@');
7560 syserr("socket map \"%s\": bad address %s (expected port@host)",
7561 map->map_mname, colon);
7565 if (isascii(*colon) && isdigit(*colon))
7566 port = htons((unsigned short) atoi(colon));
7569 # ifdef NO_GETSERVBYNAME
7570 syserr("socket map \"%s\": invalid port number %s",
7571 map->map_mname, colon);
7573 # else /* NO_GETSERVBYNAME */
7574 register struct servent *sp;
7576 sp = getservbyname(colon, "tcp");
7579 syserr("socket map \"%s\": unknown port name %s",
7580 map->map_mname, colon);
7584 # endif /* NO_GETSERVBYNAME */
7591 end = strchr(at, ']');
7596 unsigned long hid = INADDR_NONE;
7597 # endif /* NETINET */
7599 struct sockaddr_in6 hid6;
7600 # endif /* NETINET6 */
7604 if (addr.sa.sa_family == AF_INET &&
7605 (hid = inet_addr(&at[1])) != INADDR_NONE)
7607 addr.sin.sin_addr.s_addr = hid;
7608 addr.sin.sin_port = port;
7611 # endif /* NETINET */
7613 (void) memset(&hid6, '\0', sizeof(hid6));
7614 if (addr.sa.sa_family == AF_INET6 &&
7615 anynet_pton(AF_INET6, &at[1],
7616 &hid6.sin6_addr) == 1)
7618 addr.sin6.sin6_addr = hid6.sin6_addr;
7619 addr.sin6.sin6_port = port;
7622 # endif /* NETINET6 */
7626 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7627 map->map_mname, at);
7633 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7634 map->map_mname, at);
7640 hp = sm_gethostbyname(at, addr.sa.sa_family);
7643 syserr("socket map \"%s\": Unknown host name %s",
7644 map->map_mname, at);
7647 addr.sa.sa_family = hp->h_addrtype;
7648 switch (hp->h_addrtype)
7652 memmove(&addr.sin.sin_addr,
7653 hp->h_addr, INADDRSZ);
7654 addr.sin.sin_port = port;
7655 addrlen = sizeof(struct sockaddr_in);
7658 # endif /* NETINET */
7662 memmove(&addr.sin6.sin6_addr,
7663 hp->h_addr, IN6ADDRSZ);
7664 addr.sin6.sin6_port = port;
7665 addrlen = sizeof(struct sockaddr_in6);
7668 # endif /* NETINET6 */
7671 syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7672 map->map_mname, at, hp->h_addrtype);
7675 # endif /* NETINET6 */
7681 # endif /* NETINET || NETINET6 */
7683 syserr("socket map \"%s\": unknown socket protocol",
7688 /* nope, actually connecting */
7691 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
7696 sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7698 sm_errstring(save_errno));
7702 # endif /* NETINET6 */
7706 if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
7709 /* couldn't connect.... try next address */
7714 sm_dprintf("socket_open (%s): open %s failed: %s\n",
7715 map->map_mname, at, sm_errstring(save_errno));
7719 /* try next address */
7720 if (hp != NULL && hp->h_addr_list[addrno] != NULL)
7722 switch (addr.sa.sa_family)
7726 memmove(&addr.sin.sin_addr,
7727 hp->h_addr_list[addrno++],
7730 # endif /* NETINET */
7734 memmove(&addr.sin6.sin6_addr,
7735 hp->h_addr_list[addrno++],
7738 # endif /* NETINET6 */
7742 sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7747 # endif /* NETINET6 */
7755 sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7756 map->map_mname, sm_errstring(save_errno));
7761 # endif /* NETINET6 */
7770 # endif /* NETINET6 */
7771 if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
7779 sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7780 map->map_mname, sm_errstring(errno));
7784 /* Save connection for reuse */
7785 s->s_socketmap = map;
7790 ** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7792 ** Cache SOCKET connections based on the connection specifier
7793 ** and PID so we don't have multiple connections open to
7794 ** the same server for different maps. Need a separate connection
7795 ** per PID since a parent process may close the map before the
7796 ** child is done with it.
7799 ** conn -- SOCKET map connection specifier
7802 ** Symbol table entry for the SOCKET connection.
7806 socket_map_findconn(conn)
7810 STAB *SM_NONVOLATILE s = NULL;
7812 nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
7814 s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
7822 ** SOCKET_MAP_CLOSE -- close the socket
7826 socket_map_close(map)
7833 sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
7836 /* Check if already closed */
7837 if (map->map_db1 == NULL)
7840 sm_dprintf("socket_map_close(%s) already closed\n",
7844 sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
7846 /* Mark all the maps that share the connection as closed */
7847 s = socket_map_findconn(map->map_file);
7848 smap = s->s_socketmap;
7849 while (smap != NULL)
7853 if (tTd(38, 2) && smap != map)
7854 sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7855 map->map_mname, smap->map_mname);
7857 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
7858 smap->map_db1 = NULL;
7859 next = smap->socket_map_next;
7860 smap->socket_map_next = NULL;
7863 s->s_socketmap = NULL;
7867 ** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7871 socket_map_lookup(map, name, av, statp)
7877 unsigned int nettolen, replylen, recvlen;
7878 char *replybuf, *rval, *value, *status, *key;
7880 char keybuf[MAXNAME + 1];
7884 f = (SM_FILE_T *)map->map_db1;
7886 sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7887 map->map_mname, name, map->map_file);
7889 if (!bitset(MF_NOFOLDCASE, map->map_mflags))
7891 nettolen = strlen(name);
7892 if (nettolen > sizeof(keybuf) - 1)
7893 nettolen = sizeof(keybuf) - 1;
7894 memmove(keybuf, name, nettolen);
7895 keybuf[nettolen] = '\0';
7902 nettolen = strlen(map->map_mname) + 1 + strlen(key);
7903 SM_ASSERT(nettolen > strlen(map->map_mname));
7904 SM_ASSERT(nettolen > strlen(key));
7905 if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
7906 nettolen, map->map_mname, key) == SM_IO_EOF) ||
7907 (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
7910 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
7912 *statp = EX_TEMPFAIL;
7916 if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
7918 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
7920 *statp = EX_TEMPFAIL;
7923 if (replylen > SOCKETMAP_MAXL)
7925 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
7926 map->map_mname, replylen);
7927 *statp = EX_TEMPFAIL;
7930 if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
7932 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
7934 *statp = EX_TEMPFAIL;
7938 replybuf = (char *) sm_malloc(replylen + 1);
7939 if (replybuf == NULL)
7941 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
7942 map->map_mname, replylen + 1);
7947 recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
7948 if (recvlen < replylen)
7950 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
7951 map->map_mname, recvlen, replylen);
7952 *statp = EX_TEMPFAIL;
7955 if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
7957 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
7959 *statp = EX_TEMPFAIL;
7963 replybuf[recvlen] = '\0';
7964 value = strchr(replybuf, ' ');
7970 if (strcmp(status, "OK") == 0)
7974 /* collect the return value */
7975 if (bitset(MF_MATCHONLY, map->map_mflags))
7976 rval = map_rewrite(map, key, strlen(key), NULL);
7978 rval = map_rewrite(map, value, strlen(value), av);
7980 else if (strcmp(status, "NOTFOUND") == 0)
7982 *statp = EX_NOTFOUND;
7984 sm_dprintf("socket_map_lookup(%s): %s not found\n",
7985 map->map_mname, key);
7990 sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
7991 map->map_mname, key, status,
7992 value ? value : "");
7993 if ((strcmp(status, "TEMP") == 0) ||
7994 (strcmp(status, "TIMEOUT") == 0))
7995 *statp = EX_TEMPFAIL;
7996 else if(strcmp(status, "PERM") == 0)
7997 *statp = EX_UNAVAILABLE;
7999 *statp = EX_PROTOCOL;
8002 if (replybuf != NULL)
8007 socket_map_close(map);
8009 if (replybuf != NULL)
8013 #endif /* SOCKETMAP */