aboutsummaryrefslogtreecommitdiffstats
path: root/smtpd/table_db.c
diff options
context:
space:
mode:
Diffstat (limited to 'smtpd/table_db.c')
-rw-r--r--smtpd/table_db.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/smtpd/table_db.c b/smtpd/table_db.c
new file mode 100644
index 00000000..f7d766dd
--- /dev/null
+++ b/smtpd/table_db.c
@@ -0,0 +1,282 @@
+/* $OpenBSD: table_db.c,v 1.21 2019/06/28 13:32:51 deraadt 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 "includes.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef HAVE_DB_H
+#include <db.h>
+#elif defined(HAVE_DB1_DB_H)
+#include <db1/db.h>
+#elif defined(HAVE_DB_185_H)
+#include <db_185.h>
+#endif
+#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 *);
+static int table_db_update(struct table *);
+static int table_db_open(struct table *);
+static void *table_db_open2(struct table *);
+static int table_db_lookup(struct table *, enum table_service, const char *, char **);
+static int table_db_fetch(struct table *, enum table_service, char **);
+static void table_db_close(struct table *);
+static void table_db_close2(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 *));
+
+struct table_backend table_backend_db = {
+ "db",
+ K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP,
+ table_db_config,
+ NULL,
+ NULL,
+ 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 },
+ { K_MAILADDR, table_mailaddr_match }
+};
+
+struct dbhandle {
+ DB *db;
+ char pathname[PATH_MAX];
+ time_t mtime;
+ int iter;
+};
+
+static int
+table_db_config(struct table *table)
+{
+ struct dbhandle *handle;
+
+ handle = table_db_open2(table);
+ if (handle == NULL)
+ return 0;
+
+ table_db_close2(handle);
+ return 1;
+}
+
+static int
+table_db_update(struct table *table)
+{
+ struct dbhandle *handle;
+
+ handle = table_db_open2(table);
+ if (handle == NULL)
+ return 0;
+
+ table_db_close2(table->t_handle);
+ table->t_handle = handle;
+ return 1;
+}
+
+static int
+table_db_open(struct table *table)
+{
+ table->t_handle = table_db_open2(table);
+ if (table->t_handle == NULL)
+ return 0;
+ return 1;
+}
+
+static void
+table_db_close(struct table *table)
+{
+ table_db_close2(table->t_handle);
+ table->t_handle = NULL;
+}
+
+static void *
+table_db_open2(struct table *table)
+{
+ struct dbhandle *handle;
+ struct stat sb;
+
+ handle = xcalloc(1, sizeof *handle);
+ if (strlcpy(handle->pathname, table->t_config, sizeof handle->pathname)
+ >= sizeof handle->pathname)
+ goto error;
+
+ if (stat(handle->pathname, &sb) == -1)
+ 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;
+
+ return handle;
+
+error:
+ if (handle->db)
+ handle->db->close(handle->db);
+ free(handle);
+ return NULL;
+}
+
+static void
+table_db_close2(void *hdl)
+{
+ struct dbhandle *handle = hdl;
+ handle->db->close(handle->db);
+ free(handle);
+}
+
+static int
+table_db_lookup(struct table *table, enum table_service service, const char *key,
+ char **dst)
+{
+ struct dbhandle *handle = table->t_handle;
+ 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) == -1)
+ return -1;
+
+ /* DB has changed, close and reopen */
+ if (sb.st_mtime != handle->mtime) {
+ table_db_update(table);
+ handle = table->t_handle;
+ }
+
+ for (i = 0; i < nitems(keycmp); ++i)
+ if (keycmp[i].service == service)
+ match = keycmp[i].func;
+
+ if (match == NULL)
+ line = table_db_get_entry(handle, key, &len);
+ else
+ line = table_db_get_entry_match(handle, key, &len, match);
+ if (line == NULL)
+ return 0;
+
+ ret = 1;
+ if (dst)
+ *dst = line;
+ else
+ free(line);
+
+ return ret;
+}
+
+static int
+table_db_fetch(struct table *table, enum table_service service, char **dst)
+{
+ struct dbhandle *handle = table->t_handle;
+ DBT dbk;
+ DBT dbd;
+ int r;
+
+ if (handle->iter == 0)
+ r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST);
+ else
+ r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT);
+ handle->iter = 1;
+ if (!r) {
+ r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST);
+ if (!r)
+ return 0;
+ }
+
+ *dst = strdup(dbk.data);
+ if (*dst == NULL)
+ return -1;
+
+ 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);
+ 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[LINE_MAX];
+
+ /* 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);
+}