/* * Copyright (c) 1992, 1995-1997 Eric P. Allman. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)map.c 8.186 (Berkeley) 10/21/97"; #endif /* not lint */ #include "sendmail.h" #ifdef NDBM # include # ifndef LINUX # ifdef R_FIRST ERROR README: You are running the Berkeley DB version of ndbm.h. See ERROR README: the READ_ME file about tweaking Berkeley DB so it can ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile ERROR README: and use -DNEWDB instead. # endif # endif #endif #ifdef NEWDB # include #endif #ifdef NIS struct dom_binding; /* forward reference needed on IRIX */ # include # ifdef NDBM # define NDBM_YP_COMPAT /* create YP-compatible NDBM files */ # endif #endif /* ** MAP.C -- implementations for various map classes. ** ** Each map class implements a series of functions: ** ** bool map_parse(MAP *map, char *args) ** Parse the arguments from the config file. Return TRUE ** if they were ok, FALSE otherwise. Fill in map with the ** values. ** ** char *map_lookup(MAP *map, char *key, char **args, int *pstat) ** Look up the key in the given map. If found, do any ** rewriting the map wants (including "args" if desired) ** and return the value. Set *pstat to the appropriate status ** on error and return NULL. Args will be NULL if called ** from the alias routines, although this should probably ** not be relied upon. It is suggested you call map_rewrite ** to return the results -- it takes care of null termination ** and uses a dynamically expanded buffer as needed. ** ** void map_store(MAP *map, char *key, char *value) ** Store the key:value pair in the map. ** ** bool map_open(MAP *map, int mode) ** Open the map for the indicated mode. Mode should ** be either O_RDONLY or O_RDWR. Return TRUE if it ** was opened successfully, FALSE otherwise. If the open ** failed an the MF_OPTIONAL flag is not set, it should ** also print an error. If the MF_ALIAS bit is set ** and this map class understands the @:@ convention, it ** should call aliaswait() before returning. ** ** void map_close(MAP *map) ** Close the map. ** ** This file also includes the implementation for getcanonname. ** It is currently implemented in a pretty ad-hoc manner; it ought ** to be more properly integrated into the map structure. */ #define DBMMODE 0644 #ifndef EX_NOTFOUND # define EX_NOTFOUND EX_NOHOST #endif extern bool aliaswait __P((MAP *, char *, int)); extern bool extract_canonname __P((char *, char *, char[], int)); #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL # define LOCK_ON_OPEN 1 /* we can open/create a locked file */ #else # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */ #endif #ifndef O_ACCMODE # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) #endif /* ** MAP_PARSEARGS -- parse config line arguments for database lookup ** ** This is a generic version of the map_parse method. ** ** Parameters: ** map -- the map being initialized. ** ap -- a pointer to the args on the config line. ** ** Returns: ** TRUE -- if everything parsed OK. ** FALSE -- otherwise. ** ** Side Effects: ** null terminates the filename; stores it in map */ bool map_parseargs(map, ap) MAP *map; char *ap; { register char *p = ap; map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL; for (;;) { while (isascii(*p) && isspace(*p)) p++; if (*p != '-') break; switch (*++p) { case 'N': map->map_mflags |= MF_INCLNULL; map->map_mflags &= ~MF_TRY0NULL; break; case 'O': map->map_mflags &= ~MF_TRY1NULL; break; case 'o': map->map_mflags |= MF_OPTIONAL; break; case 'f': map->map_mflags |= MF_NOFOLDCASE; break; case 'm': map->map_mflags |= MF_MATCHONLY; break; case 'A': map->map_mflags |= MF_APPEND; break; case 'q': map->map_mflags |= MF_KEEPQUOTES; break; case 'a': map->map_app = ++p; break; case 'k': while (isascii(*++p) && isspace(*p)) continue; map->map_keycolnm = p; break; case 'v': while (isascii(*++p) && isspace(*p)) continue; map->map_valcolnm = p; break; case 'z': if (*++p != '\\') map->map_coldelim = *p; else { switch (*++p) { case 'n': map->map_coldelim = '\n'; break; case 't': map->map_coldelim = '\t'; break; default: map->map_coldelim = '\\'; } } break; case 't': map->map_mflags |= MF_NODEFER; break; #ifdef RESERVED_FOR_SUN case 'd': map->map_mflags |= MF_DOMAIN_WIDE; break; case 's': /* info type */ break; #endif } while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p != '\0') *p++ = '\0'; } if (map->map_app != NULL) map->map_app = newstr(map->map_app); if (map->map_keycolnm != NULL) map->map_keycolnm = newstr(map->map_keycolnm); if (map->map_valcolnm != NULL) map->map_valcolnm = newstr(map->map_valcolnm); if (*p != '\0') { map->map_file = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p != '\0') *p++ = '\0'; map->map_file = newstr(map->map_file); } while (*p != '\0' && isascii(*p) && isspace(*p)) p++; if (*p != '\0') map->map_rebuild = newstr(p); if (map->map_file == NULL && !bitset(MCF_OPTFILE, map->map_class->map_cflags)) { syserr("No file name for %s map %s", map->map_class->map_cname, map->map_mname); return FALSE; } return TRUE; } /* ** MAP_REWRITE -- rewrite a database key, interpolating %n indications. ** ** It also adds the map_app string. It can be used as a utility ** in the map_lookup method. ** ** Parameters: ** map -- the map that causes this. ** s -- the string to rewrite, NOT necessarily null terminated. ** slen -- the length of s. ** av -- arguments to interpolate into buf. ** ** Returns: ** Pointer to rewritten result. This is static data that ** should be copied if it is to be saved! ** ** Side Effects: ** none. */ char * map_rewrite(map, s, slen, av) register MAP *map; register const char *s; int slen; char **av; { register char *bp; register char c; char **avp; register char *ap; int i; int len; static int buflen = -1; static char *buf = NULL; if (tTd(39, 1)) { printf("map_rewrite(%.*s), av =", slen, s); if (av == NULL) printf(" (nullv)"); else { for (avp = av; *avp != NULL; avp++) printf("\n\t%s", *avp); } printf("\n"); } /* count expected size of output (can safely overestimate) */ i = len = slen; if (av != NULL) { const char *sp = s; for (i = slen; --i >= 0 && (c = *sp++) != 0; ) { if (c != '%') continue; if (--i < 0) break; c = *sp++; if (!(isascii(c) && isdigit(c))) continue; for (avp = av; --c >= '0' && *avp != NULL; avp++) continue; if (*avp == NULL) continue; len += strlen(*avp); } } if (map->map_app != NULL) len += strlen(map->map_app); if (buflen < ++len) { /* need to malloc additional space */ buflen = len; if (buf != NULL) free(buf); buf = xalloc(buflen); } bp = buf; if (av == NULL) { bcopy(s, bp, slen); bp += slen; } else { while (--slen >= 0 && (c = *s++) != '\0') { if (c != '%') { pushc: *bp++ = c; continue; } if (--slen < 0 || (c = *s++) == '\0') c = '%'; if (c == '%') goto pushc; if (!(isascii(c) && isdigit(c))) { *bp++ = '%'; goto pushc; } for (avp = av; --c >= '0' && *avp != NULL; avp++) continue; if (*avp == NULL) continue; /* transliterate argument into output string */ for (ap = *avp; (c = *ap++) != '\0'; ) *bp++ = c; } } if (map->map_app != NULL) strcpy(bp, map->map_app); else *bp = '\0'; if (tTd(39, 1)) printf("map_rewrite => %s\n", buf); return buf; } /* ** INITMAPS -- initialize for aliasing ** ** Parameters: ** rebuild -- if TRUE, this rebuilds the cached versions. ** e -- current envelope. ** ** Returns: ** none. ** ** Side Effects: ** initializes aliases: ** if alias database: opens the database. ** if no database available: reads aliases into the symbol table. */ void initmaps(rebuild, e) bool rebuild; register ENVELOPE *e; { extern void map_init(); #if XDEBUG checkfd012("entering initmaps"); #endif CurEnv = e; stabapply(map_init, 0); stabapply(map_init, rebuild ? 2 : 1); #if XDEBUG checkfd012("exiting initmaps"); #endif } void map_init(s, pass) register STAB *s; int pass; { bool rebuildable; register MAP *map; /* has to be a map */ if (s->s_type != ST_MAP) return; map = &s->s_map; if (!bitset(MF_VALID, map->map_mflags)) return; if (tTd(38, 2)) printf("map_init(%s:%s, %s, %d)\n", map->map_class->map_cname == NULL ? "NULL" : map->map_class->map_cname, map->map_mname == NULL ? "NULL" : map->map_mname, map->map_file == NULL ? "NULL" : map->map_file, pass); /* ** Pass 0 opens all non-rebuildable maps. ** Pass 1 opens all rebuildable maps for read. ** Pass 2 rebuilds all rebuildable maps. */ rebuildable = (bitset(MF_ALIAS, map->map_mflags) && bitset(MCF_REBUILDABLE, map->map_class->map_cflags)); if ((pass == 0 && rebuildable) || ((pass == 1 || pass == 2) && !rebuildable)) { if (tTd(38, 3)) printf("\twrong pass (pass = %d, rebuildable = %d)\n", pass, rebuildable); return; } /* if already open, close it (for nested open) */ if (bitset(MF_OPEN, map->map_mflags)) { map->map_class->map_close(map); map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); } if (pass == 2) { rebuildaliases(map, FALSE); return; } if (map->map_class->map_open(map, O_RDONLY)) { if (tTd(38, 4)) printf("\t%s:%s %s: valid\n", map->map_class->map_cname == NULL ? "NULL" : map->map_class->map_cname, map->map_mname == NULL ? "NULL" : map->map_mname, map->map_file == NULL ? "NULL" : map->map_file); map->map_mflags |= MF_OPEN; } else { if (tTd(38, 4)) printf("\t%s:%s %s: invalid: %s\n", map->map_class->map_cname == NULL ? "NULL" : map->map_class->map_cname, map->map_mname == NULL ? "NULL" : map->map_mname, map->map_file == NULL ? "NULL" : map->map_file, errstring(errno)); if (!bitset(MF_OPTIONAL, map->map_mflags)) { extern MAPCLASS BogusMapClass; map->map_class = &BogusMapClass; map->map_mflags |= MF_OPEN; } } } /* ** GETCANONNAME -- look up name using service switch ** ** Parameters: ** host -- the host name to look up. ** hbsize -- the size of the host buffer. ** trymx -- if set, try MX records. ** ** Returns: ** TRUE -- if the host was found. ** FALSE -- otherwise. */ bool getcanonname(host, hbsize, trymx) char *host; int hbsize; bool trymx; { int nmaps; int mapno; bool found = FALSE; bool got_tempfail = FALSE; auto int stat; char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; nmaps = switch_map_find("hosts", maptype, mapreturn); for (mapno = 0; mapno < nmaps; mapno++) { int i; if (tTd(38, 20)) printf("getcanonname(%s), trying %s\n", host, maptype[mapno]); if (strcmp("files", maptype[mapno]) == 0) { extern bool text_getcanonname __P((char *, int, int *)); found = text_getcanonname(host, hbsize, &stat); } #ifdef NIS else if (strcmp("nis", maptype[mapno]) == 0) { extern bool nis_getcanonname __P((char *, int, int *)); found = nis_getcanonname(host, hbsize, &stat); } #endif #ifdef NISPLUS else if (strcmp("nisplus", maptype[mapno]) == 0) { extern bool nisplus_getcanonname __P((char *, int, int *)); found = nisplus_getcanonname(host, hbsize, &stat); } #endif #if NAMED_BIND else if (strcmp("dns", maptype[mapno]) == 0) { extern bool dns_getcanonname __P((char *, int, bool, int *)); found = dns_getcanonname(host, hbsize, trymx, &stat); } #endif #if NETINFO else if (strcmp("netinfo", maptype[mapno]) == 0) { extern bool ni_getcanonname __P((char *, int, int *)); found = ni_getcanonname(host, hbsize, &stat); } #endif else { found = FALSE; stat = EX_UNAVAILABLE; } /* ** Heuristic: if $m is not set, we are running during system ** startup. In this case, when a name is apparently found ** but has no dot, treat is as not found. This avoids ** problems if /etc/hosts has no FQDN but is listed first ** in the service switch. */ if (found && (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL)) break; /* see if we should continue */ if (stat == EX_TEMPFAIL) { i = MA_TRYAGAIN; got_tempfail = TRUE; } else if (stat == EX_NOTFOUND) i = MA_NOTFOUND; else i = MA_UNAVAIL; if (bitset(1 << mapno, mapreturn[i])) break; } if (found) { char *d; if (tTd(38, 20)) printf("getcanonname(%s), found\n", host); /* ** If returned name is still single token, compensate ** by tagging on $m. This is because some sites set ** up their DNS or NIS databases wrong. */ if ((d = strchr(host, '.')) == NULL || d[1] == '\0') { d = macvalue('m', CurEnv); if (d != NULL && hbsize > (int) (strlen(host) + strlen(d) + 1)) { if (host[strlen(host) - 1] != '.') strcat(host, "."); strcat(host, d); } else { return FALSE; } } return TRUE; } if (tTd(38, 20)) printf("getcanonname(%s), failed, stat=%d\n", host, stat); #if NAMED_BIND if (got_tempfail) h_errno = TRY_AGAIN; else h_errno = HOST_NOT_FOUND; #endif return FALSE; } /* ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry ** ** Parameters: ** name -- the name against which to match. ** line -- the /etc/hosts line. ** cbuf -- the location to store the result. ** cbuflen -- the size of cbuf. ** ** Returns: ** TRUE -- if the line matched the desired name. ** FALSE -- otherwise. */ bool extract_canonname(name, line, cbuf, cbuflen) char *name; char *line; char cbuf[]; int cbuflen; { int i; char *p; bool found = FALSE; extern char *get_column __P((char *, int, char, char *, int)); cbuf[0] = '\0'; if (line[0] == '#') return FALSE; for (i = 1; ; i++) { char nbuf[MAXNAME + 1]; p = get_column(line, i, '\0', nbuf, sizeof nbuf); if (p == NULL) break; if (*p == '\0') continue; if (cbuf[0] == '\0' || (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL)) { snprintf(cbuf, cbuflen, "%s", p); } if (strcasecmp(name, p) == 0) found = TRUE; } if (found && strchr(cbuf, '.') == NULL) { /* try to add a domain on the end of the name */ char *domain = macvalue('m', CurEnv); if (domain != NULL && strlen(domain) + strlen(cbuf) + 1 < cbuflen) { p = &cbuf[strlen(cbuf)]; *p++ = '.'; strcpy(p, domain); } } return found; } /* ** NDBM modules */ #ifdef NDBM /* ** NDBM_MAP_OPEN -- DBM-style map open */ bool ndbm_map_open(map, mode) MAP *map; int mode; { register DBM *dbm; struct stat st; int dfd; int pfd; int sff; int ret; int smode = S_IREAD; char dirfile[MAXNAME + 1]; char pagfile[MAXNAME + 1]; struct stat std, stp; if (tTd(38, 2)) printf("ndbm_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); map->map_lockfd = -1; mode &= O_ACCMODE; /* do initial file and directory checks */ snprintf(dirfile, sizeof dirfile, "%s.dir", map->map_file); snprintf(pagfile, sizeof pagfile, "%s.pag", map->map_file); sff = SFF_ROOTOK|SFF_REGONLY; if (mode == O_RDWR) { sff |= SFF_NOLINK|SFF_CREAT; smode = S_IWRITE; } else { sff |= SFF_NOWLINK; } if (FatalWritableDirs) sff |= SFF_SAFEDIRPATH; if ((ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &std)) != 0 || (ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &stp)) != 0) { /* cannot open this map */ if (tTd(38, 2)) printf("\tunsafe map file: %d\n", ret); if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("dbm map \"%s\": unsafe map file %s", map->map_mname, map->map_file); return FALSE; } if (std.st_mode == ST_MODE_NOFILE) mode |= O_CREAT|O_EXCL; #if LOCK_ON_OPEN if (mode == O_RDONLY) mode |= O_SHLOCK; else mode |= O_TRUNC|O_EXLOCK; #else if ((mode & O_ACCMODE) == O_RDWR) { # if NOFTRUNCATE /* ** Warning: race condition. Try to lock the file as ** quickly as possible after opening it. ** This may also have security problems on some systems, ** but there isn't anything we can do about it. */ mode |= O_TRUNC; # else /* ** This ugly code opens the map without truncating it, ** locks the file, then truncates it. Necessary to ** avoid race conditions. */ int dirfd; int pagfd; dirfd = safeopen(dirfile, mode, DBMMODE, SFF_NOLINK|SFF_CREAT|SFF_OPENASROOT); pagfd = safeopen(pagfile, mode, DBMMODE, SFF_NOLINK|SFF_CREAT|SFF_OPENASROOT); if (dirfd < 0 || pagfd < 0) { int save_errno = errno; if (dirfd >= 0) (void) close(dirfd); if (pagfd >= 0) (void) close(pagfd); errno = save_errno; syserr("ndbm_map_open: cannot create database %s", map->map_file); return FALSE; } if (ftruncate(dirfd, (off_t) 0) < 0 || ftruncate(pagfd, (off_t) 0) < 0) { int save_errno = errno; (void) close(dirfd); (void) close(pagfd); errno = save_errno; syserr("ndbm_map_open: cannot truncate %s.{dir,pag}", map->map_file); return FALSE; } /* if new file, get "before" bits for later filechanged check */ if (std.st_mode == ST_MODE_NOFILE && (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0)) { int save_errno = errno; (void) close(dirfd); (void) close(pagfd); errno = save_errno; syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file", map->map_file); return FALSE; } /* have to save the lock for the duration (bletch) */ map->map_lockfd = dirfd; close(pagfd); /* twiddle bits for dbm_open */ mode &= ~(O_CREAT|O_EXCL); # endif } #endif /* open the database */ dbm = dbm_open(map->map_file, mode, DBMMODE); if (dbm == NULL) { int save_errno = errno; if (bitset(MF_ALIAS, map->map_mflags) && aliaswait(map, ".pag", FALSE)) return TRUE; #if !LOCK_ON_OPEN && !NOFTRUNCATE if (map->map_lockfd >= 0) close(map->map_lockfd); #endif errno = save_errno; if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("Cannot open DBM database %s", map->map_file); return FALSE; } #ifndef LINUX dfd = dbm_dirfno(dbm); pfd = dbm_pagfno(dbm); if (dfd == pfd) { /* heuristic: if files are linked, this is actually gdbm */ dbm_close(dbm); #if !LOCK_ON_OPEN && !NOFTRUNCATE if (map->map_lockfd >= 0) close(map->map_lockfd); #endif errno = 0; syserr("dbm map \"%s\": cannot support GDBM", map->map_mname); return FALSE; } #endif if (filechanged(dirfile, dfd, &std, sff) || filechanged(pagfile, pfd, &stp, sff)) { int save_errno = errno; dbm_close(dbm); #if !LOCK_ON_OPEN && !NOFTRUNCATE if (map->map_lockfd >= 0) close(map->map_lockfd); #endif errno = save_errno; syserr("ndbm_map_open(%s): file changed after open", map->map_file); return FALSE; } map->map_db1 = (ARBPTR_T) dbm; if (mode == O_RDONLY) { #if LOCK_ON_OPEN if (dfd >= 0) (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN); if (pfd >= 0) (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN); #endif if (bitset(MF_ALIAS, map->map_mflags) && !aliaswait(map, ".pag", TRUE)) return FALSE; } else { map->map_mflags |= MF_LOCKED; } if (fstat(dfd, &st) >= 0) map->map_mtime = st.st_mtime; return TRUE; } /* ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map */ char * ndbm_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { datum key, val; int fd; char keybuf[MAXNAME + 1]; struct stat stbuf; if (tTd(38, 20)) printf("ndbm_map_lookup(%s, %s)\n", map->map_mname, name); key.dptr = name; key.dsize = strlen(name); if (!bitset(MF_NOFOLDCASE, map->map_mflags)) { if (key.dsize > sizeof keybuf - 1) key.dsize = sizeof keybuf - 1; bcopy(key.dptr, keybuf, key.dsize); keybuf[key.dsize] = '\0'; makelower(keybuf); key.dptr = keybuf; } lockdbm: fd = dbm_dirfno((DBM *) map->map_db1); if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) (void) lockfile(fd, map->map_file, ".dir", LOCK_SH); if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime) { /* Reopen the database to sync the cache */ int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR : O_RDONLY; map->map_class->map_close(map); map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); if (map->map_class->map_open(map, omode)) { map->map_mflags |= MF_OPEN; if ((omode && O_ACCMODE) == O_RDWR) map->map_mflags |= MF_WRITABLE; goto lockdbm; } else { if (!bitset(MF_OPTIONAL, map->map_mflags)) { extern MAPCLASS BogusMapClass; *statp = EX_TEMPFAIL; map->map_class = &BogusMapClass; map->map_mflags |= MF_OPEN; syserr("Cannot reopen NDBM database %s", map->map_file); } return NULL; } } val.dptr = NULL; if (bitset(MF_TRY0NULL, map->map_mflags)) { val = dbm_fetch((DBM *) map->map_db1, key); if (val.dptr != NULL) map->map_mflags &= ~MF_TRY1NULL; } if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags)) { key.dsize++; val = dbm_fetch((DBM *) map->map_db1, key); if (val.dptr != NULL) map->map_mflags &= ~MF_TRY0NULL; } if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) (void) lockfile(fd, map->map_file, ".dir", LOCK_UN); if (val.dptr == NULL) return NULL; if (bitset(MF_MATCHONLY, map->map_mflags)) return map_rewrite(map, name, strlen(name), NULL); else return map_rewrite(map, val.dptr, val.dsize, av); } /* ** NDBM_MAP_STORE -- store a datum in the database */ void ndbm_map_store(map, lhs, rhs) register MAP *map; char *lhs; char *rhs; { datum key; datum data; int stat; char keybuf[MAXNAME + 1]; if (tTd(38, 12)) printf("ndbm_map_store(%s, %s, %s)\n", map->map_mname, lhs, rhs); key.dsize = strlen(lhs); key.dptr = lhs; if (!bitset(MF_NOFOLDCASE, map->map_mflags)) { if (key.dsize > sizeof keybuf - 1) key.dsize = sizeof keybuf - 1; bcopy(key.dptr, keybuf, key.dsize); keybuf[key.dsize] = '\0'; makelower(keybuf); key.dptr = keybuf; } data.dsize = strlen(rhs); data.dptr = rhs; if (bitset(MF_INCLNULL, map->map_mflags)) { key.dsize++; data.dsize++; } stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT); if (stat > 0) { if (!bitset(MF_APPEND, map->map_mflags)) message("050 Warning: duplicate alias name %s", lhs); else { static char *buf = NULL; static int bufsiz = 0; auto int xstat; datum old; old.dptr = ndbm_map_lookup(map, key.dptr, NULL, &xstat); if (old.dptr != NULL && *(char *) old.dptr != '\0') { old.dsize = strlen(old.dptr); if (data.dsize + old.dsize + 2 > bufsiz) { if (buf != NULL) (void) free(buf); bufsiz = data.dsize + old.dsize + 2; buf = xalloc(bufsiz); } snprintf(buf, bufsiz, "%s,%s", data.dptr, old.dptr); data.dsize = data.dsize + old.dsize + 1; data.dptr = buf; if (tTd(38, 9)) printf("ndbm_map_store append=%s\n", data.dptr); } } stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE); } if (stat != 0) syserr("readaliases: dbm put (%s)", lhs); } /* ** NDBM_MAP_CLOSE -- close the database */ void ndbm_map_close(map) register MAP *map; { if (tTd(38, 9)) printf("ndbm_map_close(%s, %s, %x)\n", map->map_mname, map->map_file, map->map_mflags); if (bitset(MF_WRITABLE, map->map_mflags)) { #ifdef NDBM_YP_COMPAT bool inclnull; char buf[200]; inclnull = bitset(MF_INCLNULL, map->map_mflags); map->map_mflags &= ~MF_INCLNULL; if (strstr(map->map_file, "/yp/") != NULL) { long save_mflags = map->map_mflags; map->map_mflags |= MF_NOFOLDCASE; (void) snprintf(buf, sizeof buf, "%010ld", curtime()); ndbm_map_store(map, "YP_LAST_MODIFIED", buf); (void) gethostname(buf, sizeof buf); ndbm_map_store(map, "YP_MASTER_NAME", buf); map->map_mflags = save_mflags; } if (inclnull) map->map_mflags |= MF_INCLNULL; #endif /* write out the distinguished alias */ ndbm_map_store(map, "@", "@"); } dbm_close((DBM *) map->map_db1); /* release lock (if needed) */ #if !LOCK_ON_OPEN if (map->map_lockfd >= 0) (void) close(map->map_lockfd); #endif } #endif /* ** NEWDB (Hash and BTree) Modules */ #ifdef NEWDB /* ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives. ** ** These do rather bizarre locking. If you can lock on open, ** do that to avoid the condition of opening a database that ** is being rebuilt. If you don't, we'll try to fake it, but ** there will be a race condition. If opening for read-only, ** we immediately release the lock to avoid freezing things up. ** We really ought to hold the lock, but guarantee that we won't ** be pokey about it. That's hard to do. */ extern bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *)); /* these should be K line arguments */ #ifndef DB_CACHE_SIZE # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */ #endif #ifndef DB_HASH_NELEM # define DB_HASH_NELEM 4096 /* (starting) size of hash table */ #endif bool bt_map_open(map, mode) MAP *map; int mode; { BTREEINFO btinfo; if (tTd(38, 2)) printf("bt_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); bzero(&btinfo, sizeof btinfo); btinfo.cachesize = DB_CACHE_SIZE; return db_map_open(map, mode, "btree", DB_BTREE, &btinfo); } bool hash_map_open(map, mode) MAP *map; int mode; { HASHINFO hinfo; if (tTd(38, 2)) printf("hash_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); bzero(&hinfo, sizeof hinfo); hinfo.nelem = DB_HASH_NELEM; hinfo.cachesize = DB_CACHE_SIZE; return db_map_open(map, mode, "hash", DB_HASH, &hinfo); } bool db_map_open(map, mode, mapclassname, dbtype, openinfo) MAP *map; int mode; char *mapclassname; DBTYPE dbtype; const void *openinfo; { DB *db; int i; int omode; int smode = S_IREAD; int fd; int sff; int saveerrno; struct stat st; char buf[MAXNAME + 1]; /* do initial file and directory checks */ snprintf(buf, sizeof buf - 3, "%s", map->map_file); i = strlen(buf); if (i < 3 || strcmp(&buf[i - 3], ".db") != 0) (void) strcat(buf, ".db"); mode &= O_ACCMODE; omode = mode; sff = SFF_ROOTOK|SFF_REGONLY; if (mode == O_RDWR) { sff |= SFF_NOLINK|SFF_CREAT; smode = S_IWRITE; } else { sff |= SFF_NOWLINK; } if (FatalWritableDirs) sff |= SFF_SAFEDIRPATH; if ((i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st)) != 0) { /* cannot open this map */ if (tTd(38, 2)) printf("\tunsafe map file: %s\n", errstring(i)); errno = i; if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("%s map \"%s\": unsafe map file %s", mapclassname, map->map_mname, map->map_file); return FALSE; } if (st.st_mode == ST_MODE_NOFILE) omode |= O_CREAT|O_EXCL; map->map_lockfd = -1; #if LOCK_ON_OPEN if (mode == O_RDWR) omode |= O_TRUNC|O_EXLOCK; # if !OLD_NEWDB else omode |= O_SHLOCK; # endif #else /* ** Pre-lock the file to avoid race conditions. In particular, ** since dbopen returns NULL if the file is zero length, we ** must have a locked instance around the dbopen. */ fd = open(buf, omode, DBMMODE); if (fd < 0) { if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("db_map_open: cannot pre-open database %s", buf); return FALSE; } /* make sure no baddies slipped in just before the open... */ if (filechanged(buf, fd, &st, sff)) { int save_errno = errno; (void) close(fd); errno = save_errno; syserr("db_map_open(%s): file changed after pre-open", buf); return FALSE; } /* if new file, get the "before" bits for later filechanged check */ if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0) { int save_errno = errno; (void) close(fd); errno = save_errno; syserr("db_map_open(%s): cannot fstat pre-opened file", buf); return FALSE; } /* actually lock the pre-opened file */ if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX)) syserr("db_map_open: cannot lock %s", buf); /* set up mode bits for dbopen */ if (mode == O_RDWR) omode |= O_TRUNC; omode &= ~(O_EXCL|O_CREAT); #endif db = dbopen(buf, omode, DBMMODE, dbtype, openinfo); saveerrno = errno; #if !LOCK_ON_OPEN if (mode == O_RDWR) map->map_lockfd = fd; else (void) close(fd); #endif if (db == NULL) { if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && aliaswait(map, ".db", FALSE)) return TRUE; #if !LOCK_ON_OPEN if (map->map_lockfd >= 0) (void) close(map->map_lockfd); #endif errno = saveerrno; if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("Cannot open %s database %s", mapclassname, map->map_file); return FALSE; } if (filechanged(buf, db->fd(db), &st, sff)) { int save_errno = errno; db->close(db); #if !LOCK_ON_OPEN if (map->map_lockfd >= 0) close(map->map_lockfd); #endif errno = save_errno; syserr("db_map_open(%s): file changed after open", buf); return FALSE; } if (mode == O_RDWR) map->map_mflags |= MF_LOCKED; #if !OLD_NEWDB fd = db->fd(db); # if LOCK_ON_OPEN if (fd >= 0 && mode == O_RDONLY) { (void) lockfile(fd, buf, NULL, LOCK_UN); } # endif #endif /* try to make sure that at least the database header is on disk */ if (mode == O_RDWR) #if OLD_NEWDB (void) db->sync(db); #else (void) db->sync(db, 0); if (fd >= 0 && fstat(fd, &st) >= 0) map->map_mtime = st.st_mtime; #endif map->map_db2 = (ARBPTR_T) db; if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && !aliaswait(map, ".db", TRUE)) return FALSE; return TRUE; } /* ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map */ char * db_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { DBT key, val; register DB *db = (DB *) map->map_db2; int i; int st; int saveerrno; int fd; struct stat stbuf; char keybuf[MAXNAME + 1]; char buf[MAXNAME + 1]; if (tTd(38, 20)) printf("db_map_lookup(%s, %s)\n", map->map_mname, name); i = strlen(map->map_file); if (i > MAXNAME) i = MAXNAME; strncpy(buf, map->map_file, i); buf[i] = '\0'; if (i > 3 && strcmp(&buf[i - 3], ".db") == 0) buf[i - 3] = '\0'; key.size = strlen(name); if (key.size > sizeof keybuf - 1) key.size = sizeof keybuf - 1; key.data = keybuf; bcopy(name, keybuf, key.size); keybuf[key.size] = '\0'; if (!bitset(MF_NOFOLDCASE, map->map_mflags)) makelower(keybuf); #if !OLD_NEWDB lockdb: fd = db->fd(db); if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) (void) lockfile(fd, buf, ".db", LOCK_SH); if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime) { /* Reopen the database to sync the cache */ int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR : O_RDONLY; map->map_class->map_close(map); map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); if (map->map_class->map_open(map, omode)) { map->map_mflags |= MF_OPEN; if ((omode && O_ACCMODE) == O_RDWR) map->map_mflags |= MF_WRITABLE; db = (DB *) map->map_db2; goto lockdb; } else { if (!bitset(MF_OPTIONAL, map->map_mflags)) { extern MAPCLASS BogusMapClass; *statp = EX_TEMPFAIL; map->map_class = &BogusMapClass; map->map_mflags |= MF_OPEN; syserr("Cannot reopen DB database %s", map->map_file); } return NULL; } } #endif st = 1; if (bitset(MF_TRY0NULL, map->map_mflags)) { st = db->get(db, &key, &val, 0); if (st == 0) map->map_mflags &= ~MF_TRY1NULL; } if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags)) { key.size++; st = db->get(db, &key, &val, 0); if (st == 0) map->map_mflags &= ~MF_TRY0NULL; } saveerrno = errno; #if !OLD_NEWDB if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) (void) lockfile(fd, buf, ".db", LOCK_UN); #endif if (st != 0) { errno = saveerrno; if (st < 0) syserr("db_map_lookup: get (%s)", name); return NULL; } if (bitset(MF_MATCHONLY, map->map_mflags)) return map_rewrite(map, name, strlen(name), NULL); else return map_rewrite(map, val.data, val.size, av); } /* ** DB_MAP_STORE -- store a datum in the NEWDB database */ void db_map_store(map, lhs, rhs) register MAP *map; char *lhs; char *rhs; { int stat; DBT key; DBT data; register DB *db = map->map_db2; char keybuf[MAXNAME + 1]; if (tTd(38, 12)) printf("db_map_store(%s, %s, %s)\n", map->map_mname, lhs, rhs); key.size = strlen(lhs); key.data = lhs; if (!bitset(MF_NOFOLDCASE, map->map_mflags)) { if (key.size > sizeof keybuf - 1) key.size = sizeof keybuf - 1; bcopy(key.data, keybuf, key.size); keybuf[key.size] = '\0'; makelower(keybuf); key.data = keybuf; } data.size = strlen(rhs); data.data = rhs; if (bitset(MF_INCLNULL, map->map_mflags)) { key.size++; data.size++; } stat = db->put(db, &key, &data, R_NOOVERWRITE); if (stat > 0) { if (!bitset(MF_APPEND, map->map_mflags)) message("050 Warning: duplicate alias name %s", lhs); else { static char *buf = NULL; static int bufsiz = 0; DBT old; old.data = db_map_lookup(map, key.data, NULL, &stat); if (old.data != NULL) { old.size = strlen(old.data); if (data.size + old.size + 2 > bufsiz) { if (buf != NULL) (void) free(buf); bufsiz = data.size + old.size + 2; buf = xalloc(bufsiz); } snprintf(buf, bufsiz, "%s,%s", data.data, old.data); data.size = data.size + old.size + 1; data.data = buf; if (tTd(38, 9)) printf("db_map_store append=%s\n", (char *) data.data); } } stat = db->put(db, &key, &data, 0); } if (stat != 0) syserr("readaliases: db put (%s)", lhs); } /* ** DB_MAP_CLOSE -- add distinguished entries and close the database */ void db_map_close(map) MAP *map; { register DB *db = map->map_db2; if (tTd(38, 9)) printf("db_map_close(%s, %s, %lx)\n", map->map_mname, map->map_file, map->map_mflags); if (bitset(MF_WRITABLE, map->map_mflags)) { /* write out the distinguished alias */ db_map_store(map, "@", "@"); } #if OLD_NEWDB (void) db->sync(db); #else (void) db->sync(db, 0); #endif #if !LOCK_ON_OPEN if (map->map_lockfd >= 0) (void) close(map->map_lockfd); #endif if (db->close(db) != 0) syserr("readaliases: db close failure"); } #endif /* ** NIS Modules */ # ifdef NIS # ifndef YPERR_BUSY # define YPERR_BUSY 16 # endif /* ** NIS_MAP_OPEN -- open DBM map */ bool nis_map_open(map, mode) MAP *map; int mode; { int yperr; register char *p; auto char *vp; auto int vsize; if (tTd(38, 2)) printf("nis_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; if (mode != O_RDONLY) { /* issue a pseudo-error message */ #ifdef ENOSYS errno = ENOSYS; #else # ifdef EFTYPE errno = EFTYPE; # else errno = ENXIO; # endif #endif return FALSE; } p = strchr(map->map_file, '@'); if (p != NULL) { *p++ = '\0'; if (*p != '\0') map->map_domain = p; } if (*map->map_file == '\0') map->map_file = "mail.aliases"; if (map->map_domain == NULL) { yperr = yp_get_default_domain(&map->map_domain); if (yperr != 0) { if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("421 NIS map %s specified, but NIS not running", map->map_file); return FALSE; } } /* check to see if this map actually exists */ yperr = yp_match(map->map_domain, map->map_file, "@", 1, &vp, &vsize); if (tTd(38, 10)) printf("nis_map_open: yp_match(@, %s, %s) => %s\n", map->map_domain, map->map_file, yperr_string(yperr)); if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY) { /* ** We ought to be calling aliaswait() here if this is an ** alias file, but powerful HP-UX NIS servers apparently ** don't insert the @:@ token into the alias map when it ** is rebuilt, so aliaswait() just hangs. I hate HP-UX. */ #if 0 if (!bitset(MF_ALIAS, map->map_mflags) || aliaswait(map, NULL, TRUE)) #endif return TRUE; } if (!bitset(MF_OPTIONAL, map->map_mflags)) { syserr("421 Cannot bind to map %s in domain %s: %s", map->map_file, map->map_domain, yperr_string(yperr)); } return FALSE; } /* ** NIS_MAP_LOOKUP -- look up a datum in a NIS map */ char * nis_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { char *vp; auto int vsize; int buflen; int yperr; char keybuf[MAXNAME + 1]; if (tTd(38, 20)) printf("nis_map_lookup(%s, %s)\n", map->map_mname, name); buflen = strlen(name); if (buflen > sizeof keybuf - 1) buflen = sizeof keybuf - 1; bcopy(name, keybuf, buflen); keybuf[buflen] = '\0'; if (!bitset(MF_NOFOLDCASE, map->map_mflags)) makelower(keybuf); yperr = YPERR_KEY; if (bitset(MF_TRY0NULL, map->map_mflags)) { yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, &vp, &vsize); if (yperr == 0) map->map_mflags &= ~MF_TRY1NULL; } if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags)) { buflen++; yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, &vp, &vsize); if (yperr == 0) map->map_mflags &= ~MF_TRY0NULL; } if (yperr != 0) { if (yperr != YPERR_KEY && yperr != YPERR_BUSY) map->map_mflags &= ~(MF_VALID|MF_OPEN); return NULL; } if (bitset(MF_MATCHONLY, map->map_mflags)) return map_rewrite(map, name, strlen(name), NULL); else return map_rewrite(map, vp, vsize, av); } /* ** NIS_GETCANONNAME -- look up canonical name in NIS */ bool nis_getcanonname(name, hbsize, statp) char *name; int hbsize; int *statp; { char *vp; auto int vsize; int keylen; int yperr; static bool try0null = TRUE; static bool try1null = TRUE; static char *yp_domain = NULL; char host_record[MAXLINE]; char cbuf[MAXNAME]; char nbuf[MAXNAME + 1]; if (tTd(38, 20)) printf("nis_getcanonname(%s)\n", name); if (strlen(name) >= sizeof nbuf) { *statp = EX_UNAVAILABLE; return FALSE; } (void) strcpy(nbuf, name); shorten_hostname(nbuf); keylen = strlen(nbuf); if (yp_domain == NULL) yp_get_default_domain(&yp_domain); makelower(nbuf); yperr = YPERR_KEY; if (try0null) { yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, &vp, &vsize); if (yperr == 0) try1null = FALSE; } if (yperr == YPERR_KEY && try1null) { keylen++; yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, &vp, &vsize); if (yperr == 0) try0null = FALSE; } if (yperr != 0) { if (yperr == YPERR_KEY) *statp = EX_NOHOST; else if (yperr == YPERR_BUSY) *statp = EX_TEMPFAIL; else *statp = EX_UNAVAILABLE; return FALSE; } if (vsize >= sizeof host_record) vsize = sizeof host_record - 1; strncpy(host_record, vp, vsize); host_record[vsize] = '\0'; if (tTd(38, 44)) printf("got record `%s'\n", host_record); if (!extract_canonname(nbuf, host_record, cbuf, sizeof cbuf)) { /* this should not happen, but.... */ *statp = EX_NOHOST; return FALSE; } if (hbsize < strlen(cbuf)) { *statp = EX_UNAVAILABLE; return FALSE; } strcpy(name, cbuf); *statp = EX_OK; return TRUE; } #endif /* ** NISPLUS Modules ** ** This code donated by Sun Microsystems. */ #ifdef NISPLUS #undef NIS /* symbol conflict in nis.h */ #undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */ #include #include #define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val #define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name #define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len) #define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.') /* ** NISPLUS_MAP_OPEN -- open nisplus table */ bool nisplus_map_open(map, mode) MAP *map; int mode; { nis_result *res = NULL; int retry_cnt, max_col, i; char qbuf[MAXLINE + NIS_MAXNAMELEN]; if (tTd(38, 2)) printf("nisplus_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; if (mode != O_RDONLY) { errno = ENODEV; return FALSE; } if (*map->map_file == '\0') map->map_file = "mail_aliases.org_dir"; if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL) { /* set default NISPLUS Domain to $m */ extern char *nisplus_default_domain(); map->map_domain = newstr(nisplus_default_domain()); if (tTd(38, 2)) printf("nisplus_map_open(%s): using domain %s\n", map->map_file, map->map_domain); } if (!PARTIAL_NAME(map->map_file)) { map->map_domain = newstr(""); snprintf(qbuf, sizeof qbuf, "%s", map->map_file); } else { /* check to see if this map actually exists */ snprintf(qbuf, sizeof qbuf, "%s.%s", map->map_file, map->map_domain); } retry_cnt = 0; while (res == NULL || res->status != NIS_SUCCESS) { res = nis_lookup(qbuf, FOLLOW_LINKS); switch (res->status) { case NIS_SUCCESS: break; case NIS_TRYAGAIN: case NIS_RPCERROR: case NIS_NAMEUNREACHABLE: if (retry_cnt++ > 4) { errno = EBADR; return FALSE; } /* try not to overwhelm hosed server */ sleep(2); break; default: /* all other nisplus errors */ #if 0 if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("421 Cannot find table %s.%s: %s", map->map_file, map->map_domain, nis_sperrno(res->status)); #endif errno = EBADR; return FALSE; } } if (NIS_RES_NUMOBJ(res) != 1 || (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ)) { if (tTd(38, 10)) printf("nisplus_map_open: %s is not a table\n", qbuf); #if 0 if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("421 %s.%s: %s is not a table", map->map_file, map->map_domain, nis_sperrno(res->status)); #endif errno = EBADR; return FALSE; } /* default key column is column 0 */ if (map->map_keycolnm == NULL) map->map_keycolnm = newstr(COL_NAME(res,0)); max_col = COL_MAX(res); /* verify the key column exist */ for (i=0; i< max_col; i++) { if (!strcmp(map->map_keycolnm, COL_NAME(res,i))) break; } if (i == max_col) { if (tTd(38, 2)) printf("nisplus_map_open(%s): can not find key column %s\n", map->map_file, map->map_keycolnm); errno = EBADR; return FALSE; } /* default value column is the last column */ if (map->map_valcolnm == NULL) { map->map_valcolno = max_col - 1; return TRUE; } for (i=0; i< max_col; i++) { if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0) { map->map_valcolno = i; return TRUE; } } if (tTd(38, 2)) printf("nisplus_map_open(%s): can not find column %s\n", map->map_file, map->map_keycolnm); errno = EBADR; return FALSE; } /* ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table */ char * nisplus_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { char *p; auto int vsize; char *skp; int skleft; char search_key[MAXNAME + 4]; char qbuf[MAXLINE + NIS_MAXNAMELEN]; nis_result *result; if (tTd(38, 20)) printf("nisplus_map_lookup(%s, %s)\n", map->map_mname, name); if (!bitset(MF_OPEN, map->map_mflags)) { if (nisplus_map_open(map, O_RDONLY)) map->map_mflags |= MF_OPEN; else { *statp = EX_UNAVAILABLE; return NULL; } } /* ** Copy the name to the key buffer, escaping double quote characters ** by doubling them and quoting "]" and "," to avoid having the ** NIS+ parser choke on them. */ skleft = sizeof search_key - 4; skp = search_key; for (p = name; *p != '\0' && skleft > 0; p++) { switch (*p) { case ']': case ',': /* quote the character */ *skp++ = '"'; *skp++ = *p; *skp++ = '"'; skleft -= 3; break; case '"': /* double the quote */ *skp++ = '"'; skleft--; /* fall through... */ default: *skp++ = *p; skleft--; break; } } *skp = '\0'; if (!bitset(MF_NOFOLDCASE, map->map_mflags)) makelower(search_key); /* construct the query */ if (PARTIAL_NAME(map->map_file)) snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s", map->map_keycolnm, search_key, map->map_file, map->map_domain); else snprintf(qbuf, sizeof qbuf, "[%s=%s],%s", map->map_keycolnm, search_key, map->map_file); if (tTd(38, 20)) printf("qbuf=%s\n", qbuf); result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL); if (result->status == NIS_SUCCESS) { int count; char *str; if ((count = NIS_RES_NUMOBJ(result)) != 1) { if (LogLevel > 10) sm_syslog(LOG_WARNING, CurEnv->e_id, "%s: lookup error, expected 1 entry, got %d", map->map_file, count); /* ignore second entry */ if (tTd(38, 20)) printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n", name, count); } p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno)); /* set the length of the result */ if (p == NULL) p = ""; vsize = strlen(p); if (tTd(38, 20)) printf("nisplus_map_lookup(%s), found %s\n", name, p); if (bitset(MF_MATCHONLY, map->map_mflags)) str = map_rewrite(map, name, strlen(name), NULL); else str = map_rewrite(map, p, vsize, av); nis_freeresult(result); *statp = EX_OK; return str; } else { if (result->status == NIS_NOTFOUND) *statp = EX_NOTFOUND; else if (result->status == NIS_TRYAGAIN) *statp = EX_TEMPFAIL; else { *statp = EX_UNAVAILABLE; map->map_mflags &= ~(MF_VALID|MF_OPEN); } } if (tTd(38, 20)) printf("nisplus_map_lookup(%s), failed\n", name); nis_freeresult(result); return NULL; } /* ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+ */ bool nisplus_getcanonname(name, hbsize, statp) char *name; int hbsize; int *statp; { char *vp; auto int vsize; nis_result *result; char *p; char nbuf[MAXNAME + 1]; char qbuf[MAXLINE + NIS_MAXNAMELEN]; if (strlen(name) >= sizeof nbuf) { *statp = EX_UNAVAILABLE; return FALSE; } (void) strcpy(nbuf, name); shorten_hostname(nbuf); p = strchr(nbuf, '.'); if (p == NULL) { /* single token */ snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir", nbuf); } else if (p[1] != '\0') { /* multi token -- take only first token in nbuf */ *p = '\0'; snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir.%s", nbuf, &p[1]); } else { *statp = EX_NOHOST; return FALSE; } if (tTd(38, 20)) printf("\nnisplus_getcanoname(%s), qbuf=%s\n", name, qbuf); result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH, NULL, NULL); if (result->status == NIS_SUCCESS) { int count; char *domain; if ((count = NIS_RES_NUMOBJ(result)) != 1) { if (LogLevel > 10) sm_syslog(LOG_WARNING, CurEnv->e_id, "nisplus_getcanonname: lookup error, expected 1 entry, got %d", count); /* ignore second entry */ if (tTd(38, 20)) printf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n", name, count); } if (tTd(38, 20)) printf("nisplus_getcanoname(%s), found in directory \"%s\"\n", name, (NIS_RES_OBJECT(result))->zo_domain); vp = ((NIS_RES_OBJECT(result))->EN_col(0)); vsize = strlen(vp); if (tTd(38, 20)) printf("nisplus_getcanonname(%s), found %s\n", name, vp); if (strchr(vp, '.') != NULL) { domain = ""; } else { domain = macvalue('m', CurEnv); if (domain == NULL) domain = ""; } if (hbsize > vsize + (int) strlen(domain) + 1) { if (domain[0] == '\0') strcpy(name, vp); else snprintf(name, hbsize, "%s.%s", vp, domain); *statp = EX_OK; } else *statp = EX_NOHOST; nis_freeresult(result); return TRUE; } else { if (result->status == NIS_NOTFOUND) *statp = EX_NOHOST; else if (result->status == NIS_TRYAGAIN) *statp = EX_TEMPFAIL; else *statp = EX_UNAVAILABLE; } if (tTd(38, 20)) printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n", name, result->status, *statp); nis_freeresult(result); return FALSE; } char * nisplus_default_domain() { static char default_domain[MAXNAME + 1] = ""; char *p; if (default_domain[0] != '\0') return(default_domain); p = nis_local_directory(); snprintf(default_domain, sizeof default_domain, "%s", p); return default_domain; } #endif /* NISPLUS */ /* ** LDAP Modules ** ** Contributed by Booker C. Bense . ** Get your support from him. */ #ifdef LDAPMAP # undef NEEDGETOPT /* used for something else in LDAP */ # include # include # include "ldap_map.h" /* ** LDAP_MAP_OPEN -- open LDAP map ** ** Since LDAP is TCP-based there is not much we can or should do ** here. It might be a good idea to attempt an open/close here. */ bool ldap_map_open(map, mode) MAP *map; int mode; { if (tTd(38, 2)) printf("ldap_map_open(%s, %d)\n", map->map_mname, mode); mode &= O_ACCMODE; if (mode != O_RDONLY) { /* issue a pseudo-error message */ #ifdef ENOSYS errno = ENOSYS; #else # ifdef EFTYPE errno = EFTYPE; # else errno = ENXIO; # endif #endif return FALSE; } return TRUE; } /* ** LDAP_MAP_START -- actually open LDAP map ** ** Caching should be investigated. */ bool ldap_map_start(map) MAP *map; { LDAP_MAP_STRUCT *lmap; LDAP *ld; if (tTd(38, 2)) printf("ldap_map_start(%s)\n", map->map_mname); lmap = (LDAP_MAP_STRUCT *) map->map_db1; if (tTd(38,9)) printf("ldap_open(%s, %d)\n", lmap->ldaphost, lmap->ldapport); if ((ld = ldap_open(lmap->ldaphost,lmap->ldapport)) == NULL) { if (!bitset(MF_OPTIONAL, map->map_mflags)) { syserr("ldapopen failed to %s in map %s", lmap->ldaphost, map->map_mname); } return FALSE; } ld->ld_deref = lmap->deref; ld->ld_timelimit = lmap->timelimit; ld->ld_sizelimit = lmap->sizelimit; ld->ld_options = lmap->ldap_options; if (ldap_bind_s(ld, lmap->binddn,lmap->passwd,lmap->method) != LDAP_SUCCESS) { if (!bitset(MF_OPTIONAL, map->map_mflags)) { syserr("421 Cannot bind to map %s in ldap server %s", map->map_mname, lmap->ldaphost); } } else { /* We need to cast ld into the map structure */ lmap->ld = ld; return TRUE; } return FALSE; } /* ** LDAP_MAP_CLOSE -- close ldap map */ void ldap_map_close(map) MAP *map; { LDAP_MAP_STRUCT *lmap ; lmap = (LDAP_MAP_STRUCT *) map->map_db1; if (lmap->ld != NULL) ldap_unbind(lmap->ld); } #ifdef SUNET_ID /* ** SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form ** This only makes sense at Stanford University. */ char * sunet_id_hash(str) char *str; { char *p, *p_last; p = str; p_last = p; while (*p != '\0') { if (islower(*p) || isdigit(*p)) { *p_last = *p; p_last++; } else if (isupper(*p)) { *p_last = tolower(*p); p_last++; } ++p; } if (*p_last != '\0') *p_last = '\0'; return (str); } #endif /* SUNET_ID */ /* ** LDAP_MAP_LOOKUP -- look up a datum in a LDAP map */ char * ldap_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { LDAP_MAP_STRUCT *lmap = NULL; LDAPMessage *entry; char *vp; auto int vsize; char keybuf[MAXNAME + 1]; char filter[LDAP_MAP_MAX_FILTER + 1]; char **attr_values = NULL; char *result; int name_len; if (tTd(38, 20)) printf("ldap_map_lookup(%s, %s)\n", map->map_mname, name); /* actually open the map */ if (!ldap_map_start(map)) { result = NULL; *statp = EX_TEMPFAIL; goto quick_exit; } /* Get ldap struct pointer from map */ lmap = (LDAP_MAP_STRUCT *) map->map_db1; name_len = strlen(name); if (name_len > MAXNAME) name_len = MAXNAME; strncpy(keybuf, name, name_len); keybuf[name_len] = '\0'; if (!bitset(MF_NOFOLDCASE, map->map_mflags)) #ifdef SUNET_ID sunet_id_hash(keybuf); #else makelower(keybuf); #endif /*SUNET_ID */ /* sprintf keybuf into filter */ snprintf(filter, sizeof filter, lmap->filter, keybuf); if (ldap_search_st(lmap->ld, lmap->base,lmap->scope,filter, lmap->attr, lmap->attrsonly, &(lmap->timeout), &(lmap->res)) != LDAP_SUCCESS) { /* try close/opening map */ ldap_map_close(map); if (!ldap_map_start(map)) { result = NULL; *statp = EX_TEMPFAIL; goto quick_exit; } if (ldap_search_st(lmap->ld, lmap->base, lmap->scope, filter, lmap->attr, lmap->attrsonly, &(lmap->timeout), &(lmap->res)) != LDAP_SUCCESS) { if (!bitset(MF_OPTIONAL, map->map_mflags)) { syserr("Error in ldap_search_st using %s in map %s", filter, map->map_mname); } result = NULL; *statp = EX_TEMPFAIL; goto quick_exit; } } entry = ldap_first_entry(lmap->ld,lmap->res); if (entry == NULL) { result = NULL; *statp = EX_NOTFOUND; goto quick_exit; } /* Need to build the args for map_rewrite here */ attr_values = ldap_get_values(lmap->ld,entry,lmap->attr[0]); if (attr_values == NULL) { /* bad things happened */ result = NULL; *statp = EX_NOTFOUND; goto quick_exit; } *statp = EX_OK; /* If there is more that one use the first */ vp = attr_values[0]; vsize = strlen(vp); if (LogLevel > 9) sm_syslog(LOG_INFO, CurEnv->e_id, "ldap %.100s => %s", name, vp); if (bitset(MF_MATCHONLY, map->map_mflags)) result = map_rewrite(map, name, strlen(name), NULL); else result = map_rewrite(map, vp, vsize, av); quick_exit: if (attr_values != NULL) ldap_value_free(attr_values); if (lmap != NULL) ldap_msgfree(lmap->res); ldap_map_close(map); return result ; } /* ** LDAP_MAP_DEQUOTE - helper routine for ldap_map_parseargs */ char * ldap_map_dequote(str) char *str; { char *p; char *start; p = str; if (*p == '"') { start = ++p; /* Should probably swallow initial whitespace here */ } else { return(str); } while (*p != '"' && *p != '\0') { p++; } if (*p != '\0') *p = '\0'; return start; } /* ** LDAP_MAP_PARSEARGS -- parse ldap map definition args. */ bool ldap_map_parseargs(map,args) MAP *map; char *args; { register char *p = args; register int done; LDAP_MAP_STRUCT *lmap; /* We need to alloc an LDAP_MAP_STRUCT struct */ lmap = (LDAP_MAP_STRUCT *) xalloc(sizeof(LDAP_MAP_STRUCT)); /* Set default int's here , default strings below */ lmap->ldapport = DEFAULT_LDAP_MAP_PORT; lmap->deref = DEFAULT_LDAP_MAP_DEREF; lmap->timelimit = DEFAULT_LDAP_MAP_TIMELIMIT; lmap->sizelimit = DEFAULT_LDAP_MAP_SIZELIMIT; lmap->ldap_options = DEFAULT_LDAP_MAP_LDAP_OPTIONS; lmap->method = DEFAULT_LDAP_MAP_METHOD; lmap->scope = DEFAULT_LDAP_MAP_SCOPE; lmap->attrsonly = DEFAULT_LDAP_MAP_ATTRSONLY; lmap->timeout.tv_sec = DEFAULT_LDAP_MAP_TIMELIMIT; lmap->timeout.tv_usec = 0; /* Default char ptrs to NULL */ lmap->binddn = NULL; lmap->passwd = NULL; lmap->base = NULL; lmap->ldaphost = NULL; map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL; for (;;) { while (isascii(*p) && isspace(*p)) p++; if (*p != '-') break; switch (*++p) { case 'N': map->map_mflags |= MF_INCLNULL; map->map_mflags &= ~MF_TRY0NULL; break; case 'O': map->map_mflags &= ~MF_TRY1NULL; break; case 'o': map->map_mflags |= MF_OPTIONAL; break; case 'f': map->map_mflags |= MF_NOFOLDCASE; break; case 'm': map->map_mflags |= MF_MATCHONLY; break; case 'A': map->map_mflags |= MF_APPEND; break; case 'q': map->map_mflags |= MF_KEEPQUOTES; break; case 't': map->map_mflags |= MF_NODEFER; break; case 'a': map->map_app = ++p; break; /* Start of ldap_map specific args */ case 'k': /* search field */ while (isascii(*++p) && isspace(*p)) continue; lmap->filter = p; break; case 'v': /* attr to return */ while (isascii(*++p) && isspace(*p)) continue; lmap->attr[0] = p; lmap->attr[1] = NULL; break; /* args stolen from ldapsearch.c */ case 'R': /* don't auto chase referrals */ #ifdef LDAP_REFERRALS lmap->ldap_options &= ~LDAP_OPT_REFERRALS; #else /* LDAP_REFERRALS */ syserr("compile with -DLDAP_REFERRALS for referral support\n"); #endif /* LDAP_REFERRALS */ break; case 'n': /* retrieve attribute names only -- no values */ lmap->attrsonly += 1; break; case 's': /* search scope */ if (strncasecmp(p, "base", 4) == 0) { lmap->scope = LDAP_SCOPE_BASE; } else if (strncasecmp(p, "one", 3) == 0) { lmap->scope = LDAP_SCOPE_ONELEVEL; } else if (strncasecmp(p, "sub", 3) == 0) { lmap->scope = LDAP_SCOPE_SUBTREE; } else { /* bad config line */ if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) { syserr("Scope must be [base|one|sub] not %s in map %s", p, map->map_mname); return FALSE; } } break; case 'h': /* ldap host */ while (isascii(*++p) && isspace(*p)) continue; map->map_domain = p; lmap->ldaphost = p; break; case 'b': /* search base */ while (isascii(*++p) && isspace(*p)) continue; lmap->base = p; break; case 'p': /* ldap port */ while (isascii(*++p) && isspace(*p)) continue; lmap->ldapport = atoi(p); break; case 'l': /* time limit */ while (isascii(*++p) && isspace(*p)) continue; lmap->timelimit = atoi(p); break; } /* need to account for quoted strings here arggg... */ done = isascii(*p) && isspace(*p); while (*p != '\0' && !done) { if (*p == '"') { while (*++p != '"' && *p != '\0') { continue; } if (*p != '\0') p++; } else { p++; } done = isascii(*p) && isspace(*p); } if (*p != '\0') *p++ = '\0'; } if (map->map_app != NULL) map->map_app = newstr(ldap_map_dequote(map->map_app)); if (map->map_domain != NULL) map->map_domain = newstr(ldap_map_dequote(map->map_domain)); /* ** We need to swallow up all the stuff into a struct ** and dump it into map->map_dbptr1 */ if (lmap->ldaphost != NULL) lmap->ldaphost = newstr(ldap_map_dequote(lmap->ldaphost)); else { syserr("LDAP map: -h flag is required"); return FALSE; } if (lmap->binddn != NULL) lmap->binddn = newstr(ldap_map_dequote(lmap->binddn)); else lmap->binddn = DEFAULT_LDAP_MAP_BINDDN; if (lmap->passwd != NULL) lmap->passwd = newstr(ldap_map_dequote(lmap->passwd)); else lmap->passwd = DEFAULT_LDAP_MAP_PASSWD; if (lmap->base != NULL) lmap->base = newstr(ldap_map_dequote(lmap->base)); else { syserr("LDAP map: -b flag is required"); return FALSE; } if (lmap->filter != NULL) lmap->filter = newstr(ldap_map_dequote(lmap->filter)); else { if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) { syserr("No filter given in map %s", map->map_mname); return FALSE; } } if (lmap->attr[0] != NULL) lmap->attr[0] = newstr(ldap_map_dequote(lmap->attr[0])); else { if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) { syserr("No return attribute in %s", map->map_mname); return FALSE; } } map->map_db1 = (ARBPTR_T) lmap; return TRUE; } #endif /* LDAP Modules */ /* ** syslog map */ #if _FFR_SYSLOG_MAP #define map_prio map_lockfd /* overload field */ /* ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages. */ bool syslog_map_parseargs(map, args) MAP *map; char *args; { char *p = args; char *priority = NULL; for (;;) { while (isascii(*p) && isspace(*p)) p++; if (*p != '-') break; if (*++p == 'L') priority = ++p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p != '\0') *p++ = '\0'; } if (priority == NULL) map->map_prio = LOG_INFO; else { if (strncasecmp("LOG_", priority, 4) == 0) priority += 4; #ifdef LOG_EMERG if (strcasecmp("EMERG", priority) == 0) map->map_prio = LOG_EMERG; else #endif #ifdef LOG_ALERT if (strcasecmp("ALERT", priority) == 0) map->map_prio = LOG_ALERT; else #endif #ifdef LOG_CRIT if (strcasecmp("CRIT", priority) == 0) map->map_prio = LOG_CRIT; else #endif #ifdef LOG_ERR if (strcasecmp("ERR", priority) == 0) map->map_prio = LOG_ERR; else #endif #ifdef LOG_WARNING if (strcasecmp("WARNING", priority) == 0) map->map_prio = LOG_WARNING; else #endif #ifdef LOG_NOTICE if (strcasecmp("NOTICE", priority) == 0) map->map_prio = LOG_NOTICE; else #endif #ifdef LOG_INFO if (strcasecmp("INFO", priority) == 0) map->map_prio = LOG_INFO; else #endif #ifdef LOG_DEBUG if (strcasecmp("DEBUG", priority) == 0) map->map_prio = LOG_DEBUG; else #endif { syserr("syslog_map_parseargs: Unknown priority %s\n", priority); return FALSE; } } return TRUE; } /* ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string */ char * syslog_map_lookup(map, string, args, statp) MAP *map; char *string; char **args; int *statp; { char *ptr = map_rewrite(map, string, strlen(string), args); if (ptr != NULL) { if (tTd(38, 20)) printf("syslog_map_lookup(%s (priority %d): %s\n", map->map_mname, map->map_prio, ptr); sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr); } *statp = EX_OK; return ""; } #endif /* _FFR_SYSLOG_MAP */ /* ** HESIOD Modules */ #ifdef HESIOD #include bool hes_map_open(map, mode) MAP *map; int mode; { if (tTd(38, 2)) printf("hes_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); if (mode != O_RDONLY) { /* issue a pseudo-error message */ #ifdef ENOSYS errno = ENOSYS; #else # ifdef EFTYPE errno = EFTYPE; # else errno = ENXIO; # endif #endif return FALSE; } if (hes_error() == HES_ER_UNINIT) hes_init(); switch (hes_error()) { case HES_ER_OK: case HES_ER_NOTFOUND: return TRUE; } if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("421 cannot initialize Hesiod map (%d)", hes_error()); return FALSE; } char * hes_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { char **hp; if (tTd(38, 20)) printf("hes_map_lookup(%s, %s)\n", map->map_file, name); if (name[0] == '\\') { char *np; int nl; char nbuf[MAXNAME]; nl = strlen(name); if (nl < sizeof nbuf - 1) np = nbuf; else np = xalloc(strlen(name) + 2); np[0] = '\\'; strcpy(&np[1], name); hp = hes_resolve(np, map->map_file); if (np != nbuf) free(np); } else { hp = hes_resolve(name, map->map_file); } if (hp == NULL || hp[0] == NULL) { switch (hes_error()) { case HES_ER_OK: *statp = EX_OK; break; case HES_ER_NOTFOUND: *statp = EX_NOTFOUND; break; case HES_ER_CONFIG: *statp = EX_UNAVAILABLE; break; case HES_ER_NET: *statp = EX_TEMPFAIL; break; } return NULL; } if (bitset(MF_MATCHONLY, map->map_mflags)) return map_rewrite(map, name, strlen(name), NULL); else return map_rewrite(map, hp[0], strlen(hp[0]), av); } #endif /* ** NeXT NETINFO Modules */ #if NETINFO # define NETINFO_DEFAULT_DIR "/aliases" # define NETINFO_DEFAULT_PROPERTY "members" extern char *ni_propval __P((char *, char *, char *, char *, int)); /* ** NI_MAP_OPEN -- open NetInfo Aliases */ bool ni_map_open(map, mode) MAP *map; int mode; { char *p; if (tTd(38, 2)) printf("ni_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; if (*map->map_file == '\0') map->map_file = NETINFO_DEFAULT_DIR; if (map->map_valcolnm == NULL) map->map_valcolnm = NETINFO_DEFAULT_PROPERTY; if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags)) map->map_coldelim = ','; return TRUE; } /* ** NI_MAP_LOOKUP -- look up a datum in NetInfo */ char * ni_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { char *res; char *propval; if (tTd(38, 20)) printf("ni_map_lookup(%s, %s)\n", map->map_mname, name); propval = ni_propval(map->map_file, map->map_keycolnm, name, map->map_valcolnm, map->map_coldelim); if (propval == NULL) return NULL; if (bitset(MF_MATCHONLY, map->map_mflags)) res = map_rewrite(map, name, strlen(name), NULL); else res = map_rewrite(map, propval, strlen(propval), av); free(propval); return res; } bool ni_getcanonname(name, hbsize, statp) char *name; int hbsize; int *statp; { char *vptr; char nbuf[MAXNAME + 1]; if (tTd(38, 20)) printf("ni_getcanonname(%s)\n", name); if (strlen(name) >= sizeof nbuf) { *statp = EX_UNAVAILABLE; return FALSE; } (void) strcpy(nbuf, name); shorten_hostname(nbuf); /* we only accept single token search key */ if (strchr(nbuf, '.')) { *statp = EX_NOHOST; return FALSE; } /* Do the search */ vptr = ni_propval("/machines", NULL, nbuf, "name", '\0'); if (vptr == NULL) { *statp = EX_NOHOST; return FALSE; } if (hbsize >= strlen(vptr)) { strcpy(name, vptr); *statp = EX_OK; return TRUE; } *statp = EX_UNAVAILABLE; free(vptr); return FALSE; } /* ** NI_PROPVAL -- NetInfo property value lookup routine ** ** Parameters: ** keydir -- the NetInfo directory name in which to search ** for the key. ** keyprop -- the name of the property in which to find the ** property we are interested. Defaults to "name". ** keyval -- the value for which we are really searching. ** valprop -- the property name for the value in which we ** are interested. ** sepchar -- if non-nil, this can be multiple-valued, and ** we should return a string separated by this ** character. ** ** Returns: ** NULL -- if: ** 1. the directory is not found ** 2. the property name is not found ** 3. the property contains multiple values ** 4. some error occured ** else -- the value of the lookup. ** ** Example: ** To search for an alias value, use: ** ni_propval("/aliases", "name", aliasname, "members", ',') ** ** Notes: ** Caller should free the return value of ni_proval */ # include # define LOCAL_NETINFO_DOMAIN "." # define PARENT_NETINFO_DOMAIN ".." # define MAX_NI_LEVELS 256 char * ni_propval(keydir, keyprop, keyval, valprop, sepchar) char *keydir; char *keyprop; char *keyval; char *valprop; int sepchar; { char *propval = NULL; int i; int j, alen; void *ni = NULL; void *lastni = NULL; ni_status nis; ni_id nid; ni_namelist ninl; register char *p; char keybuf[1024]; /* ** Create the full key from the two parts. ** ** Note that directory can end with, e.g., "name=" to specify ** an alternate search property. */ i = strlen(keydir) + strlen(keyval) + 2; if (keyprop != NULL) i += strlen(keyprop) + 1; if (i > sizeof keybuf) return NULL; strcpy(keybuf, keydir); strcat(keybuf, "/"); if (keyprop != NULL) { strcat(keybuf, keyprop); strcat(keybuf, "="); } strcat(keybuf, keyval); if (tTd(38, 21)) printf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n", keydir, keyprop, keyval, valprop, sepchar, keybuf); /* ** If the passed directory and property name are found ** in one of netinfo domains we need to search (starting ** from the local domain moving all the way back to the ** root domain) set propval to the property's value ** and return it. */ for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++) { if (i == 0) { nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni); if (tTd(38, 20)) printf("ni_open(LOCAL) = %d\n", nis); } else { if (lastni != NULL) ni_free(lastni); lastni = ni; nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni); if (tTd(38, 20)) printf("ni_open(PARENT) = %d\n", nis); } /* ** Don't bother if we didn't get a handle on a ** proper domain. This is not necessarily an error. ** We would get a positive ni_status if, for instance ** we never found the directory or property and tried ** to open the parent of the root domain! */ if (nis != 0) break; /* ** Find the path to the server information. */ if (ni_pathsearch(ni, &nid, keybuf) != 0) continue; /* ** Find associated value information. */ if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0) continue; if (tTd(38, 20)) printf("ni_lookupprop: len=%d\n", ninl.ni_namelist_len); /* ** See if we have an acceptable number of values. */ if (ninl.ni_namelist_len <= 0) continue; if (sepchar == '\0' && ninl.ni_namelist_len > 1) { ni_namelist_free(&ninl); continue; } /* ** Calculate number of bytes needed and build result */ alen = 1; for (j = 0; j < ninl.ni_namelist_len; j++) alen += strlen(ninl.ni_namelist_val[j]) + 1; propval = p = xalloc(alen); for (j = 0; j < ninl.ni_namelist_len; j++) { strcpy(p, ninl.ni_namelist_val[j]); p += strlen(p); *p++ = sepchar; } *--p = '\0'; ni_namelist_free(&ninl); } /* ** Clean up. */ if (ni != NULL) ni_free(ni); if (lastni != NULL && ni != lastni) ni_free(lastni); if (tTd(38, 20)) printf("ni_propval returns: '%s'\n", propval); return propval; } #endif /* ** TEXT (unindexed text file) Modules ** ** This code donated by Sun Microsystems. */ #define map_sff map_lockfd /* overload field */ /* ** TEXT_MAP_OPEN -- open text table */ bool text_map_open(map, mode) MAP *map; int mode; { int sff; int i; if (tTd(38, 2)) printf("text_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; if (mode != O_RDONLY) { errno = ENODEV; return FALSE; } if (*map->map_file == '\0') { syserr("text map \"%s\": file name required", map->map_mname); return FALSE; } if (map->map_file[0] != '/') { syserr("text map \"%s\": file name must be fully qualified", map->map_mname); return FALSE; } sff = SFF_ROOTOK|SFF_REGONLY|SFF_NOWLINK; if (FatalWritableDirs) sff |= SFF_SAFEDIRPATH; if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName, sff, S_IRUSR, NULL)) != 0) { /* cannot open this map */ if (tTd(38, 2)) printf("\tunsafe map file: %d\n", i); if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("text map \"%s\": unsafe map file %s", map->map_mname, map->map_file); return FALSE; } if (map->map_keycolnm == NULL) map->map_keycolno = 0; else { if (!isdigit(*map->map_keycolnm)) { syserr("text map \"%s\", file %s: -k should specify a number, not %s", map->map_mname, map->map_file, map->map_keycolnm); return FALSE; } map->map_keycolno = atoi(map->map_keycolnm); } if (map->map_valcolnm == NULL) map->map_valcolno = 0; else { if (!isdigit(*map->map_valcolnm)) { syserr("text map \"%s\", file %s: -v should specify a number, not %s", map->map_mname, map->map_file, map->map_valcolnm); return FALSE; } map->map_valcolno = atoi(map->map_valcolnm); } if (tTd(38, 2)) { printf("text_map_open(%s, %s): delimiter = ", map->map_mname, map->map_file); if (map->map_coldelim == '\0') printf("(white space)\n"); else printf("%c\n", map->map_coldelim); } map->map_sff = sff; return TRUE; } /* ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table */ char * text_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { char *vp; auto int vsize; int buflen; FILE *f; char delim; int key_idx; bool found_it; int sff = map->map_sff; char search_key[MAXNAME + 1]; char linebuf[MAXLINE]; char buf[MAXNAME + 1]; extern char *get_column __P((char *, int, char, char *, int)); found_it = FALSE; if (tTd(38, 20)) printf("text_map_lookup(%s, %s)\n", map->map_mname, name); buflen = strlen(name); if (buflen > sizeof search_key - 1) buflen = sizeof search_key - 1; bcopy(name, search_key, buflen); search_key[buflen] = '\0'; if (!bitset(MF_NOFOLDCASE, map->map_mflags)) makelower(search_key); f = safefopen(map->map_file, O_RDONLY, FileMode, sff); if (f == NULL) { map->map_mflags &= ~(MF_VALID|MF_OPEN); *statp = EX_UNAVAILABLE; return NULL; } key_idx = map->map_keycolno; delim = map->map_coldelim; while (fgets(linebuf, MAXLINE, f) != NULL) { char *p; /* skip comment line */ if (linebuf[0] == '#') continue; p = strchr(linebuf, '\n'); if (p != NULL) *p = '\0'; p = get_column(linebuf, key_idx, delim, buf, sizeof buf); if (p != NULL && strcasecmp(search_key, p) == 0) { found_it = TRUE; break; } } fclose(f); if (!found_it) { *statp = EX_NOTFOUND; return NULL; } vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf); vsize = strlen(vp); *statp = EX_OK; if (bitset(MF_MATCHONLY, map->map_mflags)) return map_rewrite(map, name, strlen(name), NULL); else return map_rewrite(map, vp, vsize, av); } /* ** TEXT_GETCANONNAME -- look up canonical name in hosts file */ bool text_getcanonname(name, hbsize, statp) char *name; int hbsize; int *statp; { bool found; FILE *f; char linebuf[MAXLINE]; char cbuf[MAXNAME + 1]; char nbuf[MAXNAME + 1]; if (tTd(38, 20)) printf("text_getcanonname(%s)\n", name); if (strlen(name) >= (SIZE_T) sizeof nbuf) { *statp = EX_UNAVAILABLE; return FALSE; } (void) strcpy(nbuf, name); shorten_hostname(nbuf); f = fopen(HostsFile, "r"); if (f == NULL) { *statp = EX_UNAVAILABLE; return FALSE; } found = FALSE; while (!found && fgets(linebuf, MAXLINE, f) != NULL) { char *p = strpbrk(linebuf, "#\n"); if (p != NULL) *p = '\0'; if (linebuf[0] != '\0') found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf); } fclose(f); if (!found) { *statp = EX_NOHOST; return FALSE; } if ((SIZE_T) hbsize >= strlen(cbuf)) { strcpy(name, cbuf); *statp = EX_OK; return TRUE; } *statp = EX_UNAVAILABLE; return FALSE; } /* ** STAB (Symbol Table) Modules */ /* ** STAB_MAP_LOOKUP -- look up alias in symbol table */ char * stab_map_lookup(map, name, av, pstat) register MAP *map; char *name; char **av; int *pstat; { register STAB *s; if (tTd(38, 20)) printf("stab_lookup(%s, %s)\n", map->map_mname, name); s = stab(name, ST_ALIAS, ST_FIND); if (s != NULL) return (s->s_alias); return (NULL); } /* ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild) */ void stab_map_store(map, lhs, rhs) register MAP *map; char *lhs; char *rhs; { register STAB *s; s = stab(lhs, ST_ALIAS, ST_ENTER); s->s_alias = newstr(rhs); } /* ** STAB_MAP_OPEN -- initialize (reads data file) ** ** This is a wierd case -- it is only intended as a fallback for ** aliases. For this reason, opens for write (only during a ** "newaliases") always fails, and opens for read open the ** actual underlying text file instead of the database. */ bool stab_map_open(map, mode) register MAP *map; int mode; { FILE *af; int sff; struct stat st; if (tTd(38, 2)) printf("stab_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; if (mode != O_RDONLY) { errno = ENODEV; return FALSE; } sff = SFF_ROOTOK|SFF_REGONLY|SFF_NOWLINK; if (FatalWritableDirs) sff |= SFF_SAFEDIRPATH; af = safefopen(map->map_file, O_RDONLY, 0444, sff); if (af == NULL) return FALSE; readaliases(map, af, FALSE, FALSE); if (fstat(fileno(af), &st) >= 0) map->map_mtime = st.st_mtime; fclose(af); return TRUE; } /* ** Implicit Modules ** ** Tries several types. For back compatibility of aliases. */ /* ** IMPL_MAP_LOOKUP -- lookup in best open database */ char * impl_map_lookup(map, name, av, pstat) MAP *map; char *name; char **av; int *pstat; { if (tTd(38, 20)) printf("impl_map_lookup(%s, %s)\n", map->map_mname, name); #ifdef NEWDB if (bitset(MF_IMPL_HASH, map->map_mflags)) return db_map_lookup(map, name, av, pstat); #endif #ifdef NDBM if (bitset(MF_IMPL_NDBM, map->map_mflags)) return ndbm_map_lookup(map, name, av, pstat); #endif return stab_map_lookup(map, name, av, pstat); } /* ** IMPL_MAP_STORE -- store in open databases */ void impl_map_store(map, lhs, rhs) MAP *map; char *lhs; char *rhs; { if (tTd(38, 12)) printf("impl_map_store(%s, %s, %s)\n", map->map_mname, lhs, rhs); #ifdef NEWDB if (bitset(MF_IMPL_HASH, map->map_mflags)) db_map_store(map, lhs, rhs); #endif #ifdef NDBM if (bitset(MF_IMPL_NDBM, map->map_mflags)) ndbm_map_store(map, lhs, rhs); #endif stab_map_store(map, lhs, rhs); } /* ** IMPL_MAP_OPEN -- implicit database open */ bool impl_map_open(map, mode) MAP *map; int mode; { if (tTd(38, 2)) printf("impl_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; #ifdef NEWDB map->map_mflags |= MF_IMPL_HASH; if (hash_map_open(map, mode)) { # ifdef NDBM_YP_COMPAT if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL) # endif return TRUE; } else map->map_mflags &= ~MF_IMPL_HASH; #endif #ifdef NDBM map->map_mflags |= MF_IMPL_NDBM; if (ndbm_map_open(map, mode)) { return TRUE; } else map->map_mflags &= ~MF_IMPL_NDBM; #endif #if defined(NEWDB) || defined(NDBM) if (Verbose) message("WARNING: cannot open alias database %s", map->map_file); #else if (mode != O_RDONLY) usrerr("Cannot rebuild aliases: no database format defined"); #endif return stab_map_open(map, mode); } /* ** IMPL_MAP_CLOSE -- close any open database(s) */ void impl_map_close(map) MAP *map; { if (tTd(38, 9)) printf("impl_map_close(%s, %s, %lx)\n", map->map_mname, map->map_file, map->map_mflags); #ifdef NEWDB if (bitset(MF_IMPL_HASH, map->map_mflags)) { db_map_close(map); map->map_mflags &= ~MF_IMPL_HASH; } #endif #ifdef NDBM if (bitset(MF_IMPL_NDBM, map->map_mflags)) { ndbm_map_close(map); map->map_mflags &= ~MF_IMPL_NDBM; } #endif } /* ** User map class. ** ** Provides access to the system password file. */ /* ** USER_MAP_OPEN -- open user map ** ** Really just binds field names to field numbers. */ bool user_map_open(map, mode) MAP *map; int mode; { if (tTd(38, 2)) printf("user_map_open(%s, %d)\n", map->map_mname, mode); mode &= O_ACCMODE; if (mode != O_RDONLY) { /* issue a pseudo-error message */ #ifdef ENOSYS errno = ENOSYS; #else # ifdef EFTYPE errno = EFTYPE; # else errno = ENXIO; # endif #endif return FALSE; } if (map->map_valcolnm == NULL) /* nothing */ ; else if (strcasecmp(map->map_valcolnm, "name") == 0) map->map_valcolno = 1; else if (strcasecmp(map->map_valcolnm, "passwd") == 0) map->map_valcolno = 2; else if (strcasecmp(map->map_valcolnm, "uid") == 0) map->map_valcolno = 3; else if (strcasecmp(map->map_valcolnm, "gid") == 0) map->map_valcolno = 4; else if (strcasecmp(map->map_valcolnm, "gecos") == 0) map->map_valcolno = 5; else if (strcasecmp(map->map_valcolnm, "dir") == 0) map->map_valcolno = 6; else if (strcasecmp(map->map_valcolnm, "shell") == 0) map->map_valcolno = 7; else { syserr("User map %s: unknown column name %s", map->map_mname, map->map_valcolnm); return FALSE; } return TRUE; } /* ** USER_MAP_LOOKUP -- look up a user in the passwd file. */ char * user_map_lookup(map, key, av, statp) MAP *map; char *key; char **av; int *statp; { struct passwd *pw; auto bool fuzzy; if (tTd(38, 20)) printf("user_map_lookup(%s, %s)\n", map->map_mname, key); pw = finduser(key, &fuzzy); if (pw == NULL) return NULL; if (bitset(MF_MATCHONLY, map->map_mflags)) return map_rewrite(map, key, strlen(key), NULL); else { char *rwval = NULL; char buf[30]; switch (map->map_valcolno) { case 0: case 1: rwval = pw->pw_name; break; case 2: rwval = pw->pw_passwd; break; case 3: snprintf(buf, sizeof buf, "%d", pw->pw_uid); rwval = buf; break; case 4: snprintf(buf, sizeof buf, "%d", pw->pw_gid); rwval = buf; break; case 5: rwval = pw->pw_gecos; break; case 6: rwval = pw->pw_dir; break; case 7: rwval = pw->pw_shell; break; } return map_rewrite(map, rwval, strlen(rwval), av); } } /* ** Program map type. ** ** This provides access to arbitrary programs. It should be used ** only very sparingly, since there is no way to bound the cost ** of invoking an arbitrary program. */ char * prog_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { int i; register char *p; int fd; auto pid_t pid; char *rval; int stat; char *argv[MAXPV + 1]; char buf[MAXLINE]; if (tTd(38, 20)) printf("prog_map_lookup(%s, %s) %s\n", map->map_mname, name, map->map_file); i = 0; argv[i++] = map->map_file; if (map->map_rebuild != NULL) { snprintf(buf, sizeof buf, "%s", map->map_rebuild); for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t")) { if (i >= MAXPV - 1) break; argv[i++] = p; } } argv[i++] = name; argv[i] = NULL; if (tTd(38, 21)) { printf("prog_open:"); for (i = 0; argv[i] != NULL; i++) printf(" %s", argv[i]); printf("\n"); } (void) blocksignal(SIGCHLD); pid = prog_open(argv, &fd, CurEnv); if (pid < 0) { if (!bitset(MF_OPTIONAL, map->map_mflags)) syserr("prog_map_lookup(%s) failed (%s) -- closing", map->map_mname, errstring(errno)); else if (tTd(38, 9)) printf("prog_map_lookup(%s) failed (%s) -- closing", map->map_mname, errstring(errno)); map->map_mflags &= ~(MF_VALID|MF_OPEN); *statp = EX_OSFILE; return NULL; } i = read(fd, buf, sizeof buf - 1); if (i < 0) { syserr("prog_map_lookup(%s): read error %s\n", map->map_mname, errstring(errno)); rval = NULL; } else if (i == 0) { if (tTd(38, 20)) printf("prog_map_lookup(%s): empty answer\n", map->map_mname); rval = NULL; } else { buf[i] = '\0'; p = strchr(buf, '\n'); if (p != NULL) *p = '\0'; /* collect the return value */ if (bitset(MF_MATCHONLY, map->map_mflags)) rval = map_rewrite(map, name, strlen(name), NULL); else rval = map_rewrite(map, buf, strlen(buf), NULL); /* now flush any additional output */ while ((i = read(fd, buf, sizeof buf)) > 0) continue; } /* wait for the process to terminate */ close(fd); stat = waitfor(pid); (void) releasesignal(SIGCHLD); if (stat == -1) { syserr("prog_map_lookup(%s): wait error %s\n", map->map_mname, errstring(errno)); *statp = EX_SOFTWARE; rval = NULL; } else if (WIFEXITED(stat)) { if ((*statp = WEXITSTATUS(stat)) != EX_OK) rval = NULL; } else { syserr("prog_map_lookup(%s): child died on signal %d", map->map_mname, stat); *statp = EX_UNAVAILABLE; rval = NULL; } return rval; } /* ** Sequenced map type. ** ** Tries each map in order until something matches, much like ** implicit. Stores go to the first map in the list that can ** support storing. ** ** This is slightly unusual in that there are two interfaces. ** The "sequence" interface lets you stack maps arbitrarily. ** The "switch" interface builds a sequence map by looking ** at a system-dependent configuration file such as ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix. ** ** We don't need an explicit open, since all maps are ** opened during startup, including underlying maps. */ /* ** SEQ_MAP_PARSE -- Sequenced map parsing */ bool seq_map_parse(map, ap) MAP *map; char *ap; { int maxmap; if (tTd(38, 2)) printf("seq_map_parse(%s, %s)\n", map->map_mname, ap); maxmap = 0; while (*ap != '\0') { register char *p; STAB *s; /* find beginning of map name */ while (isascii(*ap) && isspace(*ap)) ap++; for (p = ap; isascii(*p) && isalnum(*p); p++) continue; if (*p != '\0') *p++ = '\0'; while (*p != '\0' && (!isascii(*p) || !isalnum(*p))) p++; if (*ap == '\0') { ap = p; continue; } s = stab(ap, ST_MAP, ST_FIND); if (s == NULL) { syserr("Sequence map %s: unknown member map %s", map->map_mname, ap); } else if (maxmap == MAXMAPSTACK) { syserr("Sequence map %s: too many member maps (%d max)", map->map_mname, MAXMAPSTACK); maxmap++; } else if (maxmap < MAXMAPSTACK) { map->map_stack[maxmap++] = &s->s_map; } ap = p; } return TRUE; } /* ** SWITCH_MAP_OPEN -- open a switched map ** ** This looks at the system-dependent configuration and builds ** a sequence map that does the same thing. ** ** Every system must define a switch_map_find routine in conf.c ** that will return the list of service types associated with a ** given service class. */ bool switch_map_open(map, mode) MAP *map; int mode; { int mapno; int nmaps; char *maptype[MAXMAPSTACK]; if (tTd(38, 2)) printf("switch_map_open(%s, %s, %d)\n", map->map_mname, map->map_file, mode); mode &= O_ACCMODE; nmaps = switch_map_find(map->map_file, maptype, map->map_return); if (tTd(38, 19)) { printf("\tswitch_map_find => %d\n", nmaps); for (mapno = 0; mapno < nmaps; mapno++) printf("\t\t%s\n", maptype[mapno]); } if (nmaps <= 0 || nmaps > MAXMAPSTACK) return FALSE; for (mapno = 0; mapno < nmaps; mapno++) { register STAB *s; char nbuf[MAXNAME + 1]; if (maptype[mapno] == NULL) continue; (void) snprintf(nbuf, sizeof nbuf, "%s.%s", map->map_mname, maptype[mapno]); s = stab(nbuf, ST_MAP, ST_FIND); if (s == NULL) { syserr("Switch map %s: unknown member map %s", map->map_mname, nbuf); } else { map->map_stack[mapno] = &s->s_map; if (tTd(38, 4)) printf("\tmap_stack[%d] = %s:%s\n", mapno, s->s_map.map_class->map_cname, nbuf); } } return TRUE; } /* ** SEQ_MAP_CLOSE -- close all underlying maps */ void seq_map_close(map) MAP *map; { int mapno; if (tTd(38, 9)) printf("seq_map_close(%s)\n", map->map_mname); for (mapno = 0; mapno < MAXMAPSTACK; mapno++) { MAP *mm = map->map_stack[mapno]; if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags)) continue; mm->map_class->map_close(mm); mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE); } } /* ** SEQ_MAP_LOOKUP -- sequenced map lookup */ char * seq_map_lookup(map, key, args, pstat) MAP *map; char *key; char **args; int *pstat; { int mapno; int mapbit = 0x01; bool tempfail = FALSE; if (tTd(38, 20)) printf("seq_map_lookup(%s, %s)\n", map->map_mname, key); for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++) { MAP *mm = map->map_stack[mapno]; char *rv; if (mm == NULL) continue; if (!bitset(MF_OPEN, mm->map_mflags)) { if (bitset(mapbit, map->map_return[MA_UNAVAIL])) { *pstat = EX_UNAVAILABLE; return NULL; } continue; } *pstat = EX_OK; rv = mm->map_class->map_lookup(mm, key, args, pstat); if (rv != NULL) return rv; if (*pstat == EX_TEMPFAIL) { if (bitset(mapbit, map->map_return[MA_TRYAGAIN])) return NULL; tempfail = TRUE; } else if (bitset(mapbit, map->map_return[MA_NOTFOUND])) break; } if (tempfail) *pstat = EX_TEMPFAIL; else if (*pstat == EX_OK) *pstat = EX_NOTFOUND; return NULL; } /* ** SEQ_MAP_STORE -- sequenced map store */ void seq_map_store(map, key, val) MAP *map; char *key; char *val; { int mapno; if (tTd(38, 12)) printf("seq_map_store(%s, %s, %s)\n", map->map_mname, key, val); for (mapno = 0; mapno < MAXMAPSTACK; mapno++) { MAP *mm = map->map_stack[mapno]; if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags)) continue; mm->map_class->map_store(mm, key, val); return; } syserr("seq_map_store(%s, %s, %s): no writable map", map->map_mname, key, val); } /* ** NULL stubs */ bool null_map_open(map, mode) MAP *map; int mode; { return TRUE; } void null_map_close(map) MAP *map; { return; } char * null_map_lookup(map, key, args, pstat) MAP *map; char *key; char **args; int *pstat; { *pstat = EX_NOTFOUND; return NULL; } void null_map_store(map, key, val) MAP *map; char *key; char *val; { return; } /* ** BOGUS stubs */ char * bogus_map_lookup(map, key, args, pstat) MAP *map; char *key; char **args; int *pstat; { *pstat = EX_TEMPFAIL; return NULL; } MAPCLASS BogusMapClass = { "bogus-map", NULL, 0, NULL, bogus_map_lookup, null_map_store, null_map_open, null_map_close, };