/* * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * */ #include #if USERDB SM_RCSID("@(#)$Id: udb.c,v 8.160 2003/04/03 16:32:46 ca Exp $ (with USERDB)") #else /* USERDB */ SM_RCSID("@(#)$Id: udb.c,v 8.160 2003/04/03 16:32:46 ca Exp $ (without USERDB)") #endif /* USERDB */ #if USERDB # if NEWDB # include "sm/bdb.h" # else /* NEWDB */ # define DBT struct _data_base_thang_ DBT { void *data; /* pointer to data */ size_t size; /* length of data */ }; # endif /* NEWDB */ /* ** UDB.C -- interface between sendmail and Berkeley User Data Base. ** ** This depends on the 4.4BSD db package. */ struct udbent { char *udb_spec; /* string version of spec */ int udb_type; /* type of entry */ pid_t udb_pid; /* PID of process which opened db */ char *udb_default; /* default host for outgoing mail */ union { # if NETINET || NETINET6 /* type UE_REMOTE -- do remote call for lookup */ struct { SOCKADDR _udb_addr; /* address */ int _udb_timeout; /* timeout */ } udb_remote; # define udb_addr udb_u.udb_remote._udb_addr # define udb_timeout udb_u.udb_remote._udb_timeout # endif /* NETINET || NETINET6 */ /* type UE_FORWARD -- forward message to remote */ struct { char *_udb_fwdhost; /* name of forward host */ } udb_forward; # define udb_fwdhost udb_u.udb_forward._udb_fwdhost # if NEWDB /* type UE_FETCH -- lookup in local database */ struct { char *_udb_dbname; /* pathname of database */ DB *_udb_dbp; /* open database ptr */ } udb_lookup; # define udb_dbname udb_u.udb_lookup._udb_dbname # define udb_dbp udb_u.udb_lookup._udb_dbp # endif /* NEWDB */ } udb_u; }; # define UDB_EOLIST 0 /* end of list */ # define UDB_SKIP 1 /* skip this entry */ # define UDB_REMOTE 2 /* look up in remote database */ # define UDB_DBFETCH 3 /* look up in local database */ # define UDB_FORWARD 4 /* forward to remote host */ # define UDB_HESIOD 5 /* look up via hesiod */ # define MAXUDBENT 10 /* maximum number of UDB entries */ struct udb_option { char *udbo_name; char *udbo_val; }; # if HESIOD static int hes_udb_get __P((DBT *, DBT *)); # endif /* HESIOD */ static char *udbmatch __P((char *, char *, SM_RPOOL_T *)); static int _udbx_init __P((ENVELOPE *)); static int _udb_parsespec __P((char *, struct udb_option [], int)); /* ** UDBEXPAND -- look up user in database and expand ** ** Parameters: ** a -- address to expand. ** sendq -- pointer to head of sendq to put the expansions in. ** aliaslevel -- the current alias nesting depth. ** e -- the current envelope. ** ** Returns: ** EX_TEMPFAIL -- if something "odd" happened -- probably due ** to accessing a file on an NFS server that is down. ** EX_OK -- otherwise. ** ** Side Effects: ** Modifies sendq. */ static struct udbent UdbEnts[MAXUDBENT + 1]; static bool UdbInitialized = false; int udbexpand(a, sendq, aliaslevel, e) register ADDRESS *a; ADDRESS **sendq; int aliaslevel; register ENVELOPE *e; { int i; DBT key; DBT info; bool breakout; register struct udbent *up; int keylen; int naddrs; char *user; char keybuf[MAXKEY]; memset(&key, '\0', sizeof key); memset(&info, '\0', sizeof info); if (tTd(28, 1)) sm_dprintf("udbexpand(%s)\n", a->q_paddr); /* make certain we are supposed to send to this address */ if (!QS_IS_SENDABLE(a->q_state)) return EX_OK; e->e_to = a->q_paddr; /* on first call, locate the database */ if (!UdbInitialized) { if (_udbx_init(e) == EX_TEMPFAIL) return EX_TEMPFAIL; } /* short circuit the process if no chance of a match */ if (UdbSpec == NULL || UdbSpec[0] == '\0') return EX_OK; /* extract user to do userdb matching on */ user = a->q_user; /* short circuit name begins with '\\' since it can't possibly match */ /* (might want to treat this as unquoted instead) */ if (user[0] == '\\') return EX_OK; /* if name begins with a colon, it indicates our metadata */ if (user[0] == ':') return EX_OK; keylen = sm_strlcpyn(keybuf, sizeof keybuf, 2, user, ":maildrop"); /* if name is too long, assume it won't match */ if (keylen >= sizeof keybuf) return EX_OK; /* build actual database key */ breakout = false; for (up = UdbEnts; !breakout; up++) { int usersize; int userleft; char userbuf[MEMCHUNKSIZE]; # if HESIOD && HES_GETMAILHOST char pobuf[MAXNAME]; # endif /* HESIOD && HES_GETMAILHOST */ # if defined(NEWDB) && DB_VERSION_MAJOR > 1 DBC *dbc = NULL; # endif /* defined(NEWDB) && DB_VERSION_MAJOR > 1 */ user = userbuf; userbuf[0] = '\0'; usersize = sizeof userbuf; userleft = sizeof userbuf - 1; /* ** Select action based on entry type. ** ** On dropping out of this switch, "class" should ** explain the type of the data, and "user" should ** contain the user information. */ switch (up->udb_type) { # if NEWDB case UDB_DBFETCH: key.data = keybuf; key.size = keylen; if (tTd(28, 80)) sm_dprintf("udbexpand: trying %s (%d) via db\n", keybuf, keylen); # if DB_VERSION_MAJOR < 2 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); # else /* DB_VERSION_MAJOR < 2 */ i = 0; if (dbc == NULL && # if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 (errno = (*up->udb_dbp->cursor)(up->udb_dbp, NULL, &dbc, 0)) != 0) # else /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */ (errno = (*up->udb_dbp->cursor)(up->udb_dbp, NULL, &dbc)) != 0) # endif /* DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 */ i = -1; if (i != 0 || dbc == NULL || (errno = dbc->c_get(dbc, &key, &info, DB_SET)) != 0) i = 1; # endif /* DB_VERSION_MAJOR < 2 */ if (i > 0 || info.size <= 0) { if (tTd(28, 2)) sm_dprintf("udbexpand: no match on %s (%d)\n", keybuf, keylen); # if DB_VERSION_MAJOR > 1 if (dbc != NULL) { (void) dbc->c_close(dbc); dbc = NULL; } # endif /* DB_VERSION_MAJOR > 1 */ break; } if (tTd(28, 80)) sm_dprintf("udbexpand: match %.*s: %.*s\n", (int) key.size, (char *) key.data, (int) info.size, (char *) info.data); a->q_flags &= ~QSELFREF; while (i == 0 && key.size == keylen && memcmp(key.data, keybuf, keylen) == 0) { char *p; if (bitset(EF_VRFYONLY, e->e_flags)) { a->q_state = QS_VERIFIED; # if DB_VERSION_MAJOR > 1 if (dbc != NULL) { (void) dbc->c_close(dbc); dbc = NULL; } # endif /* DB_VERSION_MAJOR > 1 */ return EX_OK; } breakout = true; if (info.size >= userleft - 1) { char *nuser; int size = MEMCHUNKSIZE; if (info.size > MEMCHUNKSIZE) size = info.size; nuser = sm_malloc_x(usersize + size); memmove(nuser, user, usersize); if (user != userbuf) sm_free(user); /* XXX */ user = nuser; usersize += size; userleft += size; } p = &user[strlen(user)]; if (p != user) { *p++ = ','; userleft--; } memmove(p, info.data, info.size); p[info.size] = '\0'; userleft -= info.size; /* get the next record */ # if DB_VERSION_MAJOR < 2 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); # else /* DB_VERSION_MAJOR < 2 */ i = 0; if ((errno = dbc->c_get(dbc, &key, &info, DB_NEXT)) != 0) i = 1; # endif /* DB_VERSION_MAJOR < 2 */ } # if DB_VERSION_MAJOR > 1 if (dbc != NULL) { (void) dbc->c_close(dbc); dbc = NULL; } # endif /* DB_VERSION_MAJOR > 1 */ /* if nothing ever matched, try next database */ if (!breakout) break; message("expanded to %s", user); if (LogLevel > 10) sm_syslog(LOG_INFO, e->e_id, "expand %.100s => %s", e->e_to, shortenstring(user, MAXSHORTSTR)); naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e); if (naddrs > 0 && !bitset(QSELFREF, a->q_flags)) { if (tTd(28, 5)) { sm_dprintf("udbexpand: QS_EXPANDED "); printaddr(sm_debug_file(), a, false); } a->q_state = QS_EXPANDED; } if (i < 0) { syserr("udbexpand: db-get %.*s stat %d", (int) key.size, (char *) key.data, i); return EX_TEMPFAIL; } /* ** If this address has a -request address, reflect ** it into the envelope. */ memset(&key, '\0', sizeof key); memset(&info, '\0', sizeof info); (void) sm_strlcpyn(keybuf, sizeof keybuf, 2, a->q_user, ":mailsender"); keylen = strlen(keybuf); key.data = keybuf; key.size = keylen; # if DB_VERSION_MAJOR < 2 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); # else /* DB_VERSION_MAJOR < 2 */ i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL, &key, &info, 0); # endif /* DB_VERSION_MAJOR < 2 */ if (i != 0 || info.size <= 0) break; a->q_owner = sm_rpool_malloc_x(e->e_rpool, info.size + 1); memmove(a->q_owner, info.data, info.size); a->q_owner[info.size] = '\0'; /* announce delivery; NORECEIPT bit set later */ if (e->e_xfp != NULL) { (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Message delivered to mailing list %s\n", a->q_paddr); } e->e_flags |= EF_SENDRECEIPT; a->q_flags |= QDELIVERED|QEXPANDED; break; # endif /* NEWDB */ # if HESIOD case UDB_HESIOD: key.data = keybuf; key.size = keylen; if (tTd(28, 80)) sm_dprintf("udbexpand: trying %s (%d) via hesiod\n", keybuf, keylen); /* look up the key via hesiod */ i = hes_udb_get(&key, &info); if (i < 0) { syserr("udbexpand: hesiod-get %.*s stat %d", (int) key.size, (char *) key.data, i); return EX_TEMPFAIL; } else if (i > 0 || info.size <= 0) { # if HES_GETMAILHOST struct hes_postoffice *hp; # endif /* HES_GETMAILHOST */ if (tTd(28, 2)) sm_dprintf("udbexpand: no match on %s (%d)\n", (char *) keybuf, (int) keylen); # if HES_GETMAILHOST if (tTd(28, 8)) sm_dprintf(" ... trying hes_getmailhost(%s)\n", a->q_user); hp = hes_getmailhost(a->q_user); if (hp == NULL) { if (hes_error() == HES_ER_NET) { syserr("udbexpand: hesiod-getmail %s stat %d", a->q_user, hes_error()); return EX_TEMPFAIL; } if (tTd(28, 2)) sm_dprintf("hes_getmailhost(%s): %d\n", a->q_user, hes_error()); break; } if (strlen(hp->po_name) + strlen(hp->po_host) > sizeof pobuf - 2) { if (tTd(28, 2)) sm_dprintf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n", a->q_user, hp->po_name, hp->po_host); break; } info.data = pobuf; (void) sm_snprintf(pobuf, sizeof pobuf, "%s@%s", hp->po_name, hp->po_host); info.size = strlen(info.data); # else /* HES_GETMAILHOST */ break; # endif /* HES_GETMAILHOST */ } if (tTd(28, 80)) sm_dprintf("udbexpand: match %.*s: %.*s\n", (int) key.size, (char *) key.data, (int) info.size, (char *) info.data); a->q_flags &= ~QSELFREF; if (bitset(EF_VRFYONLY, e->e_flags)) { a->q_state = QS_VERIFIED; return EX_OK; } breakout = true; if (info.size >= usersize) user = sm_malloc_x(info.size + 1); memmove(user, info.data, info.size); user[info.size] = '\0'; message("hesioded to %s", user); if (LogLevel > 10) sm_syslog(LOG_INFO, e->e_id, "hesiod %.100s => %s", e->e_to, shortenstring(user, MAXSHORTSTR)); naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e); if (naddrs > 0 && !bitset(QSELFREF, a->q_flags)) { if (tTd(28, 5)) { sm_dprintf("udbexpand: QS_EXPANDED "); printaddr(sm_debug_file(), a, false); } a->q_state = QS_EXPANDED; } /* ** If this address has a -request address, reflect ** it into the envelope. */ (void) sm_strlcpyn(keybuf, sizeof keybuf, 2, a->q_user, ":mailsender"); keylen = strlen(keybuf); key.data = keybuf; key.size = keylen; i = hes_udb_get(&key, &info); if (i != 0 || info.size <= 0) break; a->q_owner = sm_rpool_malloc_x(e->e_rpool, info.size + 1); memmove(a->q_owner, info.data, info.size); a->q_owner[info.size] = '\0'; break; # endif /* HESIOD */ case UDB_REMOTE: /* not yet implemented */ break; case UDB_FORWARD: if (bitset(EF_VRFYONLY, e->e_flags)) { a->q_state = QS_VERIFIED; return EX_OK; } i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; if (i >= usersize) { usersize = i + 1; user = sm_malloc_x(usersize); } (void) sm_strlcpyn(user, usersize, 3, a->q_user, "@", up->udb_fwdhost); message("expanded to %s", user); a->q_flags &= ~QSELFREF; naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e); if (naddrs > 0 && !bitset(QSELFREF, a->q_flags)) { if (tTd(28, 5)) { sm_dprintf("udbexpand: QS_EXPANDED "); printaddr(sm_debug_file(), a, false); } a->q_state = QS_EXPANDED; } breakout = true; break; case UDB_EOLIST: breakout = true; break; default: /* unknown entry type */ break; } /* XXX if an exception occurs, there is a storage leak */ if (user != userbuf) sm_free(user); /* XXX */ } return EX_OK; } /* ** UDBSENDER -- return canonical external name of sender, given local name ** ** Parameters: ** sender -- the name of the sender on the local machine. ** rpool -- resource pool from which to allocate result ** ** Returns: ** The external name for this sender, if derivable from the ** database. Storage allocated from rpool. ** NULL -- if nothing is changed from the database. ** ** Side Effects: ** none. */ char * udbsender(sender, rpool) char *sender; SM_RPOOL_T *rpool; { return udbmatch(sender, "mailname", rpool); } /* ** UDBMATCH -- match user in field, return result of lookup. ** ** Parameters: ** user -- the name of the user. ** field -- the field to lookup. ** rpool -- resource pool from which to allocate result ** ** Returns: ** The external name for this sender, if derivable from the ** database. Storage allocated from rpool. ** NULL -- if nothing is changed from the database. ** ** Side Effects: ** none. */ static char * udbmatch(user, field, rpool) char *user; char *field; SM_RPOOL_T *rpool; { register char *p; register struct udbent *up; int i; int keylen; DBT key, info; char keybuf[MAXKEY]; if (tTd(28, 1)) sm_dprintf("udbmatch(%s, %s)\n", user, field); if (!UdbInitialized) { if (_udbx_init(CurEnv) == EX_TEMPFAIL) return NULL; } /* short circuit if no spec */ if (UdbSpec == NULL || UdbSpec[0] == '\0') return NULL; /* short circuit name begins with '\\' since it can't possibly match */ if (user[0] == '\\') return NULL; /* long names can never match and are a pain to deal with */ i = strlen(field); if (i < sizeof "maildrop") i = sizeof "maildrop"; if ((strlen(user) + i) > sizeof keybuf - 4) return NULL; /* names beginning with colons indicate metadata */ if (user[0] == ':') return NULL; /* build database key */ (void) sm_strlcpyn(keybuf, sizeof keybuf, 3, user, ":", field); keylen = strlen(keybuf); for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) { /* ** Select action based on entry type. */ switch (up->udb_type) { # if NEWDB case UDB_DBFETCH: memset(&key, '\0', sizeof key); memset(&info, '\0', sizeof info); key.data = keybuf; key.size = keylen; # if DB_VERSION_MAJOR < 2 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); # else /* DB_VERSION_MAJOR < 2 */ i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL, &key, &info, 0); # endif /* DB_VERSION_MAJOR < 2 */ if (i != 0 || info.size <= 0) { if (tTd(28, 2)) sm_dprintf("udbmatch: no match on %s (%d) via db\n", keybuf, keylen); continue; } p = sm_rpool_malloc_x(rpool, info.size + 1); memmove(p, info.data, info.size); p[info.size] = '\0'; if (tTd(28, 1)) sm_dprintf("udbmatch ==> %s\n", p); return p; # endif /* NEWDB */ # if HESIOD case UDB_HESIOD: key.data = keybuf; key.size = keylen; i = hes_udb_get(&key, &info); if (i != 0 || info.size <= 0) { if (tTd(28, 2)) sm_dprintf("udbmatch: no match on %s (%d) via hesiod\n", keybuf, keylen); continue; } p = sm_rpool_malloc_x(rpool, info.size + 1); memmove(p, info.data, info.size); p[info.size] = '\0'; if (tTd(28, 1)) sm_dprintf("udbmatch ==> %s\n", p); return p; # endif /* HESIOD */ } } if (strcmp(field, "mailname") != 0) return NULL; /* ** Nothing yet. Search again for a default case. But only ** use it if we also have a forward (:maildrop) pointer already ** in the database. */ /* build database key */ (void) sm_strlcpyn(keybuf, sizeof keybuf, 2, user, ":maildrop"); keylen = strlen(keybuf); for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) { switch (up->udb_type) { # if NEWDB case UDB_DBFETCH: /* get the default case for this database */ if (up->udb_default == NULL) { memset(&key, '\0', sizeof key); memset(&info, '\0', sizeof info); key.data = ":default:mailname"; key.size = strlen(key.data); # if DB_VERSION_MAJOR < 2 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); # else /* DB_VERSION_MAJOR < 2 */ i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL, &key, &info, 0); # endif /* DB_VERSION_MAJOR < 2 */ if (i != 0 || info.size <= 0) { /* no default case */ up->udb_default = ""; continue; } /* save the default case */ up->udb_default = sm_pmalloc_x(info.size + 1); memmove(up->udb_default, info.data, info.size); up->udb_default[info.size] = '\0'; } else if (up->udb_default[0] == '\0') continue; /* we have a default case -- verify user:maildrop */ memset(&key, '\0', sizeof key); memset(&info, '\0', sizeof info); key.data = keybuf; key.size = keylen; # if DB_VERSION_MAJOR < 2 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); # else /* DB_VERSION_MAJOR < 2 */ i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL, &key, &info, 0); # endif /* DB_VERSION_MAJOR < 2 */ if (i != 0 || info.size <= 0) { /* nope -- no aliasing for this user */ continue; } /* they exist -- build the actual address */ i = strlen(user) + strlen(up->udb_default) + 2; p = sm_rpool_malloc_x(rpool, i); (void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default); if (tTd(28, 1)) sm_dprintf("udbmatch ==> %s\n", p); return p; # endif /* NEWDB */ # if HESIOD case UDB_HESIOD: /* get the default case for this database */ if (up->udb_default == NULL) { key.data = ":default:mailname"; key.size = strlen(key.data); i = hes_udb_get(&key, &info); if (i != 0 || info.size <= 0) { /* no default case */ up->udb_default = ""; continue; } /* save the default case */ up->udb_default = sm_pmalloc_x(info.size + 1); memmove(up->udb_default, info.data, info.size); up->udb_default[info.size] = '\0'; } else if (up->udb_default[0] == '\0') continue; /* we have a default case -- verify user:maildrop */ key.data = keybuf; key.size = keylen; i = hes_udb_get(&key, &info); if (i != 0 || info.size <= 0) { /* nope -- no aliasing for this user */ continue; } /* they exist -- build the actual address */ i = strlen(user) + strlen(up->udb_default) + 2; p = sm_rpool_malloc_x(rpool, i); (void) sm_strlcpyn(p, i, 3, user, "@", up->udb_default); if (tTd(28, 1)) sm_dprintf("udbmatch ==> %s\n", p); return p; break; # endif /* HESIOD */ } } /* still nothing.... too bad */ return NULL; } /* ** UDB_MAP_LOOKUP -- look up arbitrary entry in user database map ** ** Parameters: ** map -- the map being queried. ** name -- the name to look up. ** av -- arguments to the map lookup. ** statp -- to get any error status. ** ** Returns: ** NULL if name not found in map. ** The rewritten name otherwise. */ /* ARGSUSED3 */ char * udb_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { char *val; char *key; char *SM_NONVOLATILE result = NULL; char keybuf[MAXNAME + 1]; if (tTd(28, 20) || tTd(38, 20)) sm_dprintf("udb_map_lookup(%s, %s)\n", map->map_mname, name); if (bitset(MF_NOFOLDCASE, map->map_mflags)) { key = name; } else { int keysize = strlen(name); if (keysize > sizeof keybuf - 1) keysize = sizeof keybuf - 1; memmove(keybuf, name, keysize); keybuf[keysize] = '\0'; makelower(keybuf); key = keybuf; } val = udbmatch(key, map->map_file, NULL); if (val == NULL) return NULL; SM_TRY if (bitset(MF_MATCHONLY, map->map_mflags)) result = map_rewrite(map, name, strlen(name), NULL); else result = map_rewrite(map, val, strlen(val), av); SM_FINALLY sm_free(val); SM_END_TRY return result; } /* ** _UDBX_INIT -- parse the UDB specification, opening any valid entries. ** ** Parameters: ** e -- the current envelope. ** ** Returns: ** EX_TEMPFAIL -- if it appeared it couldn't get hold of a ** database due to a host being down or some similar ** (recoverable) situation. ** EX_OK -- otherwise. ** ** Side Effects: ** Fills in the UdbEnts structure from UdbSpec. */ # define MAXUDBOPTS 27 static int _udbx_init(e) ENVELOPE *e; { int ents = 0; register char *p; register struct udbent *up; if (UdbInitialized) return EX_OK; # ifdef UDB_DEFAULT_SPEC if (UdbSpec == NULL) UdbSpec = UDB_DEFAULT_SPEC; # endif /* UDB_DEFAULT_SPEC */ p = UdbSpec; up = UdbEnts; while (p != NULL) { char *spec; int l; struct udb_option opts[MAXUDBOPTS + 1]; while (*p == ' ' || *p == '\t' || *p == ',') p++; if (*p == '\0') break; spec = p; p = strchr(p, ','); if (p != NULL) *p++ = '\0'; if (ents >= MAXUDBENT) { syserr("Maximum number of UDB entries exceeded"); break; } /* extract options */ (void) _udb_parsespec(spec, opts, MAXUDBOPTS); /* ** Decode database specification. ** ** In the sendmail tradition, the leading character ** defines the semantics of the rest of the entry. ** ** @hostname -- forward email to the indicated host. ** This should be the last in the list, ** since it always matches the input. ** /dbname -- search the named database on the local ** host using the Berkeley db package. ** Hesiod -- search the named database with BIND ** using the MIT Hesiod package. */ switch (*spec) { case '@': /* forward to remote host */ up->udb_type = UDB_FORWARD; up->udb_pid = CurrentPid; up->udb_fwdhost = spec + 1; ents++; up++; break; # if HESIOD case 'h': /* use hesiod */ case 'H': if (sm_strcasecmp(spec, "hesiod") != 0) goto badspec; up->udb_type = UDB_HESIOD; up->udb_pid = CurrentPid; ents++; up++; break; # endif /* HESIOD */ # if NEWDB case '/': /* look up remote name */ l = strlen(spec); if (l > 3 && strcmp(&spec[l - 3], ".db") == 0) { up->udb_dbname = spec; } else { up->udb_dbname = sm_pmalloc_x(l + 4); (void) sm_strlcpyn(up->udb_dbname, l + 4, 2, spec, ".db"); } errno = 0; # if DB_VERSION_MAJOR < 2 up->udb_dbp = dbopen(up->udb_dbname, O_RDONLY, 0644, DB_BTREE, NULL); # else /* DB_VERSION_MAJOR < 2 */ { int flags = DB_RDONLY; # if DB_VERSION_MAJOR > 2 int ret; # endif /* DB_VERSION_MAJOR > 2 */ SM_DB_FLAG_ADD(flags); up->udb_dbp = NULL; # if DB_VERSION_MAJOR > 2 ret = db_create(&up->udb_dbp, NULL, 0); if (ret != 0) { (void) up->udb_dbp->close(up->udb_dbp, 0); up->udb_dbp = NULL; } else { ret = up->udb_dbp->open(up->udb_dbp, DBTXN up->udb_dbname, NULL, DB_BTREE, flags, 0644); if (ret != 0) { #ifdef DB_OLD_VERSION if (ret == DB_OLD_VERSION) ret = EINVAL; #endif /* DB_OLD_VERSION */ (void) up->udb_dbp->close(up->udb_dbp, 0); up->udb_dbp = NULL; } } errno = ret; # else /* DB_VERSION_MAJOR > 2 */ errno = db_open(up->udb_dbname, DB_BTREE, flags, 0644, NULL, NULL, &up->udb_dbp); # endif /* DB_VERSION_MAJOR > 2 */ } # endif /* DB_VERSION_MAJOR < 2 */ if (up->udb_dbp == NULL) { if (tTd(28, 1)) { int save_errno = errno; # if DB_VERSION_MAJOR < 2 sm_dprintf("dbopen(%s): %s\n", # else /* DB_VERSION_MAJOR < 2 */ sm_dprintf("db_open(%s): %s\n", # endif /* DB_VERSION_MAJOR < 2 */ up->udb_dbname, sm_errstring(errno)); errno = save_errno; } if (errno != ENOENT && errno != EACCES) { if (LogLevel > 2) sm_syslog(LOG_ERR, e->e_id, # if DB_VERSION_MAJOR < 2 "dbopen(%s): %s", # else /* DB_VERSION_MAJOR < 2 */ "db_open(%s): %s", # endif /* DB_VERSION_MAJOR < 2 */ up->udb_dbname, sm_errstring(errno)); up->udb_type = UDB_EOLIST; if (up->udb_dbname != spec) sm_free(up->udb_dbname); /* XXX */ goto tempfail; } if (up->udb_dbname != spec) sm_free(up->udb_dbname); /* XXX */ break; } if (tTd(28, 1)) { # if DB_VERSION_MAJOR < 2 sm_dprintf("_udbx_init: dbopen(%s)\n", # else /* DB_VERSION_MAJOR < 2 */ sm_dprintf("_udbx_init: db_open(%s)\n", # endif /* DB_VERSION_MAJOR < 2 */ up->udb_dbname); } up->udb_type = UDB_DBFETCH; up->udb_pid = CurrentPid; ents++; up++; break; # endif /* NEWDB */ default: # if HESIOD badspec: # endif /* HESIOD */ syserr("Unknown UDB spec %s", spec); break; } } up->udb_type = UDB_EOLIST; if (tTd(28, 4)) { for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) { switch (up->udb_type) { case UDB_REMOTE: sm_dprintf("REMOTE: addr %s, timeo %d\n", anynet_ntoa((SOCKADDR *) &up->udb_addr), up->udb_timeout); break; case UDB_DBFETCH: # if NEWDB sm_dprintf("FETCH: file %s\n", up->udb_dbname); # else /* NEWDB */ sm_dprintf("FETCH\n"); # endif /* NEWDB */ break; case UDB_FORWARD: sm_dprintf("FORWARD: host %s\n", up->udb_fwdhost); break; case UDB_HESIOD: sm_dprintf("HESIOD\n"); break; default: sm_dprintf("UNKNOWN\n"); break; } } } UdbInitialized = true; errno = 0; return EX_OK; /* ** On temporary failure, back out anything we've already done */ tempfail: # if NEWDB for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) { if (up->udb_type == UDB_DBFETCH) { # if DB_VERSION_MAJOR < 2 (*up->udb_dbp->close)(up->udb_dbp); # else /* DB_VERSION_MAJOR < 2 */ errno = (*up->udb_dbp->close)(up->udb_dbp, 0); # endif /* DB_VERSION_MAJOR < 2 */ if (tTd(28, 1)) sm_dprintf("_udbx_init: db->close(%s)\n", up->udb_dbname); } } # endif /* NEWDB */ return EX_TEMPFAIL; } static int _udb_parsespec(udbspec, opt, maxopts) char *udbspec; struct udb_option opt[]; int maxopts; { register char *spec; register char *spec_end; register int optnum; spec_end = strchr(udbspec, ':'); for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) { register char *p; while (isascii(*spec) && isspace(*spec)) spec++; spec_end = strchr(spec, ':'); if (spec_end != NULL) *spec_end++ = '\0'; opt[optnum].udbo_name = spec; opt[optnum].udbo_val = NULL; p = strchr(spec, '='); if (p != NULL) opt[optnum].udbo_val = ++p; } return optnum; } /* ** _UDBX_CLOSE -- close all file based UDB entries. ** ** Parameters: ** none ** ** Returns: ** none */ void _udbx_close() { struct udbent *up; if (!UdbInitialized) return; for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) { if (up->udb_pid != CurrentPid) continue; # if NEWDB if (up->udb_type == UDB_DBFETCH) { # if DB_VERSION_MAJOR < 2 (*up->udb_dbp->close)(up->udb_dbp); # else /* DB_VERSION_MAJOR < 2 */ errno = (*up->udb_dbp->close)(up->udb_dbp, 0); # endif /* DB_VERSION_MAJOR < 2 */ } if (tTd(28, 1)) sm_dprintf("_udbx_init: db->close(%s)\n", up->udb_dbname); # endif /* NEWDB */ } } # if HESIOD static int hes_udb_get(key, info) DBT *key; DBT *info; { char *name, *type; char **hp; char kbuf[MAXKEY + 1]; if (sm_strlcpy(kbuf, key->data, sizeof kbuf) >= sizeof kbuf) return 0; name = kbuf; type = strrchr(name, ':'); if (type == NULL) return 1; *type++ = '\0'; if (strchr(name, '@') != NULL) return 1; if (tTd(28, 1)) sm_dprintf("hes_udb_get(%s, %s)\n", name, type); /* make the hesiod query */ # ifdef HESIOD_INIT if (HesiodContext == NULL && hesiod_init(&HesiodContext) != 0) return -1; hp = hesiod_resolve(HesiodContext, name, type); # else /* HESIOD_INIT */ hp = hes_resolve(name, type); # endif /* HESIOD_INIT */ *--type = ':'; # ifdef HESIOD_INIT if (hp == NULL) return 1; if (*hp == NULL) { hesiod_free_list(HesiodContext, hp); if (errno == ECONNREFUSED || errno == EMSGSIZE) return -1; return 1; } # else /* HESIOD_INIT */ if (hp == NULL || hp[0] == NULL) { /* network problem or timeout */ if (hes_error() == HES_ER_NET) return -1; return 1; } # endif /* HESIOD_INIT */ else { /* ** If there are multiple matches, just return the ** first one. ** ** XXX These should really be returned; for example, ** XXX it is legal for :maildrop to be multi-valued. */ info->data = hp[0]; info->size = (size_t) strlen(info->data); } if (tTd(28, 80)) sm_dprintf("hes_udb_get => %s\n", *hp); return 0; } # endif /* HESIOD */ #else /* USERDB */ int udbexpand(a, sendq, aliaslevel, e) ADDRESS *a; ADDRESS **sendq; int aliaslevel; ENVELOPE *e; { return EX_OK; } #endif /* USERDB */