#include <sendmail.h>
#include "map.h"
#if USERDB
SM_RCSID("@(#)$Id: udb.c,v 8.164 2006/12/19 19:49:51 ca Exp $ (with USERDB)")
#else
SM_RCSID("@(#)$Id: udb.c,v 8.164 2006/12/19 19:49:51 ca Exp $ (without USERDB)")
#endif
#if USERDB
#include <sm/sendmail.h>
# if NEWDB
# include "sm/bdb.h"
# else
# define DBT struct _data_base_thang_
DBT
{
void *data;
size_t size;
};
# endif
struct udbent
{
char *udb_spec;
int udb_type;
pid_t udb_pid;
char *udb_default;
union
{
# if NETINET || NETINET6
struct
{
SOCKADDR _udb_addr;
int _udb_timeout;
} udb_remote;
# define udb_addr udb_u.udb_remote._udb_addr
# define udb_timeout udb_u.udb_remote._udb_timeout
# endif
struct
{
char *_udb_fwdhost;
} udb_forward;
# define udb_fwdhost udb_u.udb_forward._udb_fwdhost
# if NEWDB
struct
{
char *_udb_dbname;
DB *_udb_dbp;
} udb_lookup;
# define udb_dbname udb_u.udb_lookup._udb_dbname
# define udb_dbp udb_u.udb_lookup._udb_dbp
# endif
} udb_u;
};
# define UDB_EOLIST 0
# define UDB_SKIP 1
# define UDB_REMOTE 2
# define UDB_DBFETCH 3
# define UDB_FORWARD 4
# define UDB_HESIOD 5
# define MAXUDBENT 10
struct udb_option
{
char *udbo_name;
char *udbo_val;
};
# if HESIOD
static int hes_udb_get __P((DBT *, DBT *));
# endif
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));
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[MAXUDBKEY];
memset(&key, '\0', sizeof(key));
memset(&info, '\0', sizeof(info));
if (tTd(28, 1))
sm_dprintf("udbexpand(%s)\n", a->q_paddr);
if (!QS_IS_SENDABLE(a->q_state))
return EX_OK;
e->e_to = a->q_paddr;
if (!UdbInitialized)
{
if (_udbx_init(e) == EX_TEMPFAIL)
return EX_TEMPFAIL;
}
if (UdbSpec == NULL || UdbSpec[0] == '\0')
return EX_OK;
user = a->q_user;
if (user[0] == '\\')
return EX_OK;
if (user[0] == ':')
return EX_OK;
keylen = sm_strlcpyn(keybuf, sizeof(keybuf), 2, user, ":maildrop");
if (keylen >= sizeof(keybuf))
return EX_OK;
breakout = false;
for (up = UdbEnts; !breakout; up++)
{
int usersize;
int userleft;
char userbuf[MEMCHUNKSIZE];
# if HESIOD && HES_GETMAILHOST
char pobuf[MAXNAME];
# endif
# if defined(NEWDB) && DB_VERSION_MAJOR > 1
DBC *dbc = NULL;
# endif
user = userbuf;
userbuf[0] = '\0';
usersize = sizeof(userbuf);
userleft = sizeof(userbuf) - 1;
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
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
(errno = (*up->udb_dbp->cursor)(up->udb_dbp,
NULL, &dbc)) != 0)
# endif
i = -1;
if (i != 0 || dbc == NULL ||
(errno = dbc->c_get(dbc, &key,
&info, DB_SET)) != 0)
i = 1;
# endif
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
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
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);
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;
# if DB_VERSION_MAJOR < 2
i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
# else
i = 0;
if ((errno = dbc->c_get(dbc, &key,
&info, DB_NEXT)) != 0)
i = 1;
# endif
}
# if DB_VERSION_MAJOR > 1
if (dbc != NULL)
{
(void) dbc->c_close(dbc);
dbc = NULL;
}
# endif
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;
}
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
i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
&key, &info, 0);
# endif
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';
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
# 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);
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
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
break;
# endif
}
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;
}
(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
case UDB_REMOTE:
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:
break;
}
if (user != userbuf)
sm_free(user);
}
return EX_OK;
}
char *
udbsender(sender, rpool)
char *sender;
SM_RPOOL_T *rpool;
{
return udbmatch(sender, "mailname", rpool);
}
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[MAXUDBKEY];
if (tTd(28, 1))
sm_dprintf("udbmatch(%s, %s)\n", user, field);
if (!UdbInitialized)
{
if (_udbx_init(CurEnv) == EX_TEMPFAIL)
return NULL;
}
if (UdbSpec == NULL || UdbSpec[0] == '\0')
return NULL;
if (user[0] == '\\')
return NULL;
i = strlen(field);
if (i < sizeof("maildrop"))
i = sizeof("maildrop");
if ((strlen(user) + i) > sizeof(keybuf) - 4)
return NULL;
if (user[0] == ':')
return NULL;
(void) sm_strlcpyn(keybuf, sizeof(keybuf), 3, user, ":", field);
keylen = strlen(keybuf);
for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
{
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
i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
&key, &info, 0);
# endif
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
# 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
}
}
if (strcmp(field, "mailname") != 0)
return NULL;
(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:
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
i = errno = (*up->udb_dbp->get)(up->udb_dbp,
NULL, &key,
&info, 0);
# endif
if (i != 0 || info.size <= 0)
{
up->udb_default = "";
continue;
}
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;
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
i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
&key, &info, 0);
# endif
if (i != 0 || info.size <= 0)
{
continue;
}
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
# if HESIOD
case UDB_HESIOD:
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)
{
up->udb_default = "";
continue;
}
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;
key.data = keybuf;
key.size = keylen;
i = hes_udb_get(&key, &info);
if (i != 0 || info.size <= 0)
{
continue;
}
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
}
}
return NULL;
}
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;
}
# 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
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;
}
(void) _udb_parsespec(spec, opts, MAXUDBOPTS);
switch (*spec)
{
case '@':
up->udb_type = UDB_FORWARD;
up->udb_pid = CurrentPid;
up->udb_fwdhost = spec + 1;
ents++;
up++;
break;
# if HESIOD
case 'h':
case 'H':
if (sm_strcasecmp(spec, "hesiod") != 0)
goto badspec;
up->udb_type = UDB_HESIOD;
up->udb_pid = CurrentPid;
ents++;
up++;
break;
# endif
# if NEWDB
case '/':
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
{
int flags = DB_RDONLY;
# if DB_VERSION_MAJOR > 2
int ret;
# endif
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
(void) up->udb_dbp->close(up->udb_dbp, 0);
up->udb_dbp = NULL;
}
}
errno = ret;
# else
errno = db_open(up->udb_dbname, DB_BTREE,
flags, 0644, NULL,
NULL, &up->udb_dbp);
# endif
}
# endif
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
sm_dprintf("db_open(%s): %s\n",
# endif
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_open(%s): %s",
# endif
up->udb_dbname,
sm_errstring(errno));
up->udb_type = UDB_EOLIST;
if (up->udb_dbname != spec)
sm_free(up->udb_dbname);
goto tempfail;
}
if (up->udb_dbname != spec)
sm_free(up->udb_dbname);
break;
}
if (tTd(28, 1))
{
# if DB_VERSION_MAJOR < 2
sm_dprintf("_udbx_init: dbopen(%s)\n",
# else
sm_dprintf("_udbx_init: db_open(%s)\n",
# endif
up->udb_dbname);
}
up->udb_type = UDB_DBFETCH;
up->udb_pid = CurrentPid;
ents++;
up++;
break;
# endif
default:
# if HESIOD
badspec:
# endif
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
sm_dprintf("FETCH\n");
# endif
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;
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
errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
# endif
if (tTd(28, 1))
sm_dprintf("_udbx_init: db->close(%s)\n",
up->udb_dbname);
}
}
# endif
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;
}
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
errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
# endif
}
if (tTd(28, 1))
sm_dprintf("_udbx_init: db->close(%s)\n",
up->udb_dbname);
# endif
}
}
# if HESIOD
static int
hes_udb_get(key, info)
DBT *key;
DBT *info;
{
char *name, *type;
char **hp;
char kbuf[MAXUDBKEY + 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);
# ifdef HESIOD_INIT
if (HesiodContext == NULL && hesiod_init(&HesiodContext) != 0)
return -1;
hp = hesiod_resolve(HesiodContext, name, type);
# else
hp = hes_resolve(name, type);
# endif
*--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
if (hp == NULL || hp[0] == NULL)
{
if (hes_error() == HES_ER_NET)
return -1;
return 1;
}
# endif
else
{
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
#else
int
udbexpand(a, sendq, aliaslevel, e)
ADDRESS *a;
ADDRESS **sendq;
int aliaslevel;
ENVELOPE *e;
{
return EX_OK;
}
#endif