aboutsummaryrefslogtreecommitdiffstats
path: root/smtpd/table_static.c
diff options
context:
space:
mode:
authorGilles Chehade <gilles@poolp.org>2020-05-22 14:35:02 +0200
committerGilles Chehade <gilles@poolp.org>2020-05-22 14:35:02 +0200
commit90087f07a7cfffe10b4958e13558a0052f85691d (patch)
treebf0aac44374cfd5cd0da91cc67a6744decd74665 /smtpd/table_static.c
parentmoving smtpd to usr.sbin/smtpd to ease cherry-picking of upstream (diff)
downloadOpenSMTPD-90087f07a7cfffe10b4958e13558a0052f85691d.tar.xz
OpenSMTPD-90087f07a7cfffe10b4958e13558a0052f85691d.zip
Revert "moving smtpd to usr.sbin/smtpd to ease cherry-picking of upstream"
This reverts commit 90620a574d8824e5b2aa18709f2d5b5b6bb3cb38.
Diffstat (limited to 'smtpd/table_static.c')
-rw-r--r--smtpd/table_static.c398
1 files changed, 398 insertions, 0 deletions
diff --git a/smtpd/table_static.c b/smtpd/table_static.c
new file mode 100644
index 00000000..8f78ae11
--- /dev/null
+++ b/smtpd/table_static.c
@@ -0,0 +1,398 @@
+/* $OpenBSD: table_static.c,v 1.32 2018/12/28 14:21:02 eric Exp $ */
+
+/*
+ * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
+ * Copyright (c) 2012 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/queue.h>
+#include <sys/tree.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+
+#include <event.h>
+#include <fcntl.h>
+#include <imsg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+#include "smtpd.h"
+#include "log.h"
+
+struct table_static_priv {
+ int type;
+ struct dict dict;
+ void *iter;
+};
+
+/* static backend */
+static int table_static_config(struct table *);
+static int table_static_add(struct table *, const char *, const char *);
+static void table_static_dump(struct table *);
+static int table_static_update(struct table *);
+static int table_static_open(struct table *);
+static int table_static_lookup(struct table *, enum table_service, const char *,
+ char **);
+static int table_static_fetch(struct table *, enum table_service, char **);
+static void table_static_close(struct table *);
+
+struct table_backend table_backend_static = {
+ "static",
+ K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|
+ K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST|
+ K_STRING|K_REGEX,
+ table_static_config,
+ table_static_add,
+ table_static_dump,
+ table_static_open,
+ table_static_update,
+ table_static_close,
+ table_static_lookup,
+ table_static_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 },
+ { K_REGEX, table_regex_match },
+};
+
+
+static void
+table_static_priv_free(struct table_static_priv *priv)
+{
+ void *p;
+
+ while (dict_poproot(&priv->dict, (void **)&p))
+ if (p != priv)
+ free(p);
+ free(priv);
+}
+
+static int
+table_static_priv_add(struct table_static_priv *priv, const char *key, const char *val)
+{
+ char lkey[1024];
+ void *old, *new = NULL;
+
+ if (!lowercase(lkey, key, sizeof lkey)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+
+ if (val) {
+ new = strdup(val);
+ if (new == NULL)
+ return (-1);
+ }
+
+ /* use priv if value is null, so we can detect duplicate entries */
+ old = dict_set(&priv->dict, lkey, new ? new : priv);
+ if (old) {
+ if (old != priv)
+ free(old);
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+table_static_priv_load(struct table_static_priv *priv, const char *path)
+{
+ FILE *fp;
+ char *buf = NULL, *p;
+ int lineno = 0;
+ size_t sz = 0;
+ ssize_t flen;
+ char *keyp;
+ char *valp;
+ int ret = 0;
+
+ if ((fp = fopen(path, "r")) == NULL) {
+ log_warn("%s: fopen", path);
+ return 0;
+ }
+
+ while ((flen = getline(&buf, &sz, fp)) != -1) {
+ lineno++;
+ if (buf[flen - 1] == '\n')
+ buf[--flen] = '\0';
+
+ keyp = buf;
+ while (isspace((unsigned char)*keyp)) {
+ ++keyp;
+ --flen;
+ }
+ if (*keyp == '\0')
+ continue;
+ while (isspace((unsigned char)keyp[flen - 1]))
+ keyp[--flen] = '\0';
+ if (*keyp == '#') {
+ if (priv->type == T_NONE) {
+ keyp++;
+ while (isspace((unsigned char)*keyp))
+ ++keyp;
+ if (!strcmp(keyp, "@list"))
+ priv->type = T_LIST;
+ }
+ continue;
+ }
+
+ if (priv->type == T_NONE) {
+ for (p = keyp; *p; p++) {
+ if (*p == ' ' || *p == '\t' || *p == ':') {
+ priv->type = T_HASH;
+ break;
+ }
+ }
+ if (priv->type == T_NONE)
+ priv->type = T_LIST;
+ }
+
+ if (priv->type == T_LIST) {
+ table_static_priv_add(priv, keyp, NULL);
+ continue;
+ }
+
+ /* T_HASH */
+ valp = keyp;
+ strsep(&valp, " \t:");
+ if (valp) {
+ while (*valp) {
+ if (!isspace((unsigned char)*valp) &&
+ !(*valp == ':' &&
+ isspace((unsigned char)*(valp + 1))))
+ break;
+ ++valp;
+ }
+ if (*valp == '\0')
+ valp = NULL;
+ }
+ if (valp == NULL) {
+ log_warnx("%s: invalid map entry line %d",
+ path, lineno);
+ goto end;
+ }
+
+ table_static_priv_add(priv, keyp, valp);
+ }
+
+ if (ferror(fp)) {
+ log_warn("%s: getline", path);
+ goto end;
+ }
+
+ /* Accept empty alias files; treat them as hashes */
+ if (priv->type == T_NONE)
+ priv->type = T_HASH;
+
+ ret = 1;
+end:
+ free(buf);
+ fclose(fp);
+ return ret;
+}
+
+static int
+table_static_config(struct table *t)
+{
+ struct table_static_priv *priv, *old;
+
+ /* already up, and no config file? ok */
+ if (t->t_handle && *t->t_config == '\0')
+ return 1;
+
+ /* new config */
+ priv = calloc(1, sizeof(*priv));
+ if (priv == NULL)
+ return 0;
+ priv->type = t->t_type;
+ dict_init(&priv->dict);
+
+ if (*t->t_config) {
+ /* load the config file */
+ if (table_static_priv_load(priv, t->t_config) == 0) {
+ table_static_priv_free(priv);
+ return 0;
+ }
+ }
+
+ if ((old = t->t_handle))
+ table_static_priv_free(old);
+ t->t_handle = priv;
+ t->t_type = priv->type;
+
+ return 1;
+}
+
+static int
+table_static_add(struct table *table, const char *key, const char *val)
+{
+ struct table_static_priv *priv = table->t_handle;
+ int r;
+
+ /* cannot add to a table read from a file */
+ if (*table->t_config)
+ return 0;
+
+ if (table->t_type == T_NONE)
+ table->t_type = val ? T_HASH : T_LIST;
+ else if (table->t_type == T_LIST && val)
+ return 0;
+ else if (table->t_type == T_HASH && val == NULL)
+ return 0;
+
+ if (priv == NULL) {
+ if (table_static_config(table) == 0)
+ return 0;
+ priv = table->t_handle;
+ }
+
+ r = table_static_priv_add(priv, key, val);
+ if (r == -1)
+ return 0;
+ return 1;
+}
+
+static void
+table_static_dump(struct table *table)
+{
+ struct table_static_priv *priv = table->t_handle;
+ const char *key;
+ char *value;
+ void *iter;
+
+ iter = NULL;
+ while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) {
+ if (value && (void*)value != (void*)priv)
+ log_debug(" \"%s\" -> \"%s\"", key, value);
+ else
+ log_debug(" \"%s\"", key);
+ }
+}
+
+static int
+table_static_update(struct table *table)
+{
+ if (table_static_config(table) == 1) {
+ log_info("info: Table \"%s\" successfully updated", table->t_name);
+ return 1;
+ }
+
+ log_info("info: Failed to update table \"%s\"", table->t_name);
+ return 0;
+}
+
+static int
+table_static_open(struct table *table)
+{
+ if (table->t_handle == NULL)
+ return table_static_config(table);
+ return 1;
+}
+
+static void
+table_static_close(struct table *table)
+{
+ struct table_static_priv *priv = table->t_handle;
+
+ if (priv)
+ table_static_priv_free(priv);
+ table->t_handle = NULL;
+}
+
+static int
+table_static_lookup(struct table *table, enum table_service service, const char *key,
+ char **dst)
+{
+ struct table_static_priv *priv = table->t_handle;
+ char *line;
+ int ret;
+ int (*match)(const char *, const char *) = NULL;
+ size_t i;
+ void *iter;
+ const char *k;
+ char *v;
+
+ for (i = 0; i < nitems(keycmp); ++i)
+ if (keycmp[i].service == service)
+ match = keycmp[i].func;
+
+ line = NULL;
+ iter = NULL;
+ ret = 0;
+ while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) {
+ if (match) {
+ if (match(key, k)) {
+ line = v;
+ ret = 1;
+ }
+ }
+ else {
+ if (strcmp(key, k) == 0) {
+ line = v;
+ ret = 1;
+ }
+ }
+ if (ret)
+ break;
+ }
+
+ if (dst == NULL)
+ return ret ? 1 : 0;
+
+ if (ret == 0)
+ return 0;
+
+ *dst = strdup(line);
+ if (*dst == NULL)
+ return -1;
+
+ return 1;
+}
+
+static int
+table_static_fetch(struct table *t, enum table_service service, char **dst)
+{
+ struct table_static_priv *priv = t->t_handle;
+ const char *k;
+
+ if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) {
+ priv->iter = NULL;
+ if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL))
+ return 0;
+ }
+
+ *dst = strdup(k);
+ if (*dst == NULL)
+ return -1;
+
+ return 1;
+}