/contrib/sendmail/src/map.c
C | 2232 lines | 1681 code | 228 blank | 323 comment | 557 complexity | 3dea58c72cbf8c9252388b2b16c7335a MD5 | raw file
- /*
- * Copyright (c) 1998-2008 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1992, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
- #include <sendmail.h>
- SM_RCSID("@(#)$Id: map.c,v 8.706 2010/07/27 03:35:42 ca Exp $")
- #if LDAPMAP
- # include <sm/ldap.h>
- #endif /* LDAPMAP */
- #if NDBM
- # include <ndbm.h>
- # ifdef R_FIRST
- ERROR README: You are running the Berkeley DB version of ndbm.h. See
- ERROR README: the README file about tweaking Berkeley DB so it can
- ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile
- ERROR README: and use -DNEWDB instead.
- # endif /* R_FIRST */
- #endif /* NDBM */
- #if NEWDB
- # include "sm/bdb.h"
- #endif /* NEWDB */
- #if NIS
- struct dom_binding; /* forward reference needed on IRIX */
- # include <rpcsvc/ypclnt.h>
- # if NDBM
- # define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
- # endif /* NDBM */
- #endif /* NIS */
- #include "map.h"
- #if NEWDB
- # if DB_VERSION_MAJOR < 2
- static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
- # endif /* DB_VERSION_MAJOR < 2 */
- # if DB_VERSION_MAJOR == 2
- static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
- # endif /* DB_VERSION_MAJOR == 2 */
- # if DB_VERSION_MAJOR > 2
- static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **));
- # endif /* DB_VERSION_MAJOR > 2 */
- #endif /* NEWDB */
- static bool extract_canonname __P((char *, char *, char *, char[], int));
- static void map_close __P((STAB *, int));
- static void map_init __P((STAB *, int));
- #ifdef LDAPMAP
- static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *));
- #endif /* LDAPMAP */
- #if NISPLUS
- static bool nisplus_getcanonname __P((char *, int, int *));
- #endif /* NISPLUS */
- #if NIS
- static bool nis_getcanonname __P((char *, int, int *));
- #endif /* NIS */
- #if NETINFO
- static bool ni_getcanonname __P((char *, int, int *));
- #endif /* NETINFO */
- static bool text_getcanonname __P((char *, int, int *));
- #if SOCKETMAP
- static STAB *socket_map_findconn __P((const char*));
- /* XXX arbitrary limit for sanity */
- # define SOCKETMAP_MAXL 1000000
- #endif /* SOCKETMAP */
- /* default error message for trying to open a map in write mode */
- #ifdef ENOSYS
- # define SM_EMAPCANTWRITE ENOSYS
- #else /* ENOSYS */
- # ifdef EFTYPE
- # define SM_EMAPCANTWRITE EFTYPE
- # else /* EFTYPE */
- # define SM_EMAPCANTWRITE ENXIO
- # endif /* EFTYPE */
- #endif /* ENOSYS */
- /*
- ** MAP.C -- implementations for various map classes.
- **
- ** Each map class implements a series of functions:
- **
- ** bool map_parse(MAP *map, char *args)
- ** Parse the arguments from the config file. Return true
- ** if they were ok, false otherwise. Fill in map with the
- ** values.
- **
- ** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
- ** Look up the key in the given map. If found, do any
- ** rewriting the map wants (including "args" if desired)
- ** and return the value. Set *pstat to the appropriate status
- ** on error and return NULL. Args will be NULL if called
- ** from the alias routines, although this should probably
- ** not be relied upon. It is suggested you call map_rewrite
- ** to return the results -- it takes care of null termination
- ** and uses a dynamically expanded buffer as needed.
- **
- ** void map_store(MAP *map, char *key, char *value)
- ** Store the key:value pair in the map.
- **
- ** bool map_open(MAP *map, int mode)
- ** Open the map for the indicated mode. Mode should
- ** be either O_RDONLY or O_RDWR. Return true if it
- ** was opened successfully, false otherwise. If the open
- ** failed and the MF_OPTIONAL flag is not set, it should
- ** also print an error. If the MF_ALIAS bit is set
- ** and this map class understands the @:@ convention, it
- ** should call aliaswait() before returning.
- **
- ** void map_close(MAP *map)
- ** Close the map.
- **
- ** This file also includes the implementation for getcanonname.
- ** It is currently implemented in a pretty ad-hoc manner; it ought
- ** to be more properly integrated into the map structure.
- */
- #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
- # define LOCK_ON_OPEN 1 /* we can open/create a locked file */
- #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
- # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
- #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
- /*
- ** MAP_PARSEARGS -- parse config line arguments for database lookup
- **
- ** This is a generic version of the map_parse method.
- **
- ** Parameters:
- ** map -- the map being initialized.
- ** ap -- a pointer to the args on the config line.
- **
- ** Returns:
- ** true -- if everything parsed OK.
- ** false -- otherwise.
- **
- ** Side Effects:
- ** null terminates the filename; stores it in map
- */
- bool
- map_parseargs(map, ap)
- MAP *map;
- char *ap;
- {
- register char *p = ap;
- /*
- ** There is no check whether there is really an argument,
- ** but that's not important enough to warrant extra code.
- */
- map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
- map->map_spacesub = SpaceSub; /* default value */
- for (;;)
- {
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p != '-')
- break;
- switch (*++p)
- {
- case 'N':
- map->map_mflags |= MF_INCLNULL;
- map->map_mflags &= ~MF_TRY0NULL;
- break;
- case 'O':
- map->map_mflags &= ~MF_TRY1NULL;
- break;
- case 'o':
- map->map_mflags |= MF_OPTIONAL;
- break;
- case 'f':
- map->map_mflags |= MF_NOFOLDCASE;
- break;
- case 'm':
- map->map_mflags |= MF_MATCHONLY;
- break;
- case 'A':
- map->map_mflags |= MF_APPEND;
- break;
- case 'q':
- map->map_mflags |= MF_KEEPQUOTES;
- break;
- case 'a':
- map->map_app = ++p;
- break;
- case 'T':
- map->map_tapp = ++p;
- break;
- case 'k':
- while (isascii(*++p) && isspace(*p))
- continue;
- map->map_keycolnm = p;
- break;
- case 'v':
- while (isascii(*++p) && isspace(*p))
- continue;
- map->map_valcolnm = p;
- break;
- case 'z':
- if (*++p != '\\')
- map->map_coldelim = *p;
- else
- {
- switch (*++p)
- {
- case 'n':
- map->map_coldelim = '\n';
- break;
- case 't':
- map->map_coldelim = '\t';
- break;
- default:
- map->map_coldelim = '\\';
- }
- }
- break;
- case 't':
- map->map_mflags |= MF_NODEFER;
- break;
- case 'S':
- map->map_spacesub = *++p;
- break;
- case 'D':
- map->map_mflags |= MF_DEFER;
- break;
- default:
- syserr("Illegal option %c map %s", *p, map->map_mname);
- break;
- }
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p++ = '\0';
- }
- if (map->map_app != NULL)
- map->map_app = newstr(map->map_app);
- if (map->map_tapp != NULL)
- map->map_tapp = newstr(map->map_tapp);
- if (map->map_keycolnm != NULL)
- map->map_keycolnm = newstr(map->map_keycolnm);
- if (map->map_valcolnm != NULL)
- map->map_valcolnm = newstr(map->map_valcolnm);
- if (*p != '\0')
- {
- map->map_file = p;
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p++ = '\0';
- map->map_file = newstr(map->map_file);
- }
- while (*p != '\0' && isascii(*p) && isspace(*p))
- p++;
- if (*p != '\0')
- map->map_rebuild = newstr(p);
- if (map->map_file == NULL &&
- !bitset(MCF_OPTFILE, map->map_class->map_cflags))
- {
- syserr("No file name for %s map %s",
- map->map_class->map_cname, map->map_mname);
- return false;
- }
- return true;
- }
- /*
- ** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
- **
- ** It also adds the map_app string. It can be used as a utility
- ** in the map_lookup method.
- **
- ** Parameters:
- ** map -- the map that causes this.
- ** s -- the string to rewrite, NOT necessarily null terminated.
- ** slen -- the length of s.
- ** av -- arguments to interpolate into buf.
- **
- ** Returns:
- ** Pointer to rewritten result. This is static data that
- ** should be copied if it is to be saved!
- */
- char *
- map_rewrite(map, s, slen, av)
- register MAP *map;
- register const char *s;
- size_t slen;
- char **av;
- {
- register char *bp;
- register char c;
- char **avp;
- register char *ap;
- size_t l;
- size_t len;
- static size_t buflen = 0;
- static char *buf = NULL;
- if (tTd(39, 1))
- {
- sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
- if (av == NULL)
- sm_dprintf(" (nullv)");
- else
- {
- for (avp = av; *avp != NULL; avp++)
- sm_dprintf("\n\t%s", *avp);
- }
- sm_dprintf("\n");
- }
- /* count expected size of output (can safely overestimate) */
- l = len = slen;
- if (av != NULL)
- {
- const char *sp = s;
- while (l-- > 0 && (c = *sp++) != '\0')
- {
- if (c != '%')
- continue;
- if (l-- <= 0)
- break;
- c = *sp++;
- if (!(isascii(c) && isdigit(c)))
- continue;
- for (avp = av; --c >= '0' && *avp != NULL; avp++)
- continue;
- if (*avp == NULL)
- continue;
- len += strlen(*avp);
- }
- }
- if (map->map_app != NULL)
- len += strlen(map->map_app);
- if (buflen < ++len)
- {
- /* need to malloc additional space */
- buflen = len;
- if (buf != NULL)
- sm_free(buf);
- buf = sm_pmalloc_x(buflen);
- }
- bp = buf;
- if (av == NULL)
- {
- memmove(bp, s, slen);
- bp += slen;
- /* assert(len > slen); */
- len -= slen;
- }
- else
- {
- while (slen-- > 0 && (c = *s++) != '\0')
- {
- if (c != '%')
- {
- pushc:
- if (len-- <= 1)
- break;
- *bp++ = c;
- continue;
- }
- if (slen-- <= 0 || (c = *s++) == '\0')
- c = '%';
- if (c == '%')
- goto pushc;
- if (!(isascii(c) && isdigit(c)))
- {
- if (len-- <= 1)
- break;
- *bp++ = '%';
- goto pushc;
- }
- for (avp = av; --c >= '0' && *avp != NULL; avp++)
- continue;
- if (*avp == NULL)
- continue;
- /* transliterate argument into output string */
- for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
- *bp++ = c;
- }
- }
- if (map->map_app != NULL && len > 0)
- (void) sm_strlcpy(bp, map->map_app, len);
- else
- *bp = '\0';
- if (tTd(39, 1))
- sm_dprintf("map_rewrite => %s\n", buf);
- return buf;
- }
- /*
- ** INITMAPS -- rebuild alias maps
- **
- ** Parameters:
- ** none.
- **
- ** Returns:
- ** none.
- */
- void
- initmaps()
- {
- #if XDEBUG
- checkfd012("entering initmaps");
- #endif /* XDEBUG */
- stabapply(map_init, 0);
- #if XDEBUG
- checkfd012("exiting initmaps");
- #endif /* XDEBUG */
- }
- /*
- ** MAP_INIT -- rebuild a map
- **
- ** Parameters:
- ** s -- STAB entry: if map: try to rebuild
- ** unused -- unused variable
- **
- ** Returns:
- ** none.
- **
- ** Side Effects:
- ** will close already open rebuildable map.
- */
- /* ARGSUSED1 */
- static void
- map_init(s, unused)
- register STAB *s;
- int unused;
- {
- register MAP *map;
- /* has to be a map */
- if (s->s_symtype != ST_MAP)
- return;
- map = &s->s_map;
- if (!bitset(MF_VALID, map->map_mflags))
- return;
- if (tTd(38, 2))
- sm_dprintf("map_init(%s:%s, %s)\n",
- map->map_class->map_cname == NULL ? "NULL" :
- map->map_class->map_cname,
- map->map_mname == NULL ? "NULL" : map->map_mname,
- map->map_file == NULL ? "NULL" : map->map_file);
- if (!bitset(MF_ALIAS, map->map_mflags) ||
- !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
- {
- if (tTd(38, 3))
- sm_dprintf("\tnot rebuildable\n");
- return;
- }
- /* if already open, close it (for nested open) */
- if (bitset(MF_OPEN, map->map_mflags))
- {
- map->map_mflags |= MF_CLOSING;
- map->map_class->map_close(map);
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
- }
- (void) rebuildaliases(map, false);
- return;
- }
- /*
- ** OPENMAP -- open a map
- **
- ** Parameters:
- ** map -- map to open (it must not be open).
- **
- ** Returns:
- ** whether open succeeded.
- */
- bool
- openmap(map)
- MAP *map;
- {
- bool restore = false;
- bool savehold = HoldErrs;
- bool savequick = QuickAbort;
- int saveerrors = Errors;
- if (!bitset(MF_VALID, map->map_mflags))
- return false;
- /* better safe than sorry... */
- if (bitset(MF_OPEN, map->map_mflags))
- return true;
- /* Don't send a map open error out via SMTP */
- if ((OnlyOneError || QuickAbort) &&
- (OpMode == MD_SMTP || OpMode == MD_DAEMON))
- {
- restore = true;
- HoldErrs = true;
- QuickAbort = false;
- }
- errno = 0;
- if (map->map_class->map_open(map, O_RDONLY))
- {
- if (tTd(38, 4))
- sm_dprintf("openmap()\t%s:%s %s: valid\n",
- map->map_class->map_cname == NULL ? "NULL" :
- map->map_class->map_cname,
- map->map_mname == NULL ? "NULL" :
- map->map_mname,
- map->map_file == NULL ? "NULL" :
- map->map_file);
- map->map_mflags |= MF_OPEN;
- map->map_pid = CurrentPid;
- }
- else
- {
- if (tTd(38, 4))
- sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
- map->map_class->map_cname == NULL ? "NULL" :
- map->map_class->map_cname,
- map->map_mname == NULL ? "NULL" :
- map->map_mname,
- map->map_file == NULL ? "NULL" :
- map->map_file,
- errno == 0 ? "" : ": ",
- errno == 0 ? "" : sm_errstring(errno));
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- {
- extern MAPCLASS BogusMapClass;
- map->map_orgclass = map->map_class;
- map->map_class = &BogusMapClass;
- map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
- map->map_pid = CurrentPid;
- }
- else
- {
- /* don't try again */
- map->map_mflags &= ~MF_VALID;
- }
- }
- if (restore)
- {
- Errors = saveerrors;
- HoldErrs = savehold;
- QuickAbort = savequick;
- }
- return bitset(MF_OPEN, map->map_mflags);
- }
- /*
- ** CLOSEMAPS -- close all open maps opened by the current pid.
- **
- ** Parameters:
- ** bogus -- only close bogus maps.
- **
- ** Returns:
- ** none.
- */
- void
- closemaps(bogus)
- bool bogus;
- {
- stabapply(map_close, bogus);
- }
- /*
- ** MAP_CLOSE -- close a map opened by the current pid.
- **
- ** Parameters:
- ** s -- STAB entry: if map: try to close
- ** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
- **
- ** Returns:
- ** none.
- */
- /* ARGSUSED1 */
- static void
- map_close(s, bogus)
- register STAB *s;
- int bogus; /* int because of stabapply(), used as bool */
- {
- MAP *map;
- extern MAPCLASS BogusMapClass;
- if (s->s_symtype != ST_MAP)
- return;
- map = &s->s_map;
- /*
- ** close the map iff:
- ** it is valid and open and opened by this process
- ** and (!bogus or it's a bogus map or it is not persistent)
- ** negate this: return iff
- ** it is not valid or it is not open or not opened by this process
- ** or (bogus and it's not a bogus map and it's not not-persistent)
- */
- if (!bitset(MF_VALID, map->map_mflags) ||
- !bitset(MF_OPEN, map->map_mflags) ||
- bitset(MF_CLOSING, map->map_mflags) ||
- map->map_pid != CurrentPid ||
- (bogus && map->map_class != &BogusMapClass &&
- !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
- return;
- if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
- map->map_orgclass != &BogusMapClass)
- map->map_class = map->map_orgclass;
- if (tTd(38, 5))
- sm_dprintf("closemaps: closing %s (%s)\n",
- map->map_mname == NULL ? "NULL" : map->map_mname,
- map->map_file == NULL ? "NULL" : map->map_file);
- if (!bitset(MF_OPENBOGUS, map->map_mflags))
- {
- map->map_mflags |= MF_CLOSING;
- map->map_class->map_close(map);
- }
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
- }
- #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- extern int getdomainname();
- /* this is mainly for backward compatibility in Sun environment */
- static char *
- sun_init_domain()
- {
- /*
- ** Get the domain name from the kernel.
- ** If it does not start with a leading dot, then remove
- ** the first component. Since leading dots are funny Unix
- ** files, we treat a leading "+" the same as a leading dot.
- ** Finally, force there to be at least one dot in the domain name
- ** (i.e. top-level domains are not allowed, like "com", must be
- ** something like "sun.com").
- */
- char buf[MAXNAME];
- char *period, *autodomain;
- if (getdomainname(buf, sizeof buf) < 0)
- return NULL;
- if (buf[0] == '\0')
- return NULL;
- if (tTd(0, 20))
- printf("domainname = %s\n", buf);
- if (buf[0] == '+')
- buf[0] = '.';
- period = strchr(buf, '.');
- if (period == NULL)
- autodomain = buf;
- else
- autodomain = period + 1;
- if (strchr(autodomain, '.') == NULL)
- return newstr(buf);
- else
- return newstr(autodomain);
- }
- #endif /* SUN_EXTENSIONS && SUN_INIT_DOMAIN */
- /*
- ** GETCANONNAME -- look up name using service switch
- **
- ** Parameters:
- ** host -- the host name to look up.
- ** hbsize -- the size of the host buffer.
- ** trymx -- if set, try MX records.
- ** pttl -- pointer to return TTL (can be NULL).
- **
- ** Returns:
- ** true -- if the host was found.
- ** false -- otherwise.
- */
- bool
- getcanonname(host, hbsize, trymx, pttl)
- char *host;
- int hbsize;
- bool trymx;
- int *pttl;
- {
- int nmaps;
- int mapno;
- bool found = false;
- bool got_tempfail = false;
- auto int status = EX_UNAVAILABLE;
- char *maptype[MAXMAPSTACK];
- short mapreturn[MAXMAPACTIONS];
- #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- bool should_try_nis_domain = false;
- static char *nis_domain = NULL;
- #endif
- nmaps = switch_map_find("hosts", maptype, mapreturn);
- if (pttl != 0)
- *pttl = SM_DEFAULT_TTL;
- for (mapno = 0; mapno < nmaps; mapno++)
- {
- int i;
- if (tTd(38, 20))
- sm_dprintf("getcanonname(%s), trying %s\n",
- host, maptype[mapno]);
- if (strcmp("files", maptype[mapno]) == 0)
- {
- found = text_getcanonname(host, hbsize, &status);
- }
- #if NIS
- else if (strcmp("nis", maptype[mapno]) == 0)
- {
- found = nis_getcanonname(host, hbsize, &status);
- # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- if (nis_domain == NULL)
- nis_domain = sun_init_domain();
- # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
- }
- #endif /* NIS */
- #if NISPLUS
- else if (strcmp("nisplus", maptype[mapno]) == 0)
- {
- found = nisplus_getcanonname(host, hbsize, &status);
- # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- if (nis_domain == NULL)
- nis_domain = sun_init_domain();
- # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
- }
- #endif /* NISPLUS */
- #if NAMED_BIND
- else if (strcmp("dns", maptype[mapno]) == 0)
- {
- found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
- }
- #endif /* NAMED_BIND */
- #if NETINFO
- else if (strcmp("netinfo", maptype[mapno]) == 0)
- {
- found = ni_getcanonname(host, hbsize, &status);
- }
- #endif /* NETINFO */
- else
- {
- found = false;
- status = EX_UNAVAILABLE;
- }
- /*
- ** Heuristic: if $m is not set, we are running during system
- ** startup. In this case, when a name is apparently found
- ** but has no dot, treat is as not found. This avoids
- ** problems if /etc/hosts has no FQDN but is listed first
- ** in the service switch.
- */
- if (found &&
- (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
- break;
- #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- if (found)
- should_try_nis_domain = true;
- /* but don't break, as we need to try all methods first */
- #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
- /* see if we should continue */
- if (status == EX_TEMPFAIL)
- {
- i = MA_TRYAGAIN;
- got_tempfail = true;
- }
- else if (status == EX_NOTFOUND)
- i = MA_NOTFOUND;
- else
- i = MA_UNAVAIL;
- if (bitset(1 << mapno, mapreturn[i]))
- break;
- }
- if (found)
- {
- char *d;
- if (tTd(38, 20))
- sm_dprintf("getcanonname(%s), found\n", host);
- /*
- ** If returned name is still single token, compensate
- ** by tagging on $m. This is because some sites set
- ** up their DNS or NIS databases wrong.
- */
- if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
- {
- d = macvalue('m', CurEnv);
- if (d != NULL &&
- hbsize > (int) (strlen(host) + strlen(d) + 1))
- {
- if (host[strlen(host) - 1] != '.')
- (void) sm_strlcat2(host, ".", d,
- hbsize);
- else
- (void) sm_strlcat(host, d, hbsize);
- }
- else
- {
- #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- if (VendorCode == VENDOR_SUN &&
- should_try_nis_domain)
- {
- goto try_nis_domain;
- }
- #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
- return false;
- }
- }
- return true;
- }
- #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
- if (VendorCode == VENDOR_SUN && should_try_nis_domain)
- {
- try_nis_domain:
- if (nis_domain != NULL &&
- strlen(nis_domain) + strlen(host) + 1 < hbsize)
- {
- (void) sm_strlcat2(host, ".", nis_domain, hbsize);
- return true;
- }
- }
- #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
- if (tTd(38, 20))
- sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
- status);
- if (got_tempfail)
- SM_SET_H_ERRNO(TRY_AGAIN);
- else
- SM_SET_H_ERRNO(HOST_NOT_FOUND);
- return false;
- }
- /*
- ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
- **
- ** Parameters:
- ** name -- the name against which to match.
- ** dot -- where to reinsert '.' to get FQDN
- ** line -- the /etc/hosts line.
- ** cbuf -- the location to store the result.
- ** cbuflen -- the size of cbuf.
- **
- ** Returns:
- ** true -- if the line matched the desired name.
- ** false -- otherwise.
- */
- static bool
- extract_canonname(name, dot, line, cbuf, cbuflen)
- char *name;
- char *dot;
- char *line;
- char cbuf[];
- int cbuflen;
- {
- int i;
- char *p;
- bool found = false;
- cbuf[0] = '\0';
- if (line[0] == '#')
- return false;
- for (i = 1; ; i++)
- {
- char nbuf[MAXNAME + 1];
- p = get_column(line, i, '\0', nbuf, sizeof(nbuf));
- if (p == NULL)
- break;
- if (*p == '\0')
- continue;
- if (cbuf[0] == '\0' ||
- (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
- {
- (void) sm_strlcpy(cbuf, p, cbuflen);
- }
- if (sm_strcasecmp(name, p) == 0)
- found = true;
- else if (dot != NULL)
- {
- /* try looking for the FQDN as well */
- *dot = '.';
- if (sm_strcasecmp(name, p) == 0)
- found = true;
- *dot = '\0';
- }
- }
- if (found && strchr(cbuf, '.') == NULL)
- {
- /* try to add a domain on the end of the name */
- char *domain = macvalue('m', CurEnv);
- if (domain != NULL &&
- strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
- {
- p = &cbuf[i];
- *p++ = '.';
- (void) sm_strlcpy(p, domain, cbuflen - i - 1);
- }
- }
- return found;
- }
- /*
- ** DNS modules
- */
- #if NAMED_BIND
- # if DNSMAP
- # include "sm_resolve.h"
- # if NETINET || NETINET6
- # include <arpa/inet.h>
- # endif /* NETINET || NETINET6 */
- /*
- ** DNS_MAP_OPEN -- stub to check proper value for dns map type
- */
- bool
- dns_map_open(map, mode)
- MAP *map;
- int mode;
- {
- if (tTd(38,2))
- sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
- mode &= O_ACCMODE;
- if (mode != O_RDONLY)
- {
- /* issue a pseudo-error message */
- errno = SM_EMAPCANTWRITE;
- return false;
- }
- return true;
- }
- /*
- ** DNS_MAP_PARSEARGS -- parse dns map definition args.
- **
- ** Parameters:
- ** map -- pointer to MAP
- ** args -- pointer to the args on the config line.
- **
- ** Returns:
- ** true -- if everything parsed OK.
- ** false -- otherwise.
- */
- #define map_sizelimit map_lockfd /* overload field */
- struct dns_map
- {
- int dns_m_type;
- };
- bool
- dns_map_parseargs(map,args)
- MAP *map;
- char *args;
- {
- register char *p = args;
- struct dns_map *map_p;
- map_p = (struct dns_map *) xalloc(sizeof(*map_p));
- map_p->dns_m_type = -1;
- map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
- for (;;)
- {
- while (isascii(*p) && isspace(*p))
- p++;
- if (*p != '-')
- break;
- switch (*++p)
- {
- case 'N':
- map->map_mflags |= MF_INCLNULL;
- map->map_mflags &= ~MF_TRY0NULL;
- break;
- case 'O':
- map->map_mflags &= ~MF_TRY1NULL;
- break;
- case 'o':
- map->map_mflags |= MF_OPTIONAL;
- break;
- case 'f':
- map->map_mflags |= MF_NOFOLDCASE;
- break;
- case 'm':
- map->map_mflags |= MF_MATCHONLY;
- break;
- case 'A':
- map->map_mflags |= MF_APPEND;
- break;
- case 'q':
- map->map_mflags |= MF_KEEPQUOTES;
- break;
- case 't':
- map->map_mflags |= MF_NODEFER;
- break;
- case 'a':
- map->map_app = ++p;
- break;
- case 'T':
- map->map_tapp = ++p;
- break;
- case 'd':
- {
- char *h;
- ++p;
- h = strchr(p, ' ');
- if (h != NULL)
- *h = '\0';
- map->map_timeout = convtime(p, 's');
- if (h != NULL)
- *h = ' ';
- }
- break;
- case 'r':
- while (isascii(*++p) && isspace(*p))
- continue;
- map->map_retry = atoi(p);
- break;
- case 'z':
- if (*++p != '\\')
- map->map_coldelim = *p;
- else
- {
- switch (*++p)
- {
- case 'n':
- map->map_coldelim = '\n';
- break;
- case 't':
- map->map_coldelim = '\t';
- break;
- default:
- map->map_coldelim = '\\';
- }
- }
- break;
- case 'Z':
- while (isascii(*++p) && isspace(*p))
- continue;
- map->map_sizelimit = atoi(p);
- break;
- /* Start of dns_map specific args */
- case 'R': /* search field */
- {
- char *h;
- while (isascii(*++p) && isspace(*p))
- continue;
- h = strchr(p, ' ');
- if (h != NULL)
- *h = '\0';
- map_p->dns_m_type = dns_string_to_type(p);
- if (h != NULL)
- *h = ' ';
- if (map_p->dns_m_type < 0)
- syserr("dns map %s: wrong type %s",
- map->map_mname, p);
- }
- break;
- case 'B': /* base domain */
- {
- char *h;
- while (isascii(*++p) && isspace(*p))
- continue;
- h = strchr(p, ' ');
- if (h != NULL)
- *h = '\0';
- /*
- ** slight abuse of map->map_file; it isn't
- ** used otherwise in this map type.
- */
- map->map_file = newstr(p);
- if (h != NULL)
- *h = ' ';
- }
- break;
- }
- while (*p != '\0' && !(isascii(*p) && isspace(*p)))
- p++;
- if (*p != '\0')
- *p++ = '\0';
- }
- if (map_p->dns_m_type < 0)
- syserr("dns map %s: missing -R type", map->map_mname);
- if (map->map_app != NULL)
- map->map_app = newstr(map->map_app);
- if (map->map_tapp != NULL)
- map->map_tapp = newstr(map->map_tapp);
- /*
- ** Assumption: assert(sizeof(int) <= sizeof(ARBPTR_T));
- ** Even if this assumption is wrong, we use only one byte,
- ** so it doesn't really matter.
- */
- map->map_db1 = (ARBPTR_T) map_p;
- return true;
- }
- /*
- ** DNS_MAP_LOOKUP -- perform dns map lookup.
- **
- ** Parameters:
- ** map -- pointer to MAP
- ** name -- name to lookup
- ** av -- arguments to interpolate into buf.
- ** statp -- pointer to status (EX_)
- **
- ** Returns:
- ** result of lookup if succeeded.
- ** NULL -- otherwise.
- */
- char *
- dns_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
- {
- int resnum = 0;
- char *vp = NULL, *result = NULL;
- size_t vsize;
- struct dns_map *map_p;
- RESOURCE_RECORD_T *rr = NULL;
- DNS_REPLY_T *r = NULL;
- # if NETINET6
- static char buf6[INET6_ADDRSTRLEN];
- # endif /* NETINET6 */
- if (tTd(38, 20))
- sm_dprintf("dns_map_lookup(%s, %s)\n",
- map->map_mname, name);
- map_p = (struct dns_map *)(map->map_db1);
- if (map->map_file != NULL && *map->map_file != '\0')
- {
- size_t len;
- char *appdomain;
- len = strlen(map->map_file) + strlen(name) + 2;
- appdomain = (char *) sm_malloc(len);
- if (appdomain == NULL)
- {
- *statp = EX_UNAVAILABLE;
- return NULL;
- }
- (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
- r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
- map->map_timeout, map->map_retry);
- sm_free(appdomain);
- }
- else
- {
- r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
- map->map_timeout, map->map_retry);
- }
- if (r == NULL)
- {
- result = NULL;
- if (h_errno == TRY_AGAIN || transienterror(errno))
- *statp = EX_TEMPFAIL;
- else
- *statp = EX_NOTFOUND;
- goto cleanup;
- }
- *statp = EX_OK;
- for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
- {
- char *type = NULL;
- char *value = NULL;
- switch (rr->rr_type)
- {
- case T_NS:
- type = "T_NS";
- value = rr->rr_u.rr_txt;
- break;
- case T_CNAME:
- type = "T_CNAME";
- value = rr->rr_u.rr_txt;
- break;
- case T_AFSDB:
- type = "T_AFSDB";
- value = rr->rr_u.rr_mx->mx_r_domain;
- break;
- case T_SRV:
- type = "T_SRV";
- value = rr->rr_u.rr_srv->srv_r_target;
- break;
- case T_PTR:
- type = "T_PTR";
- value = rr->rr_u.rr_txt;
- break;
- case T_TXT:
- type = "T_TXT";
- value = rr->rr_u.rr_txt;
- break;
- case T_MX:
- type = "T_MX";
- value = rr->rr_u.rr_mx->mx_r_domain;
- break;
- # if NETINET
- case T_A:
- type = "T_A";
- value = inet_ntoa(*(rr->rr_u.rr_a));
- break;
- # endif /* NETINET */
- # if NETINET6
- case T_AAAA:
- type = "T_AAAA";
- value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
- sizeof(buf6));
- break;
- # endif /* NETINET6 */
- }
- (void) strreplnonprt(value, 'X');
- if (map_p->dns_m_type != rr->rr_type)
- {
- if (tTd(38, 40))
- sm_dprintf("\tskipping type %s (%d) value %s\n",
- type != NULL ? type : "<UNKNOWN>",
- rr->rr_type,
- value != NULL ? value : "<NO VALUE>");
- continue;
- }
- # if NETINET6
- if (rr->rr_type == T_AAAA && value == NULL)
- {
- result = NULL;
- *statp = EX_DATAERR;
- if (tTd(38, 40))
- sm_dprintf("\tbad T_AAAA conversion\n");
- goto cleanup;
- }
- # endif /* NETINET6 */
- if (tTd(38, 40))
- sm_dprintf("\tfound type %s (%d) value %s\n",
- type != NULL ? type : "<UNKNOWN>",
- rr->rr_type,
- value != NULL ? value : "<NO VALUE>");
- if (value != NULL &&
- (map->map_coldelim == '\0' ||
- map->map_sizelimit == 1 ||
- bitset(MF_MATCHONLY, map->map_mflags)))
- {
- /* Only care about the first match */
- vp = newstr(value);
- break;
- }
- else if (vp == NULL)
- {
- /* First result */
- vp = newstr(value);
- }
- else
- {
- /* concatenate the results */
- int sz;
- char *new;
- sz = strlen(vp) + strlen(value) + 2;
- new = xalloc(sz);
- (void) sm_snprintf(new, sz, "%s%c%s",
- vp, map->map_coldelim, value);
- sm_free(vp);
- vp = new;
- if (map->map_sizelimit > 0 &&
- ++resnum >= map->map_sizelimit)
- break;
- }
- }
- if (vp == NULL)
- {
- result = NULL;
- *statp = EX_NOTFOUND;
- if (tTd(38, 40))
- sm_dprintf("\tno match found\n");
- goto cleanup;
- }
- /* Cleanly truncate for rulesets */
- truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
- vsize = strlen(vp);
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
- name, vp);
- if (bitset(MF_MATCHONLY, map->map_mflags))
- result = map_rewrite(map, name, strlen(name), NULL);
- else
- result = map_rewrite(map, vp, vsize, av);
- cleanup:
- if (vp != NULL)
- sm_free(vp);
- if (r != NULL)
- dns_free_data(r);
- return result;
- }
- # endif /* DNSMAP */
- #endif /* NAMED_BIND */
- /*
- ** NDBM modules
- */
- #if NDBM
- /*
- ** NDBM_MAP_OPEN -- DBM-style map open
- */
- bool
- ndbm_map_open(map, mode)
- MAP *map;
- int mode;
- {
- register DBM *dbm;
- int save_errno;
- int dfd;
- int pfd;
- long sff;
- int ret;
- int smode = S_IREAD;
- char dirfile[MAXPATHLEN];
- char pagfile[MAXPATHLEN];
- struct stat st;
- struct stat std, stp;
- if (tTd(38, 2))
- sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
- map->map_lockfd = -1;
- mode &= O_ACCMODE;
- /* do initial file and directory checks */
- if (sm_strlcpyn(dirfile, sizeof(dirfile), 2,
- map->map_file, ".dir") >= sizeof(dirfile) ||
- sm_strlcpyn(pagfile, sizeof(pagfile), 2,
- map->map_file, ".pag") >= sizeof(pagfile))
- {
- errno = 0;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("dbm map \"%s\": map file %s name too long",
- map->map_mname, map->map_file);
- return false;
- }
- sff = SFF_ROOTOK|SFF_REGONLY;
- if (mode == O_RDWR)
- {
- sff |= SFF_CREAT;
- if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
- sff |= SFF_NOSLINK;
- if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
- sff |= SFF_NOHLINK;
- smode = S_IWRITE;
- }
- else
- {
- if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
- sff |= SFF_NOWLINK;
- }
- if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
- sff |= SFF_SAFEDIRPATH;
- ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
- sff, smode, &std);
- if (ret == 0)
- ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
- sff, smode, &stp);
- if (ret != 0)
- {
- char *prob = "unsafe";
- /* cannot open this map */
- if (ret == ENOENT)
- prob = "missing";
- if (tTd(38, 2))
- sm_dprintf("\t%s map file: %d\n", prob, ret);
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("dbm map \"%s\": %s map file %s",
- map->map_mname, prob, map->map_file);
- return false;
- }
- if (std.st_mode == ST_MODE_NOFILE)
- mode |= O_CREAT|O_EXCL;
- # if LOCK_ON_OPEN
- if (mode == O_RDONLY)
- mode |= O_SHLOCK;
- else
- mode |= O_TRUNC|O_EXLOCK;
- # else /* LOCK_ON_OPEN */
- if ((mode & O_ACCMODE) == O_RDWR)
- {
- # if NOFTRUNCATE
- /*
- ** Warning: race condition. Try to lock the file as
- ** quickly as possible after opening it.
- ** This may also have security problems on some systems,
- ** but there isn't anything we can do about it.
- */
- mode |= O_TRUNC;
- # else /* NOFTRUNCATE */
- /*
- ** This ugly code opens the map without truncating it,
- ** locks the file, then truncates it. Necessary to
- ** avoid race conditions.
- */
- int dirfd;
- int pagfd;
- long sff = SFF_CREAT|SFF_OPENASROOT;
- if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
- sff |= SFF_NOSLINK;
- if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
- sff |= SFF_NOHLINK;
- dirfd = safeopen(dirfile, mode, DBMMODE, sff);
- pagfd = safeopen(pagfile, mode, DBMMODE, sff);
- if (dirfd < 0 || pagfd < 0)
- {
- save_errno = errno;
- if (dirfd >= 0)
- (void) close(dirfd);
- if (pagfd >= 0)
- (void) close(pagfd);
- errno = save_errno;
- syserr("ndbm_map_open: cannot create database %s",
- map->map_file);
- return false;
- }
- if (ftruncate(dirfd, (off_t) 0) < 0 ||
- ftruncate(pagfd, (off_t) 0) < 0)
- {
- save_errno = errno;
- (void) close(dirfd);
- (void) close(pagfd);
- errno = save_errno;
- syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
- map->map_file);
- return false;
- }
- /* if new file, get "before" bits for later filechanged check */
- if (std.st_mode == ST_MODE_NOFILE &&
- (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
- {
- save_errno = errno;
- (void) close(dirfd);
- (void) close(pagfd);
- errno = save_errno;
- syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
- map->map_file);
- return false;
- }
- /* have to save the lock for the duration (bletch) */
- map->map_lockfd = dirfd;
- (void) close(pagfd);
- /* twiddle bits for dbm_open */
- mode &= ~(O_CREAT|O_EXCL);
- # endif /* NOFTRUNCATE */
- }
- # endif /* LOCK_ON_OPEN */
- /* open the database */
- dbm = dbm_open(map->map_file, mode, DBMMODE);
- if (dbm == NULL)
- {
- save_errno = errno;
- if (bitset(MF_ALIAS, map->map_mflags) &&
- aliaswait(map, ".pag", false))
- return true;
- # if !LOCK_ON_OPEN && !NOFTRUNCATE
- if (map->map_lockfd >= 0)
- (void) close(map->map_lockfd);
- # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
- errno = save_errno;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("Cannot open DBM database %s", map->map_file);
- return false;
- }
- dfd = dbm_dirfno(dbm);
- pfd = dbm_pagfno(dbm);
- if (dfd == pfd)
- {
- /* heuristic: if files are linked, this is actually gdbm */
- dbm_close(dbm);
- # if !LOCK_ON_OPEN && !NOFTRUNCATE
- if (map->map_lockfd >= 0)
- (void) close(map->map_lockfd);
- # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
- errno = 0;
- syserr("dbm map \"%s\": cannot support GDBM",
- map->map_mname);
- return false;
- }
- if (filechanged(dirfile, dfd, &std) ||
- filechanged(pagfile, pfd, &stp))
- {
- save_errno = errno;
- dbm_close(dbm);
- # if !LOCK_ON_OPEN && !NOFTRUNCATE
- if (map->map_lockfd >= 0)
- (void) close(map->map_lockfd);
- # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
- errno = save_errno;
- syserr("ndbm_map_open(%s): file changed after open",
- map->map_file);
- return false;
- }
- map->map_db1 = (ARBPTR_T) dbm;
- /*
- ** Need to set map_mtime before the call to aliaswait()
- ** as aliaswait() will call map_lookup() which requires
- ** map_mtime to be set
- */
- if (fstat(pfd, &st) >= 0)
- map->map_mtime = st.st_mtime;
- if (mode == O_RDONLY)
- {
- # if LOCK_ON_OPEN
- if (dfd >= 0)
- (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
- if (pfd >= 0)
- (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
- # endif /* LOCK_ON_OPEN */
- if (bitset(MF_ALIAS, map->map_mflags) &&
- !aliaswait(map, ".pag", true))
- return false;
- }
- else
- {
- map->map_mflags |= MF_LOCKED;
- if (geteuid() == 0 && TrustedUid != 0)
- {
- # if HASFCHOWN
- if (fchown(dfd, TrustedUid, -1) < 0 ||
- fchown(pfd, TrustedUid, -1) < 0)
- {
- int err = errno;
- sm_syslog(LOG_ALERT, NOQID,
- "ownership change on %s failed: %s",
- map->map_file, sm_errstring(err));
- message("050 ownership change on %s failed: %s",
- map->map_file, sm_errstring(err));
- }
- # else /* HASFCHOWN */
- sm_syslog(LOG_ALERT, NOQID,
- "no fchown(): cannot change ownership on %s",
- map->map_file);
- message("050 no fchown(): cannot change ownership on %s",
- map->map_file);
- # endif /* HASFCHOWN */
- }
- }
- return true;
- }
- /*
- ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
- */
- char *
- ndbm_map_lookup(map, name, av, statp)
- MAP *map;
- char *name;
- char **av;
- int *statp;
- {
- datum key, val;
- int dfd, pfd;
- char keybuf[MAXNAME + 1];
- struct stat stbuf;
- if (tTd(38, 20))
- sm_dprintf("ndbm_map_lookup(%s, %s)\n",
- map->map_mname, name);
- key.dptr = name;
- key.dsize = strlen(name);
- if (!bitset(MF_NOFOLDCASE, map->map_mflags))
- {
- if (key.dsize > sizeof(keybuf) - 1)
- key.dsize = sizeof(keybuf) - 1;
- memmove(keybuf, key.dptr, key.dsize);
- keybuf[key.dsize] = '\0';
- makelower(keybuf);
- key.dptr = keybuf;
- }
- lockdbm:
- dfd = dbm_dirfno((DBM *) map->map_db1);
- if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
- (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
- pfd = dbm_pagfno((DBM *) map->map_db1);
- if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
- stbuf.st_mtime > map->map_mtime)
- {
- /* Reopen the database to sync the cache */
- int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
- : O_RDONLY;
- if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
- (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
- map->map_mflags |= MF_CLOSING;
- map->map_class->map_close(map);
- map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
- if (map->map_class->map_open(map, omode))
- {
- map->map_mflags |= MF_OPEN;
- map->map_pid = CurrentPid;
- if ((omode & O_ACCMODE) == O_RDWR)
- map->map_mflags |= MF_WRITABLE;
- goto lockdbm;
- }
- else
- {
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- {
- extern MAPCLASS BogusMapClass;
- *statp = EX_TEMPFAIL;
- map->map_orgclass = map->map_class;
- map->map_class = &BogusMapClass;
- map->map_mflags |= MF_OPEN;
- map->map_pid = CurrentPid;
- syserr("Cannot reopen NDBM database %s",
- map->map_file);
- }
- return NULL;
- }
- }
- val.dptr = NULL;
- if (bitset(MF_TRY0NULL, map->map_mflags))
- {
- val = dbm_fetch((DBM *) map->map_db1, key);
- if (val.dptr != NULL)
- map->map_mflags &= ~MF_TRY1NULL;
- }
- if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
- {
- key.dsize++;
- val = dbm_fetch((DBM *) map->map_db1, key);
- if (val.dptr != NULL)
- map->map_mflags &= ~MF_TRY0NULL;
- }
- if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
- (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
- if (val.dptr == NULL)
- return NULL;
- if (bitset(MF_MATCHONLY, map->map_mflags))
- return map_rewrite(map, name, strlen(name), NULL);
- else
- return map_rewrite(map, val.dptr, val.dsize, av);
- }
- /*
- ** NDBM_MAP_STORE -- store a datum in the database
- */
- void
- ndbm_map_store(map, lhs, rhs)
- register MAP *map;
- char *lhs;
- char *rhs;
- {
- datum key;
- datum data;
- int status;
- char keybuf[MAXNAME + 1];
- if (tTd(38, 12))
- sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
- map->map_mname, lhs, rhs);
- key.dsize = strlen(lhs);
- key.dptr = lhs;
- if (!bitset(MF_NOFOLDCASE, map->map_mflags))
- {
- if (key.dsize > sizeof(keybuf) - 1)
- key.dsize = sizeof(keybuf) - 1;
- memmove(keybuf, key.dptr, key.dsize);
- keybuf[key.dsize] = '\0';
- makelower(keybuf);
- key.dptr = keybuf;
- }
- data.dsize = strlen(rhs);
- data.dptr = rhs;
- if (bitset(MF_INCLNULL, map->map_mflags))
- {
- key.dsize++;
- data.dsize++;
- }
- status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
- if (status > 0)
- {
- if (!bitset(MF_APPEND, map->map_mflags))
- message("050 Warning: duplicate alias name %s", lhs);
- else
- {
- static char *buf = NULL;
- static int bufsiz = 0;
- auto int xstat;
- datum old;
- old.dptr = ndbm_map_lookup(map, key.dptr,
- (char **) NULL, &xstat);
- if (old.dptr != NULL && *(char *) old.dptr != '\0')
- {
- old.dsize = strlen(old.dptr);
- if (data.dsize + old.dsize + 2 > bufsiz)
- {
- if (buf != NULL)
- (void) sm_free(buf);
- bufsiz = data.dsize + old.dsize + 2;
- buf = sm_pmalloc_x(bufsiz);
- }
- (void) sm_strlcpyn(buf, bufsiz, 3,
- data.dptr, ",", old.dptr);
- data.dsize = data.dsize + old.dsize + 1;
- data.dptr = buf;
- if (tTd(38, 9))
- sm_dprintf("ndbm_map_store append=%s\n",
- data.dptr);
- }
- }
- status = dbm_store((DBM *) map->map_db1,
- key, data, DBM_REPLACE);
- }
- if (status != 0)
- syserr("readaliases: dbm put (%s): %d", lhs, status);
- }
- /*
- ** NDBM_MAP_CLOSE -- close the database
- */
- void
- ndbm_map_close(map)
- register MAP *map;
- {
- if (tTd(38, 9))
- sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
- map->map_mname, map->map_file, map->map_mflags);
- if (bitset(MF_WRITABLE, map->map_mflags))
- {
- # ifdef NDBM_YP_COMPAT
- bool inclnull;
- char buf[MAXHOSTNAMELEN];
- inclnull = bitset(MF_INCLNULL, map->map_mflags);
- map->map_mflags &= ~MF_INCLNULL;
- if (strstr(map->map_file, "/yp/") != NULL)
- {
- long save_mflags = map->map_mflags;
- map->map_mflags |= MF_NOFOLDCASE;
- (void) sm_snprintf(buf, sizeof(buf), "%010ld", curtime());
- ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
- (void) gethostname(buf, sizeof(buf));
- ndbm_map_store(map, "YP_MASTER_NAME", buf);
- map->map_mflags = save_mflags;
- }
- if (inclnull)
- map->map_mflags |= MF_INCLNULL;
- # endif /* NDBM_YP_COMPAT */
- /* write out the distinguished alias */
- ndbm_map_store(map, "@", "@");
- }
- dbm_close((DBM *) map->map_db1);
- /* release lock (if needed) */
- # if !LOCK_ON_OPEN
- if (map->map_lockfd >= 0)
- (void) close(map->map_lockfd);
- # endif /* !LOCK_ON_OPEN */
- }
- #endif /* NDBM */
- /*
- ** NEWDB (Hash and BTree) Modules
- */
- #if NEWDB
- /*
- ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
- **
- ** These do rather bizarre locking. If you can lock on open,
- ** do that to avoid the condition of opening a database that
- ** is being rebuilt. If you don't, we'll try to fake it, but
- ** there will be a race condition. If opening for read-only,
- ** we immediately release the lock to avoid freezing things up.
- ** We really ought to hold the lock, but guarantee that we won't
- ** be pokey about it. That's hard to do.
- */
- /* these should be K line arguments */
- # if DB_VERSION_MAJOR < 2
- # define db_cachesize cachesize
- # define h_nelem nelem
- # ifndef DB_CACHE_SIZE
- # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
- # endif /* ! DB_CACHE_SIZE */
- # ifndef DB_HASH_NELEM
- # define DB_HASH_NELEM 4096 /* (starting) size of hash table */
- # endif /* ! DB_HASH_NELEM */
- # endif /* DB_VERSION_MAJOR < 2 */
- bool
- bt_map_open(map, mode)
- MAP *map;
- int mode;
- {
- # if DB_VERSION_MAJOR < 2
- BTREEINFO btinfo;
- # endif /* DB_VERSION_MAJOR < 2 */
- # if DB_VERSION_MAJOR == 2
- DB_INFO btinfo;
- # endif /* DB_VERSION_MAJOR == 2 */
- # if DB_VERSION_MAJOR > 2
- void *btinfo = NULL;
- # endif /* DB_VERSION_MAJOR > 2 */
- if (tTd(38, 2))
- sm_dprintf("bt_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
- # if DB_VERSION_MAJOR < 3
- memset(&btinfo, '\0', sizeof(btinfo));
- # ifdef DB_CACHE_SIZE
- btinfo.db_cachesize = DB_CACHE_SIZE;
- # endif /* DB_CACHE_SIZE */
- # endif /* DB_VERSION_MAJOR < 3 */
- return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
- }
- bool
- hash_map_open(map, mode)
- MAP *map;
- int mode;
- {
- # if DB_VERSION_MAJOR < 2
- HASHINFO hinfo;
- # endif /* DB_VERSION_MAJOR < 2 */
- # if DB_VERSION_MAJOR == 2
- DB_INFO hinfo;
- # endif /* DB_VERSION_MAJOR == 2 */
- # if DB_VERSION_MAJOR > 2
- void *hinfo = NULL;
- # endif /* DB_VERSION_MAJOR > 2 */
- if (tTd(38, 2))
- sm_dprintf("hash_map_open(%s, %s, %d)\n",
- map->map_mname, map->map_file, mode);
- # if DB_VERSION_MAJOR < 3
- memset(&hinfo, '\0', sizeof(hinfo));
- # ifdef DB_HASH_NELEM
- hinfo.h_nelem = DB_HASH_NELEM;
- # endif /* DB_HASH_NELEM */
- # ifdef DB_CACHE_SIZE
- hinfo.db_cachesize = DB_CACHE_SIZE;
- # endif /* DB_CACHE_SIZE */
- # endif /* DB_VERSION_MAJOR < 3 */
- return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
- }
- static bool
- db_map_open(map, mode, mapclassname, dbtype, openinfo)
- MAP *map;
- int mode;
- char *mapclassname;
- DBTYPE dbtype;
- # if DB_VERSION_MAJOR < 2
- const void *openinfo;
- # endif /* DB_VERSION_MAJOR < 2 */
- # if DB_VERSION_MAJOR == 2
- DB_INFO *openinfo;
- # endif /* DB_VERSION_MAJOR == 2 */
- # if DB_VERSION_MAJOR > 2
- void **openinfo;
- # endif /* DB_VERSION_MAJOR > 2 */
- {
- DB *db = NULL;
- int i;
- int omode;
- int smode = S_IREAD;
- int fd;
- long sff;
- int save_errno;
- struct stat st;
- char buf[MAXPATHLEN];
- /* do initial file and directory checks */
- if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
- {
- errno = 0;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("map \"%s\": map file %s name too long",
- map->map_mname, map->map_file);
- return false;
- }
- i = strlen(buf);
- if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
- {
- if (sm_strlcat(buf, ".db", sizeof(buf)) >= sizeof(buf))
- {
- errno = 0;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("map \"%s\": map file %s name too long",
- map->map_mname, map->map_file);
- return false;
- }
- }
- mode &= O_ACCMODE;
- omode = mode;
- sff = SFF_ROOTOK|SFF_REGONLY;
- if (mode == O_RDWR)
- {
- sff |= SFF_CREAT;
- if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
- sff |= SFF_NOSLINK;
- if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
- sff |= SFF_NOHLINK;
- smode = S_IWRITE;
- }
- else
- {
- if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
- sff |= SFF_NOWLINK;
- }
- if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
- sff |= SFF_SAFEDIRPATH;
- i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
- if (i != 0)
- {
- char *prob = "unsafe";
- /* cannot open this map */
- if (i == ENOENT)
- prob = "missing";
- if (tTd(38, 2))
- sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
- errno = i;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("%s map \"%s\": %s map file %s",
- mapclassname, map->map_mname, prob, buf);
- return false;
- }
- if (st.st_mode == ST_MODE_NOFILE)
- omode |= O_CREAT|O_EXCL;
- map->map_lockfd = -1;
- # if LOCK_ON_OPEN
- if (mode == O_RDWR)
- omode |= O_TRUNC|O_EXLOCK;
- else
- omode |= O_SHLOCK;
- # else /* LOCK_ON_OPEN */
- /*
- ** Pre-lock the file to avoid race conditions. In particular,
- ** since dbopen returns NULL if the file is zero length, we
- ** must have a locked instance around the dbopen.
- */
- fd = open(buf, omode, DBMMODE);
- if (fd < 0)
- {
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("db_map_open: cannot pre-open database %s", buf);
- return false;
- }
- /* make sure no baddies slipped in just before the open... */
- if (filechanged(buf, fd, &st))
- {
- save_errno = errno;
- (void) close(fd);
- errno = save_errno;
- syserr("db_map_open(%s): file changed after pre-open", buf);
- return false;
- }
- /* if new file, get the "before" bits for later filechanged check */
- if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
- {
- save_errno = errno;
- (void) close(fd);
- errno = save_errno;
- syserr("db_map_open(%s): cannot fstat pre-opened file",
- buf);
- return false;
- }
- /* actually lock the pre-opened file */
- if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
- syserr("db_map_open: cannot lock %s", buf);
- /* set up mode bits for dbopen */
- if (mode == O_RDWR)
- omode |= O_TRUNC;
- omode &= ~(O_EXCL|O_CREAT);
- # endif /* LOCK_ON_OPEN */
- # if DB_VERSION_MAJOR < 2
- db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
- # else /* DB_VERSION_MAJOR < 2 */
- {
- int flags = 0;
- # if DB_VERSION_MAJOR > 2
- int ret;
- # endif /* DB_VERSION_MAJOR > 2 */
- if (mode == O_RDONLY)
- flags |= DB_RDONLY;
- if (bitset(O_CREAT, omode))
- flags |= DB_CREATE;
- if (bitset(O_TRUNC, omode))
- flags |= DB_TRUNCATE;
- SM_DB_FLAG_ADD(flags);
- # if DB_VERSION_MAJOR > 2
- ret = db_create(&db, NULL, 0);
- # ifdef DB_CACHE_SIZE
- if (ret == 0 && db != NULL)
- {
- ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
- if (ret != 0)
- {
- (void) db->close(db, 0);
- db = NULL;
- }
- }
- # endif /* DB_CACHE_SIZE */
- # ifdef DB_HASH_NELEM
- if (dbtype == DB_HASH && ret == 0 && db != NULL)
- {
- ret = db->set_h_nelem(db, DB_HASH_NELEM);
- if (ret != 0)
- {
- (void) db->close(db, 0);
- db = NULL;
- }
- }
- # endif /* DB_HASH_NELEM */
- if (ret == 0 && db != NULL)
- {
- ret = db->open(db,
- DBTXN /* transaction for DB 4.1 */
- buf, NULL, dbtype, flags, DBMMODE);
- if (ret != 0)
- {
- #ifdef DB_OLD_VERSION
- if (ret == DB_OLD_VERSION)
- ret = EINVAL;
- #endif /* DB_OLD_VERSION */
- (void) db->close(db, 0);
- db = NULL;
- }
- }
- errno = ret;
- # else /* DB_VERSION_MAJOR > 2 */
- errno = db_open(buf, dbtype, flags, DBMMODE,
- NULL, openinfo, &db);
- # endif /* DB_VERSION_MAJOR > 2 */
- }
- # endif /* DB_VERSION_MAJOR < 2 */
- save_errno = errno;
- # if !LOCK_ON_OPEN
- if (mode == O_RDWR)
- map->map_lockfd = fd;
- else
- (void) close(fd);
- # endif /* !LOCK_ON_OPEN */
- if (db == NULL)
- {
- if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
- aliaswait(map, ".db", false))
- return true;
- # if !LOCK_ON_OPEN
- if (map->map_lockfd >= 0)
- (void) close(map->map_lockfd);
- # endif /* !LOCK_ON_OPEN */
- errno = save_errno;
- if (!bitset(MF_OPTIONAL, map->map_mflags))
- syserr("Cannot open %s database %s",
- mapclassname, buf);
- return false;
- }
- # if DB_VERSION_MAJOR < 2
- fd = db->fd(db);
- # else /* DB_VERSION_MAJOR < 2 */
- fd = -1;
- errno = db->fd(db, &fd);
- # endif /* DB_VERSION_MAJOR < 2 */
- if (filechanged(buf, fd, &st))
- {
- save_errno = errno;
- # if DB_VERSION_MAJOR < 2
- (void) db->close(db);
- # else /* DB_VERSION_MAJOR < 2 */
- errno = db->close(db, 0);
- # endif /* DB_VERSION_MAJOR < 2 */
- # if !LOCK_ON_OPEN
- if (map->map_lockfd >= 0)
- (void) close(map->map_lockfd);
- # endif /* !LOCK_ON_OPEN */
- errno = save_errno;
- syserr("db