1N/A/*
1N/A * Copyright (c) 1998-2008 Sendmail, Inc. and its suppliers.
1N/A * All rights reserved.
1N/A * Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved.
1N/A * Copyright (c) 1992, 1993
1N/A * The Regents of the University of California. All rights reserved.
1N/A *
1N/A * By using this file, you agree to the terms and conditions set
1N/A * forth in the LICENSE file which can be found at the top level of
1N/A * the sendmail distribution.
1N/A *
1N/A */
1N/A
1N/A#include <sendmail.h>
1N/A
1N/ASM_RCSID("@(#)$Id: map.c,v 8.706 2010/07/27 03:35:42 ca Exp $")
1N/A
1N/A#if LDAPMAP
1N/A# include <sm/ldap.h>
1N/A#endif /* LDAPMAP */
1N/A
1N/A#if NDBM
1N/A# include <ndbm.h>
1N/A# ifdef R_FIRST
1N/A ERROR README: You are running the Berkeley DB version of ndbm.h. See
1N/A ERROR README: the README file about tweaking Berkeley DB so it can
1N/A ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile
1N/A ERROR README: and use -DNEWDB instead.
1N/A# endif /* R_FIRST */
1N/A#endif /* NDBM */
1N/A#if NEWDB
1N/A# include "sm/bdb.h"
1N/A#endif /* NEWDB */
1N/A#if NIS
1N/A struct dom_binding; /* forward reference needed on IRIX */
1N/A# include <rpcsvc/ypclnt.h>
1N/A# if NDBM
1N/A# define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
1N/A# endif /* NDBM */
1N/A#endif /* NIS */
1N/A
1N/A#include "map.h"
1N/A
1N/A#if NEWDB
1N/A# if DB_VERSION_MAJOR < 2
1N/Astatic bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A# if DB_VERSION_MAJOR == 2
1N/Astatic bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
1N/A# endif /* DB_VERSION_MAJOR == 2 */
1N/A# if DB_VERSION_MAJOR > 2
1N/Astatic bool db_map_open __P((MAP *, int, char *, DBTYPE, void **));
1N/A# endif /* DB_VERSION_MAJOR > 2 */
1N/A#endif /* NEWDB */
1N/Astatic bool extract_canonname __P((char *, char *, char *, char[], int));
1N/Astatic void map_close __P((STAB *, int));
1N/Astatic void map_init __P((STAB *, int));
1N/A#ifdef LDAPMAP
1N/Astatic STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *));
1N/A#endif /* LDAPMAP */
1N/A#if NISPLUS
1N/Astatic bool nisplus_getcanonname __P((char *, int, int *));
1N/A#endif /* NISPLUS */
1N/A#if NIS
1N/Astatic bool nis_getcanonname __P((char *, int, int *));
1N/A#endif /* NIS */
1N/A#if NETINFO
1N/Astatic bool ni_getcanonname __P((char *, int, int *));
1N/A#endif /* NETINFO */
1N/Astatic bool text_getcanonname __P((char *, int, int *));
1N/A#if SOCKETMAP
1N/Astatic STAB *socket_map_findconn __P((const char*));
1N/A
1N/A/* XXX arbitrary limit for sanity */
1N/A# define SOCKETMAP_MAXL 1000000
1N/A#endif /* SOCKETMAP */
1N/A
1N/A/* default error message for trying to open a map in write mode */
1N/A#ifdef ENOSYS
1N/A# define SM_EMAPCANTWRITE ENOSYS
1N/A#else /* ENOSYS */
1N/A# ifdef EFTYPE
1N/A# define SM_EMAPCANTWRITE EFTYPE
1N/A# else /* EFTYPE */
1N/A# define SM_EMAPCANTWRITE ENXIO
1N/A# endif /* EFTYPE */
1N/A#endif /* ENOSYS */
1N/A
1N/A/*
1N/A** MAP.C -- implementations for various map classes.
1N/A**
1N/A** Each map class implements a series of functions:
1N/A**
1N/A** bool map_parse(MAP *map, char *args)
1N/A** Parse the arguments from the config file. Return true
1N/A** if they were ok, false otherwise. Fill in map with the
1N/A** values.
1N/A**
1N/A** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
1N/A** Look up the key in the given map. If found, do any
1N/A** rewriting the map wants (including "args" if desired)
1N/A** and return the value. Set *pstat to the appropriate status
1N/A** on error and return NULL. Args will be NULL if called
1N/A** from the alias routines, although this should probably
1N/A** not be relied upon. It is suggested you call map_rewrite
1N/A** to return the results -- it takes care of null termination
1N/A** and uses a dynamically expanded buffer as needed.
1N/A**
1N/A** void map_store(MAP *map, char *key, char *value)
1N/A** Store the key:value pair in the map.
1N/A**
1N/A** bool map_open(MAP *map, int mode)
1N/A** Open the map for the indicated mode. Mode should
1N/A** be either O_RDONLY or O_RDWR. Return true if it
1N/A** was opened successfully, false otherwise. If the open
1N/A** failed and the MF_OPTIONAL flag is not set, it should
1N/A** also print an error. If the MF_ALIAS bit is set
1N/A** and this map class understands the @:@ convention, it
1N/A** should call aliaswait() before returning.
1N/A**
1N/A** void map_close(MAP *map)
1N/A** Close the map.
1N/A**
1N/A** This file also includes the implementation for getcanonname.
1N/A** It is currently implemented in a pretty ad-hoc manner; it ought
1N/A** to be more properly integrated into the map structure.
1N/A*/
1N/A
1N/A#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
1N/A# define LOCK_ON_OPEN 1 /* we can open/create a locked file */
1N/A#else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
1N/A# define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
1N/A#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
1N/A
1N/A/*
1N/A** MAP_PARSEARGS -- parse config line arguments for database lookup
1N/A**
1N/A** This is a generic version of the map_parse method.
1N/A**
1N/A** Parameters:
1N/A** map -- the map being initialized.
1N/A** ap -- a pointer to the args on the config line.
1N/A**
1N/A** Returns:
1N/A** true -- if everything parsed OK.
1N/A** false -- otherwise.
1N/A**
1N/A** Side Effects:
1N/A** null terminates the filename; stores it in map
1N/A*/
1N/A
1N/Abool
1N/Amap_parseargs(map, ap)
1N/A MAP *map;
1N/A char *ap;
1N/A{
1N/A register char *p = ap;
1N/A
1N/A /*
1N/A ** There is no check whether there is really an argument,
1N/A ** but that's not important enough to warrant extra code.
1N/A */
1N/A
1N/A map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
1N/A map->map_spacesub = SpaceSub; /* default value */
1N/A for (;;)
1N/A {
1N/A while (isascii(*p) && isspace(*p))
1N/A p++;
1N/A if (*p != '-')
1N/A break;
1N/A switch (*++p)
1N/A {
1N/A case 'N':
1N/A map->map_mflags |= MF_INCLNULL;
1N/A map->map_mflags &= ~MF_TRY0NULL;
1N/A break;
1N/A
1N/A case 'O':
1N/A map->map_mflags &= ~MF_TRY1NULL;
1N/A break;
1N/A
1N/A case 'o':
1N/A map->map_mflags |= MF_OPTIONAL;
1N/A break;
1N/A
1N/A case 'f':
1N/A map->map_mflags |= MF_NOFOLDCASE;
1N/A break;
1N/A
1N/A case 'm':
1N/A map->map_mflags |= MF_MATCHONLY;
1N/A break;
1N/A
1N/A case 'A':
1N/A map->map_mflags |= MF_APPEND;
1N/A break;
1N/A
1N/A case 'q':
1N/A map->map_mflags |= MF_KEEPQUOTES;
1N/A break;
1N/A
1N/A case 'a':
1N/A map->map_app = ++p;
1N/A break;
1N/A
1N/A case 'T':
1N/A map->map_tapp = ++p;
1N/A break;
1N/A
1N/A case 'k':
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A map->map_keycolnm = p;
1N/A break;
1N/A
1N/A case 'v':
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A map->map_valcolnm = p;
1N/A break;
1N/A
1N/A case 'z':
1N/A if (*++p != '\\')
1N/A map->map_coldelim = *p;
1N/A else
1N/A {
1N/A switch (*++p)
1N/A {
1N/A case 'n':
1N/A map->map_coldelim = '\n';
1N/A break;
1N/A
1N/A case 't':
1N/A map->map_coldelim = '\t';
1N/A break;
1N/A
1N/A default:
1N/A map->map_coldelim = '\\';
1N/A }
1N/A }
1N/A break;
1N/A
1N/A case 't':
1N/A map->map_mflags |= MF_NODEFER;
1N/A break;
1N/A
1N/A
1N/A case 'S':
1N/A map->map_spacesub = *++p;
1N/A break;
1N/A
1N/A case 'D':
1N/A map->map_mflags |= MF_DEFER;
1N/A break;
1N/A
1N/A default:
1N/A syserr("Illegal option %c map %s", *p, map->map_mname);
1N/A break;
1N/A }
1N/A while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1N/A p++;
1N/A if (*p != '\0')
1N/A *p++ = '\0';
1N/A }
1N/A if (map->map_app != NULL)
1N/A map->map_app = newstr(map->map_app);
1N/A if (map->map_tapp != NULL)
1N/A map->map_tapp = newstr(map->map_tapp);
1N/A if (map->map_keycolnm != NULL)
1N/A map->map_keycolnm = newstr(map->map_keycolnm);
1N/A if (map->map_valcolnm != NULL)
1N/A map->map_valcolnm = newstr(map->map_valcolnm);
1N/A
1N/A if (*p != '\0')
1N/A {
1N/A map->map_file = p;
1N/A while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1N/A p++;
1N/A if (*p != '\0')
1N/A *p++ = '\0';
1N/A map->map_file = newstr(map->map_file);
1N/A }
1N/A
1N/A while (*p != '\0' && isascii(*p) && isspace(*p))
1N/A p++;
1N/A if (*p != '\0')
1N/A map->map_rebuild = newstr(p);
1N/A
1N/A if (map->map_file == NULL &&
1N/A !bitset(MCF_OPTFILE, map->map_class->map_cflags))
1N/A {
1N/A syserr("No file name for %s map %s",
1N/A map->map_class->map_cname, map->map_mname);
1N/A return false;
1N/A }
1N/A return true;
1N/A}
1N/A/*
1N/A** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
1N/A**
1N/A** It also adds the map_app string. It can be used as a utility
1N/A** in the map_lookup method.
1N/A**
1N/A** Parameters:
1N/A** map -- the map that causes this.
1N/A** s -- the string to rewrite, NOT necessarily null terminated.
1N/A** slen -- the length of s.
1N/A** av -- arguments to interpolate into buf.
1N/A**
1N/A** Returns:
1N/A** Pointer to rewritten result. This is static data that
1N/A** should be copied if it is to be saved!
1N/A*/
1N/A
1N/Achar *
1N/Amap_rewrite(map, s, slen, av)
1N/A register MAP *map;
1N/A register const char *s;
1N/A size_t slen;
1N/A char **av;
1N/A{
1N/A register char *bp;
1N/A register char c;
1N/A char **avp;
1N/A register char *ap;
1N/A size_t l;
1N/A size_t len;
1N/A static size_t buflen = 0;
1N/A static char *buf = NULL;
1N/A
1N/A if (tTd(39, 1))
1N/A {
1N/A sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s);
1N/A if (av == NULL)
1N/A sm_dprintf(" (nullv)");
1N/A else
1N/A {
1N/A for (avp = av; *avp != NULL; avp++)
1N/A sm_dprintf("\n\t%s", *avp);
1N/A }
1N/A sm_dprintf("\n");
1N/A }
1N/A
1N/A /* count expected size of output (can safely overestimate) */
1N/A l = len = slen;
1N/A if (av != NULL)
1N/A {
1N/A const char *sp = s;
1N/A
1N/A while (l-- > 0 && (c = *sp++) != '\0')
1N/A {
1N/A if (c != '%')
1N/A continue;
1N/A if (l-- <= 0)
1N/A break;
1N/A c = *sp++;
1N/A if (!(isascii(c) && isdigit(c)))
1N/A continue;
1N/A for (avp = av; --c >= '0' && *avp != NULL; avp++)
1N/A continue;
1N/A if (*avp == NULL)
1N/A continue;
1N/A len += strlen(*avp);
1N/A }
1N/A }
1N/A if (map->map_app != NULL)
1N/A len += strlen(map->map_app);
1N/A if (buflen < ++len)
1N/A {
1N/A /* need to malloc additional space */
1N/A buflen = len;
1N/A if (buf != NULL)
1N/A sm_free(buf);
1N/A buf = sm_pmalloc_x(buflen);
1N/A }
1N/A
1N/A bp = buf;
1N/A if (av == NULL)
1N/A {
1N/A memmove(bp, s, slen);
1N/A bp += slen;
1N/A
1N/A /* assert(len > slen); */
1N/A len -= slen;
1N/A }
1N/A else
1N/A {
1N/A while (slen-- > 0 && (c = *s++) != '\0')
1N/A {
1N/A if (c != '%')
1N/A {
1N/A pushc:
1N/A if (len-- <= 1)
1N/A break;
1N/A *bp++ = c;
1N/A continue;
1N/A }
1N/A if (slen-- <= 0 || (c = *s++) == '\0')
1N/A c = '%';
1N/A if (c == '%')
1N/A goto pushc;
1N/A if (!(isascii(c) && isdigit(c)))
1N/A {
1N/A if (len-- <= 1)
1N/A break;
1N/A *bp++ = '%';
1N/A goto pushc;
1N/A }
1N/A for (avp = av; --c >= '0' && *avp != NULL; avp++)
1N/A continue;
1N/A if (*avp == NULL)
1N/A continue;
1N/A
1N/A /* transliterate argument into output string */
1N/A for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len)
1N/A *bp++ = c;
1N/A }
1N/A }
1N/A if (map->map_app != NULL && len > 0)
1N/A (void) sm_strlcpy(bp, map->map_app, len);
1N/A else
1N/A *bp = '\0';
1N/A if (tTd(39, 1))
1N/A sm_dprintf("map_rewrite => %s\n", buf);
1N/A return buf;
1N/A}
1N/A/*
1N/A** INITMAPS -- rebuild alias maps
1N/A**
1N/A** Parameters:
1N/A** none.
1N/A**
1N/A** Returns:
1N/A** none.
1N/A*/
1N/A
1N/Avoid
1N/Ainitmaps()
1N/A{
1N/A#if XDEBUG
1N/A checkfd012("entering initmaps");
1N/A#endif /* XDEBUG */
1N/A stabapply(map_init, 0);
1N/A#if XDEBUG
1N/A checkfd012("exiting initmaps");
1N/A#endif /* XDEBUG */
1N/A}
1N/A/*
1N/A** MAP_INIT -- rebuild a map
1N/A**
1N/A** Parameters:
1N/A** s -- STAB entry: if map: try to rebuild
1N/A** unused -- unused variable
1N/A**
1N/A** Returns:
1N/A** none.
1N/A**
1N/A** Side Effects:
1N/A** will close already open rebuildable map.
1N/A*/
1N/A
1N/A/* ARGSUSED1 */
1N/Astatic void
1N/Amap_init(s, unused)
1N/A register STAB *s;
1N/A int unused;
1N/A{
1N/A register MAP *map;
1N/A
1N/A /* has to be a map */
1N/A if (s->s_symtype != ST_MAP)
1N/A return;
1N/A
1N/A map = &s->s_map;
1N/A if (!bitset(MF_VALID, map->map_mflags))
1N/A return;
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("map_init(%s:%s, %s)\n",
1N/A map->map_class->map_cname == NULL ? "NULL" :
1N/A map->map_class->map_cname,
1N/A map->map_mname == NULL ? "NULL" : map->map_mname,
1N/A map->map_file == NULL ? "NULL" : map->map_file);
1N/A
1N/A if (!bitset(MF_ALIAS, map->map_mflags) ||
1N/A !bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
1N/A {
1N/A if (tTd(38, 3))
1N/A sm_dprintf("\tnot rebuildable\n");
1N/A return;
1N/A }
1N/A
1N/A /* if already open, close it (for nested open) */
1N/A if (bitset(MF_OPEN, map->map_mflags))
1N/A {
1N/A map->map_mflags |= MF_CLOSING;
1N/A map->map_class->map_close(map);
1N/A map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1N/A }
1N/A
1N/A (void) rebuildaliases(map, false);
1N/A return;
1N/A}
1N/A/*
1N/A** OPENMAP -- open a map
1N/A**
1N/A** Parameters:
1N/A** map -- map to open (it must not be open).
1N/A**
1N/A** Returns:
1N/A** whether open succeeded.
1N/A*/
1N/A
1N/Abool
1N/Aopenmap(map)
1N/A MAP *map;
1N/A{
1N/A bool restore = false;
1N/A bool savehold = HoldErrs;
1N/A bool savequick = QuickAbort;
1N/A int saveerrors = Errors;
1N/A
1N/A if (!bitset(MF_VALID, map->map_mflags))
1N/A return false;
1N/A
1N/A /* better safe than sorry... */
1N/A if (bitset(MF_OPEN, map->map_mflags))
1N/A return true;
1N/A
1N/A /* Don't send a map open error out via SMTP */
1N/A if ((OnlyOneError || QuickAbort) &&
1N/A (OpMode == MD_SMTP || OpMode == MD_DAEMON))
1N/A {
1N/A restore = true;
1N/A HoldErrs = true;
1N/A QuickAbort = false;
1N/A }
1N/A
1N/A errno = 0;
1N/A if (map->map_class->map_open(map, O_RDONLY))
1N/A {
1N/A if (tTd(38, 4))
1N/A sm_dprintf("openmap()\t%s:%s %s: valid\n",
1N/A map->map_class->map_cname == NULL ? "NULL" :
1N/A map->map_class->map_cname,
1N/A map->map_mname == NULL ? "NULL" :
1N/A map->map_mname,
1N/A map->map_file == NULL ? "NULL" :
1N/A map->map_file);
1N/A map->map_mflags |= MF_OPEN;
1N/A map->map_pid = CurrentPid;
1N/A }
1N/A else
1N/A {
1N/A if (tTd(38, 4))
1N/A sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
1N/A map->map_class->map_cname == NULL ? "NULL" :
1N/A map->map_class->map_cname,
1N/A map->map_mname == NULL ? "NULL" :
1N/A map->map_mname,
1N/A map->map_file == NULL ? "NULL" :
1N/A map->map_file,
1N/A errno == 0 ? "" : ": ",
1N/A errno == 0 ? "" : sm_errstring(errno));
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A {
1N/A extern MAPCLASS BogusMapClass;
1N/A
1N/A map->map_orgclass = map->map_class;
1N/A map->map_class = &BogusMapClass;
1N/A map->map_mflags |= MF_OPEN|MF_OPENBOGUS;
1N/A map->map_pid = CurrentPid;
1N/A }
1N/A else
1N/A {
1N/A /* don't try again */
1N/A map->map_mflags &= ~MF_VALID;
1N/A }
1N/A }
1N/A
1N/A if (restore)
1N/A {
1N/A Errors = saveerrors;
1N/A HoldErrs = savehold;
1N/A QuickAbort = savequick;
1N/A }
1N/A
1N/A return bitset(MF_OPEN, map->map_mflags);
1N/A}
1N/A/*
1N/A** CLOSEMAPS -- close all open maps opened by the current pid.
1N/A**
1N/A** Parameters:
1N/A** bogus -- only close bogus maps.
1N/A**
1N/A** Returns:
1N/A** none.
1N/A*/
1N/A
1N/Avoid
1N/Aclosemaps(bogus)
1N/A bool bogus;
1N/A{
1N/A stabapply(map_close, bogus);
1N/A}
1N/A/*
1N/A** MAP_CLOSE -- close a map opened by the current pid.
1N/A**
1N/A** Parameters:
1N/A** s -- STAB entry: if map: try to close
1N/A** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
1N/A**
1N/A** Returns:
1N/A** none.
1N/A*/
1N/A
1N/A/* ARGSUSED1 */
1N/Astatic void
1N/Amap_close(s, bogus)
1N/A register STAB *s;
1N/A int bogus; /* int because of stabapply(), used as bool */
1N/A{
1N/A MAP *map;
1N/A extern MAPCLASS BogusMapClass;
1N/A
1N/A if (s->s_symtype != ST_MAP)
1N/A return;
1N/A
1N/A map = &s->s_map;
1N/A
1N/A /*
1N/A ** close the map iff:
1N/A ** it is valid and open and opened by this process
1N/A ** and (!bogus or it's a bogus map or it is not persistent)
1N/A ** negate this: return iff
1N/A ** it is not valid or it is not open or not opened by this process
1N/A ** or (bogus and it's not a bogus map and it's not not-persistent)
1N/A */
1N/A
1N/A if (!bitset(MF_VALID, map->map_mflags) ||
1N/A !bitset(MF_OPEN, map->map_mflags) ||
1N/A bitset(MF_CLOSING, map->map_mflags) ||
1N/A map->map_pid != CurrentPid ||
1N/A (bogus && map->map_class != &BogusMapClass &&
1N/A !bitset(MCF_NOTPERSIST, map->map_class->map_cflags)))
1N/A return;
1N/A
1N/A if (map->map_class == &BogusMapClass && map->map_orgclass != NULL &&
1N/A map->map_orgclass != &BogusMapClass)
1N/A map->map_class = map->map_orgclass;
1N/A if (tTd(38, 5))
1N/A sm_dprintf("closemaps: closing %s (%s)\n",
1N/A map->map_mname == NULL ? "NULL" : map->map_mname,
1N/A map->map_file == NULL ? "NULL" : map->map_file);
1N/A
1N/A if (!bitset(MF_OPENBOGUS, map->map_mflags))
1N/A {
1N/A map->map_mflags |= MF_CLOSING;
1N/A map->map_class->map_close(map);
1N/A }
1N/A map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING);
1N/A}
1N/A
1N/A#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
1N/Aextern int getdomainname();
1N/A
1N/A/* this is mainly for backward compatibility in Sun environment */
1N/Astatic char *
1N/Asun_init_domain()
1N/A{
1N/A /*
1N/A ** Get the domain name from the kernel.
1N/A ** If it does not start with a leading dot, then remove
1N/A ** the first component. Since leading dots are funny Unix
1N/A ** files, we treat a leading "+" the same as a leading dot.
1N/A ** Finally, force there to be at least one dot in the domain name
1N/A ** (i.e. top-level domains are not allowed, like "com", must be
1N/A ** something like "sun.com").
1N/A */
1N/A
1N/A char buf[MAXNAME];
1N/A char *period, *autodomain;
1N/A
1N/A if (getdomainname(buf, sizeof buf) < 0)
1N/A return NULL;
1N/A
1N/A if (buf[0] == '\0')
1N/A return NULL;
1N/A
1N/A if (tTd(0, 20))
1N/A printf("domainname = %s\n", buf);
1N/A
1N/A if (buf[0] == '+')
1N/A buf[0] = '.';
1N/A period = strchr(buf, '.');
1N/A if (period == NULL)
1N/A autodomain = buf;
1N/A else
1N/A autodomain = period + 1;
1N/A if (strchr(autodomain, '.') == NULL)
1N/A return newstr(buf);
1N/A else
1N/A return newstr(autodomain);
1N/A}
1N/A#endif /* SUN_EXTENSIONS && SUN_INIT_DOMAIN */
1N/A
1N/A/*
1N/A** GETCANONNAME -- look up name using service switch
1N/A**
1N/A** Parameters:
1N/A** host -- the host name to look up.
1N/A** hbsize -- the size of the host buffer.
1N/A** trymx -- if set, try MX records.
1N/A** pttl -- pointer to return TTL (can be NULL).
1N/A**
1N/A** Returns:
1N/A** true -- if the host was found.
1N/A** false -- otherwise.
1N/A*/
1N/A
1N/Abool
1N/Agetcanonname(host, hbsize, trymx, pttl)
1N/A char *host;
1N/A int hbsize;
1N/A bool trymx;
1N/A int *pttl;
1N/A{
1N/A int nmaps;
1N/A int mapno;
1N/A bool found = false;
1N/A bool got_tempfail = false;
1N/A auto int status = EX_UNAVAILABLE;
1N/A char *maptype[MAXMAPSTACK];
1N/A short mapreturn[MAXMAPACTIONS];
1N/A#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
1N/A bool should_try_nis_domain = false;
1N/A static char *nis_domain = NULL;
1N/A#endif
1N/A
1N/A nmaps = switch_map_find("hosts", maptype, mapreturn);
1N/A if (pttl != 0)
1N/A *pttl = SM_DEFAULT_TTL;
1N/A for (mapno = 0; mapno < nmaps; mapno++)
1N/A {
1N/A int i;
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("getcanonname(%s), trying %s\n",
1N/A host, maptype[mapno]);
1N/A if (strcmp("files", maptype[mapno]) == 0)
1N/A {
1N/A found = text_getcanonname(host, hbsize, &status);
1N/A }
1N/A#if NIS
1N/A else if (strcmp("nis", maptype[mapno]) == 0)
1N/A {
1N/A found = nis_getcanonname(host, hbsize, &status);
1N/A# if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
1N/A if (nis_domain == NULL)
1N/A nis_domain = sun_init_domain();
1N/A# endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
1N/A }
1N/A#endif /* NIS */
1N/A#if NISPLUS
1N/A else if (strcmp("nisplus", maptype[mapno]) == 0)
1N/A {
1N/A found = nisplus_getcanonname(host, hbsize, &status);
1N/A# if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
1N/A if (nis_domain == NULL)
1N/A nis_domain = sun_init_domain();
1N/A# endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
1N/A }
1N/A#endif /* NISPLUS */
1N/A#if NAMED_BIND
1N/A else if (strcmp("dns", maptype[mapno]) == 0)
1N/A {
1N/A found = dns_getcanonname(host, hbsize, trymx, &status, pttl);
1N/A }
1N/A#endif /* NAMED_BIND */
1N/A#if NETINFO
1N/A else if (strcmp("netinfo", maptype[mapno]) == 0)
1N/A {
1N/A found = ni_getcanonname(host, hbsize, &status);
1N/A }
1N/A#endif /* NETINFO */
1N/A else
1N/A {
1N/A found = false;
1N/A status = EX_UNAVAILABLE;
1N/A }
1N/A
1N/A /*
1N/A ** Heuristic: if $m is not set, we are running during system
1N/A ** startup. In this case, when a name is apparently found
1N/A ** but has no dot, treat is as not found. This avoids
1N/A ** problems if /etc/hosts has no FQDN but is listed first
1N/A ** in the service switch.
1N/A */
1N/A
1N/A if (found &&
1N/A (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
1N/A break;
1N/A
1N/A#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
1N/A if (found)
1N/A should_try_nis_domain = true;
1N/A /* but don't break, as we need to try all methods first */
1N/A#endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
1N/A
1N/A /* see if we should continue */
1N/A if (status == EX_TEMPFAIL)
1N/A {
1N/A i = MA_TRYAGAIN;
1N/A got_tempfail = true;
1N/A }
1N/A else if (status == EX_NOTFOUND)
1N/A i = MA_NOTFOUND;
1N/A else
1N/A i = MA_UNAVAIL;
1N/A if (bitset(1 << mapno, mapreturn[i]))
1N/A break;
1N/A }
1N/A
1N/A if (found)
1N/A {
1N/A char *d;
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("getcanonname(%s), found\n", host);
1N/A
1N/A /*
1N/A ** If returned name is still single token, compensate
1N/A ** by tagging on $m. This is because some sites set
1N/A ** up their DNS or NIS databases wrong.
1N/A */
1N/A
1N/A if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
1N/A {
1N/A d = macvalue('m', CurEnv);
1N/A if (d != NULL &&
1N/A hbsize > (int) (strlen(host) + strlen(d) + 1))
1N/A {
1N/A if (host[strlen(host) - 1] != '.')
1N/A (void) sm_strlcat2(host, ".", d,
1N/A hbsize);
1N/A else
1N/A (void) sm_strlcat(host, d, hbsize);
1N/A }
1N/A else
1N/A {
1N/A#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
1N/A if (VendorCode == VENDOR_SUN &&
1N/A should_try_nis_domain)
1N/A {
1N/A goto try_nis_domain;
1N/A }
1N/A#endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
1N/A return false;
1N/A }
1N/A }
1N/A return true;
1N/A }
1N/A
1N/A#if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
1N/A if (VendorCode == VENDOR_SUN && should_try_nis_domain)
1N/A {
1N/A try_nis_domain:
1N/A if (nis_domain != NULL &&
1N/A strlen(nis_domain) + strlen(host) + 1 < hbsize)
1N/A {
1N/A (void) sm_strlcat2(host, ".", nis_domain, hbsize);
1N/A return true;
1N/A }
1N/A }
1N/A#endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("getcanonname(%s), failed, status=%d\n", host,
1N/A status);
1N/A
1N/A if (got_tempfail)
1N/A SM_SET_H_ERRNO(TRY_AGAIN);
1N/A else
1N/A SM_SET_H_ERRNO(HOST_NOT_FOUND);
1N/A
1N/A return false;
1N/A}
1N/A/*
1N/A** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
1N/A**
1N/A** Parameters:
1N/A** name -- the name against which to match.
1N/A** dot -- where to reinsert '.' to get FQDN
1N/A** line -- the /etc/hosts line.
1N/A** cbuf -- the location to store the result.
1N/A** cbuflen -- the size of cbuf.
1N/A**
1N/A** Returns:
1N/A** true -- if the line matched the desired name.
1N/A** false -- otherwise.
1N/A*/
1N/A
1N/Astatic bool
1N/Aextract_canonname(name, dot, line, cbuf, cbuflen)
1N/A char *name;
1N/A char *dot;
1N/A char *line;
1N/A char cbuf[];
1N/A int cbuflen;
1N/A{
1N/A int i;
1N/A char *p;
1N/A bool found = false;
1N/A
1N/A cbuf[0] = '\0';
1N/A if (line[0] == '#')
1N/A return false;
1N/A
1N/A for (i = 1; ; i++)
1N/A {
1N/A char nbuf[MAXNAME + 1];
1N/A
1N/A p = get_column(line, i, '\0', nbuf, sizeof(nbuf));
1N/A if (p == NULL)
1N/A break;
1N/A if (*p == '\0')
1N/A continue;
1N/A if (cbuf[0] == '\0' ||
1N/A (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
1N/A {
1N/A (void) sm_strlcpy(cbuf, p, cbuflen);
1N/A }
1N/A if (sm_strcasecmp(name, p) == 0)
1N/A found = true;
1N/A else if (dot != NULL)
1N/A {
1N/A /* try looking for the FQDN as well */
1N/A *dot = '.';
1N/A if (sm_strcasecmp(name, p) == 0)
1N/A found = true;
1N/A *dot = '\0';
1N/A }
1N/A }
1N/A if (found && strchr(cbuf, '.') == NULL)
1N/A {
1N/A /* try to add a domain on the end of the name */
1N/A char *domain = macvalue('m', CurEnv);
1N/A
1N/A if (domain != NULL &&
1N/A strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen)
1N/A {
1N/A p = &cbuf[i];
1N/A *p++ = '.';
1N/A (void) sm_strlcpy(p, domain, cbuflen - i - 1);
1N/A }
1N/A }
1N/A return found;
1N/A}
1N/A
1N/A/*
1N/A** DNS modules
1N/A*/
1N/A
1N/A#if NAMED_BIND
1N/A# if DNSMAP
1N/A
1N/A# include "sm_resolve.h"
1N/A# if NETINET || NETINET6
1N/A# include <arpa/inet.h>
1N/A# endif /* NETINET || NETINET6 */
1N/A
1N/A/*
1N/A** DNS_MAP_OPEN -- stub to check proper value for dns map type
1N/A*/
1N/A
1N/Abool
1N/Adns_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A if (tTd(38,2))
1N/A sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode);
1N/A
1N/A mode &= O_ACCMODE;
1N/A if (mode != O_RDONLY)
1N/A {
1N/A /* issue a pseudo-error message */
1N/A errno = SM_EMAPCANTWRITE;
1N/A return false;
1N/A }
1N/A return true;
1N/A}
1N/A
1N/A/*
1N/A** DNS_MAP_PARSEARGS -- parse dns map definition args.
1N/A**
1N/A** Parameters:
1N/A** map -- pointer to MAP
1N/A** args -- pointer to the args on the config line.
1N/A**
1N/A** Returns:
1N/A** true -- if everything parsed OK.
1N/A** false -- otherwise.
1N/A*/
1N/A
1N/A#define map_sizelimit map_lockfd /* overload field */
1N/A
1N/Astruct dns_map
1N/A{
1N/A int dns_m_type;
1N/A};
1N/A
1N/Abool
1N/Adns_map_parseargs(map,args)
1N/A MAP *map;
1N/A char *args;
1N/A{
1N/A register char *p = args;
1N/A struct dns_map *map_p;
1N/A
1N/A map_p = (struct dns_map *) xalloc(sizeof(*map_p));
1N/A map_p->dns_m_type = -1;
1N/A map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
1N/A
1N/A for (;;)
1N/A {
1N/A while (isascii(*p) && isspace(*p))
1N/A p++;
1N/A if (*p != '-')
1N/A break;
1N/A switch (*++p)
1N/A {
1N/A case 'N':
1N/A map->map_mflags |= MF_INCLNULL;
1N/A map->map_mflags &= ~MF_TRY0NULL;
1N/A break;
1N/A
1N/A case 'O':
1N/A map->map_mflags &= ~MF_TRY1NULL;
1N/A break;
1N/A
1N/A case 'o':
1N/A map->map_mflags |= MF_OPTIONAL;
1N/A break;
1N/A
1N/A case 'f':
1N/A map->map_mflags |= MF_NOFOLDCASE;
1N/A break;
1N/A
1N/A case 'm':
1N/A map->map_mflags |= MF_MATCHONLY;
1N/A break;
1N/A
1N/A case 'A':
1N/A map->map_mflags |= MF_APPEND;
1N/A break;
1N/A
1N/A case 'q':
1N/A map->map_mflags |= MF_KEEPQUOTES;
1N/A break;
1N/A
1N/A case 't':
1N/A map->map_mflags |= MF_NODEFER;
1N/A break;
1N/A
1N/A case 'a':
1N/A map->map_app = ++p;
1N/A break;
1N/A
1N/A case 'T':
1N/A map->map_tapp = ++p;
1N/A break;
1N/A
1N/A case 'd':
1N/A {
1N/A char *h;
1N/A
1N/A ++p;
1N/A h = strchr(p, ' ');
1N/A if (h != NULL)
1N/A *h = '\0';
1N/A map->map_timeout = convtime(p, 's');
1N/A if (h != NULL)
1N/A *h = ' ';
1N/A }
1N/A break;
1N/A
1N/A case 'r':
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A map->map_retry = atoi(p);
1N/A break;
1N/A
1N/A case 'z':
1N/A if (*++p != '\\')
1N/A map->map_coldelim = *p;
1N/A else
1N/A {
1N/A switch (*++p)
1N/A {
1N/A case 'n':
1N/A map->map_coldelim = '\n';
1N/A break;
1N/A
1N/A case 't':
1N/A map->map_coldelim = '\t';
1N/A break;
1N/A
1N/A default:
1N/A map->map_coldelim = '\\';
1N/A }
1N/A }
1N/A break;
1N/A
1N/A case 'Z':
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A map->map_sizelimit = atoi(p);
1N/A break;
1N/A
1N/A /* Start of dns_map specific args */
1N/A case 'R': /* search field */
1N/A {
1N/A char *h;
1N/A
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A h = strchr(p, ' ');
1N/A if (h != NULL)
1N/A *h = '\0';
1N/A map_p->dns_m_type = dns_string_to_type(p);
1N/A if (h != NULL)
1N/A *h = ' ';
1N/A if (map_p->dns_m_type < 0)
1N/A syserr("dns map %s: wrong type %s",
1N/A map->map_mname, p);
1N/A }
1N/A break;
1N/A
1N/A case 'B': /* base domain */
1N/A {
1N/A char *h;
1N/A
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A h = strchr(p, ' ');
1N/A if (h != NULL)
1N/A *h = '\0';
1N/A
1N/A /*
1N/A ** slight abuse of map->map_file; it isn't
1N/A ** used otherwise in this map type.
1N/A */
1N/A
1N/A map->map_file = newstr(p);
1N/A if (h != NULL)
1N/A *h = ' ';
1N/A }
1N/A break;
1N/A }
1N/A while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1N/A p++;
1N/A if (*p != '\0')
1N/A *p++ = '\0';
1N/A }
1N/A if (map_p->dns_m_type < 0)
1N/A syserr("dns map %s: missing -R type", map->map_mname);
1N/A if (map->map_app != NULL)
1N/A map->map_app = newstr(map->map_app);
1N/A if (map->map_tapp != NULL)
1N/A map->map_tapp = newstr(map->map_tapp);
1N/A
1N/A /*
1N/A ** Assumption: assert(sizeof(int) <= sizeof(ARBPTR_T));
1N/A ** Even if this assumption is wrong, we use only one byte,
1N/A ** so it doesn't really matter.
1N/A */
1N/A
1N/A map->map_db1 = (ARBPTR_T) map_p;
1N/A return true;
1N/A}
1N/A
1N/A/*
1N/A** DNS_MAP_LOOKUP -- perform dns map lookup.
1N/A**
1N/A** Parameters:
1N/A** map -- pointer to MAP
1N/A** name -- name to lookup
1N/A** av -- arguments to interpolate into buf.
1N/A** statp -- pointer to status (EX_)
1N/A**
1N/A** Returns:
1N/A** result of lookup if succeeded.
1N/A** NULL -- otherwise.
1N/A*/
1N/A
1N/Achar *
1N/Adns_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A int resnum = 0;
1N/A char *vp = NULL, *result = NULL;
1N/A size_t vsize;
1N/A struct dns_map *map_p;
1N/A RESOURCE_RECORD_T *rr = NULL;
1N/A DNS_REPLY_T *r = NULL;
1N/A# if NETINET6
1N/A static char buf6[INET6_ADDRSTRLEN];
1N/A# endif /* NETINET6 */
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("dns_map_lookup(%s, %s)\n",
1N/A map->map_mname, name);
1N/A
1N/A map_p = (struct dns_map *)(map->map_db1);
1N/A if (map->map_file != NULL && *map->map_file != '\0')
1N/A {
1N/A size_t len;
1N/A char *appdomain;
1N/A
1N/A len = strlen(map->map_file) + strlen(name) + 2;
1N/A appdomain = (char *) sm_malloc(len);
1N/A if (appdomain == NULL)
1N/A {
1N/A *statp = EX_UNAVAILABLE;
1N/A return NULL;
1N/A }
1N/A (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file);
1N/A r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type,
1N/A map->map_timeout, map->map_retry);
1N/A sm_free(appdomain);
1N/A }
1N/A else
1N/A {
1N/A r = dns_lookup_int(name, C_IN, map_p->dns_m_type,
1N/A map->map_timeout, map->map_retry);
1N/A }
1N/A
1N/A if (r == NULL)
1N/A {
1N/A result = NULL;
1N/A if (h_errno == TRY_AGAIN || transienterror(errno))
1N/A *statp = EX_TEMPFAIL;
1N/A else
1N/A *statp = EX_NOTFOUND;
1N/A goto cleanup;
1N/A }
1N/A *statp = EX_OK;
1N/A for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next)
1N/A {
1N/A char *type = NULL;
1N/A char *value = NULL;
1N/A
1N/A switch (rr->rr_type)
1N/A {
1N/A case T_NS:
1N/A type = "T_NS";
1N/A value = rr->rr_u.rr_txt;
1N/A break;
1N/A case T_CNAME:
1N/A type = "T_CNAME";
1N/A value = rr->rr_u.rr_txt;
1N/A break;
1N/A case T_AFSDB:
1N/A type = "T_AFSDB";
1N/A value = rr->rr_u.rr_mx->mx_r_domain;
1N/A break;
1N/A case T_SRV:
1N/A type = "T_SRV";
1N/A value = rr->rr_u.rr_srv->srv_r_target;
1N/A break;
1N/A case T_PTR:
1N/A type = "T_PTR";
1N/A value = rr->rr_u.rr_txt;
1N/A break;
1N/A case T_TXT:
1N/A type = "T_TXT";
1N/A value = rr->rr_u.rr_txt;
1N/A break;
1N/A case T_MX:
1N/A type = "T_MX";
1N/A value = rr->rr_u.rr_mx->mx_r_domain;
1N/A break;
1N/A# if NETINET
1N/A case T_A:
1N/A type = "T_A";
1N/A value = inet_ntoa(*(rr->rr_u.rr_a));
1N/A break;
1N/A# endif /* NETINET */
1N/A# if NETINET6
1N/A case T_AAAA:
1N/A type = "T_AAAA";
1N/A value = anynet_ntop(rr->rr_u.rr_aaaa, buf6,
1N/A sizeof(buf6));
1N/A break;
1N/A# endif /* NETINET6 */
1N/A }
1N/A
1N/A (void) strreplnonprt(value, 'X');
1N/A if (map_p->dns_m_type != rr->rr_type)
1N/A {
1N/A if (tTd(38, 40))
1N/A sm_dprintf("\tskipping type %s (%d) value %s\n",
1N/A type != NULL ? type : "<UNKNOWN>",
1N/A rr->rr_type,
1N/A value != NULL ? value : "<NO VALUE>");
1N/A continue;
1N/A }
1N/A
1N/A# if NETINET6
1N/A if (rr->rr_type == T_AAAA && value == NULL)
1N/A {
1N/A result = NULL;
1N/A *statp = EX_DATAERR;
1N/A if (tTd(38, 40))
1N/A sm_dprintf("\tbad T_AAAA conversion\n");
1N/A goto cleanup;
1N/A }
1N/A# endif /* NETINET6 */
1N/A if (tTd(38, 40))
1N/A sm_dprintf("\tfound type %s (%d) value %s\n",
1N/A type != NULL ? type : "<UNKNOWN>",
1N/A rr->rr_type,
1N/A value != NULL ? value : "<NO VALUE>");
1N/A if (value != NULL &&
1N/A (map->map_coldelim == '\0' ||
1N/A map->map_sizelimit == 1 ||
1N/A bitset(MF_MATCHONLY, map->map_mflags)))
1N/A {
1N/A /* Only care about the first match */
1N/A vp = newstr(value);
1N/A break;
1N/A }
1N/A else if (vp == NULL)
1N/A {
1N/A /* First result */
1N/A vp = newstr(value);
1N/A }
1N/A else
1N/A {
1N/A /* concatenate the results */
1N/A int sz;
1N/A char *new;
1N/A
1N/A sz = strlen(vp) + strlen(value) + 2;
1N/A new = xalloc(sz);
1N/A (void) sm_snprintf(new, sz, "%s%c%s",
1N/A vp, map->map_coldelim, value);
1N/A sm_free(vp);
1N/A vp = new;
1N/A if (map->map_sizelimit > 0 &&
1N/A ++resnum >= map->map_sizelimit)
1N/A break;
1N/A }
1N/A }
1N/A if (vp == NULL)
1N/A {
1N/A result = NULL;
1N/A *statp = EX_NOTFOUND;
1N/A if (tTd(38, 40))
1N/A sm_dprintf("\tno match found\n");
1N/A goto cleanup;
1N/A }
1N/A
1N/A /* Cleanly truncate for rulesets */
1N/A truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim);
1N/A
1N/A vsize = strlen(vp);
1N/A
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s",
1N/A name, vp);
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A result = map_rewrite(map, name, strlen(name), NULL);
1N/A else
1N/A result = map_rewrite(map, vp, vsize, av);
1N/A
1N/A cleanup:
1N/A if (vp != NULL)
1N/A sm_free(vp);
1N/A if (r != NULL)
1N/A dns_free_data(r);
1N/A return result;
1N/A}
1N/A# endif /* DNSMAP */
1N/A#endif /* NAMED_BIND */
1N/A
1N/A/*
1N/A** NDBM modules
1N/A*/
1N/A
1N/A#if NDBM
1N/A
1N/A/*
1N/A** NDBM_MAP_OPEN -- DBM-style map open
1N/A*/
1N/A
1N/Abool
1N/Andbm_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A register DBM *dbm;
1N/A int save_errno;
1N/A int dfd;
1N/A int pfd;
1N/A long sff;
1N/A int ret;
1N/A int smode = S_IREAD;
1N/A char dirfile[MAXPATHLEN];
1N/A char pagfile[MAXPATHLEN];
1N/A struct stat st;
1N/A struct stat std, stp;
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1N/A map->map_mname, map->map_file, mode);
1N/A map->map_lockfd = -1;
1N/A mode &= O_ACCMODE;
1N/A
1N/A /* do initial file and directory checks */
1N/A if (sm_strlcpyn(dirfile, sizeof(dirfile), 2,
1N/A map->map_file, ".dir") >= sizeof(dirfile) ||
1N/A sm_strlcpyn(pagfile, sizeof(pagfile), 2,
1N/A map->map_file, ".pag") >= sizeof(pagfile))
1N/A {
1N/A errno = 0;
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("dbm map \"%s\": map file %s name too long",
1N/A map->map_mname, map->map_file);
1N/A return false;
1N/A }
1N/A sff = SFF_ROOTOK|SFF_REGONLY;
1N/A if (mode == O_RDWR)
1N/A {
1N/A sff |= SFF_CREAT;
1N/A if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1N/A sff |= SFF_NOSLINK;
1N/A if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1N/A sff |= SFF_NOHLINK;
1N/A smode = S_IWRITE;
1N/A }
1N/A else
1N/A {
1N/A if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1N/A sff |= SFF_NOWLINK;
1N/A }
1N/A if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1N/A sff |= SFF_SAFEDIRPATH;
1N/A ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
1N/A sff, smode, &std);
1N/A if (ret == 0)
1N/A ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
1N/A sff, smode, &stp);
1N/A
1N/A if (ret != 0)
1N/A {
1N/A char *prob = "unsafe";
1N/A
1N/A /* cannot open this map */
1N/A if (ret == ENOENT)
1N/A prob = "missing";
1N/A if (tTd(38, 2))
1N/A sm_dprintf("\t%s map file: %d\n", prob, ret);
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("dbm map \"%s\": %s map file %s",
1N/A map->map_mname, prob, map->map_file);
1N/A return false;
1N/A }
1N/A if (std.st_mode == ST_MODE_NOFILE)
1N/A mode |= O_CREAT|O_EXCL;
1N/A
1N/A# if LOCK_ON_OPEN
1N/A if (mode == O_RDONLY)
1N/A mode |= O_SHLOCK;
1N/A else
1N/A mode |= O_TRUNC|O_EXLOCK;
1N/A# else /* LOCK_ON_OPEN */
1N/A if ((mode & O_ACCMODE) == O_RDWR)
1N/A {
1N/A# if NOFTRUNCATE
1N/A /*
1N/A ** Warning: race condition. Try to lock the file as
1N/A ** quickly as possible after opening it.
1N/A ** This may also have security problems on some systems,
1N/A ** but there isn't anything we can do about it.
1N/A */
1N/A
1N/A mode |= O_TRUNC;
1N/A# else /* NOFTRUNCATE */
1N/A /*
1N/A ** This ugly code opens the map without truncating it,
1N/A ** locks the file, then truncates it. Necessary to
1N/A ** avoid race conditions.
1N/A */
1N/A
1N/A int dirfd;
1N/A int pagfd;
1N/A long sff = SFF_CREAT|SFF_OPENASROOT;
1N/A
1N/A if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1N/A sff |= SFF_NOSLINK;
1N/A if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1N/A sff |= SFF_NOHLINK;
1N/A
1N/A dirfd = safeopen(dirfile, mode, DBMMODE, sff);
1N/A pagfd = safeopen(pagfile, mode, DBMMODE, sff);
1N/A
1N/A if (dirfd < 0 || pagfd < 0)
1N/A {
1N/A save_errno = errno;
1N/A if (dirfd >= 0)
1N/A (void) close(dirfd);
1N/A if (pagfd >= 0)
1N/A (void) close(pagfd);
1N/A errno = save_errno;
1N/A syserr("ndbm_map_open: cannot create database %s",
1N/A map->map_file);
1N/A return false;
1N/A }
1N/A if (ftruncate(dirfd, (off_t) 0) < 0 ||
1N/A ftruncate(pagfd, (off_t) 0) < 0)
1N/A {
1N/A save_errno = errno;
1N/A (void) close(dirfd);
1N/A (void) close(pagfd);
1N/A errno = save_errno;
1N/A syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1N/A map->map_file);
1N/A return false;
1N/A }
1N/A
1N/A /* if new file, get "before" bits for later filechanged check */
1N/A if (std.st_mode == ST_MODE_NOFILE &&
1N/A (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
1N/A {
1N/A save_errno = errno;
1N/A (void) close(dirfd);
1N/A (void) close(pagfd);
1N/A errno = save_errno;
1N/A syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1N/A map->map_file);
1N/A return false;
1N/A }
1N/A
1N/A /* have to save the lock for the duration (bletch) */
1N/A map->map_lockfd = dirfd;
1N/A (void) close(pagfd);
1N/A
1N/A /* twiddle bits for dbm_open */
1N/A mode &= ~(O_CREAT|O_EXCL);
1N/A# endif /* NOFTRUNCATE */
1N/A }
1N/A# endif /* LOCK_ON_OPEN */
1N/A
1N/A /* open the database */
1N/A dbm = dbm_open(map->map_file, mode, DBMMODE);
1N/A if (dbm == NULL)
1N/A {
1N/A save_errno = errno;
1N/A if (bitset(MF_ALIAS, map->map_mflags) &&
1N/A aliaswait(map, ".pag", false))
1N/A return true;
1N/A# if !LOCK_ON_OPEN && !NOFTRUNCATE
1N/A if (map->map_lockfd >= 0)
1N/A (void) close(map->map_lockfd);
1N/A# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1N/A errno = save_errno;
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("Cannot open DBM database %s", map->map_file);
1N/A return false;
1N/A }
1N/A dfd = dbm_dirfno(dbm);
1N/A pfd = dbm_pagfno(dbm);
1N/A if (dfd == pfd)
1N/A {
1N/A /* heuristic: if files are linked, this is actually gdbm */
1N/A dbm_close(dbm);
1N/A# if !LOCK_ON_OPEN && !NOFTRUNCATE
1N/A if (map->map_lockfd >= 0)
1N/A (void) close(map->map_lockfd);
1N/A# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1N/A errno = 0;
1N/A syserr("dbm map \"%s\": cannot support GDBM",
1N/A map->map_mname);
1N/A return false;
1N/A }
1N/A
1N/A if (filechanged(dirfile, dfd, &std) ||
1N/A filechanged(pagfile, pfd, &stp))
1N/A {
1N/A save_errno = errno;
1N/A dbm_close(dbm);
1N/A# if !LOCK_ON_OPEN && !NOFTRUNCATE
1N/A if (map->map_lockfd >= 0)
1N/A (void) close(map->map_lockfd);
1N/A# endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1N/A errno = save_errno;
1N/A syserr("ndbm_map_open(%s): file changed after open",
1N/A map->map_file);
1N/A return false;
1N/A }
1N/A
1N/A map->map_db1 = (ARBPTR_T) dbm;
1N/A
1N/A /*
1N/A ** Need to set map_mtime before the call to aliaswait()
1N/A ** as aliaswait() will call map_lookup() which requires
1N/A ** map_mtime to be set
1N/A */
1N/A
1N/A if (fstat(pfd, &st) >= 0)
1N/A map->map_mtime = st.st_mtime;
1N/A
1N/A if (mode == O_RDONLY)
1N/A {
1N/A# if LOCK_ON_OPEN
1N/A if (dfd >= 0)
1N/A (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1N/A if (pfd >= 0)
1N/A (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
1N/A# endif /* LOCK_ON_OPEN */
1N/A if (bitset(MF_ALIAS, map->map_mflags) &&
1N/A !aliaswait(map, ".pag", true))
1N/A return false;
1N/A }
1N/A else
1N/A {
1N/A map->map_mflags |= MF_LOCKED;
1N/A if (geteuid() == 0 && TrustedUid != 0)
1N/A {
1N/A# if HASFCHOWN
1N/A if (fchown(dfd, TrustedUid, -1) < 0 ||
1N/A fchown(pfd, TrustedUid, -1) < 0)
1N/A {
1N/A int err = errno;
1N/A
1N/A sm_syslog(LOG_ALERT, NOQID,
1N/A "ownership change on %s failed: %s",
1N/A map->map_file, sm_errstring(err));
1N/A message("050 ownership change on %s failed: %s",
1N/A map->map_file, sm_errstring(err));
1N/A }
1N/A# else /* HASFCHOWN */
1N/A sm_syslog(LOG_ALERT, NOQID,
1N/A "no fchown(): cannot change ownership on %s",
1N/A map->map_file);
1N/A message("050 no fchown(): cannot change ownership on %s",
1N/A map->map_file);
1N/A# endif /* HASFCHOWN */
1N/A }
1N/A }
1N/A return true;
1N/A}
1N/A
1N/A
1N/A/*
1N/A** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1N/A*/
1N/A
1N/Achar *
1N/Andbm_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A datum key, val;
1N/A int dfd, pfd;
1N/A char keybuf[MAXNAME + 1];
1N/A struct stat stbuf;
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1N/A map->map_mname, name);
1N/A
1N/A key.dptr = name;
1N/A key.dsize = strlen(name);
1N/A if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1N/A {
1N/A if (key.dsize > sizeof(keybuf) - 1)
1N/A key.dsize = sizeof(keybuf) - 1;
1N/A memmove(keybuf, key.dptr, key.dsize);
1N/A keybuf[key.dsize] = '\0';
1N/A makelower(keybuf);
1N/A key.dptr = keybuf;
1N/A }
1N/Alockdbm:
1N/A dfd = dbm_dirfno((DBM *) map->map_db1);
1N/A if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1N/A (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH);
1N/A pfd = dbm_pagfno((DBM *) map->map_db1);
1N/A if (pfd < 0 || fstat(pfd, &stbuf) < 0 ||
1N/A stbuf.st_mtime > map->map_mtime)
1N/A {
1N/A /* Reopen the database to sync the cache */
1N/A int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1N/A : O_RDONLY;
1N/A
1N/A if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1N/A (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1N/A map->map_mflags |= MF_CLOSING;
1N/A map->map_class->map_close(map);
1N/A map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1N/A if (map->map_class->map_open(map, omode))
1N/A {
1N/A map->map_mflags |= MF_OPEN;
1N/A map->map_pid = CurrentPid;
1N/A if ((omode & O_ACCMODE) == O_RDWR)
1N/A map->map_mflags |= MF_WRITABLE;
1N/A goto lockdbm;
1N/A }
1N/A else
1N/A {
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A {
1N/A extern MAPCLASS BogusMapClass;
1N/A
1N/A *statp = EX_TEMPFAIL;
1N/A map->map_orgclass = map->map_class;
1N/A map->map_class = &BogusMapClass;
1N/A map->map_mflags |= MF_OPEN;
1N/A map->map_pid = CurrentPid;
1N/A syserr("Cannot reopen NDBM database %s",
1N/A map->map_file);
1N/A }
1N/A return NULL;
1N/A }
1N/A }
1N/A val.dptr = NULL;
1N/A if (bitset(MF_TRY0NULL, map->map_mflags))
1N/A {
1N/A val = dbm_fetch((DBM *) map->map_db1, key);
1N/A if (val.dptr != NULL)
1N/A map->map_mflags &= ~MF_TRY1NULL;
1N/A }
1N/A if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
1N/A {
1N/A key.dsize++;
1N/A val = dbm_fetch((DBM *) map->map_db1, key);
1N/A if (val.dptr != NULL)
1N/A map->map_mflags &= ~MF_TRY0NULL;
1N/A }
1N/A if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1N/A (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
1N/A if (val.dptr == NULL)
1N/A return NULL;
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A return map_rewrite(map, name, strlen(name), NULL);
1N/A else
1N/A return map_rewrite(map, val.dptr, val.dsize, av);
1N/A}
1N/A
1N/A
1N/A/*
1N/A** NDBM_MAP_STORE -- store a datum in the database
1N/A*/
1N/A
1N/Avoid
1N/Andbm_map_store(map, lhs, rhs)
1N/A register MAP *map;
1N/A char *lhs;
1N/A char *rhs;
1N/A{
1N/A datum key;
1N/A datum data;
1N/A int status;
1N/A char keybuf[MAXNAME + 1];
1N/A
1N/A if (tTd(38, 12))
1N/A sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1N/A map->map_mname, lhs, rhs);
1N/A
1N/A key.dsize = strlen(lhs);
1N/A key.dptr = lhs;
1N/A if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1N/A {
1N/A if (key.dsize > sizeof(keybuf) - 1)
1N/A key.dsize = sizeof(keybuf) - 1;
1N/A memmove(keybuf, key.dptr, key.dsize);
1N/A keybuf[key.dsize] = '\0';
1N/A makelower(keybuf);
1N/A key.dptr = keybuf;
1N/A }
1N/A
1N/A data.dsize = strlen(rhs);
1N/A data.dptr = rhs;
1N/A
1N/A if (bitset(MF_INCLNULL, map->map_mflags))
1N/A {
1N/A key.dsize++;
1N/A data.dsize++;
1N/A }
1N/A
1N/A status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
1N/A if (status > 0)
1N/A {
1N/A if (!bitset(MF_APPEND, map->map_mflags))
1N/A message("050 Warning: duplicate alias name %s", lhs);
1N/A else
1N/A {
1N/A static char *buf = NULL;
1N/A static int bufsiz = 0;
1N/A auto int xstat;
1N/A datum old;
1N/A
1N/A old.dptr = ndbm_map_lookup(map, key.dptr,
1N/A (char **) NULL, &xstat);
1N/A if (old.dptr != NULL && *(char *) old.dptr != '\0')
1N/A {
1N/A old.dsize = strlen(old.dptr);
1N/A if (data.dsize + old.dsize + 2 > bufsiz)
1N/A {
1N/A if (buf != NULL)
1N/A (void) sm_free(buf);
1N/A bufsiz = data.dsize + old.dsize + 2;
1N/A buf = sm_pmalloc_x(bufsiz);
1N/A }
1N/A (void) sm_strlcpyn(buf, bufsiz, 3,
1N/A data.dptr, ",", old.dptr);
1N/A data.dsize = data.dsize + old.dsize + 1;
1N/A data.dptr = buf;
1N/A if (tTd(38, 9))
1N/A sm_dprintf("ndbm_map_store append=%s\n",
1N/A data.dptr);
1N/A }
1N/A }
1N/A status = dbm_store((DBM *) map->map_db1,
1N/A key, data, DBM_REPLACE);
1N/A }
1N/A if (status != 0)
1N/A syserr("readaliases: dbm put (%s): %d", lhs, status);
1N/A}
1N/A
1N/A
1N/A/*
1N/A** NDBM_MAP_CLOSE -- close the database
1N/A*/
1N/A
1N/Avoid
1N/Andbm_map_close(map)
1N/A register MAP *map;
1N/A{
1N/A if (tTd(38, 9))
1N/A sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1N/A map->map_mname, map->map_file, map->map_mflags);
1N/A
1N/A if (bitset(MF_WRITABLE, map->map_mflags))
1N/A {
1N/A# ifdef NDBM_YP_COMPAT
1N/A bool inclnull;
1N/A char buf[MAXHOSTNAMELEN];
1N/A
1N/A inclnull = bitset(MF_INCLNULL, map->map_mflags);
1N/A map->map_mflags &= ~MF_INCLNULL;
1N/A
1N/A if (strstr(map->map_file, "/yp/") != NULL)
1N/A {
1N/A long save_mflags = map->map_mflags;
1N/A
1N/A map->map_mflags |= MF_NOFOLDCASE;
1N/A
1N/A (void) sm_snprintf(buf, sizeof(buf), "%010ld", curtime());
1N/A ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
1N/A
1N/A (void) gethostname(buf, sizeof(buf));
1N/A ndbm_map_store(map, "YP_MASTER_NAME", buf);
1N/A
1N/A map->map_mflags = save_mflags;
1N/A }
1N/A
1N/A if (inclnull)
1N/A map->map_mflags |= MF_INCLNULL;
1N/A# endif /* NDBM_YP_COMPAT */
1N/A
1N/A /* write out the distinguished alias */
1N/A ndbm_map_store(map, "@", "@");
1N/A }
1N/A dbm_close((DBM *) map->map_db1);
1N/A
1N/A /* release lock (if needed) */
1N/A# if !LOCK_ON_OPEN
1N/A if (map->map_lockfd >= 0)
1N/A (void) close(map->map_lockfd);
1N/A# endif /* !LOCK_ON_OPEN */
1N/A}
1N/A
1N/A#endif /* NDBM */
1N/A/*
1N/A** NEWDB (Hash and BTree) Modules
1N/A*/
1N/A
1N/A#if NEWDB
1N/A
1N/A/*
1N/A** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1N/A**
1N/A** These do rather bizarre locking. If you can lock on open,
1N/A** do that to avoid the condition of opening a database that
1N/A** is being rebuilt. If you don't, we'll try to fake it, but
1N/A** there will be a race condition. If opening for read-only,
1N/A** we immediately release the lock to avoid freezing things up.
1N/A** We really ought to hold the lock, but guarantee that we won't
1N/A** be pokey about it. That's hard to do.
1N/A*/
1N/A
1N/A/* these should be K line arguments */
1N/A# if DB_VERSION_MAJOR < 2
1N/A# define db_cachesize cachesize
1N/A# define h_nelem nelem
1N/A# ifndef DB_CACHE_SIZE
1N/A# define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
1N/A# endif /* ! DB_CACHE_SIZE */
1N/A# ifndef DB_HASH_NELEM
1N/A# define DB_HASH_NELEM 4096 /* (starting) size of hash table */
1N/A# endif /* ! DB_HASH_NELEM */
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A
1N/Abool
1N/Abt_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A# if DB_VERSION_MAJOR < 2
1N/A BTREEINFO btinfo;
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A# if DB_VERSION_MAJOR == 2
1N/A DB_INFO btinfo;
1N/A# endif /* DB_VERSION_MAJOR == 2 */
1N/A# if DB_VERSION_MAJOR > 2
1N/A void *btinfo = NULL;
1N/A# endif /* DB_VERSION_MAJOR > 2 */
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("bt_map_open(%s, %s, %d)\n",
1N/A map->map_mname, map->map_file, mode);
1N/A
1N/A# if DB_VERSION_MAJOR < 3
1N/A memset(&btinfo, '\0', sizeof(btinfo));
1N/A# ifdef DB_CACHE_SIZE
1N/A btinfo.db_cachesize = DB_CACHE_SIZE;
1N/A# endif /* DB_CACHE_SIZE */
1N/A# endif /* DB_VERSION_MAJOR < 3 */
1N/A
1N/A return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
1N/A}
1N/A
1N/Abool
1N/Ahash_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A# if DB_VERSION_MAJOR < 2
1N/A HASHINFO hinfo;
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A# if DB_VERSION_MAJOR == 2
1N/A DB_INFO hinfo;
1N/A# endif /* DB_VERSION_MAJOR == 2 */
1N/A# if DB_VERSION_MAJOR > 2
1N/A void *hinfo = NULL;
1N/A# endif /* DB_VERSION_MAJOR > 2 */
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("hash_map_open(%s, %s, %d)\n",
1N/A map->map_mname, map->map_file, mode);
1N/A
1N/A# if DB_VERSION_MAJOR < 3
1N/A memset(&hinfo, '\0', sizeof(hinfo));
1N/A# ifdef DB_HASH_NELEM
1N/A hinfo.h_nelem = DB_HASH_NELEM;
1N/A# endif /* DB_HASH_NELEM */
1N/A# ifdef DB_CACHE_SIZE
1N/A hinfo.db_cachesize = DB_CACHE_SIZE;
1N/A# endif /* DB_CACHE_SIZE */
1N/A# endif /* DB_VERSION_MAJOR < 3 */
1N/A
1N/A return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
1N/A}
1N/A
1N/Astatic bool
1N/Adb_map_open(map, mode, mapclassname, dbtype, openinfo)
1N/A MAP *map;
1N/A int mode;
1N/A char *mapclassname;
1N/A DBTYPE dbtype;
1N/A# if DB_VERSION_MAJOR < 2
1N/A const void *openinfo;
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A# if DB_VERSION_MAJOR == 2
1N/A DB_INFO *openinfo;
1N/A# endif /* DB_VERSION_MAJOR == 2 */
1N/A# if DB_VERSION_MAJOR > 2
1N/A void **openinfo;
1N/A# endif /* DB_VERSION_MAJOR > 2 */
1N/A{
1N/A DB *db = NULL;
1N/A int i;
1N/A int omode;
1N/A int smode = S_IREAD;
1N/A int fd;
1N/A long sff;
1N/A int save_errno;
1N/A struct stat st;
1N/A char buf[MAXPATHLEN];
1N/A
1N/A /* do initial file and directory checks */
1N/A if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
1N/A {
1N/A errno = 0;
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("map \"%s\": map file %s name too long",
1N/A map->map_mname, map->map_file);
1N/A return false;
1N/A }
1N/A i = strlen(buf);
1N/A if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
1N/A {
1N/A if (sm_strlcat(buf, ".db", sizeof(buf)) >= sizeof(buf))
1N/A {
1N/A errno = 0;
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("map \"%s\": map file %s name too long",
1N/A map->map_mname, map->map_file);
1N/A return false;
1N/A }
1N/A }
1N/A
1N/A mode &= O_ACCMODE;
1N/A omode = mode;
1N/A
1N/A sff = SFF_ROOTOK|SFF_REGONLY;
1N/A if (mode == O_RDWR)
1N/A {
1N/A sff |= SFF_CREAT;
1N/A if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
1N/A sff |= SFF_NOSLINK;
1N/A if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
1N/A sff |= SFF_NOHLINK;
1N/A smode = S_IWRITE;
1N/A }
1N/A else
1N/A {
1N/A if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1N/A sff |= SFF_NOWLINK;
1N/A }
1N/A if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1N/A sff |= SFF_SAFEDIRPATH;
1N/A i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
1N/A
1N/A if (i != 0)
1N/A {
1N/A char *prob = "unsafe";
1N/A
1N/A /* cannot open this map */
1N/A if (i == ENOENT)
1N/A prob = "missing";
1N/A if (tTd(38, 2))
1N/A sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i));
1N/A errno = i;
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("%s map \"%s\": %s map file %s",
1N/A mapclassname, map->map_mname, prob, buf);
1N/A return false;
1N/A }
1N/A if (st.st_mode == ST_MODE_NOFILE)
1N/A omode |= O_CREAT|O_EXCL;
1N/A
1N/A map->map_lockfd = -1;
1N/A
1N/A# if LOCK_ON_OPEN
1N/A if (mode == O_RDWR)
1N/A omode |= O_TRUNC|O_EXLOCK;
1N/A else
1N/A omode |= O_SHLOCK;
1N/A# else /* LOCK_ON_OPEN */
1N/A /*
1N/A ** Pre-lock the file to avoid race conditions. In particular,
1N/A ** since dbopen returns NULL if the file is zero length, we
1N/A ** must have a locked instance around the dbopen.
1N/A */
1N/A
1N/A fd = open(buf, omode, DBMMODE);
1N/A if (fd < 0)
1N/A {
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("db_map_open: cannot pre-open database %s", buf);
1N/A return false;
1N/A }
1N/A
1N/A /* make sure no baddies slipped in just before the open... */
1N/A if (filechanged(buf, fd, &st))
1N/A {
1N/A save_errno = errno;
1N/A (void) close(fd);
1N/A errno = save_errno;
1N/A syserr("db_map_open(%s): file changed after pre-open", buf);
1N/A return false;
1N/A }
1N/A
1N/A /* if new file, get the "before" bits for later filechanged check */
1N/A if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
1N/A {
1N/A save_errno = errno;
1N/A (void) close(fd);
1N/A errno = save_errno;
1N/A syserr("db_map_open(%s): cannot fstat pre-opened file",
1N/A buf);
1N/A return false;
1N/A }
1N/A
1N/A /* actually lock the pre-opened file */
1N/A if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
1N/A syserr("db_map_open: cannot lock %s", buf);
1N/A
1N/A /* set up mode bits for dbopen */
1N/A if (mode == O_RDWR)
1N/A omode |= O_TRUNC;
1N/A omode &= ~(O_EXCL|O_CREAT);
1N/A# endif /* LOCK_ON_OPEN */
1N/A
1N/A# if DB_VERSION_MAJOR < 2
1N/A db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
1N/A# else /* DB_VERSION_MAJOR < 2 */
1N/A {
1N/A int flags = 0;
1N/A# if DB_VERSION_MAJOR > 2
1N/A int ret;
1N/A# endif /* DB_VERSION_MAJOR > 2 */
1N/A
1N/A if (mode == O_RDONLY)
1N/A flags |= DB_RDONLY;
1N/A if (bitset(O_CREAT, omode))
1N/A flags |= DB_CREATE;
1N/A if (bitset(O_TRUNC, omode))
1N/A flags |= DB_TRUNCATE;
1N/A SM_DB_FLAG_ADD(flags);
1N/A
1N/A# if DB_VERSION_MAJOR > 2
1N/A ret = db_create(&db, NULL, 0);
1N/A# ifdef DB_CACHE_SIZE
1N/A if (ret == 0 && db != NULL)
1N/A {
1N/A ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0);
1N/A if (ret != 0)
1N/A {
1N/A (void) db->close(db, 0);
1N/A db = NULL;
1N/A }
1N/A }
1N/A# endif /* DB_CACHE_SIZE */
1N/A# ifdef DB_HASH_NELEM
1N/A if (dbtype == DB_HASH && ret == 0 && db != NULL)
1N/A {
1N/A ret = db->set_h_nelem(db, DB_HASH_NELEM);
1N/A if (ret != 0)
1N/A {
1N/A (void) db->close(db, 0);
1N/A db = NULL;
1N/A }
1N/A }
1N/A# endif /* DB_HASH_NELEM */
1N/A if (ret == 0 && db != NULL)
1N/A {
1N/A ret = db->open(db,
1N/A DBTXN /* transaction for DB 4.1 */
1N/A buf, NULL, dbtype, flags, DBMMODE);
1N/A if (ret != 0)
1N/A {
1N/A#ifdef DB_OLD_VERSION
1N/A if (ret == DB_OLD_VERSION)
1N/A ret = EINVAL;
1N/A#endif /* DB_OLD_VERSION */
1N/A (void) db->close(db, 0);
1N/A db = NULL;
1N/A }
1N/A }
1N/A errno = ret;
1N/A# else /* DB_VERSION_MAJOR > 2 */
1N/A errno = db_open(buf, dbtype, flags, DBMMODE,
1N/A NULL, openinfo, &db);
1N/A# endif /* DB_VERSION_MAJOR > 2 */
1N/A }
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A save_errno = errno;
1N/A
1N/A# if !LOCK_ON_OPEN
1N/A if (mode == O_RDWR)
1N/A map->map_lockfd = fd;
1N/A else
1N/A (void) close(fd);
1N/A# endif /* !LOCK_ON_OPEN */
1N/A
1N/A if (db == NULL)
1N/A {
1N/A if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
1N/A aliaswait(map, ".db", false))
1N/A return true;
1N/A# if !LOCK_ON_OPEN
1N/A if (map->map_lockfd >= 0)
1N/A (void) close(map->map_lockfd);
1N/A# endif /* !LOCK_ON_OPEN */
1N/A errno = save_errno;
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("Cannot open %s database %s",
1N/A mapclassname, buf);
1N/A return false;
1N/A }
1N/A
1N/A# if DB_VERSION_MAJOR < 2
1N/A fd = db->fd(db);
1N/A# else /* DB_VERSION_MAJOR < 2 */
1N/A fd = -1;
1N/A errno = db->fd(db, &fd);
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A if (filechanged(buf, fd, &st))
1N/A {
1N/A save_errno = errno;
1N/A# if DB_VERSION_MAJOR < 2
1N/A (void) db->close(db);
1N/A# else /* DB_VERSION_MAJOR < 2 */
1N/A errno = db->close(db, 0);
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A# if !LOCK_ON_OPEN
1N/A if (map->map_lockfd >= 0)
1N/A (void) close(map->map_lockfd);
1N/A# endif /* !LOCK_ON_OPEN */
1N/A errno = save_errno;
1N/A syserr("db_map_open(%s): file changed after open", buf);
1N/A return false;
1N/A }
1N/A
1N/A if (mode == O_RDWR)
1N/A map->map_mflags |= MF_LOCKED;
1N/A# if LOCK_ON_OPEN
1N/A if (fd >= 0 && mode == O_RDONLY)
1N/A {
1N/A (void) lockfile(fd, buf, NULL, LOCK_UN);
1N/A }
1N/A# endif /* LOCK_ON_OPEN */
1N/A
1N/A /* try to make sure that at least the database header is on disk */
1N/A if (mode == O_RDWR)
1N/A {
1N/A (void) db->sync(db, 0);
1N/A if (geteuid() == 0 && TrustedUid != 0)
1N/A {
1N/A# if HASFCHOWN
1N/A if (fchown(fd, TrustedUid, -1) < 0)
1N/A {
1N/A int err = errno;
1N/A
1N/A sm_syslog(LOG_ALERT, NOQID,
1N/A "ownership change on %s failed: %s",
1N/A buf, sm_errstring(err));
1N/A message("050 ownership change on %s failed: %s",
1N/A buf, sm_errstring(err));
1N/A }
1N/A# else /* HASFCHOWN */
1N/A sm_syslog(LOG_ALERT, NOQID,
1N/A "no fchown(): cannot change ownership on %s",
1N/A map->map_file);
1N/A message("050 no fchown(): cannot change ownership on %s",
1N/A map->map_file);
1N/A# endif /* HASFCHOWN */
1N/A }
1N/A }
1N/A
1N/A map->map_db2 = (ARBPTR_T) db;
1N/A
1N/A /*
1N/A ** Need to set map_mtime before the call to aliaswait()
1N/A ** as aliaswait() will call map_lookup() which requires
1N/A ** map_mtime to be set
1N/A */
1N/A
1N/A if (fd >= 0 && fstat(fd, &st) >= 0)
1N/A map->map_mtime = st.st_mtime;
1N/A
1N/A if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
1N/A !aliaswait(map, ".db", true))
1N/A return false;
1N/A return true;
1N/A}
1N/A
1N/A
1N/A/*
1N/A** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
1N/A*/
1N/A
1N/Achar *
1N/Adb_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A DBT key, val;
1N/A register DB *db = (DB *) map->map_db2;
1N/A int i;
1N/A int st;
1N/A int save_errno;
1N/A int fd;
1N/A struct stat stbuf;
1N/A char keybuf[MAXNAME + 1];
1N/A char buf[MAXPATHLEN];
1N/A
1N/A memset(&key, '\0', sizeof(key));
1N/A memset(&val, '\0', sizeof(val));
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("db_map_lookup(%s, %s)\n",
1N/A map->map_mname, name);
1N/A
1N/A if (sm_strlcpy(buf, map->map_file, sizeof(buf)) >= sizeof(buf))
1N/A {
1N/A errno = 0;
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("map \"%s\": map file %s name too long",
1N/A map->map_mname, map->map_file);
1N/A return NULL;
1N/A }
1N/A i = strlen(buf);
1N/A if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
1N/A buf[i - 3] = '\0';
1N/A
1N/A key.size = strlen(name);
1N/A if (key.size > sizeof(keybuf) - 1)
1N/A key.size = sizeof(keybuf) - 1;
1N/A key.data = keybuf;
1N/A memmove(keybuf, name, key.size);
1N/A keybuf[key.size] = '\0';
1N/A if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1N/A makelower(keybuf);
1N/A lockdb:
1N/A# if DB_VERSION_MAJOR < 2
1N/A fd = db->fd(db);
1N/A# else /* DB_VERSION_MAJOR < 2 */
1N/A fd = -1;
1N/A errno = db->fd(db, &fd);
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1N/A (void) lockfile(fd, buf, ".db", LOCK_SH);
1N/A if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
1N/A {
1N/A /* Reopen the database to sync the cache */
1N/A int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
1N/A : O_RDONLY;
1N/A
1N/A if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1N/A (void) lockfile(fd, buf, ".db", LOCK_UN);
1N/A map->map_mflags |= MF_CLOSING;
1N/A map->map_class->map_close(map);
1N/A map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1N/A if (map->map_class->map_open(map, omode))
1N/A {
1N/A map->map_mflags |= MF_OPEN;
1N/A map->map_pid = CurrentPid;
1N/A if ((omode & O_ACCMODE) == O_RDWR)
1N/A map->map_mflags |= MF_WRITABLE;
1N/A db = (DB *) map->map_db2;
1N/A goto lockdb;
1N/A }
1N/A else
1N/A {
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A {
1N/A extern MAPCLASS BogusMapClass;
1N/A
1N/A *statp = EX_TEMPFAIL;
1N/A map->map_orgclass = map->map_class;
1N/A map->map_class = &BogusMapClass;
1N/A map->map_mflags |= MF_OPEN;
1N/A map->map_pid = CurrentPid;
1N/A syserr("Cannot reopen DB database %s",
1N/A map->map_file);
1N/A }
1N/A return NULL;
1N/A }
1N/A }
1N/A
1N/A st = 1;
1N/A if (bitset(MF_TRY0NULL, map->map_mflags))
1N/A {
1N/A# if DB_VERSION_MAJOR < 2
1N/A st = db->get(db, &key, &val, 0);
1N/A# else /* DB_VERSION_MAJOR < 2 */
1N/A errno = db->get(db, NULL, &key, &val, 0);
1N/A switch (errno)
1N/A {
1N/A case DB_NOTFOUND:
1N/A case DB_KEYEMPTY:
1N/A st = 1;
1N/A break;
1N/A
1N/A case 0:
1N/A st = 0;
1N/A break;
1N/A
1N/A default:
1N/A st = -1;
1N/A break;
1N/A }
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A if (st == 0)
1N/A map->map_mflags &= ~MF_TRY1NULL;
1N/A }
1N/A if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
1N/A {
1N/A key.size++;
1N/A# if DB_VERSION_MAJOR < 2
1N/A st = db->get(db, &key, &val, 0);
1N/A# else /* DB_VERSION_MAJOR < 2 */
1N/A errno = db->get(db, NULL, &key, &val, 0);
1N/A switch (errno)
1N/A {
1N/A case DB_NOTFOUND:
1N/A case DB_KEYEMPTY:
1N/A st = 1;
1N/A break;
1N/A
1N/A case 0:
1N/A st = 0;
1N/A break;
1N/A
1N/A default:
1N/A st = -1;
1N/A break;
1N/A }
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A if (st == 0)
1N/A map->map_mflags &= ~MF_TRY0NULL;
1N/A }
1N/A save_errno = errno;
1N/A if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
1N/A (void) lockfile(fd, buf, ".db", LOCK_UN);
1N/A if (st != 0)
1N/A {
1N/A errno = save_errno;
1N/A if (st < 0)
1N/A syserr("db_map_lookup: get (%s)", name);
1N/A return NULL;
1N/A }
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A return map_rewrite(map, name, strlen(name), NULL);
1N/A else
1N/A return map_rewrite(map, val.data, val.size, av);
1N/A}
1N/A
1N/A
1N/A/*
1N/A** DB_MAP_STORE -- store a datum in the NEWDB database
1N/A*/
1N/A
1N/Avoid
1N/Adb_map_store(map, lhs, rhs)
1N/A register MAP *map;
1N/A char *lhs;
1N/A char *rhs;
1N/A{
1N/A int status;
1N/A DBT key;
1N/A DBT data;
1N/A register DB *db = map->map_db2;
1N/A char keybuf[MAXNAME + 1];
1N/A
1N/A memset(&key, '\0', sizeof(key));
1N/A memset(&data, '\0', sizeof(data));
1N/A
1N/A if (tTd(38, 12))
1N/A sm_dprintf("db_map_store(%s, %s, %s)\n",
1N/A map->map_mname, lhs, rhs);
1N/A
1N/A key.size = strlen(lhs);
1N/A key.data = lhs;
1N/A if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1N/A {
1N/A if (key.size > sizeof(keybuf) - 1)
1N/A key.size = sizeof(keybuf) - 1;
1N/A memmove(keybuf, key.data, key.size);
1N/A keybuf[key.size] = '\0';
1N/A makelower(keybuf);
1N/A key.data = keybuf;
1N/A }
1N/A
1N/A data.size = strlen(rhs);
1N/A data.data = rhs;
1N/A
1N/A if (bitset(MF_INCLNULL, map->map_mflags))
1N/A {
1N/A key.size++;
1N/A data.size++;
1N/A }
1N/A
1N/A# if DB_VERSION_MAJOR < 2
1N/A status = db->put(db, &key, &data, R_NOOVERWRITE);
1N/A# else /* DB_VERSION_MAJOR < 2 */
1N/A errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
1N/A switch (errno)
1N/A {
1N/A case DB_KEYEXIST:
1N/A status = 1;
1N/A break;
1N/A
1N/A case 0:
1N/A status = 0;
1N/A break;
1N/A
1N/A default:
1N/A status = -1;
1N/A break;
1N/A }
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A if (status > 0)
1N/A {
1N/A if (!bitset(MF_APPEND, map->map_mflags))
1N/A message("050 Warning: duplicate alias name %s", lhs);
1N/A else
1N/A {
1N/A static char *buf = NULL;
1N/A static int bufsiz = 0;
1N/A DBT old;
1N/A
1N/A memset(&old, '\0', sizeof(old));
1N/A
1N/A old.data = db_map_lookup(map, key.data,
1N/A (char **) NULL, &status);
1N/A if (old.data != NULL)
1N/A {
1N/A old.size = strlen(old.data);
1N/A if (data.size + old.size + 2 > (size_t) bufsiz)
1N/A {
1N/A if (buf != NULL)
1N/A sm_free(buf);
1N/A bufsiz = data.size + old.size + 2;
1N/A buf = sm_pmalloc_x(bufsiz);
1N/A }
1N/A (void) sm_strlcpyn(buf, bufsiz, 3,
1N/A (char *) data.data, ",",
1N/A (char *) old.data);
1N/A data.size = data.size + old.size + 1;
1N/A data.data = buf;
1N/A if (tTd(38, 9))
1N/A sm_dprintf("db_map_store append=%s\n",
1N/A (char *) data.data);
1N/A }
1N/A }
1N/A# if DB_VERSION_MAJOR < 2
1N/A status = db->put(db, &key, &data, 0);
1N/A# else /* DB_VERSION_MAJOR < 2 */
1N/A status = errno = db->put(db, NULL, &key, &data, 0);
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A }
1N/A if (status != 0)
1N/A syserr("readaliases: db put (%s)", lhs);
1N/A}
1N/A
1N/A
1N/A/*
1N/A** DB_MAP_CLOSE -- add distinguished entries and close the database
1N/A*/
1N/A
1N/Avoid
1N/Adb_map_close(map)
1N/A MAP *map;
1N/A{
1N/A register DB *db = map->map_db2;
1N/A
1N/A if (tTd(38, 9))
1N/A sm_dprintf("db_map_close(%s, %s, %lx)\n",
1N/A map->map_mname, map->map_file, map->map_mflags);
1N/A
1N/A if (bitset(MF_WRITABLE, map->map_mflags))
1N/A {
1N/A /* write out the distinguished alias */
1N/A db_map_store(map, "@", "@");
1N/A }
1N/A
1N/A (void) db->sync(db, 0);
1N/A
1N/A# if !LOCK_ON_OPEN
1N/A if (map->map_lockfd >= 0)
1N/A (void) close(map->map_lockfd);
1N/A# endif /* !LOCK_ON_OPEN */
1N/A
1N/A# if DB_VERSION_MAJOR < 2
1N/A if (db->close(db) != 0)
1N/A# else /* DB_VERSION_MAJOR < 2 */
1N/A /*
1N/A ** Berkeley DB can use internal shared memory
1N/A ** locking for its memory pool. Closing a map
1N/A ** opened by another process will interfere
1N/A ** with the shared memory and locks of the parent
1N/A ** process leaving things in a bad state.
1N/A */
1N/A
1N/A /*
1N/A ** If this map was not opened by the current
1N/A ** process, do not close the map but recover
1N/A ** the file descriptor.
1N/A */
1N/A
1N/A if (map->map_pid != CurrentPid)
1N/A {
1N/A int fd = -1;
1N/A
1N/A errno = db->fd(db, &fd);
1N/A if (fd >= 0)
1N/A (void) close(fd);
1N/A return;
1N/A }
1N/A
1N/A if ((errno = db->close(db, 0)) != 0)
1N/A# endif /* DB_VERSION_MAJOR < 2 */
1N/A syserr("db_map_close(%s, %s, %lx): db close failure",
1N/A map->map_mname, map->map_file, map->map_mflags);
1N/A}
1N/A#endif /* NEWDB */
1N/A/*
1N/A** NIS Modules
1N/A*/
1N/A
1N/A#if NIS
1N/A
1N/A# ifndef YPERR_BUSY
1N/A# define YPERR_BUSY 16
1N/A# endif /* ! YPERR_BUSY */
1N/A
1N/A/*
1N/A** NIS_MAP_OPEN -- open DBM map
1N/A*/
1N/A
1N/Abool
1N/Anis_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A int yperr;
1N/A register char *p;
1N/A auto char *vp;
1N/A auto int vsize;
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("nis_map_open(%s, %s, %d)\n",
1N/A map->map_mname, map->map_file, mode);
1N/A
1N/A mode &= O_ACCMODE;
1N/A if (mode != O_RDONLY)
1N/A {
1N/A /* issue a pseudo-error message */
1N/A errno = SM_EMAPCANTWRITE;
1N/A return false;
1N/A }
1N/A
1N/A p = strchr(map->map_file, '@');
1N/A if (p != NULL)
1N/A {
1N/A *p++ = '\0';
1N/A if (*p != '\0')
1N/A map->map_domain = p;
1N/A }
1N/A
1N/A if (*map->map_file == '\0')
1N/A map->map_file = "mail.aliases";
1N/A
1N/A if (map->map_domain == NULL)
1N/A {
1N/A yperr = yp_get_default_domain(&map->map_domain);
1N/A if (yperr != 0)
1N/A {
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("451 4.3.5 NIS map %s specified, but NIS not running",
1N/A map->map_file);
1N/A return false;
1N/A }
1N/A }
1N/A
1N/A /* check to see if this map actually exists */
1N/A vp = NULL;
1N/A yperr = yp_match(map->map_domain, map->map_file, "@", 1,
1N/A &vp, &vsize);
1N/A if (tTd(38, 10))
1N/A sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
1N/A map->map_domain, map->map_file, yperr_string(yperr));
1N/A if (vp != NULL)
1N/A sm_free(vp);
1N/A
1N/A if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
1N/A {
1N/A /*
1N/A ** We ought to be calling aliaswait() here if this is an
1N/A ** alias file, but powerful HP-UX NIS servers apparently
1N/A ** don't insert the @:@ token into the alias map when it
1N/A ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
1N/A */
1N/A
1N/A# if 0
1N/A if (!bitset(MF_ALIAS, map->map_mflags) ||
1N/A aliaswait(map, NULL, true))
1N/A# endif /* 0 */
1N/A return true;
1N/A }
1N/A
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A {
1N/A syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
1N/A map->map_file, map->map_domain, yperr_string(yperr));
1N/A }
1N/A
1N/A return false;
1N/A}
1N/A
1N/A
1N/A/*
1N/A** NIS_MAP_LOOKUP -- look up a datum in a NIS map
1N/A*/
1N/A
1N/A/* ARGSUSED3 */
1N/Achar *
1N/Anis_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A char *vp;
1N/A auto int vsize;
1N/A int buflen;
1N/A int yperr;
1N/A char keybuf[MAXNAME + 1];
1N/A char *SM_NONVOLATILE result = NULL;
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("nis_map_lookup(%s, %s)\n",
1N/A map->map_mname, name);
1N/A
1N/A buflen = strlen(name);
1N/A if (buflen > sizeof(keybuf) - 1)
1N/A buflen = sizeof(keybuf) - 1;
1N/A memmove(keybuf, name, buflen);
1N/A keybuf[buflen] = '\0';
1N/A if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1N/A makelower(keybuf);
1N/A yperr = YPERR_KEY;
1N/A vp = NULL;
1N/A if (bitset(MF_TRY0NULL, map->map_mflags))
1N/A {
1N/A yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1N/A &vp, &vsize);
1N/A if (yperr == 0)
1N/A map->map_mflags &= ~MF_TRY1NULL;
1N/A }
1N/A if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
1N/A {
1N/A SM_FREE_CLR(vp);
1N/A buflen++;
1N/A yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
1N/A &vp, &vsize);
1N/A if (yperr == 0)
1N/A map->map_mflags &= ~MF_TRY0NULL;
1N/A }
1N/A if (yperr != 0)
1N/A {
1N/A if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
1N/A map->map_mflags &= ~(MF_VALID|MF_OPEN);
1N/A if (vp != NULL)
1N/A sm_free(vp);
1N/A return NULL;
1N/A }
1N/A SM_TRY
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A result = map_rewrite(map, name, strlen(name), NULL);
1N/A else
1N/A result = map_rewrite(map, vp, vsize, av);
1N/A SM_FINALLY
1N/A if (vp != NULL)
1N/A sm_free(vp);
1N/A SM_END_TRY
1N/A return result;
1N/A}
1N/A
1N/A
1N/A/*
1N/A** NIS_GETCANONNAME -- look up canonical name in NIS
1N/A*/
1N/A
1N/Astatic bool
1N/Anis_getcanonname(name, hbsize, statp)
1N/A char *name;
1N/A int hbsize;
1N/A int *statp;
1N/A{
1N/A char *vp;
1N/A auto int vsize;
1N/A int keylen;
1N/A int yperr;
1N/A static bool try0null = true;
1N/A static bool try1null = true;
1N/A static char *yp_domain = NULL;
1N/A char host_record[MAXLINE];
1N/A char cbuf[MAXNAME];
1N/A char nbuf[MAXNAME + 1];
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("nis_getcanonname(%s)\n", name);
1N/A
1N/A if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
1N/A {
1N/A *statp = EX_UNAVAILABLE;
1N/A return false;
1N/A }
1N/A (void) shorten_hostname(nbuf);
1N/A keylen = strlen(nbuf);
1N/A
1N/A if (yp_domain == NULL)
1N/A (void) yp_get_default_domain(&yp_domain);
1N/A makelower(nbuf);
1N/A yperr = YPERR_KEY;
1N/A vp = NULL;
1N/A if (try0null)
1N/A {
1N/A yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
1N/A &vp, &vsize);
1N/A if (yperr == 0)
1N/A try1null = false;
1N/A }
1N/A if (yperr == YPERR_KEY && try1null)
1N/A {
1N/A SM_FREE_CLR(vp);
1N/A keylen++;
1N/A yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
1N/A &vp, &vsize);
1N/A if (yperr == 0)
1N/A try0null = false;
1N/A }
1N/A if (yperr != 0)
1N/A {
1N/A if (yperr == YPERR_KEY)
1N/A *statp = EX_NOHOST;
1N/A else if (yperr == YPERR_BUSY)
1N/A *statp = EX_TEMPFAIL;
1N/A else
1N/A *statp = EX_UNAVAILABLE;
1N/A if (vp != NULL)
1N/A sm_free(vp);
1N/A return false;
1N/A }
1N/A (void) sm_strlcpy(host_record, vp, sizeof(host_record));
1N/A sm_free(vp);
1N/A if (tTd(38, 44))
1N/A sm_dprintf("got record `%s'\n", host_record);
1N/A vp = strpbrk(host_record, "#\n");
1N/A if (vp != NULL)
1N/A *vp = '\0';
1N/A if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof(cbuf)))
1N/A {
1N/A /* this should not happen, but.... */
1N/A *statp = EX_NOHOST;
1N/A return false;
1N/A }
1N/A if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
1N/A {
1N/A *statp = EX_UNAVAILABLE;
1N/A return false;
1N/A }
1N/A *statp = EX_OK;
1N/A return true;
1N/A}
1N/A
1N/A#endif /* NIS */
1N/A/*
1N/A** NISPLUS Modules
1N/A**
1N/A** This code donated by Sun Microsystems.
1N/A*/
1N/A
1N/A#if NISPLUS
1N/A
1N/A# undef NIS /* symbol conflict in nis.h */
1N/A# undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
1N/A# include <rpcsvc/nis.h>
1N/A# include <rpcsvc/nislib.h>
1N/A
1N/A# define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
1N/A# define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
1N/A# define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
1N/A# define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
1N/A
1N/A/*
1N/A** NISPLUS_MAP_OPEN -- open nisplus table
1N/A*/
1N/A
1N/Abool
1N/Anisplus_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A nis_result *res = NULL;
1N/A int retry_cnt, max_col, i;
1N/A char qbuf[MAXLINE + NIS_MAXNAMELEN];
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
1N/A map->map_mname, map->map_file, mode);
1N/A
1N/A mode &= O_ACCMODE;
1N/A if (mode != O_RDONLY)
1N/A {
1N/A errno = EPERM;
1N/A return false;
1N/A }
1N/A
1N/A if (*map->map_file == '\0')
1N/A map->map_file = "mail_aliases.org_dir";
1N/A
1N/A if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
1N/A {
1N/A /* set default NISPLUS Domain to $m */
1N/A map->map_domain = newstr(nisplus_default_domain());
1N/A if (tTd(38, 2))
1N/A sm_dprintf("nisplus_map_open(%s): using domain %s\n",
1N/A map->map_file, map->map_domain);
1N/A }
1N/A if (!PARTIAL_NAME(map->map_file))
1N/A {
1N/A map->map_domain = newstr("");
1N/A (void) sm_strlcpy(qbuf, map->map_file, sizeof(qbuf));
1N/A }
1N/A else
1N/A {
1N/A /* check to see if this map actually exists */
1N/A (void) sm_strlcpyn(qbuf, sizeof(qbuf), 3,
1N/A map->map_file, ".", map->map_domain);
1N/A }
1N/A
1N/A retry_cnt = 0;
1N/A while (res == NULL || res->status != NIS_SUCCESS)
1N/A {
1N/A res = nis_lookup(qbuf, FOLLOW_LINKS);
1N/A switch (res->status)
1N/A {
1N/A case NIS_SUCCESS:
1N/A break;
1N/A
1N/A case NIS_TRYAGAIN:
1N/A case NIS_RPCERROR:
1N/A case NIS_NAMEUNREACHABLE:
1N/A if (retry_cnt++ > 4)
1N/A {
1N/A errno = EAGAIN;
1N/A return false;
1N/A }
1N/A /* try not to overwhelm hosed server */
1N/A sleep(2);
1N/A break;
1N/A
1N/A default: /* all other nisplus errors */
1N/A# if 0
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("451 4.3.5 Cannot find table %s.%s: %s",
1N/A map->map_file, map->map_domain,
1N/A nis_sperrno(res->status));
1N/A# endif /* 0 */
1N/A errno = EAGAIN;
1N/A return false;
1N/A }
1N/A }
1N/A
1N/A if (NIS_RES_NUMOBJ(res) != 1 ||
1N/A (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
1N/A {
1N/A if (tTd(38, 10))
1N/A sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf);
1N/A# if 0
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("451 4.3.5 %s.%s: %s is not a table",
1N/A map->map_file, map->map_domain,
1N/A nis_sperrno(res->status));
1N/A# endif /* 0 */
1N/A errno = EBADF;
1N/A return false;
1N/A }
1N/A /* default key column is column 0 */
1N/A if (map->map_keycolnm == NULL)
1N/A map->map_keycolnm = newstr(COL_NAME(res,0));
1N/A
1N/A max_col = COL_MAX(res);
1N/A
1N/A /* verify the key column exist */
1N/A for (i = 0; i < max_col; i++)
1N/A {
1N/A if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0)
1N/A break;
1N/A }
1N/A if (i == max_col)
1N/A {
1N/A if (tTd(38, 2))
1N/A sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
1N/A map->map_file, map->map_keycolnm);
1N/A errno = ENOENT;
1N/A return false;
1N/A }
1N/A
1N/A /* default value column is the last column */
1N/A if (map->map_valcolnm == NULL)
1N/A {
1N/A map->map_valcolno = max_col - 1;
1N/A return true;
1N/A }
1N/A
1N/A for (i = 0; i< max_col; i++)
1N/A {
1N/A if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
1N/A {
1N/A map->map_valcolno = i;
1N/A return true;
1N/A }
1N/A }
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
1N/A map->map_file, map->map_keycolnm);
1N/A errno = ENOENT;
1N/A return false;
1N/A}
1N/A
1N/A
1N/A/*
1N/A** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
1N/A*/
1N/A
1N/Achar *
1N/Anisplus_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A char *p;
1N/A auto int vsize;
1N/A char *skp;
1N/A int skleft;
1N/A char search_key[MAXNAME + 4];
1N/A char qbuf[MAXLINE + NIS_MAXNAMELEN];
1N/A nis_result *result;
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("nisplus_map_lookup(%s, %s)\n",
1N/A map->map_mname, name);
1N/A
1N/A if (!bitset(MF_OPEN, map->map_mflags))
1N/A {
1N/A if (nisplus_map_open(map, O_RDONLY))
1N/A {
1N/A map->map_mflags |= MF_OPEN;
1N/A map->map_pid = CurrentPid;
1N/A }
1N/A else
1N/A {
1N/A *statp = EX_UNAVAILABLE;
1N/A return NULL;
1N/A }
1N/A }
1N/A
1N/A /*
1N/A ** Copy the name to the key buffer, escaping double quote characters
1N/A ** by doubling them and quoting "]" and "," to avoid having the
1N/A ** NIS+ parser choke on them.
1N/A */
1N/A
1N/A skleft = sizeof(search_key) - 4;
1N/A skp = search_key;
1N/A for (p = name; *p != '\0' && skleft > 0; p++)
1N/A {
1N/A switch (*p)
1N/A {
1N/A case ']':
1N/A case ',':
1N/A /* quote the character */
1N/A *skp++ = '"';
1N/A *skp++ = *p;
1N/A *skp++ = '"';
1N/A skleft -= 3;
1N/A break;
1N/A
1N/A case '"':
1N/A /* double the quote */
1N/A *skp++ = '"';
1N/A skleft--;
1N/A /* FALLTHROUGH */
1N/A
1N/A default:
1N/A *skp++ = *p;
1N/A skleft--;
1N/A break;
1N/A }
1N/A }
1N/A *skp = '\0';
1N/A if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1N/A makelower(search_key);
1N/A
1N/A /* construct the query */
1N/A if (PARTIAL_NAME(map->map_file))
1N/A (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s.%s",
1N/A map->map_keycolnm, search_key, map->map_file,
1N/A map->map_domain);
1N/A else
1N/A (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s",
1N/A map->map_keycolnm, search_key, map->map_file);
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("qbuf=%s\n", qbuf);
1N/A result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
1N/A if (result->status == NIS_SUCCESS)
1N/A {
1N/A int count;
1N/A char *str;
1N/A
1N/A if ((count = NIS_RES_NUMOBJ(result)) != 1)
1N/A {
1N/A if (LogLevel > 10)
1N/A sm_syslog(LOG_WARNING, CurEnv->e_id,
1N/A "%s: lookup error, expected 1 entry, got %d",
1N/A map->map_file, count);
1N/A
1N/A /* ignore second entry */
1N/A if (tTd(38, 20))
1N/A sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
1N/A name, count);
1N/A }
1N/A
1N/A p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
1N/A /* set the length of the result */
1N/A if (p == NULL)
1N/A p = "";
1N/A vsize = strlen(p);
1N/A if (tTd(38, 20))
1N/A sm_dprintf("nisplus_map_lookup(%s), found %s\n",
1N/A name, p);
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A str = map_rewrite(map, name, strlen(name), NULL);
1N/A else
1N/A str = map_rewrite(map, p, vsize, av);
1N/A nis_freeresult(result);
1N/A *statp = EX_OK;
1N/A return str;
1N/A }
1N/A else
1N/A {
1N/A if (result->status == NIS_NOTFOUND)
1N/A *statp = EX_NOTFOUND;
1N/A else if (result->status == NIS_TRYAGAIN)
1N/A *statp = EX_TEMPFAIL;
1N/A else
1N/A {
1N/A *statp = EX_UNAVAILABLE;
1N/A map->map_mflags &= ~(MF_VALID|MF_OPEN);
1N/A }
1N/A }
1N/A if (tTd(38, 20))
1N/A sm_dprintf("nisplus_map_lookup(%s), failed\n", name);
1N/A nis_freeresult(result);
1N/A return NULL;
1N/A}
1N/A
1N/A
1N/A
1N/A/*
1N/A** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
1N/A*/
1N/A
1N/Astatic bool
1N/Anisplus_getcanonname(name, hbsize, statp)
1N/A char *name;
1N/A int hbsize;
1N/A int *statp;
1N/A{
1N/A char *vp;
1N/A auto int vsize;
1N/A nis_result *result;
1N/A char *p;
1N/A char nbuf[MAXNAME + 1];
1N/A char qbuf[MAXLINE + NIS_MAXNAMELEN];
1N/A
1N/A if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
1N/A {
1N/A *statp = EX_UNAVAILABLE;
1N/A return false;
1N/A }
1N/A (void) shorten_hostname(nbuf);
1N/A
1N/A p = strchr(nbuf, '.');
1N/A if (p == NULL)
1N/A {
1N/A /* single token */
1N/A (void) sm_snprintf(qbuf, sizeof(qbuf),
1N/A "[name=%s],hosts.org_dir", nbuf);
1N/A }
1N/A else if (p[1] != '\0')
1N/A {
1N/A /* multi token -- take only first token in nbuf */
1N/A *p = '\0';
1N/A (void) sm_snprintf(qbuf, sizeof(qbuf),
1N/A "[name=%s],hosts.org_dir.%s", nbuf, &p[1]);
1N/A }
1N/A else
1N/A {
1N/A *statp = EX_NOHOST;
1N/A return false;
1N/A }
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
1N/A name, qbuf);
1N/A
1N/A result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
1N/A NULL, NULL);
1N/A
1N/A if (result->status == NIS_SUCCESS)
1N/A {
1N/A int count;
1N/A char *domain;
1N/A
1N/A if ((count = NIS_RES_NUMOBJ(result)) != 1)
1N/A {
1N/A if (LogLevel > 10)
1N/A sm_syslog(LOG_WARNING, CurEnv->e_id,
1N/A "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
1N/A count);
1N/A
1N/A /* ignore second entry */
1N/A if (tTd(38, 20))
1N/A sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
1N/A name, count);
1N/A }
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
1N/A name, (NIS_RES_OBJECT(result))->zo_domain);
1N/A
1N/A
1N/A vp = ((NIS_RES_OBJECT(result))->EN_col(0));
1N/A vsize = strlen(vp);
1N/A if (tTd(38, 20))
1N/A sm_dprintf("nisplus_getcanonname(%s), found %s\n",
1N/A name, vp);
1N/A if (strchr(vp, '.') != NULL)
1N/A {
1N/A domain = "";
1N/A }
1N/A else
1N/A {
1N/A domain = macvalue('m', CurEnv);
1N/A if (domain == NULL)
1N/A domain = "";
1N/A }
1N/A if (hbsize > vsize + (int) strlen(domain) + 1)
1N/A {
1N/A if (domain[0] == '\0')
1N/A (void) sm_strlcpy(name, vp, hbsize);
1N/A else
1N/A (void) sm_snprintf(name, hbsize,
1N/A "%s.%s", vp, domain);
1N/A *statp = EX_OK;
1N/A }
1N/A else
1N/A *statp = EX_NOHOST;
1N/A nis_freeresult(result);
1N/A return true;
1N/A }
1N/A else
1N/A {
1N/A if (result->status == NIS_NOTFOUND)
1N/A *statp = EX_NOHOST;
1N/A else if (result->status == NIS_TRYAGAIN)
1N/A *statp = EX_TEMPFAIL;
1N/A else
1N/A *statp = EX_UNAVAILABLE;
1N/A }
1N/A if (tTd(38, 20))
1N/A sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
1N/A name, result->status, *statp);
1N/A nis_freeresult(result);
1N/A return false;
1N/A}
1N/A
1N/Achar *
1N/Anisplus_default_domain()
1N/A{
1N/A static char default_domain[MAXNAME + 1] = "";
1N/A char *p;
1N/A
1N/A if (default_domain[0] != '\0')
1N/A return default_domain;
1N/A
1N/A p = nis_local_directory();
1N/A (void) sm_strlcpy(default_domain, p, sizeof(default_domain));
1N/A return default_domain;
1N/A}
1N/A
1N/A#endif /* NISPLUS */
1N/A/*
1N/A** LDAP Modules
1N/A*/
1N/A
1N/A/*
1N/A** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
1N/A*/
1N/A
1N/A#if defined(LDAPMAP) || defined(PH_MAP)
1N/A
1N/A# if PH_MAP
1N/A# define ph_map_dequote ldapmap_dequote
1N/A# endif /* PH_MAP */
1N/A
1N/Astatic char *ldapmap_dequote __P((char *));
1N/A
1N/Astatic char *
1N/Aldapmap_dequote(str)
1N/A char *str;
1N/A{
1N/A char *p;
1N/A char *start;
1N/A
1N/A if (str == NULL)
1N/A return NULL;
1N/A
1N/A p = str;
1N/A if (*p == '"')
1N/A {
1N/A /* Should probably swallow initial whitespace here */
1N/A start = ++p;
1N/A }
1N/A else
1N/A return str;
1N/A while (*p != '"' && *p != '\0')
1N/A p++;
1N/A if (*p != '\0')
1N/A *p = '\0';
1N/A return start;
1N/A}
1N/A#endif /* defined(LDAPMAP) || defined(PH_MAP) */
1N/A
1N/A#if LDAPMAP
1N/A
1N/Astatic SM_LDAP_STRUCT *LDAPDefaults = NULL;
1N/A
1N/A/*
1N/A** LDAPMAP_OPEN -- open LDAP map
1N/A**
1N/A** Connect to the LDAP server. Re-use existing connections since a
1N/A** single server connection to a host (with the same host, port,
1N/A** bind DN, and secret) can answer queries for multiple maps.
1N/A*/
1N/A
1N/Abool
1N/Aldapmap_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A SM_LDAP_STRUCT *lmap;
1N/A STAB *s;
1N/A char *id;
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode);
1N/A
1N/A#if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
1N/A HASLDAPGETALIASBYNAME
1N/A if (VendorCode == VENDOR_SUN &&
1N/A strcmp(map->map_mname, "aliases.ldap") == 0)
1N/A {
1N/A return true;
1N/A }
1N/A#endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
1N/A
1N/A mode &= O_ACCMODE;
1N/A
1N/A /* sendmail doesn't have the ability to write to LDAP (yet) */
1N/A if (mode != O_RDONLY)
1N/A {
1N/A /* issue a pseudo-error message */
1N/A errno = SM_EMAPCANTWRITE;
1N/A return false;
1N/A }
1N/A
1N/A lmap = (SM_LDAP_STRUCT *) map->map_db1;
1N/A
1N/A s = ldapmap_findconn(lmap);
1N/A if (s->s_lmap != NULL)
1N/A {
1N/A /* Already have a connection open to this LDAP server */
1N/A lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld;
1N/A lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid;
1N/A
1N/A /* Add this map as head of linked list */
1N/A lmap->ldap_next = s->s_lmap;
1N/A s->s_lmap = map;
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("using cached connection\n");
1N/A return true;
1N/A }
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("opening new connection\n");
1N/A
1N/A if (lmap->ldap_host != NULL)
1N/A id = lmap->ldap_host;
1N/A else if (lmap->ldap_uri != NULL)
1N/A id = lmap->ldap_uri;
1N/A else
1N/A id = "localhost";
1N/A
1N/A if (tTd(74, 104))
1N/A {
1N/A extern MAPCLASS NullMapClass;
1N/A
1N/A /* debug mode: don't actually open an LDAP connection */
1N/A map->map_orgclass = map->map_class;
1N/A map->map_class = &NullMapClass;
1N/A map->map_mflags |= MF_OPEN;
1N/A map->map_pid = CurrentPid;
1N/A return true;
1N/A }
1N/A
1N/A /* No connection yet, connect */
1N/A if (!sm_ldap_start(map->map_mname, lmap))
1N/A {
1N/A if (errno == ETIMEDOUT)
1N/A {
1N/A if (LogLevel > 1)
1N/A sm_syslog(LOG_NOTICE, CurEnv->e_id,
1N/A "timeout conning to LDAP server %.100s",
1N/A id);
1N/A }
1N/A
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A {
1N/A if (bitset(MF_NODEFER, map->map_mflags))
1N/A {
1N/A syserr("%s failed to %s in map %s",
1N/A# if USE_LDAP_INIT
1N/A "ldap_init/ldap_bind",
1N/A# else /* USE_LDAP_INIT */
1N/A "ldap_open",
1N/A# endif /* USE_LDAP_INIT */
1N/A id, map->map_mname);
1N/A }
1N/A else
1N/A {
1N/A syserr("451 4.3.5 %s failed to %s in map %s",
1N/A# if USE_LDAP_INIT
1N/A "ldap_init/ldap_bind",
1N/A# else /* USE_LDAP_INIT */
1N/A "ldap_open",
1N/A# endif /* USE_LDAP_INIT */
1N/A id, map->map_mname);
1N/A }
1N/A }
1N/A return false;
1N/A }
1N/A
1N/A /* Save connection for reuse */
1N/A s->s_lmap = map;
1N/A return true;
1N/A}
1N/A
1N/A/*
1N/A** LDAPMAP_CLOSE -- close ldap map
1N/A*/
1N/A
1N/Avoid
1N/Aldapmap_close(map)
1N/A MAP *map;
1N/A{
1N/A SM_LDAP_STRUCT *lmap;
1N/A STAB *s;
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("ldapmap_close(%s)\n", map->map_mname);
1N/A
1N/A lmap = (SM_LDAP_STRUCT *) map->map_db1;
1N/A
1N/A /* Check if already closed */
1N/A if (lmap->ldap_ld == NULL)
1N/A return;
1N/A
1N/A /* Close the LDAP connection */
1N/A sm_ldap_close(lmap);
1N/A
1N/A /* Mark all the maps that share the connection as closed */
1N/A s = ldapmap_findconn(lmap);
1N/A
1N/A while (s->s_lmap != NULL)
1N/A {
1N/A MAP *smap = s->s_lmap;
1N/A
1N/A if (tTd(38, 2) && smap != map)
1N/A sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
1N/A map->map_mname, smap->map_mname);
1N/A smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
1N/A lmap = (SM_LDAP_STRUCT *) smap->map_db1;
1N/A lmap->ldap_ld = NULL;
1N/A s->s_lmap = lmap->ldap_next;
1N/A lmap->ldap_next = NULL;
1N/A }
1N/A}
1N/A
1N/A# ifdef SUNET_ID
1N/A/*
1N/A** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
1N/A** This only makes sense at Stanford University.
1N/A*/
1N/A
1N/Astatic char *
1N/Asunet_id_hash(str)
1N/A char *str;
1N/A{
1N/A char *p, *p_last;
1N/A
1N/A p = str;
1N/A p_last = p;
1N/A while (*p != '\0')
1N/A {
1N/A if (isascii(*p) && (islower(*p) || isdigit(*p)))
1N/A {
1N/A *p_last = *p;
1N/A p_last++;
1N/A }
1N/A else if (isascii(*p) && isupper(*p))
1N/A {
1N/A *p_last = tolower(*p);
1N/A p_last++;
1N/A }
1N/A ++p;
1N/A }
1N/A if (*p_last != '\0')
1N/A *p_last = '\0';
1N/A return str;
1N/A}
1N/A# define SM_CONVERT_ID(str) sunet_id_hash(str)
1N/A# else /* SUNET_ID */
1N/A# define SM_CONVERT_ID(str) makelower(str)
1N/A# endif /* SUNET_ID */
1N/A
1N/A/*
1N/A** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
1N/A*/
1N/A
1N/Achar *
1N/Aldapmap_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A int flags;
1N/A int i;
1N/A int plen = 0;
1N/A int psize = 0;
1N/A int msgid;
1N/A int save_errno;
1N/A char *vp, *p;
1N/A char *result = NULL;
1N/A SM_RPOOL_T *rpool;
1N/A SM_LDAP_STRUCT *lmap = NULL;
1N/A char *argv[SM_LDAP_ARGS];
1N/A char keybuf[MAXKEY];
1N/A#if SM_LDAP_ARGS != MAX_MAP_ARGS
1N/A# ERROR _SM_LDAP_ARGS must be the same as _MAX_MAP_ARGS
1N/A#endif /* SM_LDAP_ARGS != MAX_MAP_ARGS */
1N/A
1N/A#if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
1N/A HASLDAPGETALIASBYNAME
1N/A if (VendorCode == VENDOR_SUN &&
1N/A strcmp(map->map_mname, "aliases.ldap") == 0)
1N/A {
1N/A int rc;
1N/A#if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
1N/A extern char *__getldapaliasbyname();
1N/A char *answer;
1N/A
1N/A answer = __getldapaliasbyname(name, &rc);
1N/A#else
1N/A char answer[MAXNAME + 1];
1N/A
1N/A rc = __getldapaliasbyname(name, answer, sizeof(answer));
1N/A#endif
1N/A if (rc != 0)
1N/A {
1N/A if (tTd(38, 20))
1N/A sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n",
1N/A name, errno);
1N/A *statp = EX_NOTFOUND;
1N/A return NULL;
1N/A }
1N/A *statp = EX_OK;
1N/A if (tTd(38, 20))
1N/A sm_dprintf("getldapaliasbyname(%.100s) => %s\n", name,
1N/A answer);
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A result = map_rewrite(map, name, strlen(name), NULL);
1N/A else
1N/A result = map_rewrite(map, answer, strlen(answer), av);
1N/A#if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
1N/A free(answer);
1N/A#endif
1N/A return result;
1N/A }
1N/A#endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
1N/A
1N/A /* Get ldap struct pointer from map */
1N/A lmap = (SM_LDAP_STRUCT *) map->map_db1;
1N/A sm_ldap_setopts(lmap->ldap_ld, lmap);
1N/A
1N/A if (lmap->ldap_multi_args)
1N/A {
1N/A SM_REQUIRE(av != NULL);
1N/A memset(argv, '\0', sizeof(argv));
1N/A for (i = 0; i < SM_LDAP_ARGS && av[i] != NULL; i++)
1N/A {
1N/A argv[i] = sm_strdup(av[i]);
1N/A if (argv[i] == NULL)
1N/A {
1N/A int save_errno, j;
1N/A
1N/A save_errno = errno;
1N/A for (j = 0; j < i && argv[j] != NULL; j++)
1N/A SM_FREE(argv[j]);
1N/A *statp = EX_TEMPFAIL;
1N/A errno = save_errno;
1N/A return NULL;
1N/A }
1N/A
1N/A if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1N/A SM_CONVERT_ID(av[i]);
1N/A }
1N/A }
1N/A else
1N/A {
1N/A (void) sm_strlcpy(keybuf, name, sizeof(keybuf));
1N/A
1N/A if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1N/A SM_CONVERT_ID(keybuf);
1N/A }
1N/A
1N/A if (tTd(38, 20))
1N/A {
1N/A if (lmap->ldap_multi_args)
1N/A {
1N/A sm_dprintf("ldapmap_lookup(%s, argv)\n",
1N/A map->map_mname);
1N/A for (i = 0; i < SM_LDAP_ARGS; i++)
1N/A {
1N/A sm_dprintf(" argv[%d] = %s\n", i,
1N/A argv[i] == NULL ? "NULL" : argv[i]);
1N/A }
1N/A }
1N/A else
1N/A {
1N/A sm_dprintf("ldapmap_lookup(%s, %s)\n",
1N/A map->map_mname, name);
1N/A }
1N/A }
1N/A
1N/A if (lmap->ldap_multi_args)
1N/A {
1N/A msgid = sm_ldap_search_m(lmap, argv);
1N/A
1N/A /* free the argv array and its content, no longer needed */
1N/A for (i = 0; i < SM_LDAP_ARGS && argv[i] != NULL; i++)
1N/A SM_FREE(argv[i]);
1N/A }
1N/A else
1N/A msgid = sm_ldap_search(lmap, keybuf);
1N/A if (msgid == SM_LDAP_ERR)
1N/A {
1N/A errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE;
1N/A save_errno = errno;
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A {
1N/A /*
1N/A ** Do not include keybuf as this error may be shown
1N/A ** to outsiders.
1N/A */
1N/A
1N/A if (bitset(MF_NODEFER, map->map_mflags))
1N/A syserr("Error in ldap_search in map %s",
1N/A map->map_mname);
1N/A else
1N/A syserr("451 4.3.5 Error in ldap_search in map %s",
1N/A map->map_mname);
1N/A }
1N/A *statp = EX_TEMPFAIL;
1N/A switch (save_errno - E_LDAPBASE)
1N/A {
1N/A# ifdef LDAP_SERVER_DOWN
1N/A case LDAP_SERVER_DOWN:
1N/A# endif /* LDAP_SERVER_DOWN */
1N/A case LDAP_TIMEOUT:
1N/A case LDAP_UNAVAILABLE:
1N/A /* server disappeared, try reopen on next search */
1N/A ldapmap_close(map);
1N/A break;
1N/A }
1N/A errno = save_errno;
1N/A return NULL;
1N/A }
1N/A#if SM_LDAP_ERROR_ON_MISSING_ARGS
1N/A else if (msgid == SM_LDAP_ERR_ARG_MISS)
1N/A {
1N/A if (bitset(MF_NODEFER, map->map_mflags))
1N/A syserr("Error in ldap_search in map %s, too few arguments",
1N/A map->map_mname);
1N/A else
1N/A syserr("554 5.3.5 Error in ldap_search in map %s, too few arguments",
1N/A map->map_mname);
1N/A *statp = EX_CONFIG;
1N/A return NULL;
1N/A }
1N/A#endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
1N/A
1N/A *statp = EX_NOTFOUND;
1N/A vp = NULL;
1N/A
1N/A flags = 0;
1N/A if (bitset(MF_SINGLEMATCH, map->map_mflags))
1N/A flags |= SM_LDAP_SINGLEMATCH;
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A flags |= SM_LDAP_MATCHONLY;
1N/A# if _FFR_LDAP_SINGLEDN
1N/A if (bitset(MF_SINGLEDN, map->map_mflags))
1N/A flags |= SM_LDAP_SINGLEDN;
1N/A# endif /* _FFR_LDAP_SINGLEDN */
1N/A
1N/A /* Create an rpool for search related memory usage */
1N/A rpool = sm_rpool_new_x(NULL);
1N/A
1N/A p = NULL;
1N/A *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim,
1N/A rpool, &p, &plen, &psize, NULL);
1N/A save_errno = errno;
1N/A
1N/A /* Copy result so rpool can be freed */
1N/A if (*statp == EX_OK && p != NULL)
1N/A vp = newstr(p);
1N/A sm_rpool_free(rpool);
1N/A
1N/A /* need to restart LDAP connection? */
1N/A if (*statp == EX_RESTART)
1N/A {
1N/A *statp = EX_TEMPFAIL;
1N/A ldapmap_close(map);
1N/A }
1N/A
1N/A errno = save_errno;
1N/A if (*statp != EX_OK && *statp != EX_NOTFOUND)
1N/A {
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A {
1N/A if (bitset(MF_NODEFER, map->map_mflags))
1N/A syserr("Error getting LDAP results in map %s",
1N/A map->map_mname);
1N/A else
1N/A syserr("451 4.3.5 Error getting LDAP results in map %s",
1N/A map->map_mname);
1N/A }
1N/A errno = save_errno;
1N/A return NULL;
1N/A }
1N/A
1N/A /* Did we match anything? */
1N/A if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags))
1N/A return NULL;
1N/A
1N/A if (*statp == EX_OK)
1N/A {
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_INFO, CurEnv->e_id,
1N/A "ldap %.100s => %s", name,
1N/A vp == NULL ? "<NULL>" : vp);
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A result = map_rewrite(map, name, strlen(name), NULL);
1N/A else
1N/A {
1N/A /* vp != NULL according to test above */
1N/A result = map_rewrite(map, vp, strlen(vp), av);
1N/A }
1N/A if (vp != NULL)
1N/A sm_free(vp); /* XXX */
1N/A }
1N/A return result;
1N/A}
1N/A
1N/A/*
1N/A** LDAPMAP_FINDCONN -- find an LDAP connection to the server
1N/A**
1N/A** Cache LDAP connections based on the host, port, bind DN,
1N/A** secret, and PID so we don't have multiple connections open to
1N/A** the same server for different maps. Need a separate connection
1N/A** per PID since a parent process may close the map before the
1N/A** child is done with it.
1N/A**
1N/A** Parameters:
1N/A** lmap -- LDAP map information
1N/A**
1N/A** Returns:
1N/A** Symbol table entry for the LDAP connection.
1N/A*/
1N/A
1N/Astatic STAB *
1N/Aldapmap_findconn(lmap)
1N/A SM_LDAP_STRUCT *lmap;
1N/A{
1N/A char *format;
1N/A char *nbuf;
1N/A char *id;
1N/A STAB *SM_NONVOLATILE s = NULL;
1N/A
1N/A if (lmap->ldap_host != NULL)
1N/A id = lmap->ldap_host;
1N/A else if (lmap->ldap_uri != NULL)
1N/A id = lmap->ldap_uri;
1N/A else
1N/A id = "localhost";
1N/A
1N/A format = "%s%c%d%c%d%c%s%c%s%d";
1N/A nbuf = sm_stringf_x(format,
1N/A id,
1N/A CONDELSE,
1N/A lmap->ldap_port,
1N/A CONDELSE,
1N/A lmap->ldap_version,
1N/A CONDELSE,
1N/A (lmap->ldap_binddn == NULL ? ""
1N/A : lmap->ldap_binddn),
1N/A CONDELSE,
1N/A (lmap->ldap_secret == NULL ? ""
1N/A : lmap->ldap_secret),
1N/A (int) CurrentPid);
1N/A SM_TRY
1N/A s = stab(nbuf, ST_LMAP, ST_ENTER);
1N/A SM_FINALLY
1N/A sm_free(nbuf);
1N/A SM_END_TRY
1N/A return s;
1N/A}
1N/A/*
1N/A** LDAPMAP_PARSEARGS -- parse ldap map definition args.
1N/A*/
1N/A
1N/Astatic struct lamvalues LDAPAuthMethods[] =
1N/A{
1N/A { "none", LDAP_AUTH_NONE },
1N/A { "simple", LDAP_AUTH_SIMPLE },
1N/A# ifdef LDAP_AUTH_KRBV4
1N/A { "krbv4", LDAP_AUTH_KRBV4 },
1N/A# endif /* LDAP_AUTH_KRBV4 */
1N/A { NULL, 0 }
1N/A};
1N/A
1N/Astatic struct ladvalues LDAPAliasDereference[] =
1N/A{
1N/A { "never", LDAP_DEREF_NEVER },
1N/A { "always", LDAP_DEREF_ALWAYS },
1N/A { "search", LDAP_DEREF_SEARCHING },
1N/A { "find", LDAP_DEREF_FINDING },
1N/A { NULL, 0 }
1N/A};
1N/A
1N/Astatic struct lssvalues LDAPSearchScope[] =
1N/A{
1N/A { "base", LDAP_SCOPE_BASE },
1N/A { "one", LDAP_SCOPE_ONELEVEL },
1N/A { "sub", LDAP_SCOPE_SUBTREE },
1N/A { NULL, 0 }
1N/A};
1N/A
1N/Abool
1N/Aldapmap_parseargs(map, args)
1N/A MAP *map;
1N/A char *args;
1N/A{
1N/A bool secretread = true;
1N/A bool attrssetup = false;
1N/A int i;
1N/A register char *p = args;
1N/A SM_LDAP_STRUCT *lmap;
1N/A struct lamvalues *lam;
1N/A struct ladvalues *lad;
1N/A struct lssvalues *lss;
1N/A char ldapfilt[MAXLINE];
1N/A char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD];
1N/A
1N/A /* Get ldap struct pointer from map */
1N/A lmap = (SM_LDAP_STRUCT *) map->map_db1;
1N/A
1N/A /* Check if setting the initial LDAP defaults */
1N/A if (lmap == NULL || lmap != LDAPDefaults)
1N/A {
1N/A /* We need to alloc an SM_LDAP_STRUCT struct */
1N/A lmap = (SM_LDAP_STRUCT *) xalloc(sizeof(*lmap));
1N/A if (LDAPDefaults == NULL)
1N/A sm_ldap_clear(lmap);
1N/A else
1N/A STRUCTCOPY(*LDAPDefaults, *lmap);
1N/A }
1N/A
1N/A /* there is no check whether there is really an argument */
1N/A map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
1N/A map->map_spacesub = SpaceSub; /* default value */
1N/A
1N/A /* Check if setting up an alias or file class LDAP map */
1N/A if (bitset(MF_ALIAS, map->map_mflags))
1N/A {
1N/A /* Comma separate if used as an alias file */
1N/A map->map_coldelim = ',';
1N/A if (*args == '\0')
1N/A {
1N/A int n;
1N/A char *lc;
1N/A char jbuf[MAXHOSTNAMELEN];
1N/A char lcbuf[MAXLINE];
1N/A
1N/A /* Get $j */
1N/A expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
1N/A if (jbuf[0] == '\0')
1N/A {
1N/A (void) sm_strlcpy(jbuf, "localhost",
1N/A sizeof(jbuf));
1N/A }
1N/A
1N/A lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
1N/A if (lc == NULL)
1N/A lc = "";
1N/A else
1N/A {
1N/A expand(lc, lcbuf, sizeof(lcbuf), CurEnv);
1N/A lc = lcbuf;
1N/A }
1N/A
1N/A n = sm_snprintf(ldapfilt, sizeof(ldapfilt),
1N/A "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
1N/A lc, jbuf);
1N/A if (n >= sizeof(ldapfilt))
1N/A {
1N/A syserr("%s: Default LDAP string too long",
1N/A map->map_mname);
1N/A return false;
1N/A }
1N/A
1N/A /* default args for an alias LDAP entry */
1N/A lmap->ldap_filter = ldapfilt;
1N/A lmap->ldap_attr[0] = "objectClass";
1N/A lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS;
1N/A lmap->ldap_attr_needobjclass[0] = NULL;
1N/A lmap->ldap_attr[1] = "sendmailMTAAliasValue";
1N/A lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL;
1N/A lmap->ldap_attr_needobjclass[1] = NULL;
1N/A lmap->ldap_attr[2] = "sendmailMTAAliasSearch";
1N/A lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER;
1N/A lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject";
1N/A lmap->ldap_attr[3] = "sendmailMTAAliasURL";
1N/A lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL;
1N/A lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject";
1N/A lmap->ldap_attr[4] = NULL;
1N/A lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE;
1N/A lmap->ldap_attr_needobjclass[4] = NULL;
1N/A attrssetup = true;
1N/A }
1N/A }
1N/A else if (bitset(MF_FILECLASS, map->map_mflags))
1N/A {
1N/A /* Space separate if used as a file class file */
1N/A map->map_coldelim = ' ';
1N/A }
1N/A
1N/A# if _FFR_LDAP_NETWORK_TIMEOUT
1N/A lmap->ldap_networktmo = 120;
1N/A# endif /* _FFR_LDAP_NETWORK_TIMEOUT */
1N/A
1N/A for (;;)
1N/A {
1N/A while (isascii(*p) && isspace(*p))
1N/A p++;
1N/A if (*p != '-')
1N/A break;
1N/A switch (*++p)
1N/A {
1N/A case 'A':
1N/A map->map_mflags |= MF_APPEND;
1N/A break;
1N/A
1N/A case 'a':
1N/A map->map_app = ++p;
1N/A break;
1N/A
1N/A case 'D':
1N/A map->map_mflags |= MF_DEFER;
1N/A break;
1N/A
1N/A case 'f':
1N/A map->map_mflags |= MF_NOFOLDCASE;
1N/A break;
1N/A
1N/A case 'm':
1N/A map->map_mflags |= MF_MATCHONLY;
1N/A break;
1N/A
1N/A case 'N':
1N/A map->map_mflags |= MF_INCLNULL;
1N/A map->map_mflags &= ~MF_TRY0NULL;
1N/A break;
1N/A
1N/A case 'O':
1N/A map->map_mflags &= ~MF_TRY1NULL;
1N/A break;
1N/A
1N/A case 'o':
1N/A map->map_mflags |= MF_OPTIONAL;
1N/A break;
1N/A
1N/A case 'q':
1N/A map->map_mflags |= MF_KEEPQUOTES;
1N/A break;
1N/A
1N/A case 'S':
1N/A map->map_spacesub = *++p;
1N/A break;
1N/A
1N/A case 'T':
1N/A map->map_tapp = ++p;
1N/A break;
1N/A
1N/A case 't':
1N/A map->map_mflags |= MF_NODEFER;
1N/A break;
1N/A
1N/A case 'z':
1N/A if (*++p != '\\')
1N/A map->map_coldelim = *p;
1N/A else
1N/A {
1N/A switch (*++p)
1N/A {
1N/A case 'n':
1N/A map->map_coldelim = '\n';
1N/A break;
1N/A
1N/A case 't':
1N/A map->map_coldelim = '\t';
1N/A break;
1N/A
1N/A default:
1N/A map->map_coldelim = '\\';
1N/A }
1N/A }
1N/A break;
1N/A
1N/A /* Start of ldapmap specific args */
1N/A case '1':
1N/A map->map_mflags |= MF_SINGLEMATCH;
1N/A break;
1N/A
1N/A# if _FFR_LDAP_SINGLEDN
1N/A case '2':
1N/A map->map_mflags |= MF_SINGLEDN;
1N/A break;
1N/A# endif /* _FFR_LDAP_SINGLEDN */
1N/A
1N/A case 'b': /* search base */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A lmap->ldap_base = p;
1N/A break;
1N/A
1N/A# if _FFR_LDAP_NETWORK_TIMEOUT
1N/A case 'c': /* network (connect) timeout */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A lmap->ldap_networktmo = atoi(p);
1N/A break;
1N/A# endif /* _FFR_LDAP_NETWORK_TIMEOUT */
1N/A
1N/A case 'd': /* Dn to bind to server as */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A lmap->ldap_binddn = p;
1N/A break;
1N/A
1N/A case 'H': /* Use LDAP URI */
1N/A# if !USE_LDAP_INIT
1N/A syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
1N/A map->map_mname);
1N/A return false;
1N/A# else /* !USE_LDAP_INIT */
1N/A if (lmap->ldap_host != NULL)
1N/A {
1N/A syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
1N/A map->map_mname);
1N/A return false;
1N/A }
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A lmap->ldap_uri = p;
1N/A break;
1N/A# endif /* !USE_LDAP_INIT */
1N/A
1N/A case 'h': /* ldap host */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A if (lmap->ldap_uri != NULL)
1N/A {
1N/A syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
1N/A map->map_mname);
1N/A return false;
1N/A }
1N/A lmap->ldap_host = p;
1N/A break;
1N/A
1N/A case 'K':
1N/A lmap->ldap_multi_args = true;
1N/A break;
1N/A
1N/A case 'k': /* search field */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A lmap->ldap_filter = p;
1N/A break;
1N/A
1N/A case 'l': /* time limit */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A lmap->ldap_timelimit = atoi(p);
1N/A lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit;
1N/A break;
1N/A
1N/A case 'M': /* Method for binding */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A
1N/A if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0)
1N/A p += 10;
1N/A
1N/A for (lam = LDAPAuthMethods;
1N/A lam != NULL && lam->lam_name != NULL; lam++)
1N/A {
1N/A if (sm_strncasecmp(p, lam->lam_name,
1N/A strlen(lam->lam_name)) == 0)
1N/A break;
1N/A }
1N/A if (lam->lam_name != NULL)
1N/A lmap->ldap_method = lam->lam_code;
1N/A else
1N/A {
1N/A /* bad config line */
1N/A if (!bitset(MCF_OPTFILE,
1N/A map->map_class->map_cflags))
1N/A {
1N/A char *ptr;
1N/A
1N/A if ((ptr = strchr(p, ' ')) != NULL)
1N/A *ptr = '\0';
1N/A syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
1N/A p, map->map_mname);
1N/A if (ptr != NULL)
1N/A *ptr = ' ';
1N/A return false;
1N/A }
1N/A }
1N/A break;
1N/A
1N/A case 'n': /* retrieve attribute names only */
1N/A lmap->ldap_attrsonly = LDAPMAP_TRUE;
1N/A break;
1N/A
1N/A /*
1N/A ** This is a string that is dependent on the
1N/A ** method used defined by 'M'.
1N/A */
1N/A
1N/A case 'P': /* Secret password for binding */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A lmap->ldap_secret = p;
1N/A secretread = false;
1N/A break;
1N/A
1N/A case 'p': /* ldap port */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A lmap->ldap_port = atoi(p);
1N/A break;
1N/A
1N/A /* args stolen from ldapsearch.c */
1N/A case 'R': /* don't auto chase referrals */
1N/A# ifdef LDAP_REFERRALS
1N/A lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
1N/A# else /* LDAP_REFERRALS */
1N/A syserr("compile with -DLDAP_REFERRALS for referral support");
1N/A# endif /* LDAP_REFERRALS */
1N/A break;
1N/A
1N/A case 'r': /* alias dereferencing */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A
1N/A if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0)
1N/A p += 11;
1N/A
1N/A for (lad = LDAPAliasDereference;
1N/A lad != NULL && lad->lad_name != NULL; lad++)
1N/A {
1N/A if (sm_strncasecmp(p, lad->lad_name,
1N/A strlen(lad->lad_name)) == 0)
1N/A break;
1N/A }
1N/A if (lad->lad_name != NULL)
1N/A lmap->ldap_deref = lad->lad_code;
1N/A else
1N/A {
1N/A /* bad config line */
1N/A if (!bitset(MCF_OPTFILE,
1N/A map->map_class->map_cflags))
1N/A {
1N/A char *ptr;
1N/A
1N/A if ((ptr = strchr(p, ' ')) != NULL)
1N/A *ptr = '\0';
1N/A syserr("Deref must be [never|always|search|find] (not %s) in map %s",
1N/A p, map->map_mname);
1N/A if (ptr != NULL)
1N/A *ptr = ' ';
1N/A return false;
1N/A }
1N/A }
1N/A break;
1N/A
1N/A case 's': /* search scope */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A
1N/A if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0)
1N/A p += 11;
1N/A
1N/A for (lss = LDAPSearchScope;
1N/A lss != NULL && lss->lss_name != NULL; lss++)
1N/A {
1N/A if (sm_strncasecmp(p, lss->lss_name,
1N/A strlen(lss->lss_name)) == 0)
1N/A break;
1N/A }
1N/A if (lss->lss_name != NULL)
1N/A lmap->ldap_scope = lss->lss_code;
1N/A else
1N/A {
1N/A /* bad config line */
1N/A if (!bitset(MCF_OPTFILE,
1N/A map->map_class->map_cflags))
1N/A {
1N/A char *ptr;
1N/A
1N/A if ((ptr = strchr(p, ' ')) != NULL)
1N/A *ptr = '\0';
1N/A syserr("Scope must be [base|one|sub] (not %s) in map %s",
1N/A p, map->map_mname);
1N/A if (ptr != NULL)
1N/A *ptr = ' ';
1N/A return false;
1N/A }
1N/A }
1N/A break;
1N/A
1N/A case 'V':
1N/A if (*++p != '\\')
1N/A lmap->ldap_attrsep = *p;
1N/A else
1N/A {
1N/A switch (*++p)
1N/A {
1N/A case 'n':
1N/A lmap->ldap_attrsep = '\n';
1N/A break;
1N/A
1N/A case 't':
1N/A lmap->ldap_attrsep = '\t';
1N/A break;
1N/A
1N/A default:
1N/A lmap->ldap_attrsep = '\\';
1N/A }
1N/A }
1N/A break;
1N/A
1N/A case 'v': /* attr to return */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A lmap->ldap_attr[0] = p;
1N/A lmap->ldap_attr[1] = NULL;
1N/A break;
1N/A
1N/A case 'w':
1N/A /* -w should be for passwd, -P should be for version */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A lmap->ldap_version = atoi(p);
1N/A# ifdef LDAP_VERSION_MAX
1N/A if (lmap->ldap_version > LDAP_VERSION_MAX)
1N/A {
1N/A syserr("LDAP version %d exceeds max of %d in map %s",
1N/A lmap->ldap_version, LDAP_VERSION_MAX,
1N/A map->map_mname);
1N/A return false;
1N/A }
1N/A# endif /* LDAP_VERSION_MAX */
1N/A# ifdef LDAP_VERSION_MIN
1N/A if (lmap->ldap_version < LDAP_VERSION_MIN)
1N/A {
1N/A syserr("LDAP version %d is lower than min of %d in map %s",
1N/A lmap->ldap_version, LDAP_VERSION_MIN,
1N/A map->map_mname);
1N/A return false;
1N/A }
1N/A# endif /* LDAP_VERSION_MIN */
1N/A break;
1N/A
1N/A case 'Z':
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A lmap->ldap_sizelimit = atoi(p);
1N/A break;
1N/A
1N/A default:
1N/A syserr("Illegal option %c map %s", *p, map->map_mname);
1N/A break;
1N/A }
1N/A
1N/A /* need to account for quoted strings here */
1N/A while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1N/A {
1N/A if (*p == '"')
1N/A {
1N/A while (*++p != '"' && *p != '\0')
1N/A continue;
1N/A if (*p != '\0')
1N/A p++;
1N/A }
1N/A else
1N/A p++;
1N/A }
1N/A
1N/A if (*p != '\0')
1N/A *p++ = '\0';
1N/A }
1N/A
1N/A if (map->map_app != NULL)
1N/A map->map_app = newstr(ldapmap_dequote(map->map_app));
1N/A if (map->map_tapp != NULL)
1N/A map->map_tapp = newstr(ldapmap_dequote(map->map_tapp));
1N/A
1N/A /*
1N/A ** We need to swallow up all the stuff into a struct
1N/A ** and dump it into map->map_dbptr1
1N/A */
1N/A
1N/A if (lmap->ldap_host != NULL &&
1N/A (LDAPDefaults == NULL ||
1N/A LDAPDefaults == lmap ||
1N/A LDAPDefaults->ldap_host != lmap->ldap_host))
1N/A lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host));
1N/A map->map_domain = lmap->ldap_host;
1N/A
1N/A if (lmap->ldap_uri != NULL &&
1N/A (LDAPDefaults == NULL ||
1N/A LDAPDefaults == lmap ||
1N/A LDAPDefaults->ldap_uri != lmap->ldap_uri))
1N/A lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri));
1N/A map->map_domain = lmap->ldap_uri;
1N/A
1N/A if (lmap->ldap_binddn != NULL &&
1N/A (LDAPDefaults == NULL ||
1N/A LDAPDefaults == lmap ||
1N/A LDAPDefaults->ldap_binddn != lmap->ldap_binddn))
1N/A lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn));
1N/A
1N/A if (lmap->ldap_secret != NULL &&
1N/A (LDAPDefaults == NULL ||
1N/A LDAPDefaults == lmap ||
1N/A LDAPDefaults->ldap_secret != lmap->ldap_secret))
1N/A {
1N/A SM_FILE_T *sfd;
1N/A long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES;
1N/A
1N/A if (DontLockReadFiles)
1N/A sff |= SFF_NOLOCK;
1N/A
1N/A /* need to use method to map secret to passwd string */
1N/A switch (lmap->ldap_method)
1N/A {
1N/A case LDAP_AUTH_NONE:
1N/A /* Do nothing */
1N/A break;
1N/A
1N/A case LDAP_AUTH_SIMPLE:
1N/A
1N/A /*
1N/A ** Secret is the name of a file with
1N/A ** the first line as the password.
1N/A */
1N/A
1N/A /* Already read in the secret? */
1N/A if (secretread)
1N/A break;
1N/A
1N/A sfd = safefopen(ldapmap_dequote(lmap->ldap_secret),
1N/A O_RDONLY, 0, sff);
1N/A if (sfd == NULL)
1N/A {
1N/A syserr("LDAP map: cannot open secret %s",
1N/A ldapmap_dequote(lmap->ldap_secret));
1N/A return false;
1N/A }
1N/A lmap->ldap_secret = sfgets(m_tmp, sizeof(m_tmp),
1N/A sfd, TimeOuts.to_fileopen,
1N/A "ldapmap_parseargs");
1N/A (void) sm_io_close(sfd, SM_TIME_DEFAULT);
1N/A if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD)
1N/A {
1N/A syserr("LDAP map: secret in %s too long",
1N/A ldapmap_dequote(lmap->ldap_secret));
1N/A return false;
1N/A }
1N/A if (lmap->ldap_secret != NULL &&
1N/A strlen(m_tmp) > 0)
1N/A {
1N/A /* chomp newline */
1N/A if (m_tmp[strlen(m_tmp) - 1] == '\n')
1N/A m_tmp[strlen(m_tmp) - 1] = '\0';
1N/A
1N/A lmap->ldap_secret = m_tmp;
1N/A }
1N/A break;
1N/A
1N/A# ifdef LDAP_AUTH_KRBV4
1N/A case LDAP_AUTH_KRBV4:
1N/A
1N/A /*
1N/A ** Secret is where the ticket file is
1N/A ** stashed
1N/A */
1N/A
1N/A (void) sm_snprintf(m_tmp, sizeof(m_tmp),
1N/A "KRBTKFILE=%s",
1N/A ldapmap_dequote(lmap->ldap_secret));
1N/A lmap->ldap_secret = m_tmp;
1N/A break;
1N/A# endif /* LDAP_AUTH_KRBV4 */
1N/A
1N/A default: /* Should NEVER get here */
1N/A syserr("LDAP map: Illegal value in lmap method");
1N/A return false;
1N/A /* NOTREACHED */
1N/A break;
1N/A }
1N/A }
1N/A
1N/A if (lmap->ldap_secret != NULL &&
1N/A (LDAPDefaults == NULL ||
1N/A LDAPDefaults == lmap ||
1N/A LDAPDefaults->ldap_secret != lmap->ldap_secret))
1N/A lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret));
1N/A
1N/A if (lmap->ldap_base != NULL &&
1N/A (LDAPDefaults == NULL ||
1N/A LDAPDefaults == lmap ||
1N/A LDAPDefaults->ldap_base != lmap->ldap_base))
1N/A lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base));
1N/A
1N/A /*
1N/A ** Save the server from extra work. If request is for a single
1N/A ** match, tell the server to only return enough records to
1N/A ** determine if there is a single match or not. This can not
1N/A ** be one since the server would only return one and we wouldn't
1N/A ** know if there were others available.
1N/A */
1N/A
1N/A if (bitset(MF_SINGLEMATCH, map->map_mflags))
1N/A lmap->ldap_sizelimit = 2;
1N/A
1N/A /* If setting defaults, don't process ldap_filter and ldap_attr */
1N/A if (lmap == LDAPDefaults)
1N/A return true;
1N/A
1N/A if (lmap->ldap_filter != NULL)
1N/A lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter));
1N/A else
1N/A {
1N/A if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
1N/A {
1N/A syserr("No filter given in map %s", map->map_mname);
1N/A return false;
1N/A }
1N/A }
1N/A
1N/A if (!attrssetup && lmap->ldap_attr[0] != NULL)
1N/A {
1N/A bool recurse = false;
1N/A bool normalseen = false;
1N/A
1N/A i = 0;
1N/A p = ldapmap_dequote(lmap->ldap_attr[0]);
1N/A lmap->ldap_attr[0] = NULL;
1N/A
1N/A /* Prime the attr list with the objectClass attribute */
1N/A lmap->ldap_attr[i] = "objectClass";
1N/A lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS;
1N/A lmap->ldap_attr_needobjclass[i] = NULL;
1N/A i++;
1N/A
1N/A while (p != NULL)
1N/A {
1N/A char *v;
1N/A
1N/A while (isascii(*p) && isspace(*p))
1N/A p++;
1N/A if (*p == '\0')
1N/A break;
1N/A v = p;
1N/A p = strchr(v, ',');
1N/A if (p != NULL)
1N/A *p++ = '\0';
1N/A
1N/A if (i >= LDAPMAP_MAX_ATTR)
1N/A {
1N/A syserr("Too many return attributes in %s (max %d)",
1N/A map->map_mname, LDAPMAP_MAX_ATTR);
1N/A return false;
1N/A }
1N/A if (*v != '\0')
1N/A {
1N/A int j;
1N/A int use;
1N/A char *type;
1N/A char *needobjclass;
1N/A
1N/A type = strchr(v, ':');
1N/A if (type != NULL)
1N/A {
1N/A *type++ = '\0';
1N/A needobjclass = strchr(type, ':');
1N/A if (needobjclass != NULL)
1N/A *needobjclass++ = '\0';
1N/A }
1N/A else
1N/A {
1N/A needobjclass = NULL;
1N/A }
1N/A
1N/A use = i;
1N/A
1N/A /* allow override on "objectClass" type */
1N/A if (sm_strcasecmp(v, "objectClass") == 0 &&
1N/A lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS)
1N/A {
1N/A use = 0;
1N/A }
1N/A else
1N/A {
1N/A /*
1N/A ** Don't add something to attribute
1N/A ** list twice.
1N/A */
1N/A
1N/A for (j = 1; j < i; j++)
1N/A {
1N/A if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0)
1N/A {
1N/A syserr("Duplicate attribute (%s) in %s",
1N/A v, map->map_mname);
1N/A return false;
1N/A }
1N/A }
1N/A
1N/A lmap->ldap_attr[use] = newstr(v);
1N/A if (needobjclass != NULL &&
1N/A *needobjclass != '\0' &&
1N/A *needobjclass != '*')
1N/A {
1N/A lmap->ldap_attr_needobjclass[use] = newstr(needobjclass);
1N/A }
1N/A else
1N/A {
1N/A lmap->ldap_attr_needobjclass[use] = NULL;
1N/A }
1N/A
1N/A }
1N/A
1N/A if (type != NULL && *type != '\0')
1N/A {
1N/A if (sm_strcasecmp(type, "dn") == 0)
1N/A {
1N/A recurse = true;
1N/A lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN;
1N/A }
1N/A else if (sm_strcasecmp(type, "filter") == 0)
1N/A {
1N/A recurse = true;
1N/A lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER;
1N/A }
1N/A else if (sm_strcasecmp(type, "url") == 0)
1N/A {
1N/A recurse = true;
1N/A lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL;
1N/A }
1N/A else if (sm_strcasecmp(type, "normal") == 0)
1N/A {
1N/A lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
1N/A normalseen = true;
1N/A }
1N/A else
1N/A {
1N/A syserr("Unknown attribute type (%s) in %s",
1N/A type, map->map_mname);
1N/A return false;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL;
1N/A normalseen = true;
1N/A }
1N/A i++;
1N/A }
1N/A }
1N/A lmap->ldap_attr[i] = NULL;
1N/A
1N/A /* Set in case needed in future code */
1N/A attrssetup = true;
1N/A
1N/A if (recurse && !normalseen)
1N/A {
1N/A syserr("LDAP recursion requested in %s but no returnable attribute given",
1N/A map->map_mname);
1N/A return false;
1N/A }
1N/A if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE)
1N/A {
1N/A syserr("LDAP recursion requested in %s can not be used with -n",
1N/A map->map_mname);
1N/A return false;
1N/A }
1N/A }
1N/A map->map_db1 = (ARBPTR_T) lmap;
1N/A return true;
1N/A}
1N/A
1N/A/*
1N/A** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
1N/A**
1N/A** Parameters:
1N/A** spec -- map argument string from LDAPDefaults option
1N/A**
1N/A** Returns:
1N/A** None.
1N/A*/
1N/A
1N/Avoid
1N/Aldapmap_set_defaults(spec)
1N/A char *spec;
1N/A{
1N/A STAB *class;
1N/A MAP map;
1N/A
1N/A /* Allocate and set the default values */
1N/A if (LDAPDefaults == NULL)
1N/A LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof(*LDAPDefaults));
1N/A sm_ldap_clear(LDAPDefaults);
1N/A
1N/A memset(&map, '\0', sizeof(map));
1N/A
1N/A /* look up the class */
1N/A class = stab("ldap", ST_MAPCLASS, ST_FIND);
1N/A if (class == NULL)
1N/A {
1N/A syserr("readcf: LDAPDefaultSpec: class ldap not available");
1N/A return;
1N/A }
1N/A map.map_class = &class->s_mapclass;
1N/A map.map_db1 = (ARBPTR_T) LDAPDefaults;
1N/A map.map_mname = "O LDAPDefaultSpec";
1N/A
1N/A (void) ldapmap_parseargs(&map, spec);
1N/A
1N/A /* These should never be set in LDAPDefaults */
1N/A if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) ||
1N/A map.map_spacesub != SpaceSub ||
1N/A map.map_app != NULL ||
1N/A map.map_tapp != NULL)
1N/A {
1N/A syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
1N/A SM_FREE_CLR(map.map_app);
1N/A SM_FREE_CLR(map.map_tapp);
1N/A }
1N/A
1N/A if (LDAPDefaults->ldap_filter != NULL)
1N/A {
1N/A syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
1N/A
1N/A /* don't free, it isn't malloc'ed in parseargs */
1N/A LDAPDefaults->ldap_filter = NULL;
1N/A }
1N/A
1N/A if (LDAPDefaults->ldap_attr[0] != NULL)
1N/A {
1N/A syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
1N/A /* don't free, they aren't malloc'ed in parseargs */
1N/A LDAPDefaults->ldap_attr[0] = NULL;
1N/A }
1N/A}
1N/A#endif /* LDAPMAP */
1N/A/*
1N/A** PH map
1N/A*/
1N/A
1N/A#if PH_MAP
1N/A
1N/A/*
1N/A** Support for the CCSO Nameserver (ph/qi).
1N/A** This code is intended to replace the so-called "ph mailer".
1N/A** Contributed by Mark D. Roth. Contact him for support.
1N/A*/
1N/A
1N/A/* what version of the ph map code we're running */
1N/Astatic char phmap_id[128];
1N/A
1N/A/* sendmail version for phmap id string */
1N/Aextern const char Version[];
1N/A
1N/A/* assume we're using nph-1.2.x if not specified */
1N/A# ifndef NPH_VERSION
1N/A# define NPH_VERSION 10200
1N/A# endif
1N/A
1N/A/* compatibility for versions older than nph-1.2.0 */
1N/A# if NPH_VERSION < 10200
1N/A# define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
1N/A# define PH_OPEN_DONTID PH_DONTID
1N/A# define PH_CLOSE_FAST PH_FASTCLOSE
1N/A# define PH_ERR_DATAERR PH_DATAERR
1N/A# define PH_ERR_NOMATCH PH_NOMATCH
1N/A# endif /* NPH_VERSION < 10200 */
1N/A
1N/A/*
1N/A** PH_MAP_PARSEARGS -- parse ph map definition args.
1N/A*/
1N/A
1N/Abool
1N/Aph_map_parseargs(map, args)
1N/A MAP *map;
1N/A char *args;
1N/A{
1N/A register bool done;
1N/A register char *p = args;
1N/A PH_MAP_STRUCT *pmap = NULL;
1N/A
1N/A /* initialize version string */
1N/A (void) sm_snprintf(phmap_id, sizeof(phmap_id),
1N/A "sendmail-%s phmap-20010529 libphclient-%s",
1N/A Version, libphclient_version);
1N/A
1N/A pmap = (PH_MAP_STRUCT *) xalloc(sizeof(*pmap));
1N/A
1N/A /* defaults */
1N/A pmap->ph_servers = NULL;
1N/A pmap->ph_field_list = NULL;
1N/A pmap->ph = NULL;
1N/A pmap->ph_timeout = 0;
1N/A pmap->ph_fastclose = 0;
1N/A
1N/A map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL;
1N/A for (;;)
1N/A {
1N/A while (isascii(*p) && isspace(*p))
1N/A p++;
1N/A if (*p != '-')
1N/A break;
1N/A switch (*++p)
1N/A {
1N/A case 'N':
1N/A map->map_mflags |= MF_INCLNULL;
1N/A map->map_mflags &= ~MF_TRY0NULL;
1N/A break;
1N/A
1N/A case 'O':
1N/A map->map_mflags &= ~MF_TRY1NULL;
1N/A break;
1N/A
1N/A case 'o':
1N/A map->map_mflags |= MF_OPTIONAL;
1N/A break;
1N/A
1N/A case 'f':
1N/A map->map_mflags |= MF_NOFOLDCASE;
1N/A break;
1N/A
1N/A case 'm':
1N/A map->map_mflags |= MF_MATCHONLY;
1N/A break;
1N/A
1N/A case 'A':
1N/A map->map_mflags |= MF_APPEND;
1N/A break;
1N/A
1N/A case 'q':
1N/A map->map_mflags |= MF_KEEPQUOTES;
1N/A break;
1N/A
1N/A case 't':
1N/A map->map_mflags |= MF_NODEFER;
1N/A break;
1N/A
1N/A case 'a':
1N/A map->map_app = ++p;
1N/A break;
1N/A
1N/A case 'T':
1N/A map->map_tapp = ++p;
1N/A break;
1N/A
1N/A case 'l':
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A pmap->ph_timeout = atoi(p);
1N/A break;
1N/A
1N/A case 'S':
1N/A map->map_spacesub = *++p;
1N/A break;
1N/A
1N/A case 'D':
1N/A map->map_mflags |= MF_DEFER;
1N/A break;
1N/A
1N/A case 'h': /* PH server list */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A pmap->ph_servers = p;
1N/A break;
1N/A
1N/A case 'k': /* fields to search for */
1N/A while (isascii(*++p) && isspace(*p))
1N/A continue;
1N/A pmap->ph_field_list = p;
1N/A break;
1N/A
1N/A default:
1N/A syserr("ph_map_parseargs: unknown option -%c", *p);
1N/A }
1N/A
1N/A /* try to account for quoted strings */
1N/A done = isascii(*p) && isspace(*p);
1N/A while (*p != '\0' && !done)
1N/A {
1N/A if (*p == '"')
1N/A {
1N/A while (*++p != '"' && *p != '\0')
1N/A continue;
1N/A if (*p != '\0')
1N/A p++;
1N/A }
1N/A else
1N/A p++;
1N/A done = isascii(*p) && isspace(*p);
1N/A }
1N/A
1N/A if (*p != '\0')
1N/A *p++ = '\0';
1N/A }
1N/A
1N/A if (map->map_app != NULL)
1N/A map->map_app = newstr(ph_map_dequote(map->map_app));
1N/A if (map->map_tapp != NULL)
1N/A map->map_tapp = newstr(ph_map_dequote(map->map_tapp));
1N/A
1N/A if (pmap->ph_field_list != NULL)
1N/A pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list));
1N/A
1N/A if (pmap->ph_servers != NULL)
1N/A pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers));
1N/A else
1N/A {
1N/A syserr("ph_map_parseargs: -h flag is required");
1N/A return false;
1N/A }
1N/A
1N/A map->map_db1 = (ARBPTR_T) pmap;
1N/A return true;
1N/A}
1N/A
1N/A/*
1N/A** PH_MAP_CLOSE -- close the connection to the ph server
1N/A*/
1N/A
1N/Avoid
1N/Aph_map_close(map)
1N/A MAP *map;
1N/A{
1N/A PH_MAP_STRUCT *pmap;
1N/A
1N/A pmap = (PH_MAP_STRUCT *)map->map_db1;
1N/A if (tTd(38, 9))
1N/A sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
1N/A map->map_mname, pmap->ph_fastclose);
1N/A
1N/A
1N/A if (pmap->ph != NULL)
1N/A {
1N/A ph_set_sendhook(pmap->ph, NULL);
1N/A ph_set_recvhook(pmap->ph, NULL);
1N/A ph_close(pmap->ph, pmap->ph_fastclose);
1N/A }
1N/A
1N/A map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
1N/A}
1N/A
1N/Astatic jmp_buf PHTimeout;
1N/A
1N/A/* ARGSUSED */
1N/Astatic void
1N/Aph_timeout(unused)
1N/A int unused;
1N/A{
1N/A /*
1N/A ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
1N/A ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
1N/A ** DOING.
1N/A */
1N/A
1N/A errno = ETIMEDOUT;
1N/A longjmp(PHTimeout, 1);
1N/A}
1N/A
1N/Astatic void
1N/A#if NPH_VERSION >= 10200
1N/Aph_map_send_debug(appdata, text)
1N/A void *appdata;
1N/A#else
1N/Aph_map_send_debug(text)
1N/A#endif
1N/A char *text;
1N/A{
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_NOTICE, CurEnv->e_id,
1N/A "ph_map_send_debug: ==> %s", text);
1N/A if (tTd(38, 20))
1N/A sm_dprintf("ph_map_send_debug: ==> %s\n", text);
1N/A}
1N/A
1N/Astatic void
1N/A#if NPH_VERSION >= 10200
1N/Aph_map_recv_debug(appdata, text)
1N/A void *appdata;
1N/A#else
1N/Aph_map_recv_debug(text)
1N/A#endif
1N/A char *text;
1N/A{
1N/A if (LogLevel > 10)
1N/A sm_syslog(LOG_NOTICE, CurEnv->e_id,
1N/A "ph_map_recv_debug: <== %s", text);
1N/A if (tTd(38, 21))
1N/A sm_dprintf("ph_map_recv_debug: <== %s\n", text);
1N/A}
1N/A
1N/A/*
1N/A** PH_MAP_OPEN -- sub for opening PH map
1N/A*/
1N/Abool
1N/Aph_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A PH_MAP_STRUCT *pmap;
1N/A register SM_EVENT *ev = NULL;
1N/A int save_errno = 0;
1N/A char *hostlist, *host;
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("ph_map_open(%s)\n", map->map_mname);
1N/A
1N/A mode &= O_ACCMODE;
1N/A if (mode != O_RDONLY)
1N/A {
1N/A /* issue a pseudo-error message */
1N/A errno = SM_EMAPCANTWRITE;
1N/A return false;
1N/A }
1N/A
1N/A if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER &&
1N/A bitset(MF_DEFER, map->map_mflags))
1N/A {
1N/A if (tTd(9, 1))
1N/A sm_dprintf("ph_map_open(%s) => DEFERRED\n",
1N/A map->map_mname);
1N/A
1N/A /*
1N/A ** Unset MF_DEFER here so that map_lookup() returns
1N/A ** a temporary failure using the bogus map and
1N/A ** map->map_tapp instead of the default permanent error.
1N/A */
1N/A
1N/A map->map_mflags &= ~MF_DEFER;
1N/A return false;
1N/A }
1N/A
1N/A pmap = (PH_MAP_STRUCT *)map->map_db1;
1N/A pmap->ph_fastclose = 0; /* refresh field for reopen */
1N/A
1N/A /* try each host in the list */
1N/A hostlist = newstr(pmap->ph_servers);
1N/A for (host = strtok(hostlist, " ");
1N/A host != NULL;
1N/A host = strtok(NULL, " "))
1N/A {
1N/A /* set timeout */
1N/A if (pmap->ph_timeout != 0)
1N/A {
1N/A if (setjmp(PHTimeout) != 0)
1N/A {
1N/A ev = NULL;
1N/A if (LogLevel > 1)
1N/A sm_syslog(LOG_NOTICE, CurEnv->e_id,
1N/A "timeout connecting to PH server %.100s",
1N/A host);
1N/A errno = ETIMEDOUT;
1N/A goto ph_map_open_abort;
1N/A }
1N/A ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
1N/A }
1N/A
1N/A /* open connection to server */
1N/A if (ph_open(&(pmap->ph), host,
1N/A PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID,
1N/A ph_map_send_debug, ph_map_recv_debug
1N/A#if NPH_VERSION >= 10200
1N/A , NULL
1N/A#endif
1N/A ) == 0
1N/A && ph_id(pmap->ph, phmap_id) == 0)
1N/A {
1N/A if (ev != NULL)
1N/A sm_clrevent(ev);
1N/A sm_free(hostlist); /* XXX */
1N/A return true;
1N/A }
1N/A
1N/A ph_map_open_abort:
1N/A save_errno = errno;
1N/A if (ev != NULL)
1N/A sm_clrevent(ev);
1N/A pmap->ph_fastclose = PH_CLOSE_FAST;
1N/A ph_map_close(map);
1N/A errno = save_errno;
1N/A }
1N/A
1N/A if (bitset(MF_NODEFER, map->map_mflags))
1N/A {
1N/A if (errno == 0)
1N/A errno = EAGAIN;
1N/A syserr("ph_map_open: %s: cannot connect to PH server",
1N/A map->map_mname);
1N/A }
1N/A else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1)
1N/A sm_syslog(LOG_NOTICE, CurEnv->e_id,
1N/A "ph_map_open: %s: cannot connect to PH server",
1N/A map->map_mname);
1N/A sm_free(hostlist); /* XXX */
1N/A return false;
1N/A}
1N/A
1N/A/*
1N/A** PH_MAP_LOOKUP -- look up key from ph server
1N/A*/
1N/A
1N/Achar *
1N/Aph_map_lookup(map, key, args, pstat)
1N/A MAP *map;
1N/A char *key;
1N/A char **args;
1N/A int *pstat;
1N/A{
1N/A int i, save_errno = 0;
1N/A register SM_EVENT *ev = NULL;
1N/A PH_MAP_STRUCT *pmap;
1N/A char *value = NULL;
1N/A
1N/A pmap = (PH_MAP_STRUCT *)map->map_db1;
1N/A
1N/A *pstat = EX_OK;
1N/A
1N/A /* set timeout */
1N/A if (pmap->ph_timeout != 0)
1N/A {
1N/A if (setjmp(PHTimeout) != 0)
1N/A {
1N/A ev = NULL;
1N/A if (LogLevel > 1)
1N/A sm_syslog(LOG_NOTICE, CurEnv->e_id,
1N/A "timeout during PH lookup of %.100s",
1N/A key);
1N/A errno = ETIMEDOUT;
1N/A *pstat = EX_TEMPFAIL;
1N/A goto ph_map_lookup_abort;
1N/A }
1N/A ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0);
1N/A }
1N/A
1N/A /* perform lookup */
1N/A i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value);
1N/A if (i == -1)
1N/A *pstat = EX_TEMPFAIL;
1N/A else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR)
1N/A *pstat = EX_UNAVAILABLE;
1N/A
1N/A ph_map_lookup_abort:
1N/A if (ev != NULL)
1N/A sm_clrevent(ev);
1N/A
1N/A /*
1N/A ** Close the connection if the timer popped
1N/A ** or we got a temporary PH error
1N/A */
1N/A
1N/A if (*pstat == EX_TEMPFAIL)
1N/A {
1N/A save_errno = errno;
1N/A pmap->ph_fastclose = PH_CLOSE_FAST;
1N/A ph_map_close(map);
1N/A errno = save_errno;
1N/A }
1N/A
1N/A if (*pstat == EX_OK)
1N/A {
1N/A if (tTd(38,20))
1N/A sm_dprintf("ph_map_lookup: %s => %s\n", key, value);
1N/A
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A return map_rewrite(map, key, strlen(key), NULL);
1N/A else
1N/A return map_rewrite(map, value, strlen(value), args);
1N/A }
1N/A
1N/A return NULL;
1N/A}
1N/A#endif /* PH_MAP */
1N/A
1N/A/*
1N/A** syslog map
1N/A*/
1N/A
1N/A#define map_prio map_lockfd /* overload field */
1N/A
1N/A/*
1N/A** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
1N/A*/
1N/A
1N/Abool
1N/Asyslog_map_parseargs(map, args)
1N/A MAP *map;
1N/A char *args;
1N/A{
1N/A char *p = args;
1N/A char *priority = NULL;
1N/A
1N/A /* there is no check whether there is really an argument */
1N/A while (*p != '\0')
1N/A {
1N/A while (isascii(*p) && isspace(*p))
1N/A p++;
1N/A if (*p != '-')
1N/A break;
1N/A ++p;
1N/A if (*p == 'D')
1N/A {
1N/A map->map_mflags |= MF_DEFER;
1N/A ++p;
1N/A }
1N/A else if (*p == 'S')
1N/A {
1N/A map->map_spacesub = *++p;
1N/A if (*p != '\0')
1N/A p++;
1N/A }
1N/A else if (*p == 'L')
1N/A {
1N/A while (*++p != '\0' && isascii(*p) && isspace(*p))
1N/A continue;
1N/A if (*p == '\0')
1N/A break;
1N/A priority = p;
1N/A while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1N/A p++;
1N/A if (*p != '\0')
1N/A *p++ = '\0';
1N/A }
1N/A else
1N/A {
1N/A syserr("Illegal option %c map syslog", *p);
1N/A ++p;
1N/A }
1N/A }
1N/A
1N/A if (priority == NULL)
1N/A map->map_prio = LOG_INFO;
1N/A else
1N/A {
1N/A if (sm_strncasecmp("LOG_", priority, 4) == 0)
1N/A priority += 4;
1N/A
1N/A#ifdef LOG_EMERG
1N/A if (sm_strcasecmp("EMERG", priority) == 0)
1N/A map->map_prio = LOG_EMERG;
1N/A else
1N/A#endif /* LOG_EMERG */
1N/A#ifdef LOG_ALERT
1N/A if (sm_strcasecmp("ALERT", priority) == 0)
1N/A map->map_prio = LOG_ALERT;
1N/A else
1N/A#endif /* LOG_ALERT */
1N/A#ifdef LOG_CRIT
1N/A if (sm_strcasecmp("CRIT", priority) == 0)
1N/A map->map_prio = LOG_CRIT;
1N/A else
1N/A#endif /* LOG_CRIT */
1N/A#ifdef LOG_ERR
1N/A if (sm_strcasecmp("ERR", priority) == 0)
1N/A map->map_prio = LOG_ERR;
1N/A else
1N/A#endif /* LOG_ERR */
1N/A#ifdef LOG_WARNING
1N/A if (sm_strcasecmp("WARNING", priority) == 0)
1N/A map->map_prio = LOG_WARNING;
1N/A else
1N/A#endif /* LOG_WARNING */
1N/A#ifdef LOG_NOTICE
1N/A if (sm_strcasecmp("NOTICE", priority) == 0)
1N/A map->map_prio = LOG_NOTICE;
1N/A else
1N/A#endif /* LOG_NOTICE */
1N/A#ifdef LOG_INFO
1N/A if (sm_strcasecmp("INFO", priority) == 0)
1N/A map->map_prio = LOG_INFO;
1N/A else
1N/A#endif /* LOG_INFO */
1N/A#ifdef LOG_DEBUG
1N/A if (sm_strcasecmp("DEBUG", priority) == 0)
1N/A map->map_prio = LOG_DEBUG;
1N/A else
1N/A#endif /* LOG_DEBUG */
1N/A {
1N/A syserr("syslog_map_parseargs: Unknown priority %s",
1N/A priority);
1N/A return false;
1N/A }
1N/A }
1N/A return true;
1N/A}
1N/A
1N/A/*
1N/A** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
1N/A*/
1N/A
1N/Achar *
1N/Asyslog_map_lookup(map, string, args, statp)
1N/A MAP *map;
1N/A char *string;
1N/A char **args;
1N/A int *statp;
1N/A{
1N/A char *ptr = map_rewrite(map, string, strlen(string), args);
1N/A
1N/A if (ptr != NULL)
1N/A {
1N/A if (tTd(38, 20))
1N/A sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
1N/A map->map_mname, map->map_prio, ptr);
1N/A
1N/A sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
1N/A }
1N/A
1N/A *statp = EX_OK;
1N/A return "";
1N/A}
1N/A
1N/A#if _FFR_DPRINTF_MAP
1N/A/*
1N/A** dprintf map
1N/A*/
1N/A
1N/A#define map_dbg_level map_lockfd /* overload field */
1N/A
1N/A/*
1N/A** DPRINTF_MAP_PARSEARGS -- check for priority level to dprintf messages.
1N/A*/
1N/A
1N/Abool
1N/Adprintf_map_parseargs(map, args)
1N/A MAP *map;
1N/A char *args;
1N/A{
1N/A char *p = args;
1N/A char *dbg_level = NULL;
1N/A
1N/A /* there is no check whether there is really an argument */
1N/A while (*p != '\0')
1N/A {
1N/A while (isascii(*p) && isspace(*p))
1N/A p++;
1N/A if (*p != '-')
1N/A break;
1N/A ++p;
1N/A if (*p == 'D')
1N/A {
1N/A map->map_mflags |= MF_DEFER;
1N/A ++p;
1N/A }
1N/A else if (*p == 'S')
1N/A {
1N/A map->map_spacesub = *++p;
1N/A if (*p != '\0')
1N/A p++;
1N/A }
1N/A else if (*p == 'd')
1N/A {
1N/A while (*++p != '\0' && isascii(*p) && isspace(*p))
1N/A continue;
1N/A if (*p == '\0')
1N/A break;
1N/A dbg_level = p;
1N/A while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1N/A p++;
1N/A if (*p != '\0')
1N/A *p++ = '\0';
1N/A }
1N/A else
1N/A {
1N/A syserr("Illegal option %c map dprintf", *p);
1N/A ++p;
1N/A }
1N/A }
1N/A
1N/A if (dbg_level == NULL)
1N/A map->map_dbg_level = 0;
1N/A else
1N/A {
1N/A if (!(isascii(*dbg_level) && isdigit(*dbg_level)))
1N/A {
1N/A syserr("dprintf map \"%s\", file %s: -d should specify a number, not %s",
1N/A map->map_mname, map->map_file,
1N/A dbg_level);
1N/A return false;
1N/A }
1N/A map->map_dbg_level = atoi(dbg_level);
1N/A }
1N/A return true;
1N/A}
1N/A
1N/A/*
1N/A** DPRINTF_MAP_LOOKUP -- rewrite and print message. Always return empty string
1N/A*/
1N/A
1N/Achar *
1N/Adprintf_map_lookup(map, string, args, statp)
1N/A MAP *map;
1N/A char *string;
1N/A char **args;
1N/A int *statp;
1N/A{
1N/A char *ptr = map_rewrite(map, string, strlen(string), args);
1N/A
1N/A if (ptr != NULL && tTd(85, map->map_dbg_level))
1N/A sm_dprintf("%s\n", ptr);
1N/A *statp = EX_OK;
1N/A return "";
1N/A}
1N/A#endif /* _FFR_DPRINTF_MAP */
1N/A
1N/A/*
1N/A** HESIOD Modules
1N/A*/
1N/A
1N/A#if HESIOD
1N/A
1N/Abool
1N/Ahes_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A if (tTd(38, 2))
1N/A sm_dprintf("hes_map_open(%s, %s, %d)\n",
1N/A map->map_mname, map->map_file, mode);
1N/A
1N/A if (mode != O_RDONLY)
1N/A {
1N/A /* issue a pseudo-error message */
1N/A errno = SM_EMAPCANTWRITE;
1N/A return false;
1N/A }
1N/A
1N/A# ifdef HESIOD_INIT
1N/A if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
1N/A return true;
1N/A
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
1N/A sm_errstring(errno));
1N/A return false;
1N/A# else /* HESIOD_INIT */
1N/A if (hes_error() == HES_ER_UNINIT)
1N/A hes_init();
1N/A switch (hes_error())
1N/A {
1N/A case HES_ER_OK:
1N/A case HES_ER_NOTFOUND:
1N/A return true;
1N/A }
1N/A
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
1N/A
1N/A return false;
1N/A# endif /* HESIOD_INIT */
1N/A}
1N/A
1N/Achar *
1N/Ahes_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A char **hp;
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name);
1N/A
1N/A if (name[0] == '\\')
1N/A {
1N/A char *np;
1N/A int nl;
1N/A int save_errno;
1N/A char nbuf[MAXNAME];
1N/A
1N/A nl = strlen(name);
1N/A if (nl < sizeof(nbuf) - 1)
1N/A np = nbuf;
1N/A else
1N/A np = xalloc(strlen(name) + 2);
1N/A np[0] = '\\';
1N/A (void) sm_strlcpy(&np[1], name, (sizeof(nbuf)) - 1);
1N/A# ifdef HESIOD_INIT
1N/A hp = hesiod_resolve(HesiodContext, np, map->map_file);
1N/A# else /* HESIOD_INIT */
1N/A hp = hes_resolve(np, map->map_file);
1N/A# endif /* HESIOD_INIT */
1N/A save_errno = errno;
1N/A if (np != nbuf)
1N/A sm_free(np); /* XXX */
1N/A errno = save_errno;
1N/A }
1N/A else
1N/A {
1N/A# ifdef HESIOD_INIT
1N/A hp = hesiod_resolve(HesiodContext, name, map->map_file);
1N/A# else /* HESIOD_INIT */
1N/A hp = hes_resolve(name, map->map_file);
1N/A# endif /* HESIOD_INIT */
1N/A }
1N/A# ifdef HESIOD_INIT
1N/A if (hp == NULL || *hp == NULL)
1N/A {
1N/A switch (errno)
1N/A {
1N/A case ENOENT:
1N/A *statp = EX_NOTFOUND;
1N/A break;
1N/A case ECONNREFUSED:
1N/A *statp = EX_TEMPFAIL;
1N/A break;
1N/A case EMSGSIZE:
1N/A case ENOMEM:
1N/A default:
1N/A *statp = EX_UNAVAILABLE;
1N/A break;
1N/A }
1N/A if (hp != NULL)
1N/A hesiod_free_list(HesiodContext, hp);
1N/A return NULL;
1N/A }
1N/A# else /* HESIOD_INIT */
1N/A if (hp == NULL || hp[0] == NULL)
1N/A {
1N/A switch (hes_error())
1N/A {
1N/A case HES_ER_OK:
1N/A *statp = EX_OK;
1N/A break;
1N/A
1N/A case HES_ER_NOTFOUND:
1N/A *statp = EX_NOTFOUND;
1N/A break;
1N/A
1N/A case HES_ER_CONFIG:
1N/A *statp = EX_UNAVAILABLE;
1N/A break;
1N/A
1N/A case HES_ER_NET:
1N/A *statp = EX_TEMPFAIL;
1N/A break;
1N/A }
1N/A return NULL;
1N/A }
1N/A# endif /* HESIOD_INIT */
1N/A
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A return map_rewrite(map, name, strlen(name), NULL);
1N/A else
1N/A return map_rewrite(map, hp[0], strlen(hp[0]), av);
1N/A}
1N/A
1N/A/*
1N/A** HES_MAP_CLOSE -- free the Hesiod context
1N/A*/
1N/A
1N/Avoid
1N/Ahes_map_close(map)
1N/A MAP *map;
1N/A{
1N/A if (tTd(38, 20))
1N/A sm_dprintf("hes_map_close(%s)\n", map->map_file);
1N/A
1N/A# ifdef HESIOD_INIT
1N/A /* Free the hesiod context */
1N/A if (HesiodContext != NULL)
1N/A {
1N/A hesiod_end(HesiodContext);
1N/A HesiodContext = NULL;
1N/A }
1N/A# endif /* HESIOD_INIT */
1N/A}
1N/A
1N/A#endif /* HESIOD */
1N/A/*
1N/A** NeXT NETINFO Modules
1N/A*/
1N/A
1N/A#if NETINFO
1N/A
1N/A# define NETINFO_DEFAULT_DIR "/aliases"
1N/A# define NETINFO_DEFAULT_PROPERTY "members"
1N/A
1N/A/*
1N/A** NI_MAP_OPEN -- open NetInfo Aliases
1N/A*/
1N/A
1N/Abool
1N/Ani_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A if (tTd(38, 2))
1N/A sm_dprintf("ni_map_open(%s, %s, %d)\n",
1N/A map->map_mname, map->map_file, mode);
1N/A mode &= O_ACCMODE;
1N/A
1N/A if (*map->map_file == '\0')
1N/A map->map_file = NETINFO_DEFAULT_DIR;
1N/A
1N/A if (map->map_valcolnm == NULL)
1N/A map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
1N/A
1N/A if (map->map_coldelim == '\0')
1N/A {
1N/A if (bitset(MF_ALIAS, map->map_mflags))
1N/A map->map_coldelim = ',';
1N/A else if (bitset(MF_FILECLASS, map->map_mflags))
1N/A map->map_coldelim = ' ';
1N/A }
1N/A return true;
1N/A}
1N/A
1N/A
1N/A/*
1N/A** NI_MAP_LOOKUP -- look up a datum in NetInfo
1N/A*/
1N/A
1N/Achar *
1N/Ani_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A char *res;
1N/A char *propval;
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
1N/A
1N/A propval = ni_propval(map->map_file, map->map_keycolnm, name,
1N/A map->map_valcolnm, map->map_coldelim);
1N/A
1N/A if (propval == NULL)
1N/A return NULL;
1N/A
1N/A SM_TRY
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A res = map_rewrite(map, name, strlen(name), NULL);
1N/A else
1N/A res = map_rewrite(map, propval, strlen(propval), av);
1N/A SM_FINALLY
1N/A sm_free(propval);
1N/A SM_END_TRY
1N/A return res;
1N/A}
1N/A
1N/A
1N/Astatic bool
1N/Ani_getcanonname(name, hbsize, statp)
1N/A char *name;
1N/A int hbsize;
1N/A int *statp;
1N/A{
1N/A char *vptr;
1N/A char *ptr;
1N/A char nbuf[MAXNAME + 1];
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("ni_getcanonname(%s)\n", name);
1N/A
1N/A if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
1N/A {
1N/A *statp = EX_UNAVAILABLE;
1N/A return false;
1N/A }
1N/A (void) shorten_hostname(nbuf);
1N/A
1N/A /* we only accept single token search key */
1N/A if (strchr(nbuf, '.'))
1N/A {
1N/A *statp = EX_NOHOST;
1N/A return false;
1N/A }
1N/A
1N/A /* Do the search */
1N/A vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
1N/A
1N/A if (vptr == NULL)
1N/A {
1N/A *statp = EX_NOHOST;
1N/A return false;
1N/A }
1N/A
1N/A /* Only want the first machine name */
1N/A if ((ptr = strchr(vptr, '\n')) != NULL)
1N/A *ptr = '\0';
1N/A
1N/A if (sm_strlcpy(name, vptr, hbsize) >= hbsize)
1N/A {
1N/A sm_free(vptr);
1N/A *statp = EX_UNAVAILABLE;
1N/A return true;
1N/A }
1N/A sm_free(vptr);
1N/A *statp = EX_OK;
1N/A return false;
1N/A}
1N/A#endif /* NETINFO */
1N/A/*
1N/A** TEXT (unindexed text file) Modules
1N/A**
1N/A** This code donated by Sun Microsystems.
1N/A*/
1N/A
1N/A#define map_sff map_lockfd /* overload field */
1N/A
1N/A
1N/A/*
1N/A** TEXT_MAP_OPEN -- open text table
1N/A*/
1N/A
1N/Abool
1N/Atext_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A long sff;
1N/A int i;
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("text_map_open(%s, %s, %d)\n",
1N/A map->map_mname, map->map_file, mode);
1N/A
1N/A mode &= O_ACCMODE;
1N/A if (mode != O_RDONLY)
1N/A {
1N/A errno = EPERM;
1N/A return false;
1N/A }
1N/A
1N/A if (*map->map_file == '\0')
1N/A {
1N/A syserr("text map \"%s\": file name required",
1N/A map->map_mname);
1N/A return false;
1N/A }
1N/A
1N/A if (map->map_file[0] != '/')
1N/A {
1N/A syserr("text map \"%s\": file name must be fully qualified",
1N/A map->map_mname);
1N/A return false;
1N/A }
1N/A
1N/A sff = SFF_ROOTOK|SFF_REGONLY;
1N/A if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1N/A sff |= SFF_NOWLINK;
1N/A if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1N/A sff |= SFF_SAFEDIRPATH;
1N/A if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
1N/A sff, S_IRUSR, NULL)) != 0)
1N/A {
1N/A int save_errno = errno;
1N/A
1N/A /* cannot open this map */
1N/A if (tTd(38, 2))
1N/A sm_dprintf("\tunsafe map file: %d\n", i);
1N/A errno = save_errno;
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("text map \"%s\": unsafe map file %s",
1N/A map->map_mname, map->map_file);
1N/A return false;
1N/A }
1N/A
1N/A if (map->map_keycolnm == NULL)
1N/A map->map_keycolno = 0;
1N/A else
1N/A {
1N/A if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
1N/A {
1N/A syserr("text map \"%s\", file %s: -k should specify a number, not %s",
1N/A map->map_mname, map->map_file,
1N/A map->map_keycolnm);
1N/A return false;
1N/A }
1N/A map->map_keycolno = atoi(map->map_keycolnm);
1N/A }
1N/A
1N/A if (map->map_valcolnm == NULL)
1N/A map->map_valcolno = 0;
1N/A else
1N/A {
1N/A if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
1N/A {
1N/A syserr("text map \"%s\", file %s: -v should specify a number, not %s",
1N/A map->map_mname, map->map_file,
1N/A map->map_valcolnm);
1N/A return false;
1N/A }
1N/A map->map_valcolno = atoi(map->map_valcolnm);
1N/A }
1N/A
1N/A if (tTd(38, 2))
1N/A {
1N/A sm_dprintf("text_map_open(%s, %s): delimiter = ",
1N/A map->map_mname, map->map_file);
1N/A if (map->map_coldelim == '\0')
1N/A sm_dprintf("(white space)\n");
1N/A else
1N/A sm_dprintf("%c\n", map->map_coldelim);
1N/A }
1N/A
1N/A map->map_sff = sff;
1N/A return true;
1N/A}
1N/A
1N/A
1N/A/*
1N/A** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
1N/A*/
1N/A
1N/Achar *
1N/Atext_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A char *vp;
1N/A auto int vsize;
1N/A int buflen;
1N/A SM_FILE_T *f;
1N/A char delim;
1N/A int key_idx;
1N/A bool found_it;
1N/A long sff = map->map_sff;
1N/A char search_key[MAXNAME + 1];
1N/A char linebuf[MAXLINE];
1N/A char buf[MAXNAME + 1];
1N/A
1N/A found_it = false;
1N/A if (tTd(38, 20))
1N/A sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name);
1N/A
1N/A buflen = strlen(name);
1N/A if (buflen > sizeof(search_key) - 1)
1N/A buflen = sizeof(search_key) - 1; /* XXX just cut if off? */
1N/A memmove(search_key, name, buflen);
1N/A search_key[buflen] = '\0';
1N/A if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1N/A makelower(search_key);
1N/A
1N/A f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
1N/A if (f == NULL)
1N/A {
1N/A map->map_mflags &= ~(MF_VALID|MF_OPEN);
1N/A *statp = EX_UNAVAILABLE;
1N/A return NULL;
1N/A }
1N/A key_idx = map->map_keycolno;
1N/A delim = map->map_coldelim;
1N/A while (sm_io_fgets(f, SM_TIME_DEFAULT,
1N/A linebuf, sizeof(linebuf)) != NULL)
1N/A {
1N/A char *p;
1N/A
1N/A /* skip comment line */
1N/A if (linebuf[0] == '#')
1N/A continue;
1N/A p = strchr(linebuf, '\n');
1N/A if (p != NULL)
1N/A *p = '\0';
1N/A p = get_column(linebuf, key_idx, delim, buf, sizeof(buf));
1N/A if (p != NULL && sm_strcasecmp(search_key, p) == 0)
1N/A {
1N/A found_it = true;
1N/A break;
1N/A }
1N/A }
1N/A (void) sm_io_close(f, SM_TIME_DEFAULT);
1N/A if (!found_it)
1N/A {
1N/A *statp = EX_NOTFOUND;
1N/A return NULL;
1N/A }
1N/A vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof(buf));
1N/A if (vp == NULL)
1N/A {
1N/A *statp = EX_NOTFOUND;
1N/A return NULL;
1N/A }
1N/A vsize = strlen(vp);
1N/A *statp = EX_OK;
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A return map_rewrite(map, name, strlen(name), NULL);
1N/A else
1N/A return map_rewrite(map, vp, vsize, av);
1N/A}
1N/A
1N/A/*
1N/A** TEXT_GETCANONNAME -- look up canonical name in hosts file
1N/A*/
1N/A
1N/Astatic bool
1N/Atext_getcanonname(name, hbsize, statp)
1N/A char *name;
1N/A int hbsize;
1N/A int *statp;
1N/A{
1N/A bool found;
1N/A char *dot;
1N/A SM_FILE_T *f;
1N/A char linebuf[MAXLINE];
1N/A char cbuf[MAXNAME + 1];
1N/A char nbuf[MAXNAME + 1];
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("text_getcanonname(%s)\n", name);
1N/A
1N/A if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf))
1N/A {
1N/A *statp = EX_UNAVAILABLE;
1N/A return false;
1N/A }
1N/A dot = shorten_hostname(nbuf);
1N/A
1N/A f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY,
1N/A NULL);
1N/A if (f == NULL)
1N/A {
1N/A *statp = EX_UNAVAILABLE;
1N/A return false;
1N/A }
1N/A found = false;
1N/A while (!found &&
1N/A sm_io_fgets(f, SM_TIME_DEFAULT,
1N/A linebuf, sizeof(linebuf)) != NULL)
1N/A {
1N/A char *p = strpbrk(linebuf, "#\n");
1N/A
1N/A if (p != NULL)
1N/A *p = '\0';
1N/A if (linebuf[0] != '\0')
1N/A found = extract_canonname(nbuf, dot, linebuf,
1N/A cbuf, sizeof(cbuf));
1N/A }
1N/A (void) sm_io_close(f, SM_TIME_DEFAULT);
1N/A if (!found)
1N/A {
1N/A *statp = EX_NOHOST;
1N/A return false;
1N/A }
1N/A
1N/A if (sm_strlcpy(name, cbuf, hbsize) >= hbsize)
1N/A {
1N/A *statp = EX_UNAVAILABLE;
1N/A return false;
1N/A }
1N/A *statp = EX_OK;
1N/A return true;
1N/A}
1N/A/*
1N/A** STAB (Symbol Table) Modules
1N/A*/
1N/A
1N/A
1N/A/*
1N/A** STAB_MAP_LOOKUP -- look up alias in symbol table
1N/A*/
1N/A
1N/A/* ARGSUSED2 */
1N/Achar *
1N/Astab_map_lookup(map, name, av, pstat)
1N/A register MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *pstat;
1N/A{
1N/A register STAB *s;
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("stab_lookup(%s, %s)\n",
1N/A map->map_mname, name);
1N/A
1N/A s = stab(name, ST_ALIAS, ST_FIND);
1N/A if (s == NULL)
1N/A return NULL;
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A return map_rewrite(map, name, strlen(name), NULL);
1N/A else
1N/A return map_rewrite(map, s->s_alias, strlen(s->s_alias), av);
1N/A}
1N/A
1N/A/*
1N/A** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
1N/A*/
1N/A
1N/Avoid
1N/Astab_map_store(map, lhs, rhs)
1N/A register MAP *map;
1N/A char *lhs;
1N/A char *rhs;
1N/A{
1N/A register STAB *s;
1N/A
1N/A s = stab(lhs, ST_ALIAS, ST_ENTER);
1N/A s->s_alias = newstr(rhs);
1N/A}
1N/A
1N/A
1N/A/*
1N/A** STAB_MAP_OPEN -- initialize (reads data file)
1N/A**
1N/A** This is a weird case -- it is only intended as a fallback for
1N/A** aliases. For this reason, opens for write (only during a
1N/A** "newaliases") always fails, and opens for read open the
1N/A** actual underlying text file instead of the database.
1N/A*/
1N/A
1N/Abool
1N/Astab_map_open(map, mode)
1N/A register MAP *map;
1N/A int mode;
1N/A{
1N/A SM_FILE_T *af;
1N/A long sff;
1N/A struct stat st;
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("stab_map_open(%s, %s, %d)\n",
1N/A map->map_mname, map->map_file, mode);
1N/A
1N/A mode &= O_ACCMODE;
1N/A if (mode != O_RDONLY)
1N/A {
1N/A errno = EPERM;
1N/A return false;
1N/A }
1N/A
1N/A sff = SFF_ROOTOK|SFF_REGONLY;
1N/A if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
1N/A sff |= SFF_NOWLINK;
1N/A if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
1N/A sff |= SFF_SAFEDIRPATH;
1N/A af = safefopen(map->map_file, O_RDONLY, 0444, sff);
1N/A if (af == NULL)
1N/A return false;
1N/A readaliases(map, af, false, false);
1N/A
1N/A if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0)
1N/A map->map_mtime = st.st_mtime;
1N/A (void) sm_io_close(af, SM_TIME_DEFAULT);
1N/A
1N/A return true;
1N/A}
1N/A/*
1N/A** Implicit Modules
1N/A**
1N/A** Tries several types. For back compatibility of aliases.
1N/A*/
1N/A
1N/A
1N/A/*
1N/A** IMPL_MAP_LOOKUP -- lookup in best open database
1N/A*/
1N/A
1N/Achar *
1N/Aimpl_map_lookup(map, name, av, pstat)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *pstat;
1N/A{
1N/A if (tTd(38, 20))
1N/A sm_dprintf("impl_map_lookup(%s, %s)\n",
1N/A map->map_mname, name);
1N/A
1N/A#if NEWDB
1N/A if (bitset(MF_IMPL_HASH, map->map_mflags))
1N/A return db_map_lookup(map, name, av, pstat);
1N/A#endif /* NEWDB */
1N/A#if NDBM
1N/A if (bitset(MF_IMPL_NDBM, map->map_mflags))
1N/A return ndbm_map_lookup(map, name, av, pstat);
1N/A#endif /* NDBM */
1N/A return stab_map_lookup(map, name, av, pstat);
1N/A}
1N/A
1N/A/*
1N/A** IMPL_MAP_STORE -- store in open databases
1N/A*/
1N/A
1N/Avoid
1N/Aimpl_map_store(map, lhs, rhs)
1N/A MAP *map;
1N/A char *lhs;
1N/A char *rhs;
1N/A{
1N/A if (tTd(38, 12))
1N/A sm_dprintf("impl_map_store(%s, %s, %s)\n",
1N/A map->map_mname, lhs, rhs);
1N/A#if NEWDB
1N/A if (bitset(MF_IMPL_HASH, map->map_mflags))
1N/A db_map_store(map, lhs, rhs);
1N/A#endif /* NEWDB */
1N/A#if NDBM
1N/A if (bitset(MF_IMPL_NDBM, map->map_mflags))
1N/A ndbm_map_store(map, lhs, rhs);
1N/A#endif /* NDBM */
1N/A stab_map_store(map, lhs, rhs);
1N/A}
1N/A
1N/A/*
1N/A** IMPL_MAP_OPEN -- implicit database open
1N/A*/
1N/A
1N/Abool
1N/Aimpl_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A if (tTd(38, 2))
1N/A sm_dprintf("impl_map_open(%s, %s, %d)\n",
1N/A map->map_mname, map->map_file, mode);
1N/A
1N/A mode &= O_ACCMODE;
1N/A#if NEWDB
1N/A map->map_mflags |= MF_IMPL_HASH;
1N/A if (hash_map_open(map, mode))
1N/A {
1N/A# ifdef NDBM_YP_COMPAT
1N/A if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
1N/A# endif /* NDBM_YP_COMPAT */
1N/A return true;
1N/A }
1N/A else
1N/A map->map_mflags &= ~MF_IMPL_HASH;
1N/A#endif /* NEWDB */
1N/A#if NDBM
1N/A map->map_mflags |= MF_IMPL_NDBM;
1N/A if (ndbm_map_open(map, mode))
1N/A {
1N/A return true;
1N/A }
1N/A else
1N/A map->map_mflags &= ~MF_IMPL_NDBM;
1N/A#endif /* NDBM */
1N/A
1N/A#if defined(NEWDB) || defined(NDBM)
1N/A if (Verbose)
1N/A message("WARNING: cannot open alias database %s%s",
1N/A map->map_file,
1N/A mode == O_RDONLY ? "; reading text version" : "");
1N/A#else /* defined(NEWDB) || defined(NDBM) */
1N/A if (mode != O_RDONLY)
1N/A usrerr("Cannot rebuild aliases: no database format defined");
1N/A#endif /* defined(NEWDB) || defined(NDBM) */
1N/A
1N/A if (mode == O_RDONLY)
1N/A return stab_map_open(map, mode);
1N/A else
1N/A return false;
1N/A}
1N/A
1N/A
1N/A/*
1N/A** IMPL_MAP_CLOSE -- close any open database(s)
1N/A*/
1N/A
1N/Avoid
1N/Aimpl_map_close(map)
1N/A MAP *map;
1N/A{
1N/A if (tTd(38, 9))
1N/A sm_dprintf("impl_map_close(%s, %s, %lx)\n",
1N/A map->map_mname, map->map_file, map->map_mflags);
1N/A#if NEWDB
1N/A if (bitset(MF_IMPL_HASH, map->map_mflags))
1N/A {
1N/A db_map_close(map);
1N/A map->map_mflags &= ~MF_IMPL_HASH;
1N/A }
1N/A#endif /* NEWDB */
1N/A
1N/A#if NDBM
1N/A if (bitset(MF_IMPL_NDBM, map->map_mflags))
1N/A {
1N/A ndbm_map_close(map);
1N/A map->map_mflags &= ~MF_IMPL_NDBM;
1N/A }
1N/A#endif /* NDBM */
1N/A}
1N/A/*
1N/A** User map class.
1N/A**
1N/A** Provides access to the system password file.
1N/A*/
1N/A
1N/A/*
1N/A** USER_MAP_OPEN -- open user map
1N/A**
1N/A** Really just binds field names to field numbers.
1N/A*/
1N/A
1N/Abool
1N/Auser_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A if (tTd(38, 2))
1N/A sm_dprintf("user_map_open(%s, %d)\n",
1N/A map->map_mname, mode);
1N/A
1N/A mode &= O_ACCMODE;
1N/A if (mode != O_RDONLY)
1N/A {
1N/A /* issue a pseudo-error message */
1N/A errno = SM_EMAPCANTWRITE;
1N/A return false;
1N/A }
1N/A if (map->map_valcolnm == NULL)
1N/A /* EMPTY */
1N/A /* nothing */ ;
1N/A else if (sm_strcasecmp(map->map_valcolnm, "name") == 0)
1N/A map->map_valcolno = 1;
1N/A else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0)
1N/A map->map_valcolno = 2;
1N/A else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0)
1N/A map->map_valcolno = 3;
1N/A else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0)
1N/A map->map_valcolno = 4;
1N/A else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0)
1N/A map->map_valcolno = 5;
1N/A else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0)
1N/A map->map_valcolno = 6;
1N/A else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0)
1N/A map->map_valcolno = 7;
1N/A else
1N/A {
1N/A syserr("User map %s: unknown column name %s",
1N/A map->map_mname, map->map_valcolnm);
1N/A return false;
1N/A }
1N/A return true;
1N/A}
1N/A
1N/A
1N/A/*
1N/A** USER_MAP_LOOKUP -- look up a user in the passwd file.
1N/A*/
1N/A
1N/A/* ARGSUSED3 */
1N/Achar *
1N/Auser_map_lookup(map, key, av, statp)
1N/A MAP *map;
1N/A char *key;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A auto bool fuzzy;
1N/A SM_MBDB_T user;
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("user_map_lookup(%s, %s)\n",
1N/A map->map_mname, key);
1N/A
1N/A *statp = finduser(key, &fuzzy, &user);
1N/A if (*statp != EX_OK)
1N/A return NULL;
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A return map_rewrite(map, key, strlen(key), NULL);
1N/A else
1N/A {
1N/A char *rwval = NULL;
1N/A char buf[30];
1N/A
1N/A switch (map->map_valcolno)
1N/A {
1N/A case 0:
1N/A case 1:
1N/A rwval = user.mbdb_name;
1N/A break;
1N/A
1N/A case 2:
1N/A rwval = "x"; /* passwd no longer supported */
1N/A break;
1N/A
1N/A case 3:
1N/A (void) sm_snprintf(buf, sizeof(buf), "%d",
1N/A (int) user.mbdb_uid);
1N/A rwval = buf;
1N/A break;
1N/A
1N/A case 4:
1N/A (void) sm_snprintf(buf, sizeof(buf), "%d",
1N/A (int) user.mbdb_gid);
1N/A rwval = buf;
1N/A break;
1N/A
1N/A case 5:
1N/A rwval = user.mbdb_fullname;
1N/A break;
1N/A
1N/A case 6:
1N/A rwval = user.mbdb_homedir;
1N/A break;
1N/A
1N/A case 7:
1N/A rwval = user.mbdb_shell;
1N/A break;
1N/A default:
1N/A syserr("user_map %s: bogus field %d",
1N/A map->map_mname, map->map_valcolno);
1N/A return NULL;
1N/A }
1N/A return map_rewrite(map, rwval, strlen(rwval), av);
1N/A }
1N/A}
1N/A/*
1N/A** Program map type.
1N/A**
1N/A** This provides access to arbitrary programs. It should be used
1N/A** only very sparingly, since there is no way to bound the cost
1N/A** of invoking an arbitrary program.
1N/A*/
1N/A
1N/Achar *
1N/Aprog_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A int i;
1N/A int save_errno;
1N/A int fd;
1N/A int status;
1N/A auto pid_t pid;
1N/A register char *p;
1N/A char *rval;
1N/A char *argv[MAXPV + 1];
1N/A char buf[MAXLINE];
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("prog_map_lookup(%s, %s) %s\n",
1N/A map->map_mname, name, map->map_file);
1N/A
1N/A i = 0;
1N/A argv[i++] = map->map_file;
1N/A if (map->map_rebuild != NULL)
1N/A {
1N/A (void) sm_strlcpy(buf, map->map_rebuild, sizeof(buf));
1N/A for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
1N/A {
1N/A if (i >= MAXPV - 1)
1N/A break;
1N/A argv[i++] = p;
1N/A }
1N/A }
1N/A argv[i++] = name;
1N/A argv[i] = NULL;
1N/A if (tTd(38, 21))
1N/A {
1N/A sm_dprintf("prog_open:");
1N/A for (i = 0; argv[i] != NULL; i++)
1N/A sm_dprintf(" %s", argv[i]);
1N/A sm_dprintf("\n");
1N/A }
1N/A (void) sm_blocksignal(SIGCHLD);
1N/A pid = prog_open(argv, &fd, CurEnv);
1N/A if (pid < 0)
1N/A {
1N/A if (!bitset(MF_OPTIONAL, map->map_mflags))
1N/A syserr("prog_map_lookup(%s) failed (%s) -- closing",
1N/A map->map_mname, sm_errstring(errno));
1N/A else if (tTd(38, 9))
1N/A sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
1N/A map->map_mname, sm_errstring(errno));
1N/A map->map_mflags &= ~(MF_VALID|MF_OPEN);
1N/A *statp = EX_OSFILE;
1N/A return NULL;
1N/A }
1N/A i = read(fd, buf, sizeof(buf) - 1);
1N/A if (i < 0)
1N/A {
1N/A syserr("prog_map_lookup(%s): read error %s",
1N/A map->map_mname, sm_errstring(errno));
1N/A rval = NULL;
1N/A }
1N/A else if (i == 0)
1N/A {
1N/A if (tTd(38, 20))
1N/A sm_dprintf("prog_map_lookup(%s): empty answer\n",
1N/A map->map_mname);
1N/A rval = NULL;
1N/A }
1N/A else
1N/A {
1N/A buf[i] = '\0';
1N/A p = strchr(buf, '\n');
1N/A if (p != NULL)
1N/A *p = '\0';
1N/A
1N/A /* collect the return value */
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A rval = map_rewrite(map, name, strlen(name), NULL);
1N/A else
1N/A rval = map_rewrite(map, buf, strlen(buf), av);
1N/A
1N/A /* now flush any additional output */
1N/A while ((i = read(fd, buf, sizeof(buf))) > 0)
1N/A continue;
1N/A }
1N/A
1N/A /* wait for the process to terminate */
1N/A (void) close(fd);
1N/A status = waitfor(pid);
1N/A save_errno = errno;
1N/A (void) sm_releasesignal(SIGCHLD);
1N/A errno = save_errno;
1N/A
1N/A if (status == -1)
1N/A {
1N/A syserr("prog_map_lookup(%s): wait error %s",
1N/A map->map_mname, sm_errstring(errno));
1N/A *statp = EX_SOFTWARE;
1N/A rval = NULL;
1N/A }
1N/A else if (WIFEXITED(status))
1N/A {
1N/A if ((*statp = WEXITSTATUS(status)) != EX_OK)
1N/A rval = NULL;
1N/A }
1N/A else
1N/A {
1N/A syserr("prog_map_lookup(%s): child died on signal %d",
1N/A map->map_mname, status);
1N/A *statp = EX_UNAVAILABLE;
1N/A rval = NULL;
1N/A }
1N/A return rval;
1N/A}
1N/A/*
1N/A** Sequenced map type.
1N/A**
1N/A** Tries each map in order until something matches, much like
1N/A** implicit. Stores go to the first map in the list that can
1N/A** support storing.
1N/A**
1N/A** This is slightly unusual in that there are two interfaces.
1N/A** The "sequence" interface lets you stack maps arbitrarily.
1N/A** The "switch" interface builds a sequence map by looking
1N/A** at a system-dependent configuration file such as
1N/A** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
1N/A**
1N/A** We don't need an explicit open, since all maps are
1N/A** opened on demand.
1N/A*/
1N/A
1N/A/*
1N/A** SEQ_MAP_PARSE -- Sequenced map parsing
1N/A*/
1N/A
1N/Abool
1N/Aseq_map_parse(map, ap)
1N/A MAP *map;
1N/A char *ap;
1N/A{
1N/A int maxmap;
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
1N/A maxmap = 0;
1N/A while (*ap != '\0')
1N/A {
1N/A register char *p;
1N/A STAB *s;
1N/A
1N/A /* find beginning of map name */
1N/A while (isascii(*ap) && isspace(*ap))
1N/A ap++;
1N/A for (p = ap;
1N/A (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.';
1N/A p++)
1N/A continue;
1N/A if (*p != '\0')
1N/A *p++ = '\0';
1N/A while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
1N/A p++;
1N/A if (*ap == '\0')
1N/A {
1N/A ap = p;
1N/A continue;
1N/A }
1N/A s = stab(ap, ST_MAP, ST_FIND);
1N/A if (s == NULL)
1N/A {
1N/A syserr("Sequence map %s: unknown member map %s",
1N/A map->map_mname, ap);
1N/A }
1N/A else if (maxmap >= MAXMAPSTACK)
1N/A {
1N/A syserr("Sequence map %s: too many member maps (%d max)",
1N/A map->map_mname, MAXMAPSTACK);
1N/A maxmap++;
1N/A }
1N/A else if (maxmap < MAXMAPSTACK)
1N/A {
1N/A map->map_stack[maxmap++] = &s->s_map;
1N/A }
1N/A ap = p;
1N/A }
1N/A return true;
1N/A}
1N/A
1N/A/*
1N/A** SWITCH_MAP_OPEN -- open a switched map
1N/A**
1N/A** This looks at the system-dependent configuration and builds
1N/A** a sequence map that does the same thing.
1N/A**
1N/A** Every system must define a switch_map_find routine in conf.c
1N/A** that will return the list of service types associated with a
1N/A** given service class.
1N/A*/
1N/A
1N/Abool
1N/Aswitch_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A int mapno;
1N/A int nmaps;
1N/A char *maptype[MAXMAPSTACK];
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("switch_map_open(%s, %s, %d)\n",
1N/A map->map_mname, map->map_file, mode);
1N/A
1N/A mode &= O_ACCMODE;
1N/A nmaps = switch_map_find(map->map_file, maptype, map->map_return);
1N/A if (tTd(38, 19))
1N/A {
1N/A sm_dprintf("\tswitch_map_find => %d\n", nmaps);
1N/A for (mapno = 0; mapno < nmaps; mapno++)
1N/A sm_dprintf("\t\t%s\n", maptype[mapno]);
1N/A }
1N/A if (nmaps <= 0 || nmaps > MAXMAPSTACK)
1N/A return false;
1N/A
1N/A for (mapno = 0; mapno < nmaps; mapno++)
1N/A {
1N/A register STAB *s;
1N/A char nbuf[MAXNAME + 1];
1N/A
1N/A if (maptype[mapno] == NULL)
1N/A continue;
1N/A (void) sm_strlcpyn(nbuf, sizeof(nbuf), 3,
1N/A map->map_mname, ".", maptype[mapno]);
1N/A s = stab(nbuf, ST_MAP, ST_FIND);
1N/A if (s == NULL)
1N/A {
1N/A syserr("Switch map %s: unknown member map %s",
1N/A map->map_mname, nbuf);
1N/A }
1N/A else
1N/A {
1N/A map->map_stack[mapno] = &s->s_map;
1N/A if (tTd(38, 4))
1N/A sm_dprintf("\tmap_stack[%d] = %s:%s\n",
1N/A mapno,
1N/A s->s_map.map_class->map_cname,
1N/A nbuf);
1N/A }
1N/A }
1N/A return true;
1N/A}
1N/A
1N/A#if 0
1N/A/*
1N/A** SEQ_MAP_CLOSE -- close all underlying maps
1N/A*/
1N/A
1N/Avoid
1N/Aseq_map_close(map)
1N/A MAP *map;
1N/A{
1N/A int mapno;
1N/A
1N/A if (tTd(38, 9))
1N/A sm_dprintf("seq_map_close(%s)\n", map->map_mname);
1N/A
1N/A for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
1N/A {
1N/A MAP *mm = map->map_stack[mapno];
1N/A
1N/A if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
1N/A continue;
1N/A mm->map_mflags |= MF_CLOSING;
1N/A mm->map_class->map_close(mm);
1N/A mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1N/A }
1N/A}
1N/A#endif /* 0 */
1N/A
1N/A/*
1N/A** SEQ_MAP_LOOKUP -- sequenced map lookup
1N/A*/
1N/A
1N/Achar *
1N/Aseq_map_lookup(map, key, args, pstat)
1N/A MAP *map;
1N/A char *key;
1N/A char **args;
1N/A int *pstat;
1N/A{
1N/A int mapno;
1N/A int mapbit = 0x01;
1N/A bool tempfail = false;
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
1N/A
1N/A for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
1N/A {
1N/A MAP *mm = map->map_stack[mapno];
1N/A char *rv;
1N/A
1N/A if (mm == NULL)
1N/A continue;
1N/A if (!bitset(MF_OPEN, mm->map_mflags) &&
1N/A !openmap(mm))
1N/A {
1N/A if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
1N/A {
1N/A *pstat = EX_UNAVAILABLE;
1N/A return NULL;
1N/A }
1N/A continue;
1N/A }
1N/A *pstat = EX_OK;
1N/A rv = mm->map_class->map_lookup(mm, key, args, pstat);
1N/A if (rv != NULL)
1N/A return rv;
1N/A if (*pstat == EX_TEMPFAIL)
1N/A {
1N/A if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
1N/A return NULL;
1N/A tempfail = true;
1N/A }
1N/A else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
1N/A break;
1N/A }
1N/A if (tempfail)
1N/A *pstat = EX_TEMPFAIL;
1N/A else if (*pstat == EX_OK)
1N/A *pstat = EX_NOTFOUND;
1N/A return NULL;
1N/A}
1N/A
1N/A/*
1N/A** SEQ_MAP_STORE -- sequenced map store
1N/A*/
1N/A
1N/Avoid
1N/Aseq_map_store(map, key, val)
1N/A MAP *map;
1N/A char *key;
1N/A char *val;
1N/A{
1N/A int mapno;
1N/A
1N/A if (tTd(38, 12))
1N/A sm_dprintf("seq_map_store(%s, %s, %s)\n",
1N/A map->map_mname, key, val);
1N/A
1N/A for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
1N/A {
1N/A MAP *mm = map->map_stack[mapno];
1N/A
1N/A if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
1N/A continue;
1N/A
1N/A mm->map_class->map_store(mm, key, val);
1N/A return;
1N/A }
1N/A syserr("seq_map_store(%s, %s, %s): no writable map",
1N/A map->map_mname, key, val);
1N/A}
1N/A/*
1N/A** NULL stubs
1N/A*/
1N/A
1N/A/* ARGSUSED */
1N/Abool
1N/Anull_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A return true;
1N/A}
1N/A
1N/A/* ARGSUSED */
1N/Avoid
1N/Anull_map_close(map)
1N/A MAP *map;
1N/A{
1N/A return;
1N/A}
1N/A
1N/Achar *
1N/Anull_map_lookup(map, key, args, pstat)
1N/A MAP *map;
1N/A char *key;
1N/A char **args;
1N/A int *pstat;
1N/A{
1N/A *pstat = EX_NOTFOUND;
1N/A return NULL;
1N/A}
1N/A
1N/A/* ARGSUSED */
1N/Avoid
1N/Anull_map_store(map, key, val)
1N/A MAP *map;
1N/A char *key;
1N/A char *val;
1N/A{
1N/A return;
1N/A}
1N/A
1N/AMAPCLASS NullMapClass =
1N/A{
1N/A "null-map", NULL, 0,
1N/A NULL, null_map_lookup, null_map_store,
1N/A null_map_open, null_map_close,
1N/A};
1N/A
1N/A/*
1N/A** BOGUS stubs
1N/A*/
1N/A
1N/Achar *
1N/Abogus_map_lookup(map, key, args, pstat)
1N/A MAP *map;
1N/A char *key;
1N/A char **args;
1N/A int *pstat;
1N/A{
1N/A *pstat = EX_TEMPFAIL;
1N/A return NULL;
1N/A}
1N/A
1N/AMAPCLASS BogusMapClass =
1N/A{
1N/A "bogus-map", NULL, 0,
1N/A NULL, bogus_map_lookup, null_map_store,
1N/A null_map_open, null_map_close,
1N/A};
1N/A/*
1N/A** MACRO modules
1N/A*/
1N/A
1N/Achar *
1N/Amacro_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A int mid;
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname,
1N/A name == NULL ? "NULL" : name);
1N/A
1N/A if (name == NULL ||
1N/A *name == '\0' ||
1N/A (mid = macid(name)) == 0)
1N/A {
1N/A *statp = EX_CONFIG;
1N/A return NULL;
1N/A }
1N/A
1N/A if (av[1] == NULL)
1N/A macdefine(&CurEnv->e_macro, A_PERM, mid, NULL);
1N/A else
1N/A macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]);
1N/A
1N/A *statp = EX_OK;
1N/A return "";
1N/A}
1N/A/*
1N/A** REGEX modules
1N/A*/
1N/A
1N/A#if MAP_REGEX
1N/A
1N/A# include <regex.h>
1N/A
1N/A# define DEFAULT_DELIM CONDELSE
1N/A# define END_OF_FIELDS -1
1N/A# define ERRBUF_SIZE 80
1N/A# define MAX_MATCH 32
1N/A
1N/A# define xnalloc(s) memset(xalloc(s), '\0', s);
1N/A
1N/Astruct regex_map
1N/A{
1N/A regex_t *regex_pattern_buf; /* xalloc it */
1N/A int *regex_subfields; /* move to type MAP */
1N/A char *regex_delim; /* move to type MAP */
1N/A};
1N/A
1N/Astatic int parse_fields __P((char *, int *, int, int));
1N/Astatic char *regex_map_rewrite __P((MAP *, const char*, size_t, char **));
1N/A
1N/Astatic int
1N/Aparse_fields(s, ibuf, blen, nr_substrings)
1N/A char *s;
1N/A int *ibuf; /* array */
1N/A int blen; /* number of elements in ibuf */
1N/A int nr_substrings; /* number of substrings in the pattern */
1N/A{
1N/A register char *cp;
1N/A int i = 0;
1N/A bool lastone = false;
1N/A
1N/A blen--; /* for terminating END_OF_FIELDS */
1N/A cp = s;
1N/A do
1N/A {
1N/A for (;; cp++)
1N/A {
1N/A if (*cp == ',')
1N/A {
1N/A *cp = '\0';
1N/A break;
1N/A }
1N/A if (*cp == '\0')
1N/A {
1N/A lastone = true;
1N/A break;
1N/A }
1N/A }
1N/A if (i < blen)
1N/A {
1N/A int val = atoi(s);
1N/A
1N/A if (val < 0 || val >= nr_substrings)
1N/A {
1N/A syserr("field (%d) out of range, only %d substrings in pattern",
1N/A val, nr_substrings);
1N/A return -1;
1N/A }
1N/A ibuf[i++] = val;
1N/A }
1N/A else
1N/A {
1N/A syserr("too many fields, %d max", blen);
1N/A return -1;
1N/A }
1N/A s = ++cp;
1N/A } while (!lastone);
1N/A ibuf[i] = END_OF_FIELDS;
1N/A return i;
1N/A}
1N/A
1N/Abool
1N/Aregex_map_init(map, ap)
1N/A MAP *map;
1N/A char *ap;
1N/A{
1N/A int regerr;
1N/A struct regex_map *map_p;
1N/A register char *p;
1N/A char *sub_param = NULL;
1N/A int pflags;
1N/A static char defdstr[] = { (char) DEFAULT_DELIM, '\0' };
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
1N/A map->map_mname, ap);
1N/A
1N/A pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
1N/A p = ap;
1N/A map_p = (struct regex_map *) xnalloc(sizeof(*map_p));
1N/A map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t));
1N/A
1N/A for (;;)
1N/A {
1N/A while (isascii(*p) && isspace(*p))
1N/A p++;
1N/A if (*p != '-')
1N/A break;
1N/A switch (*++p)
1N/A {
1N/A case 'n': /* not */
1N/A map->map_mflags |= MF_REGEX_NOT;
1N/A break;
1N/A
1N/A case 'f': /* case sensitive */
1N/A map->map_mflags |= MF_NOFOLDCASE;
1N/A pflags &= ~REG_ICASE;
1N/A break;
1N/A
1N/A case 'b': /* basic regular expressions */
1N/A pflags &= ~REG_EXTENDED;
1N/A break;
1N/A
1N/A case 's': /* substring match () syntax */
1N/A sub_param = ++p;
1N/A pflags &= ~REG_NOSUB;
1N/A break;
1N/A
1N/A case 'd': /* delimiter */
1N/A map_p->regex_delim = ++p;
1N/A break;
1N/A
1N/A case 'a': /* map append */
1N/A map->map_app = ++p;
1N/A break;
1N/A
1N/A case 'm': /* matchonly */
1N/A map->map_mflags |= MF_MATCHONLY;
1N/A break;
1N/A
1N/A case 'q':
1N/A map->map_mflags |= MF_KEEPQUOTES;
1N/A break;
1N/A
1N/A case 'S':
1N/A map->map_spacesub = *++p;
1N/A break;
1N/A
1N/A case 'D':
1N/A map->map_mflags |= MF_DEFER;
1N/A break;
1N/A
1N/A }
1N/A while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1N/A p++;
1N/A if (*p != '\0')
1N/A *p++ = '\0';
1N/A }
1N/A if (tTd(38, 3))
1N/A sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
1N/A
1N/A if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0)
1N/A {
1N/A /* Errorhandling */
1N/A char errbuf[ERRBUF_SIZE];
1N/A
1N/A (void) regerror(regerr, map_p->regex_pattern_buf,
1N/A errbuf, sizeof(errbuf));
1N/A syserr("pattern-compile-error: %s", errbuf);
1N/A sm_free(map_p->regex_pattern_buf); /* XXX */
1N/A sm_free(map_p); /* XXX */
1N/A return false;
1N/A }
1N/A
1N/A if (map->map_app != NULL)
1N/A map->map_app = newstr(map->map_app);
1N/A if (map_p->regex_delim != NULL)
1N/A map_p->regex_delim = newstr(map_p->regex_delim);
1N/A else
1N/A map_p->regex_delim = defdstr;
1N/A
1N/A if (!bitset(REG_NOSUB, pflags))
1N/A {
1N/A /* substring matching */
1N/A int substrings;
1N/A int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1));
1N/A
1N/A substrings = map_p->regex_pattern_buf->re_nsub + 1;
1N/A
1N/A if (tTd(38, 3))
1N/A sm_dprintf("regex_map_init: nr of substrings %d\n",
1N/A substrings);
1N/A
1N/A if (substrings >= MAX_MATCH)
1N/A {
1N/A syserr("too many substrings, %d max", MAX_MATCH);
1N/A sm_free(map_p->regex_pattern_buf); /* XXX */
1N/A sm_free(map_p); /* XXX */
1N/A return false;
1N/A }
1N/A if (sub_param != NULL && sub_param[0] != '\0')
1N/A {
1N/A /* optional parameter -sfields */
1N/A if (parse_fields(sub_param, fields,
1N/A MAX_MATCH + 1, substrings) == -1)
1N/A return false;
1N/A }
1N/A else
1N/A {
1N/A int i;
1N/A
1N/A /* set default fields */
1N/A for (i = 0; i < substrings; i++)
1N/A fields[i] = i;
1N/A fields[i] = END_OF_FIELDS;
1N/A }
1N/A map_p->regex_subfields = fields;
1N/A if (tTd(38, 3))
1N/A {
1N/A int *ip;
1N/A
1N/A sm_dprintf("regex_map_init: subfields");
1N/A for (ip = fields; *ip != END_OF_FIELDS; ip++)
1N/A sm_dprintf(" %d", *ip);
1N/A sm_dprintf("\n");
1N/A }
1N/A }
1N/A map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */
1N/A return true;
1N/A}
1N/A
1N/Astatic char *
1N/Aregex_map_rewrite(map, s, slen, av)
1N/A MAP *map;
1N/A const char *s;
1N/A size_t slen;
1N/A char **av;
1N/A{
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A return map_rewrite(map, av[0], strlen(av[0]), NULL);
1N/A else
1N/A return map_rewrite(map, s, slen, av);
1N/A}
1N/A
1N/Achar *
1N/Aregex_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A int reg_res;
1N/A struct regex_map *map_p;
1N/A regmatch_t pmatch[MAX_MATCH];
1N/A
1N/A if (tTd(38, 20))
1N/A {
1N/A char **cpp;
1N/A
1N/A sm_dprintf("regex_map_lookup: key '%s'\n", name);
1N/A for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
1N/A sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp);
1N/A }
1N/A
1N/A map_p = (struct regex_map *)(map->map_db1);
1N/A reg_res = regexec(map_p->regex_pattern_buf,
1N/A name, MAX_MATCH, pmatch, 0);
1N/A
1N/A if (bitset(MF_REGEX_NOT, map->map_mflags))
1N/A {
1N/A /* option -n */
1N/A if (reg_res == REG_NOMATCH)
1N/A return regex_map_rewrite(map, "", (size_t) 0, av);
1N/A else
1N/A return NULL;
1N/A }
1N/A if (reg_res == REG_NOMATCH)
1N/A return NULL;
1N/A
1N/A if (map_p->regex_subfields != NULL)
1N/A {
1N/A /* option -s */
1N/A static char retbuf[MAXNAME];
1N/A int fields[MAX_MATCH + 1];
1N/A bool first = true;
1N/A int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
1N/A bool quotemode = false, bslashmode = false;
1N/A register char *dp, *sp;
1N/A char *endp, *ldp;
1N/A int *ip;
1N/A
1N/A dp = retbuf;
1N/A ldp = retbuf + sizeof(retbuf) - 1;
1N/A
1N/A if (av[1] != NULL)
1N/A {
1N/A if (parse_fields(av[1], fields, MAX_MATCH + 1,
1N/A (int) map_p->regex_pattern_buf->re_nsub + 1) == -1)
1N/A {
1N/A *statp = EX_CONFIG;
1N/A return NULL;
1N/A }
1N/A ip = fields;
1N/A }
1N/A else
1N/A ip = map_p->regex_subfields;
1N/A
1N/A for ( ; *ip != END_OF_FIELDS; ip++)
1N/A {
1N/A if (!first)
1N/A {
1N/A for (sp = map_p->regex_delim; *sp; sp++)
1N/A {
1N/A if (dp < ldp)
1N/A *dp++ = *sp;
1N/A }
1N/A }
1N/A else
1N/A first = false;
1N/A
1N/A if (*ip >= MAX_MATCH ||
1N/A pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
1N/A continue;
1N/A
1N/A sp = name + pmatch[*ip].rm_so;
1N/A endp = name + pmatch[*ip].rm_eo;
1N/A for (; endp > sp; sp++)
1N/A {
1N/A if (dp < ldp)
1N/A {
1N/A if (bslashmode)
1N/A {
1N/A *dp++ = *sp;
1N/A bslashmode = false;
1N/A }
1N/A else if (quotemode && *sp != '"' &&
1N/A *sp != '\\')
1N/A {
1N/A *dp++ = *sp;
1N/A }
1N/A else switch (*dp++ = *sp)
1N/A {
1N/A case '\\':
1N/A bslashmode = true;
1N/A break;
1N/A
1N/A case '(':
1N/A cmntcnt++;
1N/A break;
1N/A
1N/A case ')':
1N/A cmntcnt--;
1N/A break;
1N/A
1N/A case '<':
1N/A anglecnt++;
1N/A break;
1N/A
1N/A case '>':
1N/A anglecnt--;
1N/A break;
1N/A
1N/A case ' ':
1N/A spacecnt++;
1N/A break;
1N/A
1N/A case '"':
1N/A quotemode = !quotemode;
1N/A break;
1N/A }
1N/A }
1N/A }
1N/A }
1N/A if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
1N/A bslashmode || spacecnt != 0)
1N/A {
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "Warning: regex may cause prescan() failure map=%s lookup=%s",
1N/A map->map_mname, name);
1N/A return NULL;
1N/A }
1N/A
1N/A *dp = '\0';
1N/A
1N/A return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
1N/A }
1N/A return regex_map_rewrite(map, "", (size_t)0, av);
1N/A}
1N/A#endif /* MAP_REGEX */
1N/A/*
1N/A** NSD modules
1N/A*/
1N/A#if MAP_NSD
1N/A
1N/A# include <ndbm.h>
1N/A# define _DATUM_DEFINED
1N/A# include <ns_api.h>
1N/A
1N/Atypedef struct ns_map_list
1N/A{
1N/A ns_map_t *map; /* XXX ns_ ? */
1N/A char *mapname;
1N/A struct ns_map_list *next;
1N/A} ns_map_list_t;
1N/A
1N/Astatic ns_map_t *
1N/Ans_map_t_find(mapname)
1N/A char *mapname;
1N/A{
1N/A static ns_map_list_t *ns_maps = NULL;
1N/A ns_map_list_t *ns_map;
1N/A
1N/A /* walk the list of maps looking for the correctly named map */
1N/A for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next)
1N/A {
1N/A if (strcmp(ns_map->mapname, mapname) == 0)
1N/A break;
1N/A }
1N/A
1N/A /* if we are looking at a NULL ns_map_list_t, then create a new one */
1N/A if (ns_map == NULL)
1N/A {
1N/A ns_map = (ns_map_list_t *) xalloc(sizeof(*ns_map));
1N/A ns_map->mapname = newstr(mapname);
1N/A ns_map->map = (ns_map_t *) xalloc(sizeof(*ns_map->map));
1N/A memset(ns_map->map, '\0', sizeof(*ns_map->map));
1N/A ns_map->next = ns_maps;
1N/A ns_maps = ns_map;
1N/A }
1N/A return ns_map->map;
1N/A}
1N/A
1N/Achar *
1N/Ansd_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A int buflen, r;
1N/A char *p;
1N/A ns_map_t *ns_map;
1N/A char keybuf[MAXNAME + 1];
1N/A char buf[MAXLINE];
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name);
1N/A
1N/A buflen = strlen(name);
1N/A if (buflen > sizeof(keybuf) - 1)
1N/A buflen = sizeof(keybuf) - 1; /* XXX simply cut off? */
1N/A memmove(keybuf, name, buflen);
1N/A keybuf[buflen] = '\0';
1N/A if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1N/A makelower(keybuf);
1N/A
1N/A ns_map = ns_map_t_find(map->map_file);
1N/A if (ns_map == NULL)
1N/A {
1N/A if (tTd(38, 20))
1N/A sm_dprintf("nsd_map_t_find failed\n");
1N/A *statp = EX_UNAVAILABLE;
1N/A return NULL;
1N/A }
1N/A r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL,
1N/A buf, sizeof(buf));
1N/A if (r == NS_UNAVAIL || r == NS_TRYAGAIN)
1N/A {
1N/A *statp = EX_TEMPFAIL;
1N/A return NULL;
1N/A }
1N/A if (r == NS_BADREQ
1N/A# ifdef NS_NOPERM
1N/A || r == NS_NOPERM
1N/A# endif /* NS_NOPERM */
1N/A )
1N/A {
1N/A *statp = EX_CONFIG;
1N/A return NULL;
1N/A }
1N/A if (r != NS_SUCCESS)
1N/A {
1N/A *statp = EX_NOTFOUND;
1N/A return NULL;
1N/A }
1N/A
1N/A *statp = EX_OK;
1N/A
1N/A /* Null out trailing \n */
1N/A if ((p = strchr(buf, '\n')) != NULL)
1N/A *p = '\0';
1N/A
1N/A return map_rewrite(map, buf, strlen(buf), av);
1N/A}
1N/A#endif /* MAP_NSD */
1N/A
1N/Achar *
1N/Aarith_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A long r;
1N/A long v[2];
1N/A bool res = false;
1N/A bool boolres;
1N/A static char result[16];
1N/A char **cpp;
1N/A
1N/A if (tTd(38, 2))
1N/A {
1N/A sm_dprintf("arith_map_lookup: key '%s'\n", name);
1N/A for (cpp = av; cpp != NULL && *cpp != NULL; cpp++)
1N/A sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp);
1N/A }
1N/A r = 0;
1N/A boolres = false;
1N/A cpp = av;
1N/A *statp = EX_OK;
1N/A
1N/A /*
1N/A ** read arguments for arith map
1N/A ** - no check is made whether they are really numbers
1N/A ** - just ignores args after the second
1N/A */
1N/A
1N/A for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++)
1N/A v[r++] = strtol(*cpp, NULL, 0);
1N/A
1N/A /* operator and (at least) two operands given? */
1N/A if (name != NULL && r == 2)
1N/A {
1N/A switch (*name)
1N/A {
1N/A case '|':
1N/A r = v[0] | v[1];
1N/A break;
1N/A
1N/A case '&':
1N/A r = v[0] & v[1];
1N/A break;
1N/A
1N/A case '%':
1N/A if (v[1] == 0)
1N/A return NULL;
1N/A r = v[0] % v[1];
1N/A break;
1N/A case '+':
1N/A r = v[0] + v[1];
1N/A break;
1N/A
1N/A case '-':
1N/A r = v[0] - v[1];
1N/A break;
1N/A
1N/A case '*':
1N/A r = v[0] * v[1];
1N/A break;
1N/A
1N/A case '/':
1N/A if (v[1] == 0)
1N/A return NULL;
1N/A r = v[0] / v[1];
1N/A break;
1N/A
1N/A case 'l':
1N/A res = v[0] < v[1];
1N/A boolres = true;
1N/A break;
1N/A
1N/A case '=':
1N/A res = v[0] == v[1];
1N/A boolres = true;
1N/A break;
1N/A
1N/A case 'r':
1N/A r = v[1] - v[0] + 1;
1N/A if (r <= 0)
1N/A return NULL;
1N/A r = get_random() % r + v[0];
1N/A break;
1N/A
1N/A default:
1N/A /* XXX */
1N/A *statp = EX_CONFIG;
1N/A if (LogLevel > 10)
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "arith_map: unknown operator %c",
1N/A (isascii(*name) && isprint(*name)) ?
1N/A *name : '?');
1N/A return NULL;
1N/A }
1N/A if (boolres)
1N/A (void) sm_snprintf(result, sizeof(result),
1N/A res ? "TRUE" : "FALSE");
1N/A else
1N/A (void) sm_snprintf(result, sizeof(result), "%ld", r);
1N/A return result;
1N/A }
1N/A *statp = EX_CONFIG;
1N/A return NULL;
1N/A}
1N/A
1N/A#if SOCKETMAP
1N/A
1N/A# if NETINET || NETINET6
1N/A# include <arpa/inet.h>
1N/A# endif /* NETINET || NETINET6 */
1N/A
1N/A# define socket_map_next map_stack[0]
1N/A
1N/A/*
1N/A** SOCKET_MAP_OPEN -- open socket table
1N/A*/
1N/A
1N/Abool
1N/Asocket_map_open(map, mode)
1N/A MAP *map;
1N/A int mode;
1N/A{
1N/A STAB *s;
1N/A int sock = 0;
1N/A SOCKADDR_LEN_T addrlen = 0;
1N/A int addrno = 0;
1N/A int save_errno;
1N/A char *p;
1N/A char *colon;
1N/A char *at;
1N/A struct hostent *hp = NULL;
1N/A SOCKADDR addr;
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("socket_map_open(%s, %s, %d)\n",
1N/A map->map_mname, map->map_file, mode);
1N/A
1N/A mode &= O_ACCMODE;
1N/A
1N/A /* sendmail doesn't have the ability to write to SOCKET (yet) */
1N/A if (mode != O_RDONLY)
1N/A {
1N/A /* issue a pseudo-error message */
1N/A errno = SM_EMAPCANTWRITE;
1N/A return false;
1N/A }
1N/A
1N/A if (*map->map_file == '\0')
1N/A {
1N/A syserr("socket map \"%s\": empty or missing socket information",
1N/A map->map_mname);
1N/A return false;
1N/A }
1N/A
1N/A s = socket_map_findconn(map->map_file);
1N/A if (s->s_socketmap != NULL)
1N/A {
1N/A /* Copy open connection */
1N/A map->map_db1 = s->s_socketmap->map_db1;
1N/A
1N/A /* Add this map as head of linked list */
1N/A map->socket_map_next = s->s_socketmap;
1N/A s->s_socketmap = map;
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("using cached connection\n");
1N/A return true;
1N/A }
1N/A
1N/A if (tTd(38, 2))
1N/A sm_dprintf("opening new connection\n");
1N/A
1N/A /* following code is ripped from milter.c */
1N/A /* XXX It should be put in a library... */
1N/A
1N/A /* protocol:filename or protocol:port@host */
1N/A memset(&addr, '\0', sizeof(addr));
1N/A p = map->map_file;
1N/A colon = strchr(p, ':');
1N/A if (colon != NULL)
1N/A {
1N/A *colon = '\0';
1N/A
1N/A if (*p == '\0')
1N/A {
1N/A# if NETUNIX
1N/A /* default to AF_UNIX */
1N/A addr.sa.sa_family = AF_UNIX;
1N/A# else /* NETUNIX */
1N/A# if NETINET
1N/A /* default to AF_INET */
1N/A addr.sa.sa_family = AF_INET;
1N/A# else /* NETINET */
1N/A# if NETINET6
1N/A /* default to AF_INET6 */
1N/A addr.sa.sa_family = AF_INET6;
1N/A# else /* NETINET6 */
1N/A /* no protocols available */
1N/A syserr("socket map \"%s\": no valid socket protocols available",
1N/A map->map_mname);
1N/A return false;
1N/A# endif /* NETINET6 */
1N/A# endif /* NETINET */
1N/A# endif /* NETUNIX */
1N/A }
1N/A# if NETUNIX
1N/A else if (sm_strcasecmp(p, "unix") == 0 ||
1N/A sm_strcasecmp(p, "local") == 0)
1N/A addr.sa.sa_family = AF_UNIX;
1N/A# endif /* NETUNIX */
1N/A# if NETINET
1N/A else if (sm_strcasecmp(p, "inet") == 0)
1N/A addr.sa.sa_family = AF_INET;
1N/A# endif /* NETINET */
1N/A# if NETINET6
1N/A else if (sm_strcasecmp(p, "inet6") == 0)
1N/A addr.sa.sa_family = AF_INET6;
1N/A# endif /* NETINET6 */
1N/A else
1N/A {
1N/A# ifdef EPROTONOSUPPORT
1N/A errno = EPROTONOSUPPORT;
1N/A# else /* EPROTONOSUPPORT */
1N/A errno = EINVAL;
1N/A# endif /* EPROTONOSUPPORT */
1N/A syserr("socket map \"%s\": unknown socket type %s",
1N/A map->map_mname, p);
1N/A return false;
1N/A }
1N/A *colon++ = ':';
1N/A }
1N/A else
1N/A {
1N/A colon = p;
1N/A#if NETUNIX
1N/A /* default to AF_UNIX */
1N/A addr.sa.sa_family = AF_UNIX;
1N/A#else /* NETUNIX */
1N/A# if NETINET
1N/A /* default to AF_INET */
1N/A addr.sa.sa_family = AF_INET;
1N/A# else /* NETINET */
1N/A# if NETINET6
1N/A /* default to AF_INET6 */
1N/A addr.sa.sa_family = AF_INET6;
1N/A# else /* NETINET6 */
1N/A syserr("socket map \"%s\": unknown socket type %s",
1N/A map->map_mname, p);
1N/A return false;
1N/A# endif /* NETINET6 */
1N/A# endif /* NETINET */
1N/A#endif /* NETUNIX */
1N/A }
1N/A
1N/A# if NETUNIX
1N/A if (addr.sa.sa_family == AF_UNIX)
1N/A {
1N/A long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK;
1N/A
1N/A at = colon;
1N/A if (strlen(colon) >= sizeof(addr.sunix.sun_path))
1N/A {
1N/A syserr("socket map \"%s\": local socket name %s too long",
1N/A map->map_mname, colon);
1N/A return false;
1N/A }
1N/A errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
1N/A S_IRUSR|S_IWUSR, NULL);
1N/A
1N/A if (errno != 0)
1N/A {
1N/A /* if not safe, don't create */
1N/A syserr("socket map \"%s\": local socket name %s unsafe",
1N/A map->map_mname, colon);
1N/A return false;
1N/A }
1N/A
1N/A (void) sm_strlcpy(addr.sunix.sun_path, colon,
1N/A sizeof(addr.sunix.sun_path));
1N/A addrlen = sizeof(struct sockaddr_un);
1N/A }
1N/A else
1N/A# endif /* NETUNIX */
1N/A# if NETINET || NETINET6
1N/A if (false
1N/A# if NETINET
1N/A || addr.sa.sa_family == AF_INET
1N/A# endif /* NETINET */
1N/A# if NETINET6
1N/A || addr.sa.sa_family == AF_INET6
1N/A# endif /* NETINET6 */
1N/A )
1N/A {
1N/A unsigned short port;
1N/A
1N/A /* Parse port@host */
1N/A at = strchr(colon, '@');
1N/A if (at == NULL)
1N/A {
1N/A syserr("socket map \"%s\": bad address %s (expected port@host)",
1N/A map->map_mname, colon);
1N/A return false;
1N/A }
1N/A *at = '\0';
1N/A if (isascii(*colon) && isdigit(*colon))
1N/A port = htons((unsigned short) atoi(colon));
1N/A else
1N/A {
1N/A# ifdef NO_GETSERVBYNAME
1N/A syserr("socket map \"%s\": invalid port number %s",
1N/A map->map_mname, colon);
1N/A return false;
1N/A# else /* NO_GETSERVBYNAME */
1N/A register struct servent *sp;
1N/A
1N/A sp = getservbyname(colon, "tcp");
1N/A if (sp == NULL)
1N/A {
1N/A syserr("socket map \"%s\": unknown port name %s",
1N/A map->map_mname, colon);
1N/A return false;
1N/A }
1N/A port = sp->s_port;
1N/A# endif /* NO_GETSERVBYNAME */
1N/A }
1N/A *at++ = '@';
1N/A if (*at == '[')
1N/A {
1N/A char *end;
1N/A
1N/A end = strchr(at, ']');
1N/A if (end != NULL)
1N/A {
1N/A bool found = false;
1N/A# if NETINET
1N/A unsigned long hid = INADDR_NONE;
1N/A# endif /* NETINET */
1N/A# if NETINET6
1N/A struct sockaddr_in6 hid6;
1N/A# endif /* NETINET6 */
1N/A
1N/A *end = '\0';
1N/A# if NETINET
1N/A if (addr.sa.sa_family == AF_INET &&
1N/A (hid = inet_addr(&at[1])) != INADDR_NONE)
1N/A {
1N/A addr.sin.sin_addr.s_addr = hid;
1N/A addr.sin.sin_port = port;
1N/A found = true;
1N/A }
1N/A# endif /* NETINET */
1N/A# if NETINET6
1N/A (void) memset(&hid6, '\0', sizeof(hid6));
1N/A if (addr.sa.sa_family == AF_INET6 &&
1N/A anynet_pton(AF_INET6, &at[1],
1N/A &hid6.sin6_addr) == 1)
1N/A {
1N/A addr.sin6.sin6_addr = hid6.sin6_addr;
1N/A addr.sin6.sin6_port = port;
1N/A found = true;
1N/A }
1N/A# endif /* NETINET6 */
1N/A *end = ']';
1N/A if (!found)
1N/A {
1N/A syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
1N/A map->map_mname, at);
1N/A return false;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
1N/A map->map_mname, at);
1N/A return false;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A hp = sm_gethostbyname(at, addr.sa.sa_family);
1N/A if (hp == NULL)
1N/A {
1N/A syserr("socket map \"%s\": Unknown host name %s",
1N/A map->map_mname, at);
1N/A return false;
1N/A }
1N/A addr.sa.sa_family = hp->h_addrtype;
1N/A switch (hp->h_addrtype)
1N/A {
1N/A# if NETINET
1N/A case AF_INET:
1N/A memmove(&addr.sin.sin_addr,
1N/A hp->h_addr, INADDRSZ);
1N/A addr.sin.sin_port = port;
1N/A addrlen = sizeof(struct sockaddr_in);
1N/A addrno = 1;
1N/A break;
1N/A# endif /* NETINET */
1N/A
1N/A# if NETINET6
1N/A case AF_INET6:
1N/A memmove(&addr.sin6.sin6_addr,
1N/A hp->h_addr, IN6ADDRSZ);
1N/A addr.sin6.sin6_port = port;
1N/A addrlen = sizeof(struct sockaddr_in6);
1N/A addrno = 1;
1N/A break;
1N/A# endif /* NETINET6 */
1N/A
1N/A default:
1N/A syserr("socket map \"%s\": Unknown protocol for %s (%d)",
1N/A map->map_mname, at, hp->h_addrtype);
1N/A# if NETINET6
1N/A freehostent(hp);
1N/A# endif /* NETINET6 */
1N/A return false;
1N/A }
1N/A }
1N/A }
1N/A else
1N/A# endif /* NETINET || NETINET6 */
1N/A {
1N/A syserr("socket map \"%s\": unknown socket protocol",
1N/A map->map_mname);
1N/A return false;
1N/A }
1N/A
1N/A /* nope, actually connecting */
1N/A for (;;)
1N/A {
1N/A sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
1N/A if (sock < 0)
1N/A {
1N/A save_errno = errno;
1N/A if (tTd(38, 5))
1N/A sm_dprintf("socket map \"%s\": error creating socket: %s\n",
1N/A map->map_mname,
1N/A sm_errstring(save_errno));
1N/A# if NETINET6
1N/A if (hp != NULL)
1N/A freehostent(hp);
1N/A# endif /* NETINET6 */
1N/A return false;
1N/A }
1N/A
1N/A if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0)
1N/A break;
1N/A
1N/A /* couldn't connect.... try next address */
1N/A save_errno = errno;
1N/A p = CurHostName;
1N/A CurHostName = at;
1N/A if (tTd(38, 5))
1N/A sm_dprintf("socket_open (%s): open %s failed: %s\n",
1N/A map->map_mname, at, sm_errstring(save_errno));
1N/A CurHostName = p;
1N/A (void) close(sock);
1N/A
1N/A /* try next address */
1N/A if (hp != NULL && hp->h_addr_list[addrno] != NULL)
1N/A {
1N/A switch (addr.sa.sa_family)
1N/A {
1N/A# if NETINET
1N/A case AF_INET:
1N/A memmove(&addr.sin.sin_addr,
1N/A hp->h_addr_list[addrno++],
1N/A INADDRSZ);
1N/A break;
1N/A# endif /* NETINET */
1N/A
1N/A# if NETINET6
1N/A case AF_INET6:
1N/A memmove(&addr.sin6.sin6_addr,
1N/A hp->h_addr_list[addrno++],
1N/A IN6ADDRSZ);
1N/A break;
1N/A# endif /* NETINET6 */
1N/A
1N/A default:
1N/A if (tTd(38, 5))
1N/A sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
1N/A map->map_mname, at,
1N/A hp->h_addrtype);
1N/A# if NETINET6
1N/A freehostent(hp);
1N/A# endif /* NETINET6 */
1N/A return false;
1N/A }
1N/A continue;
1N/A }
1N/A p = CurHostName;
1N/A CurHostName = at;
1N/A if (tTd(38, 5))
1N/A sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
1N/A map->map_mname, sm_errstring(save_errno));
1N/A CurHostName = p;
1N/A# if NETINET6
1N/A if (hp != NULL)
1N/A freehostent(hp);
1N/A# endif /* NETINET6 */
1N/A return false;
1N/A }
1N/A# if NETINET6
1N/A if (hp != NULL)
1N/A {
1N/A freehostent(hp);
1N/A hp = NULL;
1N/A }
1N/A# endif /* NETINET6 */
1N/A if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd,
1N/A SM_TIME_DEFAULT,
1N/A (void *) &sock,
1N/A SM_IO_RDWR,
1N/A NULL)) == NULL)
1N/A {
1N/A close(sock);
1N/A if (tTd(38, 2))
1N/A sm_dprintf("socket_open (%s): failed to create stream: %s\n",
1N/A map->map_mname, sm_errstring(errno));
1N/A return false;
1N/A }
1N/A
1N/A /* Save connection for reuse */
1N/A s->s_socketmap = map;
1N/A return true;
1N/A}
1N/A
1N/A/*
1N/A** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
1N/A**
1N/A** Cache SOCKET connections based on the connection specifier
1N/A** and PID so we don't have multiple connections open to
1N/A** the same server for different maps. Need a separate connection
1N/A** per PID since a parent process may close the map before the
1N/A** child is done with it.
1N/A**
1N/A** Parameters:
1N/A** conn -- SOCKET map connection specifier
1N/A**
1N/A** Returns:
1N/A** Symbol table entry for the SOCKET connection.
1N/A*/
1N/A
1N/Astatic STAB *
1N/Asocket_map_findconn(conn)
1N/A const char *conn;
1N/A{
1N/A char *nbuf;
1N/A STAB *SM_NONVOLATILE s = NULL;
1N/A
1N/A nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid);
1N/A SM_TRY
1N/A s = stab(nbuf, ST_SOCKETMAP, ST_ENTER);
1N/A SM_FINALLY
1N/A sm_free(nbuf);
1N/A SM_END_TRY
1N/A return s;
1N/A}
1N/A
1N/A/*
1N/A** SOCKET_MAP_CLOSE -- close the socket
1N/A*/
1N/A
1N/Avoid
1N/Asocket_map_close(map)
1N/A MAP *map;
1N/A{
1N/A STAB *s;
1N/A MAP *smap;
1N/A
1N/A if (tTd(38, 20))
1N/A sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file,
1N/A (long) CurrentPid);
1N/A
1N/A /* Check if already closed */
1N/A if (map->map_db1 == NULL)
1N/A {
1N/A if (tTd(38, 20))
1N/A sm_dprintf("socket_map_close(%s) already closed\n",
1N/A map->map_file);
1N/A return;
1N/A }
1N/A sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT);
1N/A
1N/A /* Mark all the maps that share the connection as closed */
1N/A s = socket_map_findconn(map->map_file);
1N/A smap = s->s_socketmap;
1N/A while (smap != NULL)
1N/A {
1N/A MAP *next;
1N/A
1N/A if (tTd(38, 2) && smap != map)
1N/A sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
1N/A map->map_mname, smap->map_mname);
1N/A
1N/A smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
1N/A smap->map_db1 = NULL;
1N/A next = smap->socket_map_next;
1N/A smap->socket_map_next = NULL;
1N/A smap = next;
1N/A }
1N/A s->s_socketmap = NULL;
1N/A}
1N/A
1N/A/*
1N/A** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
1N/A*/
1N/A
1N/Achar *
1N/Asocket_map_lookup(map, name, av, statp)
1N/A MAP *map;
1N/A char *name;
1N/A char **av;
1N/A int *statp;
1N/A{
1N/A unsigned int nettolen, replylen, recvlen;
1N/A char *replybuf, *rval, *value, *status, *key;
1N/A SM_FILE_T *f;
1N/A char keybuf[MAXNAME + 1];
1N/A
1N/A replybuf = NULL;
1N/A rval = NULL;
1N/A f = (SM_FILE_T *)map->map_db1;
1N/A if (tTd(38, 20))
1N/A sm_dprintf("socket_map_lookup(%s, %s) %s\n",
1N/A map->map_mname, name, map->map_file);
1N/A
1N/A if (!bitset(MF_NOFOLDCASE, map->map_mflags))
1N/A {
1N/A nettolen = strlen(name);
1N/A if (nettolen > sizeof(keybuf) - 1)
1N/A nettolen = sizeof(keybuf) - 1;
1N/A memmove(keybuf, name, nettolen);
1N/A keybuf[nettolen] = '\0';
1N/A makelower(keybuf);
1N/A key = keybuf;
1N/A }
1N/A else
1N/A key = name;
1N/A
1N/A nettolen = strlen(map->map_mname) + 1 + strlen(key);
1N/A SM_ASSERT(nettolen > strlen(map->map_mname));
1N/A SM_ASSERT(nettolen > strlen(key));
1N/A if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,",
1N/A nettolen, map->map_mname, key) == SM_IO_EOF) ||
1N/A (sm_io_flush(f, SM_TIME_DEFAULT) != 0) ||
1N/A (sm_io_error(f)))
1N/A {
1N/A syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
1N/A map->map_mname);
1N/A *statp = EX_TEMPFAIL;
1N/A goto errcl;
1N/A }
1N/A
1N/A if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1)
1N/A {
1N/A syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
1N/A map->map_mname);
1N/A *statp = EX_TEMPFAIL;
1N/A goto errcl;
1N/A }
1N/A if (replylen > SOCKETMAP_MAXL)
1N/A {
1N/A syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
1N/A map->map_mname, replylen);
1N/A *statp = EX_TEMPFAIL;
1N/A goto errcl;
1N/A }
1N/A if (sm_io_getc(f, SM_TIME_DEFAULT) != ':')
1N/A {
1N/A syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
1N/A map->map_mname);
1N/A *statp = EX_TEMPFAIL;
1N/A goto error;
1N/A }
1N/A
1N/A replybuf = (char *) sm_malloc(replylen + 1);
1N/A if (replybuf == NULL)
1N/A {
1N/A syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
1N/A map->map_mname, replylen + 1);
1N/A *statp = EX_OSERR;
1N/A goto error;
1N/A }
1N/A
1N/A recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen);
1N/A if (recvlen < replylen)
1N/A {
1N/A syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
1N/A map->map_mname, recvlen, replylen);
1N/A *statp = EX_TEMPFAIL;
1N/A goto errcl;
1N/A }
1N/A if (sm_io_getc(f, SM_TIME_DEFAULT) != ',')
1N/A {
1N/A syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
1N/A map->map_mname);
1N/A *statp = EX_TEMPFAIL;
1N/A goto errcl;
1N/A }
1N/A status = replybuf;
1N/A replybuf[recvlen] = '\0';
1N/A value = strchr(replybuf, ' ');
1N/A if (value != NULL)
1N/A {
1N/A *value = '\0';
1N/A value++;
1N/A }
1N/A if (strcmp(status, "OK") == 0)
1N/A {
1N/A *statp = EX_OK;
1N/A
1N/A /* collect the return value */
1N/A if (bitset(MF_MATCHONLY, map->map_mflags))
1N/A rval = map_rewrite(map, key, strlen(key), NULL);
1N/A else
1N/A rval = map_rewrite(map, value, strlen(value), av);
1N/A }
1N/A else if (strcmp(status, "NOTFOUND") == 0)
1N/A {
1N/A *statp = EX_NOTFOUND;
1N/A if (tTd(38, 20))
1N/A sm_dprintf("socket_map_lookup(%s): %s not found\n",
1N/A map->map_mname, key);
1N/A }
1N/A else
1N/A {
1N/A if (tTd(38, 5))
1N/A sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
1N/A map->map_mname, key, status,
1N/A value ? value : "");
1N/A if ((strcmp(status, "TEMP") == 0) ||
1N/A (strcmp(status, "TIMEOUT") == 0))
1N/A *statp = EX_TEMPFAIL;
1N/A else if(strcmp(status, "PERM") == 0)
1N/A *statp = EX_UNAVAILABLE;
1N/A else
1N/A *statp = EX_PROTOCOL;
1N/A }
1N/A
1N/A if (replybuf != NULL)
1N/A sm_free(replybuf);
1N/A return rval;
1N/A
1N/A errcl:
1N/A socket_map_close(map);
1N/A error:
1N/A if (replybuf != NULL)
1N/A sm_free(replybuf);
1N/A return rval;
1N/A}
1N/A#endif /* SOCKETMAP */