Hi,
  This is another try to add table-procexec to smtpd. This allows for table 
backends to communicate with smtpd with a very simple line protocol, similar to 
filter proc-exec.

The code is simple enough and after a bit of time can be used as a replace for 
table-proc (which uses imsg). Currently it is not replacing anything and is 
just available as an extra. I have a WIP perl-ldap table which can talk this 
line protocol and its on github right now (quite old) - 
https://github.com/bsd-ac/table-ldap_perl

OK to import?

Cheers,
Aisha

diff --git a/usr.sbin/smtpd/smtpctl/Makefile b/usr.sbin/smtpd/smtpctl/Makefile
index ef8148be8c9..2e8beff1ad1 100644
--- a/usr.sbin/smtpd/smtpctl/Makefile
+++ b/usr.sbin/smtpd/smtpctl/Makefile
@@ -48,6 +48,7 @@ SRCS+=        table_static.c
 SRCS+= table_db.c
 SRCS+= table_getpwnam.c
 SRCS+= table_proc.c
+SRCS+= table_procexec.c
 SRCS+= unpack_dns.c
 SRCS+= spfwalk.c
 
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index 125a6a5dfbe..ca54d54ea66 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1662,6 +1662,7 @@ int table_regex_match(const char *, const char *);
 void   table_open_all(struct smtpd *);
 void   table_dump_all(struct smtpd *);
 void   table_close_all(struct smtpd *);
+const char *table_service_name(enum table_service );
 
 
 /* to.c */
diff --git a/usr.sbin/smtpd/smtpd/Makefile b/usr.sbin/smtpd/smtpd/Makefile
index d914b43f705..3fcfcd1c19d 100644
--- a/usr.sbin/smtpd/smtpd/Makefile
+++ b/usr.sbin/smtpd/smtpd/Makefile
@@ -63,6 +63,7 @@ SRCS+=                compress_gzip.c
 SRCS+=         table_db.c
 SRCS+=         table_getpwnam.c
 SRCS+=         table_proc.c
+SRCS+=         table_procexec.c
 SRCS+=         table_static.c
 
 SRCS+=         queue_fs.c
diff --git a/usr.sbin/smtpd/table.c b/usr.sbin/smtpd/table.c
index 7328cf5df6e..4f9adfe4c57 100644
--- a/usr.sbin/smtpd/table.c
+++ b/usr.sbin/smtpd/table.c
@@ -36,8 +36,8 @@ extern struct table_backend table_backend_static;
 extern struct table_backend table_backend_db;
 extern struct table_backend table_backend_getpwnam;
 extern struct table_backend table_backend_proc;
+extern struct table_backend table_backend_procexec;
 
-static const char * table_service_name(enum table_service);
 static int table_parse_lookup(enum table_service, const char *, const char *,
     union lookup *);
 static int parse_sockaddr(struct sockaddr *, int, const char *);
@@ -49,6 +49,7 @@ static struct table_backend *backends[] = {
        &table_backend_db,
        &table_backend_getpwnam,
        &table_backend_proc,
+       &table_backend_procexec,
        NULL
 };
 
@@ -67,7 +68,7 @@ table_backend_lookup(const char *backend)
        return NULL;
 }
 
