diff options
author | Gilles Chehade <gilles@cvs.openbsd.org> | 2013-01-26 09:37:25 +0000 |
---|---|---|
committer | Gilles Chehade <gilles@cvs.openbsd.org> | 2013-01-26 09:37:25 +0000 |
commit | 52e93b0e61fd0a116dbb373054e2cd0ea3bfcf39 (patch) | |
tree | 41934d0fc43bfebf55ba5a199e0d699adf24aff1 /usr.sbin/smtpd/table_db.c | |
parent | 3b78bd2481525635417ca0fc75396ef754c09171 (diff) |
Sync with our smtpd repo:
* first bricks of ldap and sqlite support (not finished but both working)
* new table API to replace map API, all lookups are done through tables
* improved handling of temporary errors throughout the daemon
* improved scheduler and mta logic: connection reuse, optimizes batches
* improved queue: more tolerant to admin errors, new layout, less disk-IO
* improved memory usage under high load
* SSL certs/keys isolated to lookup process to avoid facing network
* VIRTUAL support improved, fully virtual setups possible now
* runtime tracing of processes through smtpctl trace
* ssl_privsep.c sync-ed with relayd
* ssl.c no longer contains smtpd specific interfaces
* smtpd-specific ssl bits moved to ssl_smtpd.c
* update mail address in copyright
FLUSH YOUR QUEUE. FLUSH YOUR QUEUE. FLUSH YOUR QUEUE. FLUSH YOUR QUEUE.
smtpd.conf(5) simplified, it will require adaptations
ok eric@
Diffstat (limited to 'usr.sbin/smtpd/table_db.c')
-rw-r--r-- | usr.sbin/smtpd/table_db.c | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/usr.sbin/smtpd/table_db.c b/usr.sbin/smtpd/table_db.c new file mode 100644 index 00000000000..0688f23bd03 --- /dev/null +++ b/usr.sbin/smtpd/table_db.c @@ -0,0 +1,388 @@ +/* $OpenBSD: table_db.c,v 1.1 2013/01/26 09:37:24 gilles Exp $ */ + +/* + * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/queue.h> +#include <sys/tree.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <db.h> +#include <ctype.h> +#include <err.h> +#include <event.h> +#include <fcntl.h> +#include <imsg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "smtpd.h" +#include "log.h" + + +/* db(3) backend */ +static int table_db_config(struct table *, const char *); +static int table_db_update(struct table *); +static void *table_db_open(struct table *); +static int table_db_lookup(void *, const char *, enum table_service, void **); +static int table_db_fetch(void *, enum table_service, char **); +static void table_db_close(void *); + +static char *table_db_get_entry(void *, const char *, size_t *); +static char *table_db_get_entry_match(void *, const char *, size_t *, + int(*)(const char *, const char *)); + +static int table_db_credentials(const char *, char *, size_t, void **); +static int table_db_alias(const char *, char *, size_t, void **); +static int table_db_domain(const char *, char *, size_t, void **); +static int table_db_netaddr(const char *, char *, size_t, void **); +static int table_db_userinfo(const char *, char *, size_t, void **); + +struct table_backend table_backend_db = { + K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|K_SOURCE, + table_db_config, + table_db_open, + table_db_update, + table_db_close, + table_db_lookup, + table_db_fetch, +}; + +static struct keycmp { + enum table_service service; + int (*func)(const char *, const char *); +} keycmp[] = { + { K_DOMAIN, table_domain_match }, + { K_NETADDR, table_netaddr_match } +}; + +struct dbhandle { + DB *db; + char pathname[MAXPATHLEN]; + time_t mtime; + struct table *table; +}; + +static int +table_db_config(struct table *table, const char *config) +{ + DB *db; + + db = table_db_open(table); + if (db == NULL) + return 0; + + table_db_close(db); + return 1; +} + +static int +table_db_update(struct table *table) +{ + struct dbhandle *handle; + + handle = table_db_open(table); + if (handle == NULL) + return 0; + + table_db_close(table->t_handle); + free(table->t_handle); + table->t_handle = handle; + return 1; +} + +static void * +table_db_open(struct table *table) +{ + struct dbhandle *handle; + struct stat sb; + + handle = xcalloc(1, sizeof *handle, "table_db_open"); + if (strlcpy(handle->pathname, table->t_config, sizeof handle->pathname) + >= sizeof handle->pathname) + goto error; + + if (stat(handle->pathname, &sb) < 0) + goto error; + + handle->mtime = sb.st_mtime; + handle->db = dbopen(table->t_config, O_RDONLY, 0600, DB_HASH, NULL); + if (handle->db == NULL) + goto error; + handle->table = table; + + return handle; + +error: + if (handle->db) + handle->db->close(handle->db); + free(handle); + return NULL; +} + +static void +table_db_close(void *hdl) +{ + struct dbhandle *handle = hdl; + handle->db->close(handle->db); +} + +static int +table_db_lookup(void *hdl, const char *key, enum table_service service, + void **retp) +{ + struct dbhandle *handle = hdl; + char *line; + size_t len = 0; + int ret; + int (*match)(const char *, const char *) = NULL; + size_t i; + struct stat sb; + + if (stat(handle->pathname, &sb) < 0) + return -1; + + /* DB has changed, close and reopen */ + if (sb.st_mtime != handle->mtime) + table_db_update(handle->table); + + for (i = 0; i < nitems(keycmp); ++i) + if (keycmp[i].service == service) + match = keycmp[i].func; + + if (match == NULL) + line = table_db_get_entry(hdl, key, &len); + else + line = table_db_get_entry_match(hdl, key, &len, match); + if (line == NULL) + return 0; + + if (retp == NULL) + return 1; + + ret = 0; + switch (service) { + case K_ALIAS: + ret = table_db_alias(key, line, len, retp); + break; + + case K_CREDENTIALS: + ret = table_db_credentials(key, line, len, retp); + break; + + case K_DOMAIN: + ret = table_db_domain(key, line, len, retp); + break; + + case K_NETADDR: + ret = table_db_netaddr(key, line, len, retp); + break; + + case K_USERINFO: + ret = table_db_userinfo(key, line, len, retp); + break; + + default: + break; + } + + free(line); + + return ret; +} + +static int +table_db_fetch(void *hdl, enum table_service service, char **retp) +{ + struct dbhandle *handle = hdl; + struct table *table = handle->table; + DBT dbk; + DBT dbd; + int r; + + if (table->t_iter == NULL) + r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST); + else + r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT); + table->t_iter = handle->db; + if (!r) { + r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST); + if (!r) + return 0; + } + *retp = xmemdup(dbk.data, dbk.size, "table_db_get_entry_cmp"); + return 1; +} + + +static char * +table_db_get_entry_match(void *hdl, const char *key, size_t *len, + int(*func)(const char *, const char *)) +{ + struct dbhandle *handle = hdl; + DBT dbk; + DBT dbd; + int r; + char *buf = NULL; + + for (r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST); !r; + r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT)) { + buf = xmemdup(dbk.data, dbk.size, "table_db_get_entry_cmp"); + if (func(key, buf)) { + *len = dbk.size; + return buf; + } + free(buf); + } + return NULL; +} + +static char * +table_db_get_entry(void *hdl, const char *key, size_t *len) +{ + struct dbhandle *handle = hdl; + int ret; + DBT dbk; + DBT dbv; + char pkey[MAX_LINE_SIZE]; + + /* workaround the stupidity of the DB interface */ + if (strlcpy(pkey, key, sizeof pkey) >= sizeof pkey) + errx(1, "table_db_get_entry: key too long"); + dbk.data = pkey; + dbk.size = strlen(pkey) + 1; + + if ((ret = handle->db->get(handle->db, &dbk, &dbv, 0)) != 0) + return NULL; + + *len = dbv.size; + + return xmemdup(dbv.data, dbv.size, "table_db_get_entry"); +} + +static int +table_db_credentials(const char *key, char *line, size_t len, void **retp) +{ + struct credentials *credentials = NULL; + char *p; + + /* credentials are stored as user:password */ + if (len < 3) + return -1; + + /* too big to fit in a smtp session line */ + if (len >= MAX_LINE_SIZE) + return -1; + + p = strchr(line, ':'); + if (p == NULL) + return -1; + + if (p == line || p == line + len - 1) + return -1; + *p++ = '\0'; + + credentials = xcalloc(1, sizeof *credentials, + "table_db_credentials"); + if (strlcpy(credentials->username, line, sizeof(credentials->username)) + >= sizeof(credentials->username)) + goto err; + + if (strlcpy(credentials->password, p, sizeof(credentials->password)) + >= sizeof(credentials->password)) + goto err; + + *retp = credentials; + return 1; + +err: + *retp = NULL; + free(credentials); + return -1; +} + +static int +table_db_alias(const char *key, char *line, size_t len, void **retp) +{ + struct expand *xp = NULL; + + xp = xcalloc(1, sizeof *xp, "table_db_alias"); + if (! expand_line(xp, line, 1)) + goto error; + *retp = xp; + return 1; + +error: + *retp = NULL; + expand_free(xp); + return -1; +} + +static int +table_db_netaddr(const char *key, char *line, size_t len, void **retp) +{ + struct netaddr *netaddr; + + netaddr = xcalloc(1, sizeof *netaddr, "table_db_netaddr"); + if (! text_to_netaddr(netaddr, line)) + goto error; + *retp = netaddr; + return 1; + +error: + *retp = NULL; + free(netaddr); + return -1; +} + +static int +table_db_domain(const char *key, char *line, size_t len, void **retp) +{ + struct destination *destination; + + destination = xcalloc(1, sizeof *destination, "table_db_domain"); + if (strlcpy(destination->name, line, sizeof destination->name) + >= sizeof destination->name) + goto error; + *retp = destination; + return 1; + +error: + *retp = NULL; + free(destination); + return -1; +} + +static int +table_db_userinfo(const char *key, char *line, size_t len, void **retp) +{ + struct userinfo *userinfo; + + userinfo = xcalloc(1, sizeof *userinfo, "table_db_userinfo"); + if (! text_to_userinfo(userinfo, line)) + goto error; + *retp = userinfo; + return 1; + +error: + *retp = NULL; + free(userinfo); + return -1; +} |