-static const char *
+const char *
 table_service_name(enum table_service s)
 {
        switch (s) {
diff --git a/usr.sbin/smtpd/table_procexec.c b/usr.sbin/smtpd/table_procexec.c
new file mode 100644
index 00000000000..9375da5c0ad
--- /dev/null
+++ b/usr.sbin/smtpd/table_procexec.c
@@ -0,0 +1,326 @@
+/*     $OpenBSD$       */
+
+/*
+ * Copyright (c) 2023 Aisha Tammy <ai...@bsd.ac>
+ * Copyright (c) 2020 Gilles Chehade <gil...@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 <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <imsg.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <paths.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/tree.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "smtpd.h"
+
+#define PROCEXEC_VERSION "1"
+#define PROCEXEC_TIMEOUT 500
+
+static int table_procexec_open(struct table *);
+static int table_procexec_update(struct table *);
+static void table_procexec_close(struct table *);
+static int table_procexec_lookup(struct table *, enum table_service,
+                                 const char *, char **);
+static int table_procexec_fetch(struct table *, enum table_service, char **);
+
+enum procexec_query;
+static int table_procexec_helper(struct table *, enum procexec_query,
+                                 enum table_service, const char *, char **);
+
+struct table_backend table_backend_procexec = {
+    "proc-exec",
+    K_ANY,
+    NULL,
+    NULL,
+    NULL,
+    table_procexec_open,
+    table_procexec_update,
+    table_procexec_close,
+    table_procexec_lookup,
+    table_procexec_fetch,
+};
+
+struct procexec_handle {
+       FILE *backend_w;
+       FILE *backend_r;
+       int read_fd;
+       pid_t pid;
+       int status;
+       useconds_t msec;
+};
+
+enum procexec_query { T_UPDATE, T_LOOKUP, T_FETCH };
+
+static int table_procexec_update(struct table *t) {
+       return table_procexec_helper(t, T_UPDATE, (0), NULL, NULL);
+}
+
+static int table_procexec_lookup(struct table *t, enum table_service service,
+                                 const char *key, char **dst) {
+       return table_procexec_helper(t, T_LOOKUP, service, key, dst);
+}
+
+static int table_procexec_fetch(struct table *t, enum table_service service,
+                                char **dst) {
+       return table_procexec_helper(t, T_FETCH, service, NULL, dst);
+}
+
+static void table_set_ready(int fd, short evt, void *arg) {
+       char *line = NULL;
+       size_t len = 0;
+       ssize_t nchar;
+
+       struct procexec_handle *pe_handle = (struct procexec_handle *)arg;
+
+       nchar = getline(&line, &len, pe_handle->backend_r);
+
+       if (nchar <= 15 || strncmp(line, "TABLE-INIT|READY", 16) != 0) {
+               pe_handle->status = -1;
+       } else {
+               pe_handle->status = 1;
+       }
+       free(line);
+}
+
+static int table_procexec_open(struct table *t) {
+       struct procexec_handle *pe_handle;
+       pid_t pid;
+       int sp[2];
+
+       pe_handle = xcalloc(1, sizeof(*pe_handle));
+
+       if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) {
+               fatalx("procexec - socket pair: %s", t->t_name);
+       }
+
+       if ((pid = fork()) == -1) {
+               fatalx("procexec - fork: %s", t->t_name);
+       }
+
+       if (pid > 0) {
+               close(sp[0]);
+               FILE *backend_w, *backend_r;
+               if ((backend_w = fdopen(sp[1], "w")) == NULL)
+                       fatalx("procexec - backend_w: %s", t->t_name);
+
+               if ((backend_r = fdopen(sp[1], "r")) == NULL)
+                       fatalx("procexec - backend_r: %s", t->t_name);
+               pe_handle->read_fd = sp[1];
+
+               pe_handle->pid = pid;
+               pe_handle->backend_w = backend_w;
+               pe_handle->backend_r = backend_r;
+               pe_handle->status = 0;
+               pe_handle->msec = PROCEXEC_TIMEOUT;
+               t->t_handle = pe_handle;
+
+               fprintf(backend_w, "TABLE-INIT|%s|%d\n", PROCEXEC_VERSION,
+                       PROCEXEC_TIMEOUT);
+               fflush(backend_w);
+
+               struct event *ev =
+                   (struct event *)xcalloc(1, sizeof(struct event));
+               event_set(ev, sp[1], EV_READ, table_set_ready,
+                         (void *)pe_handle);
+               event_add(ev, NULL);
+
+               return 1;
+       } else {
+               close(sp[1]);
+               dup2(sp[0], STDIN_FILENO);
+               dup2(sp[0], STDOUT_FILENO);
+
+               int execr;
+               char exec[_POSIX_ARG_MAX];
+               execr = snprintf(exec, sizeof(exec), "exec %s", t->t_config);
+               if (execr < 0 || execr >= (int)sizeof(exec))
+                       fatalx("procexec - execr: %s", t->t_name);
+               execl("/bin/sh", "/bin/sh", "-c", exec, (char *)NULL);
+       }
+       return 1;
+}
+
+static void table_procexec_close(struct table *t) {
+       struct procexec_handle *pe_handle =
+           (struct procexec_handle *)t->t_handle;
+       fclose(pe_handle->backend_r);
+       fclose(pe_handle->backend_w);
+       close(pe_handle->read_fd);
+       waitpid(pe_handle->pid, 0, 0);
+       free(pe_handle);
+       pe_handle = NULL;
+}
+
+static void procexec_clear_io(int fd, short evt, void *arg) {
+       char *line;
+       size_t len;
+
+       struct procexec_handle *pe_handle = (struct procexec_handle *)arg;
+
+       line = NULL;
+       len = 0;
+       getline(&line, &len, pe_handle->backend_r);
+       free(line);
+       pe_handle->status = 1;
+}
+
+static int procexec_parse(struct procexec_handle *pe_handle, uint64_t reqid,
+                          enum procexec_query query, char **dst) {
+       char *oline, *line, *tline;
+       size_t len;
+       ssize_t nchar;
+       uint64_t resid;
+
+       oline = NULL;
+       len = 0;
+       struct pollfd pfd = {pe_handle->read_fd, POLLIN, 0};
+       int ret = 0;
+       ret = poll(&pfd, 1, PROCEXEC_TIMEOUT);
+       if (ret == 1) {
+               oline = NULL;
+               len = 0;
+               nchar = getline(&oline, &len, pe_handle->backend_r);
+       } else {
+               log_trace(TRACE_IO, "io: table backend io has timed out");
+               pe_handle->status = -1;
+
+               struct event *ev =
+                   (struct event *)xcalloc(1, sizeof(struct event));
+               event_set(ev, pe_handle->read_fd, EV_READ, procexec_clear_io,
+                         (void *)pe_handle);
+               event_add(ev, NULL);
+
+               goto tempfail;
+       }
+       if (len == 0 || nchar < 0) goto tempfail;
+       line = oline;
+
+       len = strlen(oline);
+       line[len - 1] = '\0';
+
+       if (len < 13 || strncmp(line, "TABLE-RESULT|", 13) != 0) goto tempfail;
+       line += 13;
+       len -= 13;
+
+       resid = strtoull(line, &tline, 16);
+       if (len < 17 || *tline != '|') goto tempfail;
+       if (errno == ERANGE && resid == ULLONG_MAX) goto tempfail;
+       if (reqid != resid) goto tempfail;
+       line += 17;
+       len -= 17;
+
+       if (strncmp(line, "NOT-FOUND", 9) == 0) goto failure;
+       if (len < 7 || strncmp(line, "FAILURE", 7) == 0 ||
+           strncmp(line, "SUCCESS", 7) != 0)
+               goto failure;
+       line += 8;
+       len -= 8;
+       switch (query) {
+               case T_UPDATE:
+                       goto success;
+                       break;
+               case T_LOOKUP:
+               case T_FETCH:
+                       *dst = xcalloc(len, sizeof(*dst));
+                       if (strlcpy(*dst, line, len) >= len) {
+                               free(*dst);
+                               goto tempfail;
+                       }
+                       goto success;
+                       break;
+       }
+
+failure:
+       free(oline);
+       return 0;
+
+tempfail:
+       free(oline);
+       return -1;
+
+success:
+       free(oline);
+       return 1;
+}
+
+static int table_procexec_helper(struct table *t, enum procexec_query query,
+                                 enum table_service service, const char *key,
+                                 char **dst) {
+       struct procexec_handle *pe_handle;
+       FILE *backend_w;
+       struct timeval tv;
+       uint64_t reqid;
+       int retval;
+
+       pe_handle = (struct procexec_handle *)t->t_handle;
+       if (pe_handle == NULL || pe_handle->status != 1) {
+               log_trace(TRACE_IO, "io: table backend io is %s",
+                         (pe_handle->status == -1) ? "broken" : "not ready");
+               goto tempfail;
+       }
+       backend_w = pe_handle->backend_w;
+
+       reqid = generate_uid();
+       gettimeofday(&tv, NULL);
+
+       switch (query) {
+               case T_UPDATE:
+                       fprintf(backend_w,
+                               "TABLE|%s|%lld.%06ld|UPDATE|%016" PRIx64 "\n",
+                               PROCEXEC_VERSION, tv.tv_sec, tv.tv_usec, reqid);
+                       fflush(backend_w);
+                       retval = procexec_parse(pe_handle, reqid, query, dst);
+                       goto retval;
+               case T_LOOKUP:
+                       fprintf(backend_w,
+                               "TABLE|%s|%lld.%06ld|LOOKUP|%016" PRIx64
+                               "|%s|%s\n",
+                               PROCEXEC_VERSION, tv.tv_sec, tv.tv_usec, reqid,
+                               table_service_name(service), key);
+                       fflush(backend_w);
+                       retval = procexec_parse(pe_handle, reqid, query, dst);
+                       goto retval;
+               case T_FETCH:
+                       fprintf(backend_w,
+                               "TABLE|%s|%lld.%06ld|FETCH|%016" PRIx64 "|%s\n",
+                               PROCEXEC_VERSION, tv.tv_sec, tv.tv_usec, reqid,
+                               table_service_name(service));
+                       fflush(backend_w);
+                       retval = procexec_parse(pe_handle, reqid, query, dst);
+                       goto retval;
+       }
+
+tempfail:
+       return -1;
+
+retval:
+       return retval;
+}

Reply via email to