commit - /dev/null
commit + ce062d502fe54f3757676855142be3d2eca13ed0
blob - /dev/null
blob + 864603f9f811412dcb93f4dce43c90cb99532c73 (mode 644)
--- /dev/null
+++ Makefile
+# $OpenBSD: Makefile,v 1.33 2018/02/08 05:56:49 jsing Exp $
+
+CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+
+CLEANFILES= ${VERSION_SCRIPT}
+
+WARNINGS= Yes
+
+LIB= opensmtpd
+
+#DPADD= ${LIBEVENT}
+
+#LDADD+= -levent
+
+
+VERSION_SCRIPT= Symbols.map
+SYMBOL_LIST= ${.CURDIR}/Symbols.list
+
+HDRS= opensmtpd.h
+
+SRCS= opensmtpd.c \
+ iobuf.c \
+ ioev.c
+
+includes:
+ @cd ${.CURDIR}; for i in $(HDRS); do \
+ j="cmp -s $$i ${DESTDIR}/usr/include/$$i || \
+ ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m 444 $$i\
+ ${DESTDIR}/usr/include/"; \
+ echo $$j; \
+ eval "$$j"; \
+ done;
+
+${VERSION_SCRIPT}: ${SYMBOL_LIST}
+ { printf '{\n\tglobal:\n'; \
+ sed '/^[._a-zA-Z]/s/$$/;/; s/^/ /' ${SYMBOL_LIST}; \
+ printf '\n\tlocal:\n\t\t*;\n};\n'; } >$@.tmp && mv $@.tmp $@
+
+.include <bsd.lib.mk>
blob - /dev/null
blob + 1da9ce37515d8e8047a437bfda5a062f4127a35a (mode 644)
--- /dev/null
+++ Symbols.list
+osmtpd_register_filter_connect
+osmtpd_register_filter_helo
+osmtpd_register_filter_ehlo
+osmtpd_register_filter_starttls
+osmtpd_register_filter_auth
+osmtpd_register_filter_mailfrom
+osmtpd_register_filter_rcptto
+osmtpd_register_filter_data
+osmtpd_register_filter_dataline
+osmtpd_register_filter_rset
+osmtpd_register_filter_quit
+osmtpd_register_filter_noop
+osmtpd_register_filter_help
+osmtpd_register_filter_wiz
+osmtpd_register_filter_commit
+osmtpd_register_report_connect
+osmtpd_register_report_disconnect
+osmtpd_register_report_identify
+osmtpd_register_report_tls
+osmtpd_register_report_begin
+osmtpd_register_report_mail
+osmtpd_register_report_rcpt
+osmtpd_register_report_envelope
+osmtpd_register_report_data
+osmtpd_register_report_commit
+osmtpd_register_report_rollback
+osmtpd_register_report_client
+osmtpd_register_report_server
+osmtpd_register_report_response
+osmtpd_register_report_timeout
+osmtpd_filter_proceed
+osmtpd_filter_reject
+osmtpd_filter_disconnect
+osmtpd_filter_dataline
+osmtpd_localdata
+osmtpd_run
blob - /dev/null
blob + 38aa0b6138838f56fe1a277c6321c0bdff6e0ea4 (mode 644)
--- /dev/null
+++ iobuf.c
+/* $OpenBSD: iobuf.c,v 1.11 2019/06/12 17:42:53 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.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/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef IO_TLS
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#endif
+
+#include "iobuf.h"
+
+#define IOBUF_MAX 65536
+#define IOBUFQ_MIN 4096
+
+struct ioqbuf *ioqbuf_alloc(struct iobuf *, size_t);
+void iobuf_drain(struct iobuf *, size_t);
+
+int
+iobuf_init(struct iobuf *io, size_t size, size_t max)
+{
+ memset(io, 0, sizeof *io);
+
+ if (max == 0)
+ max = IOBUF_MAX;
+
+ if (size == 0)
+ size = max;
+
+ if (size > max)
+ return (-1);
+
+ if ((io->buf = calloc(size, 1)) == NULL)
+ return (-1);
+
+ io->size = size;
+ io->max = max;
+
+ return (0);
+}
+
+void
+iobuf_clear(struct iobuf *io)
+{
+ struct ioqbuf *q;
+
+ free(io->buf);
+
+ while ((q = io->outq)) {
+ io->outq = q->next;
+ free(q);
+ }
+
+ memset(io, 0, sizeof (*io));
+}
+
+void
+iobuf_drain(struct iobuf *io, size_t n)
+{
+ struct ioqbuf *q;
+ size_t left = n;
+
+ while ((q = io->outq) && left) {
+ if ((q->wpos - q->rpos) > left) {
+ q->rpos += left;
+ left = 0;
+ } else {
+ left -= q->wpos - q->rpos;
+ io->outq = q->next;
+ free(q);
+ }
+ }
+
+ io->queued -= (n - left);
+ if (io->outq == NULL)
+ io->outqlast = NULL;
+}
+
+int
+iobuf_extend(struct iobuf *io, size_t n)
+{
+ char *t;
+
+ if (n > io->max)
+ return (-1);
+
+ if (io->max - io->size < n)
+ return (-1);
+
+ t = recallocarray(io->buf, io->size, io->size + n, 1);
+ if (t == NULL)
+ return (-1);
+
+ io->size += n;
+ io->buf = t;
+
+ return (0);
+}
+
+size_t
+iobuf_left(struct iobuf *io)
+{
+ return io->size - io->wpos;
+}
+
+size_t
+iobuf_space(struct iobuf *io)
+{
+ return io->size - (io->wpos - io->rpos);
+}
+
+size_t
+iobuf_len(struct iobuf *io)
+{
+ return io->wpos - io->rpos;
+}
+
+char *
+iobuf_data(struct iobuf *io)
+{
+ return io->buf + io->rpos;
+}
+
+void
+iobuf_drop(struct iobuf *io, size_t n)
+{
+ if (n >= iobuf_len(io)) {
+ io->rpos = io->wpos = 0;
+ return;
+ }
+
+ io->rpos += n;
+}
+
+char *
+iobuf_getline(struct iobuf *iobuf, size_t *rlen)
+{
+ char *buf;
+ size_t len, i;
+
+ buf = iobuf_data(iobuf);
+ len = iobuf_len(iobuf);
+
+ for (i = 0; i + 1 <= len; i++)
+ if (buf[i] == '\n') {
+ /* Note: the returned address points into the iobuf
+ * buffer. We NUL-end it for convenience, and discard
+ * the data from the iobuf, so that the caller doesn't
+ * have to do it. The data remains "valid" as long
+ * as the iobuf does not overwrite it, that is until
+ * the next call to iobuf_normalize() or iobuf_extend().
+ */
+ iobuf_drop(iobuf, i + 1);
+ len = (i && buf[i - 1] == '\r') ? i - 1 : i;
+ buf[len] = '\0';
+ if (rlen)
+ *rlen = len;
+ return (buf);
+ }
+
+ return (NULL);
+}
+
+void
+iobuf_normalize(struct iobuf *io)
+{
+ if (io->rpos == 0)
+ return;
+
+ if (io->rpos == io->wpos) {
+ io->rpos = io->wpos = 0;
+ return;
+ }
+
+ memmove(io->buf, io->buf + io->rpos, io->wpos - io->rpos);
+ io->wpos -= io->rpos;
+ io->rpos = 0;
+}
+
+ssize_t
+iobuf_read(struct iobuf *io, int fd)
+{
+ ssize_t n;
+
+ n = read(fd, io->buf + io->wpos, iobuf_left(io));
+ if (n == -1) {
+ /* XXX is this really what we want? */
+ if (errno == EAGAIN || errno == EINTR)
+ return (IOBUF_WANT_READ);
+ return (IOBUF_ERROR);
+ }
+ if (n == 0)
+ return (IOBUF_CLOSED);
+
+ io->wpos += n;
+
+ return (n);
+}
+
+struct ioqbuf *
+ioqbuf_alloc(struct iobuf *io, size_t len)
+{
+ struct ioqbuf *q;
+
+ if (len < IOBUFQ_MIN)
+ len = IOBUFQ_MIN;
+
+ if ((q = malloc(sizeof(*q) + len)) == NULL)
+ return (NULL);
+
+ q->rpos = 0;
+ q->wpos = 0;
+ q->size = len;
+ q->next = NULL;
+ q->buf = (char *)(q) + sizeof(*q);
+
+ if (io->outqlast == NULL)
+ io->outq = q;
+ else
+ io->outqlast->next = q;
+ io->outqlast = q;
+
+ return (q);
+}
+
+size_t
+iobuf_queued(struct iobuf *io)
+{
+ return io->queued;
+}
+
+void *
+iobuf_reserve(struct iobuf *io, size_t len)
+{
+ struct ioqbuf *q;
+ void *r;
+
+ if (len == 0)
+ return (NULL);
+
+ if (((q = io->outqlast) == NULL) || q->size - q->wpos <= len) {
+ if ((q = ioqbuf_alloc(io, len)) == NULL)
+ return (NULL);
+ }
+
+ r = q->buf + q->wpos;
+ q->wpos += len;
+ io->queued += len;
+
+ return (r);
+}
+
+int
+iobuf_queue(struct iobuf *io, const void *data, size_t len)
+{
+ void *buf;
+
+ if (len == 0)
+ return (0);
+
+ if ((buf = iobuf_reserve(io, len)) == NULL)
+ return (-1);
+
+ memmove(buf, data, len);
+
+ return (len);
+}
+
+int
+iobuf_queuev(struct iobuf *io, const struct iovec *iov, int iovcnt)
+{
+ int i;
+ size_t len = 0;
+ char *buf;
+
+ for (i = 0; i < iovcnt; i++)
+ len += iov[i].iov_len;
+
+ if ((buf = iobuf_reserve(io, len)) == NULL)
+ return (-1);
+
+ for (i = 0; i < iovcnt; i++) {
+ if (iov[i].iov_len == 0)
+ continue;
+ memmove(buf, iov[i].iov_base, iov[i].iov_len);
+ buf += iov[i].iov_len;
+ }
+
+ return (0);
+
+}
+
+int
+iobuf_fqueue(struct iobuf *io, const char *fmt, ...)
+{
+ va_list ap;
+ int len;
+
+ va_start(ap, fmt);
+ len = iobuf_vfqueue(io, fmt, ap);
+ va_end(ap);
+
+ return (len);
+}
+
+int
+iobuf_vfqueue(struct iobuf *io, const char *fmt, va_list ap)
+{
+ char *buf;
+ int len;
+
+ len = vasprintf(&buf, fmt, ap);
+
+ if (len == -1)
+ return (-1);
+
+ len = iobuf_queue(io, buf, len);
+ free(buf);
+
+ return (len);
+}
+
+ssize_t
+iobuf_write(struct iobuf *io, int fd)
+{
+ struct iovec iov[IOV_MAX];
+ struct ioqbuf *q;
+ int i;
+ ssize_t n;
+
+ i = 0;
+ for (q = io->outq; q ; q = q->next) {
+ if (i >= IOV_MAX)
+ break;
+ iov[i].iov_base = q->buf + q->rpos;
+ iov[i].iov_len = q->wpos - q->rpos;
+ i++;
+ }
+
+ n = writev(fd, iov, i);
+ if (n == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ return (IOBUF_WANT_WRITE);
+ if (errno == EPIPE)
+ return (IOBUF_CLOSED);
+ return (IOBUF_ERROR);
+ }
+
+ iobuf_drain(io, n);
+
+ return (n);
+}
+
+int
+iobuf_flush(struct iobuf *io, int fd)
+{
+ ssize_t s;
+
+ while (io->queued)
+ if ((s = iobuf_write(io, fd)) < 0)
+ return (s);
+
+ return (0);
+}
+
+#ifdef IO_TLS
+
+int
+iobuf_flush_tls(struct iobuf *io, void *tls)
+{
+ ssize_t s;
+
+ while (io->queued)
+ if ((s = iobuf_write_tls(io, tls)) < 0)
+ return (s);
+
+ return (0);
+}
+
+ssize_t
+iobuf_write_tls(struct iobuf *io, void *tls)
+{
+ struct ioqbuf *q;
+ int r;
+ ssize_t n;
+
+ q = io->outq;
+ n = SSL_write(tls, q->buf + q->rpos, q->wpos - q->rpos);
+ if (n <= 0) {
+ switch ((r = SSL_get_error(tls, n))) {
+ case SSL_ERROR_WANT_READ:
+ return (IOBUF_WANT_READ);
+ case SSL_ERROR_WANT_WRITE:
+ return (IOBUF_WANT_WRITE);
+ case SSL_ERROR_ZERO_RETURN: /* connection closed */
+ return (IOBUF_CLOSED);
+ case SSL_ERROR_SYSCALL:
+ if (ERR_peek_last_error())
+ return (IOBUF_TLSERROR);
+ if (r == 0)
+ errno = EPIPE;
+ return (IOBUF_ERROR);
+ default:
+ return (IOBUF_TLSERROR);
+ }
+ }
+ iobuf_drain(io, n);
+
+ return (n);
+}
+
+ssize_t
+iobuf_read_tls(struct iobuf *io, void *tls)
+{
+ ssize_t n;
+ int r;
+
+ n = SSL_read(tls, io->buf + io->wpos, iobuf_left(io));
+ if (n < 0) {
+ switch ((r = SSL_get_error(tls, n))) {
+ case SSL_ERROR_WANT_READ:
+ return (IOBUF_WANT_READ);
+ case SSL_ERROR_WANT_WRITE:
+ return (IOBUF_WANT_WRITE);
+ case SSL_ERROR_SYSCALL:
+ if (ERR_peek_last_error())
+ return (IOBUF_TLSERROR);
+ if (r == 0)
+ errno = EPIPE;
+ return (IOBUF_ERROR);
+ default:
+ return (IOBUF_TLSERROR);
+ }
+ } else if (n == 0)
+ return (IOBUF_CLOSED);
+
+ io->wpos += n;
+
+ return (n);
+}
+
+#endif /* IO_TLS */
blob - /dev/null
blob + c454d0a14077f5b78aa1e60b285b112a0257d9bb (mode 644)
--- /dev/null
+++ iobuf.h
+/* $OpenBSD: iobuf.h,v 1.5 2019/06/12 17:42:53 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.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.
+ */
+
+struct ioqbuf {
+ struct ioqbuf *next;
+ char *buf;
+ size_t size;
+ size_t wpos;
+ size_t rpos;
+};
+
+struct iobuf {
+ char *buf;
+ size_t max;
+ size_t size;
+ size_t wpos;
+ size_t rpos;
+
+ size_t queued;
+ struct ioqbuf *outq;
+ struct ioqbuf *outqlast;
+};
+
+#define IOBUF_WANT_READ -1
+#define IOBUF_WANT_WRITE -2
+#define IOBUF_CLOSED -3
+#define IOBUF_ERROR -4
+#define IOBUF_TLSERROR -5
+
+int iobuf_init(struct iobuf *, size_t, size_t);
+void iobuf_clear(struct iobuf *);
+
+int iobuf_extend(struct iobuf *, size_t);
+void iobuf_normalize(struct iobuf *);
+void iobuf_drop(struct iobuf *, size_t);
+size_t iobuf_space(struct iobuf *);
+size_t iobuf_len(struct iobuf *);
+size_t iobuf_left(struct iobuf *);
+char *iobuf_data(struct iobuf *);
+char *iobuf_getline(struct iobuf *, size_t *);
+ssize_t iobuf_read(struct iobuf *, int);
+ssize_t iobuf_read_tls(struct iobuf *, void *);
+
+size_t iobuf_queued(struct iobuf *);
+void* iobuf_reserve(struct iobuf *, size_t);
+int iobuf_queue(struct iobuf *, const void*, size_t);
+int iobuf_queuev(struct iobuf *, const struct iovec *, int);
+int iobuf_fqueue(struct iobuf *, const char *, ...);
+int iobuf_vfqueue(struct iobuf *, const char *, va_list);
+int iobuf_flush(struct iobuf *, int);
+int iobuf_flush_tls(struct iobuf *, void *);
+ssize_t iobuf_write(struct iobuf *, int);
+ssize_t iobuf_write_tls(struct iobuf *, void *);
blob - /dev/null
blob + d36210fd7dd61cb8b90377276b985ae62690f370 (mode 644)
--- /dev/null
+++ ioev.c
+/* $OpenBSD: ioev.c,v 1.42 2019/06/12 17:42:53 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.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/queue.h>
+#include <sys/socket.h>
+
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "ioev.h"
+#include "iobuf.h"
+
+#ifdef IO_TLS
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#endif
+
+enum {
+ IO_STATE_NONE,
+ IO_STATE_CONNECT,
+ IO_STATE_CONNECT_TLS,
+ IO_STATE_ACCEPT_TLS,
+ IO_STATE_UP,
+
+ IO_STATE_MAX,
+};
+
+#define IO_PAUSE_IN IO_IN
+#define IO_PAUSE_OUT IO_OUT
+#define IO_READ 0x04
+#define IO_WRITE 0x08
+#define IO_RW (IO_READ | IO_WRITE)
+#define IO_RESET 0x10 /* internal */
+#define IO_HELD 0x20 /* internal */
+
+struct io {
+ int sock;
+ void *arg;
+ void (*cb)(struct io*, int, void *);
+ struct iobuf iobuf;
+ size_t lowat;
+ int timeout;
+ int flags;
+ int state;
+ struct event ev;
+ void *tls;
+ const char *error; /* only valid immediately on callback */
+};
+
+const char* io_strflags(int);
+const char* io_evstr(short);
+
+void _io_init(void);
+void io_hold(struct io *);
+void io_release(struct io *);
+void io_callback(struct io*, int);
+void io_dispatch(int, short, void *);
+void io_dispatch_connect(int, short, void *);
+size_t io_pending(struct io *);
+size_t io_queued(struct io*);
+void io_reset(struct io *, short, void (*)(int, short, void*));
+void io_frame_enter(const char *, struct io *, int);
+void io_frame_leave(struct io *);
+
+#ifdef IO_TLS
+void ssl_error(const char *); /* XXX external */
+
+static const char* io_tls_error(void);
+void io_dispatch_accept_tls(int, short, void *);
+void io_dispatch_connect_tls(int, short, void *);
+void io_dispatch_read_tls(int, short, void *);
+void io_dispatch_write_tls(int, short, void *);
+void io_reload_tls(struct io *io);
+#endif
+
+static struct io *current = NULL;
+static uint64_t frame = 0;
+static int _io_debug = 0;
+
+#define io_debug(args...) do { if (_io_debug) printf(args); } while(0)
+
+
+const char*
+io_strio(struct io *io)
+{
+ static char buf[128];
+ char ssl[128];
+
+ ssl[0] = '\0';
+#ifdef IO_TLS
+ if (io->tls) {
+ (void)snprintf(ssl, sizeof ssl, " tls=%s:%s:%d",
+ SSL_get_version(io->tls),
+ SSL_get_cipher_name(io->tls),
+ SSL_get_cipher_bits(io->tls, NULL));
+ }
+#endif
+
+ (void)snprintf(buf, sizeof buf,
+ "<io:%p fd=%d to=%d fl=%s%s ib=%zu ob=%zu>",
+ io, io->sock, io->timeout, io_strflags(io->flags), ssl,
+ io_pending(io), io_queued(io));
+
+ return (buf);
+}
+
+#define CASE(x) case x : return #x
+
+const char*
+io_strevent(int evt)
+{
+ static char buf[32];
+
+ switch (evt) {
+ CASE(IO_CONNECTED);
+ CASE(IO_TLSREADY);
+ CASE(IO_DATAIN);
+ CASE(IO_LOWAT);
+ CASE(IO_DISCONNECTED);
+ CASE(IO_TIMEOUT);
+ CASE(IO_ERROR);
+ default:
+ (void)snprintf(buf, sizeof(buf), "IO_? %d", evt);
+ return buf;
+ }
+}
+
+void
+io_set_nonblocking(int fd)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL)) == -1)
+ err(1, "io_set_blocking:fcntl(F_GETFL)");
+
+ flags |= O_NONBLOCK;
+
+ if (fcntl(fd, F_SETFL, flags) == -1)
+ err(1, "io_set_blocking:fcntl(F_SETFL)");
+}
+
+void
+io_set_nolinger(int fd)
+{
+ struct linger l;
+
+ memset(&l, 0, sizeof(l));
+ if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) == -1)
+ err(1, "io_set_linger:setsockopt()");
+}
+
+/*
+ * Event framing must not rely on an io pointer to refer to the "same" io
+ * throughout the frame, because this is not always the case:
+ *
+ * 1) enter(addr0) -> free(addr0) -> leave(addr0) = SEGV
+ * 2) enter(addr0) -> free(addr0) -> malloc == addr0 -> leave(addr0) = BAD!
+ *
+ * In both case, the problem is that the io is freed in the callback, so
+ * the pointer becomes invalid. If that happens, the user is required to
+ * call io_clear, so we can adapt the frame state there.
+ */
+void
+io_frame_enter(const char *where, struct io *io, int ev)
+{
+ io_debug("\n=== %" PRIu64 " ===\n"
+ "io_frame_enter(%s, %s, %s)\n",
+ frame, where, io_evstr(ev), io_strio(io));
+
+ if (current)
+ errx(1, "io_frame_enter: interleaved frames");
+
+ current = io;
+
+ io_hold(io);
+}
+
+void
+io_frame_leave(struct io *io)
+{
+ io_debug("io_frame_leave(%" PRIu64 ")\n", frame);
+
+ if (current && current != io)
+ errx(1, "io_frame_leave: io mismatch");
+
+ /* io has been cleared */
+ if (current == NULL)
+ goto done;
+
+ /* TODO: There is a possible optimization there:
+ * In a typical half-duplex request/response scenario,
+ * the io is waiting to read a request, and when done, it queues
+ * the response in the output buffer and goes to write mode.
+ * There, the write event is set and will be triggered in the next
+ * event frame. In most case, the write call could be done
+ * immediately as part of the last read frame, thus avoiding to go
+ * through the event loop machinery. So, as an optimisation, we
+ * could detect that case here and force an event dispatching.
+ */
+
+ /* Reload the io if it has not been reset already. */
+ io_release(io);
+ current = NULL;
+ done:
+ io_debug("=== /%" PRIu64 "\n", frame);
+
+ frame += 1;
+}
+
+void
+_io_init()
+{
+ static int init = 0;
+
+ if (init)
+ return;
+
+ init = 1;
+ _io_debug = getenv("IO_DEBUG") != NULL;
+}
+
+struct io *
+io_new(void)
+{
+ struct io *io;
+
+ _io_init();
+
+ if ((io = calloc(1, sizeof(*io))) == NULL)
+ return NULL;
+
+ io->sock = -1;
+ io->timeout = -1;
+
+ if (iobuf_init(&io->iobuf, 0, 0) == -1) {
+ free(io);
+ return NULL;
+ }
+
+ return io;
+}
+
+void
+io_free(struct io *io)
+{
+ io_debug("io_clear(%p)\n", io);
+
+ /* the current io is virtually dead */
+ if (io == current)
+ current = NULL;
+
+#ifdef IO_TLS
+ SSL_free(io->tls);
+ io->tls = NULL;
+#endif
+
+ if (event_initialized(&io->ev))
+ event_del(&io->ev);
+ if (io->sock != -1) {
+ close(io->sock);
+ io->sock = -1;
+ }
+
+ iobuf_clear(&io->iobuf);
+ free(io);
+}
+
+void
+io_hold(struct io *io)
+{
+ io_debug("io_enter(%p)\n", io);
+
+ if (io->flags & IO_HELD)
+ errx(1, "io_hold: io is already held");
+
+ io->flags &= ~IO_RESET;
+ io->flags |= IO_HELD;
+}
+
+void
+io_release(struct io *io)
+{
+ if (!(io->flags & IO_HELD))
+ errx(1, "io_release: io is not held");
+
+ io->flags &= ~IO_HELD;
+ if (!(io->flags & IO_RESET))
+ io_reload(io);
+}
+
+void
+io_set_fd(struct io *io, int fd)
+{
+ io->sock = fd;
+ if (fd != -1)
+ io_reload(io);
+}
+
+void
+io_set_callback(struct io *io, void(*cb)(struct io *, int, void *), void *arg)
+{
+ io->cb = cb;
+ io->arg = arg;
+}
+
+void
+io_set_timeout(struct io *io, int msec)
+{
+ io_debug("io_set_timeout(%p, %d)\n", io, msec);
+
+ io->timeout = msec;
+}
+
+void
+io_set_lowat(struct io *io, size_t lowat)
+{
+ io_debug("io_set_lowat(%p, %zu)\n", io, lowat);
+
+ io->lowat = lowat;
+}
+
+void
+io_pause(struct io *io, int dir)
+{
+ io_debug("io_pause(%p, %x)\n", io, dir);
+
+ io->flags |= dir & (IO_PAUSE_IN | IO_PAUSE_OUT);
+ io_reload(io);
+}
+
+void
+io_resume(struct io *io, int dir)
+{
+ io_debug("io_resume(%p, %x)\n", io, dir);
+
+ io->flags &= ~(dir & (IO_PAUSE_IN | IO_PAUSE_OUT));
+ io_reload(io);
+}
+
+void
+io_set_read(struct io *io)
+{
+ int mode;
+
+ io_debug("io_set_read(%p)\n", io);
+
+ mode = io->flags & IO_RW;
+ if (!(mode == 0 || mode == IO_WRITE))
+ errx(1, "io_set_read(): full-duplex or reading");
+
+ io->flags &= ~IO_RW;
+ io->flags |= IO_READ;
+ io_reload(io);
+}
+
+void
+io_set_write(struct io *io)
+{
+ int mode;
+
+ io_debug("io_set_write(%p)\n", io);
+
+ mode = io->flags & IO_RW;
+ if (!(mode == 0 || mode == IO_READ))
+ errx(1, "io_set_write(): full-duplex or writing");
+
+ io->flags &= ~IO_RW;
+ io->flags |= IO_WRITE;
+ io_reload(io);
+}
+
+const char *
+io_error(struct io *io)
+{
+ return io->error;
+}
+
+void *
+io_tls(struct io *io)
+{
+ return io->tls;
+}
+
+int
+io_fileno(struct io *io)
+{
+ return io->sock;
+}
+
+int
+io_paused(struct io *io, int what)
+{
+ return (io->flags & (IO_PAUSE_IN | IO_PAUSE_OUT)) == what;
+}
+
+/*
+ * Buffered output functions
+ */
+
+int
+io_write(struct io *io, const void *buf, size_t len)
+{
+ int r;
+
+ r = iobuf_queue(&io->iobuf, buf, len);
+
+ io_reload(io);
+
+ return r;
+}
+
+int
+io_writev(struct io *io, const struct iovec *iov, int iovcount)
+{
+ int r;
+
+ r = iobuf_queuev(&io->iobuf, iov, iovcount);
+
+ io_reload(io);
+
+ return r;
+}
+
+int
+io_print(struct io *io, const char *s)
+{
+ return io_write(io, s, strlen(s));
+}
+
+int
+io_printf(struct io *io, const char *fmt, ...)
+{
+ va_list ap;
+ int r;
+
+ va_start(ap, fmt);
+ r = io_vprintf(io, fmt, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int
+io_vprintf(struct io *io, const char *fmt, va_list ap)
+{
+
+ char *buf;
+ int len;
+
+ len = vasprintf(&buf, fmt, ap);
+ if (len == -1)
+ return -1;
+ len = io_write(io, buf, len);
+ free(buf);
+
+ return len;
+}
+
+size_t
+io_queued(struct io *io)
+{
+ return iobuf_queued(&io->iobuf);
+}
+
+/*
+ * Buffered input functions
+ */
+
+void *
+io_data(struct io *io)
+{
+ return iobuf_data(&io->iobuf);
+}
+
+size_t
+io_datalen(struct io *io)
+{
+ return iobuf_len(&io->iobuf);
+}
+
+char *
+io_getline(struct io *io, size_t *sz)
+{
+ return iobuf_getline(&io->iobuf, sz);
+}
+
+void
+io_drop(struct io *io, size_t sz)
+{
+ return iobuf_drop(&io->iobuf, sz);
+}
+
+
+#define IO_READING(io) (((io)->flags & IO_RW) != IO_WRITE)
+#define IO_WRITING(io) (((io)->flags & IO_RW) != IO_READ)
+
+/*
+ * Setup the necessary events as required by the current io state,
+ * honouring duplex mode and i/o pauses.
+ */
+void
+io_reload(struct io *io)
+{
+ short events;
+
+ /* io will be reloaded at release time */
+ if (io->flags & IO_HELD)
+ return;
+
+ iobuf_normalize(&io->iobuf);
+
+#ifdef IO_TLS
+ if (io->tls) {
+ io_reload_tls(io);
+ return;
+ }
+#endif
+
+ io_debug("io_reload(%p)\n", io);
+
+ events = 0;
+ if (IO_READING(io) && !(io->flags & IO_PAUSE_IN))
+ events = EV_READ;
+ if (IO_WRITING(io) && !(io->flags & IO_PAUSE_OUT) && io_queued(io))
+ events |= EV_WRITE;
+
+ io_reset(io, events, io_dispatch);
+}
+
+/* Set the requested event. */
+void
+io_reset(struct io *io, short events, void (*dispatch)(int, short, void*))
+{
+ struct timeval tv, *ptv;
+
+ io_debug("io_reset(%p, %s, %p) -> %s\n",
+ io, io_evstr(events), dispatch, io_strio(io));
+
+ /*
+ * Indicate that the event has already been reset so that reload
+ * is not called on frame_leave.
+ */
+ io->flags |= IO_RESET;
+
+ if (event_initialized(&io->ev))
+ event_del(&io->ev);
+
+ /*
+ * The io is paused by the user, so we don't want the timeout to be
+ * effective.
+ */
+ if (events == 0)
+ return;
+
+ event_set(&io->ev, io->sock, events, dispatch, io);
+ if (io->timeout >= 0) {
+ tv.tv_sec = io->timeout / 1000;
+ tv.tv_usec = (io->timeout % 1000) * 1000;
+ ptv = &tv;
+ } else
+ ptv = NULL;
+
+ event_add(&io->ev, ptv);
+}
+
+size_t
+io_pending(struct io *io)
+{
+ return iobuf_len(&io->iobuf);
+}
+
+const char*
+io_strflags(int flags)
+{
+ static char buf[64];
+
+ buf[0] = '\0';
+
+ switch (flags & IO_RW) {
+ case 0:
+ (void)strlcat(buf, "rw", sizeof buf);
+ break;
+ case IO_READ:
+ (void)strlcat(buf, "R", sizeof buf);
+ break;
+ case IO_WRITE:
+ (void)strlcat(buf, "W", sizeof buf);
+ break;
+ case IO_RW:
+ (void)strlcat(buf, "RW", sizeof buf);
+ break;
+ }
+
+ if (flags & IO_PAUSE_IN)
+ (void)strlcat(buf, ",F_PI", sizeof buf);
+ if (flags & IO_PAUSE_OUT)
+ (void)strlcat(buf, ",F_PO", sizeof buf);
+
+ return buf;
+}
+
+const char*
+io_evstr(short ev)
+{
+ static char buf[64];
+ char buf2[16];
+ int n;
+
+ n = 0;
+ buf[0] = '\0';
+
+ if (ev == 0) {
+ (void)strlcat(buf, "<NONE>", sizeof(buf));
+ return buf;
+ }
+
+ if (ev & EV_TIMEOUT) {
+ (void)strlcat(buf, "EV_TIMEOUT", sizeof(buf));
+ ev &= ~EV_TIMEOUT;
+ n++;
+ }
+
+ if (ev & EV_READ) {
+ if (n)
+ (void)strlcat(buf, "|", sizeof(buf));
+ (void)strlcat(buf, "EV_READ", sizeof(buf));
+ ev &= ~EV_READ;
+ n++;
+ }
+
+ if (ev & EV_WRITE) {
+ if (n)
+ (void)strlcat(buf, "|", sizeof(buf));
+ (void)strlcat(buf, "EV_WRITE", sizeof(buf));
+ ev &= ~EV_WRITE;
+ n++;
+ }
+
+ if (ev & EV_SIGNAL) {
+ if (n)
+ (void)strlcat(buf, "|", sizeof(buf));
+ (void)strlcat(buf, "EV_SIGNAL", sizeof(buf));
+ ev &= ~EV_SIGNAL;
+ n++;
+ }
+
+ if (ev) {
+ if (n)
+ (void)strlcat(buf, "|", sizeof(buf));
+ (void)strlcat(buf, "EV_?=0x", sizeof(buf));
+ (void)snprintf(buf2, sizeof(buf2), "%hx", ev);
+ (void)strlcat(buf, buf2, sizeof(buf));
+ }
+
+ return buf;
+}
+
+void
+io_dispatch(int fd, short ev, void *humppa)
+{
+ struct io *io = humppa;
+ size_t w;
+ ssize_t n;
+ int saved_errno;
+
+ io_frame_enter("io_dispatch", io, ev);
+
+ if (ev == EV_TIMEOUT) {
+ io_callback(io, IO_TIMEOUT);
+ goto leave;
+ }
+
+ if (ev & EV_WRITE && (w = io_queued(io))) {
+ if ((n = iobuf_write(&io->iobuf, io->sock)) < 0) {
+ if (n == IOBUF_WANT_WRITE) /* kqueue bug? */
+ goto read;
+ if (n == IOBUF_CLOSED)
+ io_callback(io, IO_DISCONNECTED);
+ else {
+ saved_errno = errno;
+ io->error = strerror(errno);
+ errno = saved_errno;
+ io_callback(io, IO_ERROR);
+ }
+ goto leave;
+ }
+ if (w > io->lowat && w - n <= io->lowat)
+ io_callback(io, IO_LOWAT);
+ }
+ read:
+
+ if (ev & EV_READ) {
+ iobuf_normalize(&io->iobuf);
+ if ((n = iobuf_read(&io->iobuf, io->sock)) < 0) {
+ if (n == IOBUF_CLOSED)
+ io_callback(io, IO_DISCONNECTED);
+ else {
+ saved_errno = errno;
+ io->error = strerror(errno);
+ errno = saved_errno;
+ io_callback(io, IO_ERROR);
+ }
+ goto leave;
+ }
+ if (n)
+ io_callback(io, IO_DATAIN);
+ }
+
+leave:
+ io_frame_leave(io);
+}
+
+void
+io_callback(struct io *io, int evt)
+{
+ io->cb(io, evt, io->arg);
+}
+
+int
+io_connect(struct io *io, const struct sockaddr *sa, const struct sockaddr *bsa)
+{
+ int sock, errno_save;
+
+ if ((sock = socket(sa->sa_family, SOCK_STREAM, 0)) == -1)
+ goto fail;
+
+ io_set_nonblocking(sock);
+ io_set_nolinger(sock);
+
+ if (bsa && bind(sock, bsa, bsa->sa_len) == -1)
+ goto fail;
+
+ if (connect(sock, sa, sa->sa_len) == -1)
+ if (errno != EINPROGRESS)
+ goto fail;
+
+ io->sock = sock;
+ io_reset(io, EV_WRITE, io_dispatch_connect);
+
+ return (sock);
+
+ fail:
+ if (sock != -1) {
+ errno_save = errno;
+ close(sock);
+ errno = errno_save;
+ io->error = strerror(errno);
+ }
+ return (-1);
+}
+
+void
+io_dispatch_connect(int fd, short ev, void *humppa)
+{
+ struct io *io = humppa;
+ int r, e;
+ socklen_t sl;
+
+ io_frame_enter("io_dispatch_connect", io, ev);
+
+ if (ev == EV_TIMEOUT) {
+ close(fd);
+ io->sock = -1;
+ io_callback(io, IO_TIMEOUT);
+ } else {
+ sl = sizeof(e);
+ r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &e, &sl);
+ if (r == -1) {
+ warn("io_dispatch_connect: getsockopt");
+ e = errno;
+ }
+ if (e) {
+ close(fd);
+ io->sock = -1;
+ io->error = strerror(e);
+ io_callback(io, e == ETIMEDOUT ? IO_TIMEOUT : IO_ERROR);
+ }
+ else {
+ io->state = IO_STATE_UP;
+ io_callback(io, IO_CONNECTED);
+ }
+ }
+
+ io_frame_leave(io);
+}
+
+#ifdef IO_TLS
+
+static const char*
+io_tls_error(void)
+{
+ static char buf[128];
+ unsigned long e;
+
+ e = ERR_peek_last_error();
+ if (e) {
+ ERR_error_string(e, buf);
+ return (buf);
+ }
+
+ return ("No TLS error");
+}
+
+int
+io_start_tls(struct io *io, void *tls)
+{
+ int mode;
+
+ mode = io->flags & IO_RW;
+ if (mode == 0 || mode == IO_RW)
+ errx(1, "io_start_tls(): full-duplex or unset");
+
+ if (io->tls)
+ errx(1, "io_start_tls(): TLS already started");
+ io->tls = tls;
+
+ if (SSL_set_fd(io->tls, io->sock) == 0) {
+ ssl_error("io_start_tls:SSL_set_fd");
+ return (-1);
+ }
+
+ if (mode == IO_WRITE) {
+ io->state = IO_STATE_CONNECT_TLS;
+ SSL_set_connect_state(io->tls);
+ io_reset(io, EV_WRITE, io_dispatch_connect_tls);
+ } else {
+ io->state = IO_STATE_ACCEPT_TLS;
+ SSL_set_accept_state(io->tls);
+ io_reset(io, EV_READ, io_dispatch_accept_tls);
+ }
+
+ return (0);
+}
+
+void
+io_dispatch_accept_tls(int fd, short event, void *humppa)
+{
+ struct io *io = humppa;
+ int e, ret;
+
+ io_frame_enter("io_dispatch_accept_tls", io, event);
+
+ if (event == EV_TIMEOUT) {
+ io_callback(io, IO_TIMEOUT);
+ goto leave;
+ }
+
+ if ((ret = SSL_accept(io->tls)) > 0) {
+ io->state = IO_STATE_UP;
+ io_callback(io, IO_TLSREADY);
+ goto leave;
+ }
+
+ switch ((e = SSL_get_error(io->tls, ret))) {
+ case SSL_ERROR_WANT_READ:
+ io_reset(io, EV_READ, io_dispatch_accept_tls);
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ io_reset(io, EV_WRITE, io_dispatch_accept_tls);
+ break;
+ default:
+ io->error = io_tls_error();
+ ssl_error("io_dispatch_accept_tls:SSL_accept");
+ io_callback(io, IO_ERROR);
+ break;
+ }
+
+ leave:
+ io_frame_leave(io);
+}
+
+void
+io_dispatch_connect_tls(int fd, short event, void *humppa)
+{
+ struct io *io = humppa;
+ int e, ret;
+
+ io_frame_enter("io_dispatch_connect_tls", io, event);
+
+ if (event == EV_TIMEOUT) {
+ io_callback(io, IO_TIMEOUT);
+ goto leave;
+ }
+
+ if ((ret = SSL_connect(io->tls)) > 0) {
+ io->state = IO_STATE_UP;
+ io_callback(io, IO_TLSREADY);
+ goto leave;
+ }
+
+ switch ((e = SSL_get_error(io->tls, ret))) {
+ case SSL_ERROR_WANT_READ:
+ io_reset(io, EV_READ, io_dispatch_connect_tls);
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ io_reset(io, EV_WRITE, io_dispatch_connect_tls);
+ break;
+ default:
+ io->error = io_tls_error();
+ ssl_error("io_dispatch_connect_ssl:SSL_connect");
+ io_callback(io, IO_TLSERROR);
+ break;
+ }
+
+ leave:
+ io_frame_leave(io);
+}
+
+void
+io_dispatch_read_tls(int fd, short event, void *humppa)
+{
+ struct io *io = humppa;
+ int n, saved_errno;
+
+ io_frame_enter("io_dispatch_read_tls", io, event);
+
+ if (event == EV_TIMEOUT) {
+ io_callback(io, IO_TIMEOUT);
+ goto leave;
+ }
+
+again:
+ iobuf_normalize(&io->iobuf);
+ switch ((n = iobuf_read_tls(&io->iobuf, (SSL*)io->tls))) {
+ case IOBUF_WANT_READ:
+ io_reset(io, EV_READ, io_dispatch_read_tls);
+ break;
+ case IOBUF_WANT_WRITE:
+ io_reset(io, EV_WRITE, io_dispatch_read_tls);
+ break;
+ case IOBUF_CLOSED:
+ io_callback(io, IO_DISCONNECTED);
+ break;
+ case IOBUF_ERROR:
+ saved_errno = errno;
+ io->error = strerror(errno);
+ errno = saved_errno;
+ io_callback(io, IO_ERROR);
+ break;
+ case IOBUF_TLSERROR:
+ io->error = io_tls_error();
+ ssl_error("io_dispatch_read_tls:SSL_read");
+ io_callback(io, IO_ERROR);
+ break;
+ default:
+ io_debug("io_dispatch_read_tls(...) -> r=%d\n", n);
+ io_callback(io, IO_DATAIN);
+ if (current == io && IO_READING(io) && SSL_pending(io->tls))
+ goto again;
+ }
+
+ leave:
+ io_frame_leave(io);
+}
+
+void
+io_dispatch_write_tls(int fd, short event, void *humppa)
+{
+ struct io *io = humppa;
+ int n, saved_errno;
+ size_t w2, w;
+
+ io_frame_enter("io_dispatch_write_tls", io, event);
+
+ if (event == EV_TIMEOUT) {
+ io_callback(io, IO_TIMEOUT);
+ goto leave;
+ }
+
+ w = io_queued(io);
+ switch ((n = iobuf_write_tls(&io->iobuf, (SSL*)io->tls))) {
+ case IOBUF_WANT_READ:
+ io_reset(io, EV_READ, io_dispatch_write_tls);
+ break;
+ case IOBUF_WANT_WRITE:
+ io_reset(io, EV_WRITE, io_dispatch_write_tls);
+ break;
+ case IOBUF_CLOSED:
+ io_callback(io, IO_DISCONNECTED);
+ break;
+ case IOBUF_ERROR:
+ saved_errno = errno;
+ io->error = strerror(errno);
+ errno = saved_errno;
+ io_callback(io, IO_ERROR);
+ break;
+ case IOBUF_TLSERROR:
+ io->error = io_tls_error();
+ ssl_error("io_dispatch_write_tls:SSL_write");
+ io_callback(io, IO_ERROR);
+ break;
+ default:
+ io_debug("io_dispatch_write_tls(...) -> w=%d\n", n);
+ w2 = io_queued(io);
+ if (w > io->lowat && w2 <= io->lowat)
+ io_callback(io, IO_LOWAT);
+ break;
+ }
+
+ leave:
+ io_frame_leave(io);
+}
+
+void
+io_reload_tls(struct io *io)
+{
+ short ev = 0;
+ void (*dispatch)(int, short, void*) = NULL;
+
+ switch (io->state) {
+ case IO_STATE_CONNECT_TLS:
+ ev = EV_WRITE;
+ dispatch = io_dispatch_connect_tls;
+ break;
+ case IO_STATE_ACCEPT_TLS:
+ ev = EV_READ;
+ dispatch = io_dispatch_accept_tls;
+ break;
+ case IO_STATE_UP:
+ ev = 0;
+ if (IO_READING(io) && !(io->flags & IO_PAUSE_IN)) {
+ ev = EV_READ;
+ dispatch = io_dispatch_read_tls;
+ }
+ else if (IO_WRITING(io) && !(io->flags & IO_PAUSE_OUT) &&
+ io_queued(io)) {
+ ev = EV_WRITE;
+ dispatch = io_dispatch_write_tls;
+ }
+ if (!ev)
+ return; /* paused */
+ break;
+ default:
+ errx(1, "io_reload_tls(): bad state");
+ }
+
+ io_reset(io, ev, dispatch);
+}
+
+#endif /* IO_TLS */
blob - /dev/null
blob + 015340c2b1664d79c58e83c288b59689a8d97ac1 (mode 644)
--- /dev/null
+++ ioev.h
+/* $OpenBSD: ioev.h,v 1.17 2019/06/12 17:42:53 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.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.
+ */
+
+enum {
+ IO_CONNECTED = 0, /* connection successful */
+ IO_TLSREADY, /* TLS started successfully */
+ IO_TLSERROR, /* XXX - needs more work */
+ IO_DATAIN, /* new data in input buffer */
+ IO_LOWAT, /* output queue running low */
+ IO_DISCONNECTED, /* error? */
+ IO_TIMEOUT, /* error? */
+ IO_ERROR, /* details? */
+};
+
+#define IO_IN 0x01
+#define IO_OUT 0x02
+
+struct io;
+
+void io_set_nonblocking(int);
+void io_set_nolinger(int);
+
+struct io *io_new(void);
+void io_free(struct io *);
+void io_set_read(struct io *);
+void io_set_write(struct io *);
+void io_set_fd(struct io *, int);
+void io_set_callback(struct io *io, void(*)(struct io *, int, void *), void *);
+void io_set_timeout(struct io *, int);
+void io_set_lowat(struct io *, size_t);
+void io_pause(struct io *, int);
+void io_resume(struct io *, int);
+void io_reload(struct io *);
+int io_connect(struct io *, const struct sockaddr *, const struct sockaddr *);
+int io_start_tls(struct io *, void *);
+const char* io_strio(struct io *);
+const char* io_strevent(int);
+const char* io_error(struct io *);
+void* io_tls(struct io *);
+int io_fileno(struct io *);
+int io_paused(struct io *, int);
+
+/* Buffered output functions */
+int io_write(struct io *, const void *, size_t);
+int io_writev(struct io *, const struct iovec *, int);
+int io_print(struct io *, const char *);
+int io_printf(struct io *, const char *, ...);
+int io_vprintf(struct io *, const char *, va_list);
+size_t io_queued(struct io *);
+
+/* Buffered input functions */
+void* io_data(struct io *);
+size_t io_datalen(struct io *);
+char* io_getline(struct io *, size_t *);
+void io_drop(struct io *, size_t);
blob - /dev/null
blob + 76e526d31261c26e5d4088450acdf143432e8c08 (mode 644)
--- /dev/null
+++ opensmtpd.c
+/*
+ * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.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/time.h>
+#include <sys/tree.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <arpa/inet.h>
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "opensmtpd.h"
+#include "ioev.h"
+#include "opensmtpd_priv.h"
+
+#define NITEMS(x) (sizeof(x) / sizeof(*x))
+
+static struct io *io_stdout;
+
+RB_HEAD(osmtpd_sessions, osmtpd_session) osmtpd_sessions = RB_INITIALIZER(NULL);
+RB_PROTOTYPE(osmtpd_sessions, osmtpd_session, entry, osmtpd_session_cmp);
+
+static int ready = 0;
+/* Default from smtpd */
+static int session_timeout = 300;
+
+void
+osmtpd_register_filter_connect(void (*cb)(struct osmtpd_ctx *, const char *,
+ struct sockaddr_storage *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_CONNECT, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_helo(void (*cb)(struct osmtpd_ctx *, const char *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_HELO, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_ehlo(void (*cb)(struct osmtpd_ctx *, const char *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_EHLO, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_starttls(void (*cb)(struct osmtpd_ctx *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_STARTTLS, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_auth(void (*cb)(struct osmtpd_ctx *, const char *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_AUTH, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_mailfrom(void (*cb)(struct osmtpd_ctx *, const char *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_MAIL_FROM, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_rcptto(void (*cb)(struct osmtpd_ctx *, const char *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_RCPT_TO, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_data(void (*cb)(struct osmtpd_ctx *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_DATA, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_dataline(void (*cb)(struct osmtpd_ctx *, const char *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_DATA_LINE, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_rset(void (*cb)(struct osmtpd_ctx *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_RSET, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_quit(void (*cb)(struct osmtpd_ctx *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_QUIT, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_noop(void (*cb)(struct osmtpd_ctx *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_NOOP, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_help(void (*cb)(struct osmtpd_ctx *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_HELP, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_wiz(void (*cb)(struct osmtpd_ctx *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_WIZ, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_filter_commit(void (*cb)(struct osmtpd_ctx *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_COMMIT, 1, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT, 1, 0,
+ NULL);
+}
+
+void
+osmtpd_register_report_connect(int incoming, void (*cb)(struct osmtpd_ctx *,
+ const char *, const char *, struct sockaddr_storage *,
+ struct sockaddr_storage *))
+{
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_LINK_CONNECT, incoming,
+ 0, (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_disconnect(int incoming, void (*cb)(struct osmtpd_ctx *))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, (void *)cb);
+}
+
+void
+osmtpd_register_report_identify(int incoming, void (*cb)(struct osmtpd_ctx *,
+ const char *))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_IDENTIFY,
+ incoming, 0, (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_tls(int incoming, void (*cb)(struct osmtpd_ctx *,
+ const char *))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_TLS, incoming, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_begin(int incoming, void (*cb)(struct osmtpd_ctx *,
+ uint32_t))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_BEGIN, incoming, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_mail(int incoming, void (*cb)(struct osmtpd_ctx *,
+ uint32_t, const char *, enum osmtpd_status))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_MAIL, incoming, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_rcpt(int incoming, void (*cb)(struct osmtpd_ctx *,
+ uint32_t, const char *, enum osmtpd_status))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_RCPT, incoming, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_envelope(int incoming, void (*cb)(struct osmtpd_ctx *,
+ uint32_t, uint64_t))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_ENVELOPE, incoming,
+ 0, (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_data(int incoming, void (*cb)(struct osmtpd_ctx *,
+ uint32_t, enum osmtpd_status))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_DATA, incoming, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_commit(int incoming, void (*cb)(struct osmtpd_ctx *,
+ uint32_t, size_t))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_COMMIT, incoming, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_rollback(int incoming, void (*cb)(struct osmtpd_ctx *,
+ uint32_t))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_ROLLBACK, incoming,
+ 0, (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_client(int incoming, void (*cb)(struct osmtpd_ctx *,
+ const char *))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_PROTOCOL_CLIENT,
+ incoming, 0, (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_server(int incoming, void (*cb)(struct osmtpd_ctx *,
+ const char *))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_PROTOCOL_SERVER,
+ incoming, 0, (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_response(int incoming, void (*cb)(struct osmtpd_ctx *,
+ const char *))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_FILTER_RESPONSE,
+ incoming, 0, (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_register_report_timeout(int incoming, void (*cb)(struct osmtpd_ctx *))
+{
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TIMEOUT, incoming, 0,
+ (void *)cb);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_localdata(void *(*oncreate)(struct osmtpd_ctx *),
+ void (*ondelete)(struct osmtpd_ctx *, void *))
+{
+ oncreatecb = oncreate;
+ ondeletecb = ondelete;
+}
+
+void
+osmtpd_need(int incoming, int needs)
+{
+ if (needs & (OSMTPD_NEED_SRC | OSMTPD_NEED_DST | OSMTPD_NEED_RDNS |
+ OSMTPD_NEED_FRDNS))
+ osmtpd_register(OSMTPD_TYPE_FILTER, OSMTPD_PHASE_LINK_CONNECT,
+ incoming, 1, NULL);
+ if (needs & OSMTPD_NEED_IDENTITY)
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_IDENTIFY,
+ incoming, 1, NULL);
+ if (needs & OSMTPD_NEED_CIPHERS)
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_TLS,
+ incoming, 1, NULL);
+ if (needs & OSMTPD_NEED_MSGID) {
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_BEGIN,
+ incoming, 1, NULL);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_ROLLBACK,
+ incoming, 0, NULL);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_COMMIT,
+ incoming, 0, NULL);
+ }
+ if (needs & OSMTPD_NEED_MAILFROM) {
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_MAIL,
+ incoming, 1, NULL);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_ROLLBACK,
+ incoming, 0, NULL);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_COMMIT,
+ incoming, 0, NULL);
+ }
+ if (needs & OSMTPD_NEED_RCPTTO) {
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_RCPT,
+ incoming, 1, NULL);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_ROLLBACK,
+ incoming, 0, NULL);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_COMMIT,
+ incoming, 0, NULL);
+ }
+ if (needs & OSMTPD_NEED_EVPID) {
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_ENVELOPE,
+ incoming, 1, NULL);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_ROLLBACK,
+ incoming, 0, NULL);
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_TX_COMMIT,
+ incoming, 0, NULL);
+ }
+
+ osmtpd_register(OSMTPD_TYPE_REPORT, OSMTPD_PHASE_LINK_DISCONNECT,
+ incoming, 0, NULL);
+}
+
+void
+osmtpd_run(void)
+{
+ size_t i = 0;
+ int registered = 0;
+ /* Doesn't leak, since it needs to exists until the application ends. */
+ struct io *io_stdin;
+
+ event_init();
+
+ if ((io_stdin = io_new()) == NULL ||
+ (io_stdout = io_new()) == NULL)
+ err(1, "io_new");
+ io_set_nonblocking(STDIN_FILENO);
+ io_set_fd(io_stdin, STDIN_FILENO);
+ io_set_callback(io_stdin, osmtpd_newline, NULL);
+ io_set_read(io_stdin);
+ io_set_nonblocking(STDOUT_FILENO);
+ io_set_fd(io_stdout, STDOUT_FILENO);
+ io_set_callback(io_stdout, osmtpd_outevt, NULL);
+ io_set_write(io_stdout);
+
+ for (i = 0; i < NITEMS(osmtpd_callbacks); i++) {
+ if (osmtpd_callbacks[i].doregister) {
+ if (osmtpd_callbacks[i].cb != NULL)
+ registered = 1;
+ io_printf(io_stdout, "register|%s|smtp-%s|%s\n",
+ osmtpd_typetostr(osmtpd_callbacks[i].type),
+ osmtpd_callbacks[i].incoming ? "in" : "out",
+ osmtpd_phasetostr(osmtpd_callbacks[i].phase));
+ }
+ }
+
+ if (!registered)
+ errx(1, "No events registered");
+ io_printf(io_stdout, "register|ready\n");
+ ready = 1;
+
+ event_dispatch();
+}
+
+static void
+osmtpd_newline(struct io *io, int ev, void *arg)
+{
+ static char *linedup = NULL;
+ static size_t dupsize = 0;
+ struct osmtpd_session *ctx, search;
+ enum osmtpd_type type;
+ enum osmtpd_phase phase;
+ int version_major, version_minor, incoming;
+ struct timespec tm;
+ char *line = NULL;
+ const char *errstr = NULL;
+ size_t linelen;
+ char *end;
+ size_t i;
+
+ if (ev == IO_DISCONNECTED)
+ exit(0);
+ if (ev != IO_DATAIN)
+ return;
+ while ((line = io_getline(io, &linelen)) > 0) {
+ if (dupsize < linelen) {
+ if ((linedup = realloc(linedup, linelen + 1)) == NULL)
+ err(1, NULL);
+ dupsize = linelen + 1;
+ }
+ strlcpy(linedup, line, dupsize);
+ if ((end = strchr(line, '|')) == NULL)
+ errx(1, "Invalid line received: missing version: %s", linedup);
+ end++[0] = '\0';
+ if (strcmp(line, "filter") == 0)
+ type = OSMTPD_TYPE_FILTER;
+ else if (strcmp(line, "report") == 0)
+ type = OSMTPD_TYPE_REPORT;
+ else if (strcmp(line, "config") == 0) {
+ line = end;
+ if (strcmp(line, "ready") == 0)
+ continue;
+ if ((end = strchr(line, '|')) == NULL)
+ errx(1, "Invalid line received: missing key: %s", linedup);
+ end++[0] = '\0';
+ if (strcmp(line, "smtp-session-timeout") == 0) {
+ session_timeout = strtonum(end, 0, INT_MAX,
+ &errstr);
+ if (errstr != NULL)
+ errx(1, "Invalid line received: "
+ "invalid smtp-sesion-timeout: %s",
+ linedup);
+ }
+ continue;
+ }
+ else
+ errx(1, "Invalid line received: unknown message type: %s", linedup);
+ line = end;
+ if ((end = strchr(line, '|')) == NULL)
+ errx(1, "Invalid line received: missing time: %s", linedup);
+ end++[0] = '\0';
+ if (strcmp(line, "0.1") != 0)
+ errx(1, "Unsupported protocol received: %s", linedup);
+ version_major = 0;
+ version_minor = 1;
+ line = end;
+ if ((end = strchr(line, '.')) == NULL)
+ errx(1, "Invalid line received: invalid timestamp: %s", linedup);
+ end++[0] = '\0';
+ tm.tv_sec = (time_t) strtonum(line, 0, INT64_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "Invalid line received: invalid timestamp: %s", linedup);
+ line = end;
+ if ((end = strchr(line, '|')) == NULL)
+ errx(1, "Invalid line received: missing direction: %s", linedup);
+ end++[0] = '\0';
+ tm.tv_nsec = (long) strtonum(line, 0, LONG_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "Invalid line received: invalid timestamp: %s",
+ linedup);
+ tm.tv_nsec *= 10 * (9 - (end - line));
+ line = end;
+ if ((end = strchr(line, '|')) == NULL)
+ errx(1, "Invalid line receieved: missing phase: %s",
+ linedup);
+ end++[0] = '\0';
+ if (strcmp(line, "smtp-in") == 0)
+ incoming = 1;
+ else if (strcmp(line, "smtp-out") == 0)
+ incoming = 0;
+ else
+ errx(1, "Invalid line: invalid direction: %s", linedup);
+ line = end;
+ if ((end = strchr(line, '|')) == NULL)
+ errx(1, "Invalid line received: missing reqid: %s",
+ linedup);
+ end++[0] = '\0';
+ phase = osmtpd_strtophase(line, linedup);
+ line = end;
+ errno = 0;
+ search.ctx.reqid = strtoull(line, &end, 16);
+ if ((search.ctx.reqid == ULLONG_MAX && errno != 0) ||
+ (end[0] != '|' && end[0] != '\0'))
+ errx(1, "Invalid line received: invalid reqid: %s",
+ linedup);
+ line = end + 1;
+ ctx = RB_FIND(osmtpd_sessions, &osmtpd_sessions, &search);
+ if (ctx == NULL) {
+ if ((ctx = malloc(sizeof(*ctx))) == NULL)
+ err(1, NULL);
+ ctx->ctx.reqid = search.ctx.reqid;
+ ctx->ctx.rdns = NULL;
+ ctx->ctx.fcrdns = NULL;
+ ctx->ctx.identity = NULL;
+ ctx->ctx.ciphers = NULL;
+ ctx->ctx.msgid = 0;
+ ctx->ctx.mailfrom = NULL;
+ ctx->ctx.rcptto = malloc(sizeof(*(ctx->ctx.rcptto)));
+ if (ctx->ctx.rcptto == NULL)
+ err(1, "malloc");
+ ctx->ctx.rcptto[0] = NULL;
+ memset(&(ctx->ctx.src), 0, sizeof(ctx->ctx.src));
+ ctx->ctx.src.ss_family = AF_UNSPEC;
+ memset(&(ctx->ctx.dst), 0, sizeof(ctx->ctx.dst));
+ ctx->ctx.dst.ss_family = AF_UNSPEC;
+ RB_INSERT(osmtpd_sessions, &osmtpd_sessions, ctx);
+ ctx->ctx.evpid = 0;
+ ctx->ctx.local = NULL;
+ if (oncreatecb != NULL)
+ ctx->ctx.local = oncreatecb(&ctx->ctx);
+ }
+ ctx->ctx.type = type;
+ ctx->ctx.phase = phase;
+ ctx->ctx.version_major = version_major;
+ ctx->ctx.version_minor = version_minor;
+ ctx->ctx.incoming = incoming;
+ ctx->ctx.tm.tv_sec = tm.tv_sec;
+ ctx->ctx.tm.tv_nsec = tm.tv_nsec;
+ ctx->ctx.token = 0;
+
+ for (i = 0; i < NITEMS(osmtpd_callbacks); i++) {
+ if (ctx->ctx.type == osmtpd_callbacks[i].type &&
+ ctx->ctx.phase == osmtpd_callbacks[i].phase &&
+ ctx->ctx.incoming == osmtpd_callbacks[i].incoming)
+ break;
+ }
+ if (i == NITEMS(osmtpd_callbacks)) {
+ errx(1, "Invalid line received: received unregistered "
+ "line: %s", linedup);
+ }
+ if (ctx->ctx.type == OSMTPD_TYPE_FILTER) {
+ ctx->ctx.token = strtoull(line, &end, 16);
+ if ((ctx->ctx.token == ULLONG_MAX && errno != 0) ||
+ end[0] != '|')
+ errx(1, "Invalid line received: invalid token: %s", linedup);
+ line = end + 1;
+ }
+ osmtpd_callbacks[i].osmtpd_cb(&(osmtpd_callbacks[i]),
+ &(ctx->ctx), line, linedup);
+ }
+}
+
+static void
+osmtpd_outevt(struct io *io, int evt, void *arg)
+{
+ switch (evt) {
+ case IO_LOWAT:
+ return;
+ case IO_DISCONNECTED:
+ exit(0);
+ default:
+ errx(1, "Unexpectd event");
+ }
+}
+
+static void
+osmtpd_noargs(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx, char *params,
+ char *linedup)
+{
+ void (*f)(struct osmtpd_ctx *);
+
+ f = cb->cb;
+ f(ctx);
+}
+
+static void
+osmtpd_onearg(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx, char *line,
+ char *linedup)
+{
+ void (*f)(struct osmtpd_ctx *, const char *);
+
+ f = cb->cb;
+ f(ctx, line);
+}
+
+static void
+osmtpd_connect(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx, char *params,
+ char *linedup)
+{
+ struct sockaddr_storage ss;
+ char *hostname;
+ char *address;
+ void (*f)(struct osmtpd_ctx *, const char *, struct sockaddr_storage *);
+
+ hostname = params;
+ if ((address = strchr(params, '|')) == NULL)
+ errx(1, "Invalid line received: missing address: %s", linedup);
+ address++[0] = '\0';
+
+ osmtpd_addrtoss(address, &ss, 0, linedup);
+
+ f = cb->cb;
+ f(ctx, hostname, &ss);
+}
+
+static void
+osmtpd_link_connect(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx,
+ char *params, char *linedup)
+{
+ char *end, *rdns, *fcrdns;
+ struct sockaddr_storage src, dst;
+ void (*f)(struct osmtpd_ctx *, const char *, const char *,
+ struct sockaddr_storage *, struct sockaddr_storage *);
+
+ if ((end = strchr(params, '|')) == NULL)
+ errx(1, "Invalid line received: missing fcrdns: %s", linedup);
+ end++[0] = '\0';
+ rdns = params;
+ params = end;
+ if ((end = strchr(params, '|')) == NULL)
+ errx(1, "Invalid line received: missing src: %s", linedup);
+ end++[0] = '\0';
+ fcrdns = params;
+ params = end;
+ if ((end = strchr(params, '|')) == NULL)
+ errx(1, "Invalid line received: missing dst: %s", linedup);
+ end++[0] = '\0';
+ osmtpd_addrtoss(params, &src, 1, linedup);
+ params = end;
+ osmtpd_addrtoss(params, &dst, 1, linedup);
+ if (cb->storereport) {
+ if ((ctx->rdns = strdup(params)) == NULL)
+ err(1, NULL);
+ if ((ctx->fcrdns = strdup(params)) == NULL)
+ err(1, NULL);
+ memcpy(&(ctx->src), &src, sizeof(ctx->src));
+ memcpy(&(ctx->dst), &dst, sizeof(ctx->dst));
+ }
+ if ((f = cb->cb) != NULL)
+ f(ctx, rdns, fcrdns, &src, &dst);
+}
+
+static void
+osmtpd_link_disconnect(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx,
+ char *param, char *linedup)
+{
+ void (*f)(struct osmtpd_ctx *);
+ size_t i;
+ struct osmtpd_session *session, search;
+
+ if ((f = cb->cb) != NULL)
+ f(ctx);
+
+ search.ctx.reqid = ctx->reqid;
+ session = RB_FIND(osmtpd_sessions, &osmtpd_sessions, &search);
+ if (session != NULL) {
+ RB_REMOVE(osmtpd_sessions, &osmtpd_sessions, session);
+ if (ondeletecb != NULL)
+ ondeletecb(ctx, session->ctx.local);
+ free(session->ctx.rdns);
+ free(session->ctx.fcrdns);
+ free(session->ctx.identity);
+ free(session->ctx.ciphers);
+ free(session->ctx.mailfrom);
+ for (i = 0; session->ctx.rcptto[i] != NULL; i++)
+ free(session->ctx.rcptto[i]);
+ free(session->ctx.rcptto);
+ free(session);
+ }
+}
+
+static void
+osmtpd_link_identify(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx,
+ char *identity, char *linedup)
+{
+ void (*f)(struct osmtpd_ctx *, const char *);
+
+ if (cb->storereport) {
+ if ((ctx->identity = strdup(identity)) == NULL)
+ err(1, NULL);
+ }
+
+ if ((f = cb->cb) != NULL)
+ f(ctx, identity);
+}
+
+static void
+osmtpd_link_tls(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx,
+ char *ciphers, char *linedup)
+{
+ void (*f)(struct osmtpd_ctx *, const char *);
+
+ if (cb->storereport) {
+ if ((ctx->ciphers = strdup(ciphers)) == NULL)
+ err(1, NULL);
+ }
+
+ if ((f = cb->cb) != NULL)
+ f(ctx, ciphers);
+}
+
+static void
+osmtpd_tx_begin(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx,
+ char *msgid, char *linedup)
+{
+ unsigned long imsgid;
+ char *endptr;
+ void (*f)(struct osmtpd_ctx *, uint32_t);
+
+ errno = 0;
+ imsgid = strtoul(msgid, &endptr, 16);
+ if ((imsgid == ULONG_MAX && errno != 0) || endptr[0] != '\0')
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+ ctx->msgid = imsgid;
+ /* Check if we're in range */
+ if ((unsigned long) ctx->msgid != imsgid)
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+
+ if (!cb->storereport)
+ ctx->msgid = 0;
+
+ if ((f = cb->cb) != NULL)
+ f(ctx, imsgid);
+}
+
+static void
+osmtpd_tx_mail(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx,
+ char *params, char *linedup)
+{
+ char *end, *mailfrom;
+ unsigned long imsgid;
+ uint32_t msgid;
+ void (*f)(struct osmtpd_ctx *, uint32_t, const char *,
+ enum osmtpd_status);
+
+ errno = 0;
+ imsgid = strtoul(params, &end, 16);
+ if ((imsgid == ULONG_MAX && errno != 0))
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+ if (end[0] != '|')
+ errx(1, "Invalid line received: missing address: %s", linedup);
+ msgid = imsgid;
+ if ((unsigned long) msgid != imsgid)
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+ params = end + 1;
+
+ if ((end = strchr(params, '|')) == NULL)
+ errx(1, "Invalid line received: missing status: %s", linedup);
+ end++[0] = '\0';
+ mailfrom = params;
+ if (cb->storereport) {
+ if ((ctx->mailfrom = strdup(mailfrom)) == NULL)
+ err(1, NULL);
+ }
+ params = end;
+
+ if ((f = cb->cb) != NULL)
+ f(ctx, msgid, ctx->mailfrom,
+ osmtpd_strtostatus(params, linedup));
+}
+
+static void
+osmtpd_tx_rcpt(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx,
+ char *params, char *linedup)
+{
+ char *end, *rcptto;
+ unsigned long imsgid;
+ uint32_t msgid;
+ size_t i;
+ void (*f)(struct osmtpd_ctx *, uint32_t, const char *,
+ enum osmtpd_status);
+
+ errno = 0;
+ imsgid = strtoul(params, &end, 16);
+ if ((imsgid == ULONG_MAX && errno != 0))
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+ if (end[0] != '|')
+ errx(1, "Invalid line received: missing address: %s", linedup);
+ msgid = imsgid;
+ if ((unsigned long) msgid != imsgid)
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+ params = end + 1;
+
+ if ((end = strchr(params, '|')) == NULL)
+ errx(1, "Invalid line received: missing status: %s", linedup);
+ end++[0] = '\0';
+
+ rcptto = params;
+ params = end;
+
+ if (cb->storereport) {
+ for (i = 0; ctx->rcptto[i] != NULL; i++)
+ ;
+ ctx->rcptto = reallocarray(ctx->rcptto, i + 2,
+ sizeof(*(ctx->rcptto)));
+ if (ctx->rcptto == NULL)
+ err(1, NULL);
+
+ if ((ctx->rcptto[i] = strdup(rcptto)) == NULL)
+ err(1, NULL);
+ ctx->rcptto[i + 1] = NULL;
+ }
+
+ if ((f = cb->cb) != NULL)
+ f(ctx, msgid, rcptto,
+ osmtpd_strtostatus(params, linedup));
+}
+
+static void
+osmtpd_tx_envelope(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx,
+ char *params, char *linedup)
+{
+ unsigned long imsgid;
+ uint32_t msgid;
+ uint64_t evpid;
+ char *end;
+ void (*f)(struct osmtpd_ctx *, uint32_t, uint64_t);
+
+ errno = 0;
+ imsgid = strtoul(params, &end, 16);
+ if ((imsgid == ULONG_MAX && errno != 0))
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+ if (end[0] != '|')
+ errx(1, "Invalid line received: missing address: %s", linedup);
+ msgid = imsgid;
+ if ((unsigned long) msgid != imsgid)
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+ params = end + 1;
+
+ evpid = strtoull(params, &end, 16);
+ if ((ctx->evpid == ULLONG_MAX && errno != 0) ||
+ end[0] != '\0')
+ errx(1, "Invalid line received: invalid evpid: %s", linedup);
+ if (cb->storereport)
+ ctx->evpid = evpid;
+
+ if ((f = cb->cb) != NULL)
+ f(ctx, msgid, evpid);
+}
+
+static void
+osmtpd_tx_data(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx,
+ char *params, char *linedup)
+{
+ char *end;
+ unsigned long imsgid;
+ uint32_t msgid;
+ void (*f)(struct osmtpd_ctx *, uint32_t, enum osmtpd_status);
+
+ errno = 0;
+ imsgid = strtoul(params, &end, 16);
+ if ((imsgid == ULONG_MAX && errno != 0))
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+ if (end[0] != '|')
+ errx(1, "Invalid line received: missing address: %s", linedup);
+ msgid = imsgid;
+ if ((unsigned long) msgid != imsgid)
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+ params = end + 1;
+
+ if ((f = cb->cb) != NULL)
+ f(ctx, msgid, osmtpd_strtostatus(params, linedup));
+}
+
+static void
+osmtpd_tx_commit(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx,
+ char *params, char *linedup)
+{
+ char *end;
+ const char *errstr;
+ unsigned long imsgid;
+ uint32_t msgid;
+ size_t i, msgsz;
+ void (*f)(struct osmtpd_ctx *, uint32_t, size_t);
+
+ errno = 0;
+ imsgid = strtoul(params, &end, 16);
+ if ((imsgid == ULONG_MAX && errno != 0))
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+ if (end[0] != '|')
+ errx(1, "Invalid line received: missing address: %s", linedup);
+ msgid = imsgid;
+ if ((unsigned long) msgid != imsgid)
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+ params = end + 1;
+
+ msgsz = strtonum(params, 0, SIZE_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "Invalid line received: invalid msg size: %s", linedup);
+
+ free(ctx->mailfrom);
+ ctx->mailfrom = NULL;
+
+ for (i = 0; ctx->rcptto[i] != NULL; i++)
+ free(ctx->rcptto[i]);
+ ctx->rcptto[0] = NULL;
+ ctx->evpid = 0;
+ ctx->msgid = 0;
+
+ if ((f = cb->cb) != NULL)
+ f(ctx, msgid, msgsz);
+}
+
+static void
+osmtpd_tx_rollback(struct osmtpd_callback *cb, struct osmtpd_ctx *ctx,
+ char *params, char *linedup)
+{
+ char *end;
+ unsigned long imsgid;
+ uint32_t msgid;
+ size_t i;
+ void (*f)(struct osmtpd_ctx *, uint32_t);
+
+ errno = 0;
+ imsgid = strtoul(params, &end, 16);
+ if ((imsgid == ULONG_MAX && errno != 0))
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+ if (end[0] != '\0')
+ errx(1, "Invalid line received: missing address: %s", linedup);
+ msgid = imsgid;
+ if ((unsigned long) msgid != imsgid)
+ errx(1, "Invalid line received: invalid msgid: %s", linedup);
+
+ if ((f = cb->cb) != NULL)
+ f(ctx, msgid);
+
+ free(ctx->mailfrom);
+ ctx->mailfrom = NULL;
+
+ for (i = 0; ctx->rcptto[i] != NULL; i++)
+ free(ctx->rcptto[i]);
+ ctx->rcptto[0] = NULL;
+ ctx->evpid = 0;
+ ctx->msgid = 0;
+}
+
+void
+osmtpd_filter_proceed(struct osmtpd_ctx *ctx)
+{
+ io_printf(io_stdout, "filter-result|%016"PRIx64"|%016"PRIx64"|"
+ "proceed\n", ctx->token, ctx->reqid);
+}
+
+void
+osmtpd_filter_reject(struct osmtpd_ctx *ctx, int code, const char *reason, ...)
+{
+ va_list ap;
+
+ if (code < 200 || code > 599)
+ errx(1, "Invalid reject code");
+
+ io_printf(io_stdout, "filter-result|%016"PRIx64"|%016"PRIx64"|reject|"
+ "%d ", ctx->token, ctx->reqid, code);
+ va_start(ap, reason);
+ io_vprintf(io_stdout, reason, ap);
+ va_end(ap);
+ io_printf(io_stdout, "\n");
+}
+
+void
+osmtpd_filter_disconnect(struct osmtpd_ctx *ctx, const char *reason, ...)
+{
+ va_list ap;
+
+ io_printf(io_stdout, "filter-result|%016"PRIx64"|%016"PRIx64"|"
+ "disconnect|421 ", ctx->token, ctx->reqid);
+ va_start(ap, reason);
+ io_vprintf(io_stdout, reason, ap);
+ va_end(ap);
+ io_printf(io_stdout, "\n");
+}
+
+void
+osmtpd_filter_dataline(struct osmtpd_ctx *ctx, const char *line, ...)
+{
+ va_list ap;
+
+ io_printf(io_stdout, "filter-dataline|%016"PRIx64"|%016"PRIx64"|",
+ ctx->token, ctx->reqid);
+ va_start(ap, line);
+ io_vprintf(io_stdout, line, ap);
+ va_end(ap);
+ io_printf(io_stdout, "\n");
+}
+
+static void
+osmtpd_register(enum osmtpd_type type, enum osmtpd_phase phase, int incoming,
+ int storereport, void *cb)
+{
+ size_t i;
+
+ if (ready)
+ errx(1, "Can't register when proc is running");
+
+ for (i = 0; i < NITEMS(osmtpd_callbacks); i++) {
+ if (type == osmtpd_callbacks[i].type &&
+ phase == osmtpd_callbacks[i].phase &&
+ incoming == osmtpd_callbacks[i].incoming) {
+ if (osmtpd_callbacks[i].cb != NULL && cb != NULL)
+ errx(1, "Event already registered");
+ osmtpd_callbacks[i].cb = cb;
+ osmtpd_callbacks[i].doregister = 1;
+ if (storereport)
+ osmtpd_callbacks[i].storereport = 1;
+ return;
+ }
+ }
+ /* NOT REACHED */
+ errx(1, "Trying to register unknown event");
+}
+
+static enum osmtpd_phase
+osmtpd_strtophase(const char *phase, const char *linedup)
+{
+ if (strcmp(phase, "connect") == 0)
+ return OSMTPD_PHASE_CONNECT;
+ if (strcmp(phase, "helo") == 0)
+ return OSMTPD_PHASE_HELO;
+ if (strcmp(phase, "ehlo") == 0)
+ return OSMTPD_PHASE_EHLO;
+ if (strcmp(phase, "starttls") == 0)
+ return OSMTPD_PHASE_STARTTLS;
+ if (strcmp(phase, "auth") == 0)
+ return OSMTPD_PHASE_AUTH;
+ if (strcmp(phase, "mail-from") == 0)
+ return OSMTPD_PHASE_MAIL_FROM;
+ if (strcmp(phase, "rcpt-to") == 0)
+ return OSMTPD_PHASE_RCPT_TO;
+ if (strcmp(phase, "data") == 0)
+ return OSMTPD_PHASE_DATA;
+ if (strcmp(phase, "data-line") == 0)
+ return OSMTPD_PHASE_DATA_LINE;
+ if (strcmp(phase, "rset") == 0)
+ return OSMTPD_PHASE_RSET;
+ if (strcmp(phase, "quit") == 0)
+ return OSMTPD_PHASE_QUIT;
+ if (strcmp(phase, "noop") == 0)
+ return OSMTPD_PHASE_NOOP;
+ if (strcmp(phase, "help") == 0)
+ return OSMTPD_PHASE_HELP;
+ if (strcmp(phase, "wiz") == 0)
+ return OSMTPD_PHASE_WIZ;
+ if (strcmp(phase, "commit") == 0)
+ return OSMTPD_PHASE_COMMIT;
+ if (strcmp(phase, "link-connect") == 0)
+ return OSMTPD_PHASE_LINK_CONNECT;
+ if (strcmp(phase, "link-disconnect") == 0)
+ return OSMTPD_PHASE_LINK_DISCONNECT;
+ if (strcmp(phase, "link-identify") == 0)
+ return OSMTPD_PHASE_LINK_IDENTIFY;
+ if (strcmp(phase, "link-tls") == 0)
+ return OSMTPD_PHASE_LINK_TLS;
+ if (strcmp(phase, "tx-begin") == 0)
+ return OSMTPD_PHASE_TX_BEGIN;
+ if (strcmp(phase, "tx-mail") == 0)
+ return OSMTPD_PHASE_TX_MAIL;
+ if (strcmp(phase, "tx-rcpt") == 0)
+ return OSMTPD_PHASE_TX_RCPT;
+ if (strcmp(phase, "tx-envelope") == 0)
+ return OSMTPD_PHASE_TX_ENVELOPE;
+ if (strcmp(phase, "tx-data") == 0)
+ return OSMTPD_PHASE_TX_DATA;
+ if (strcmp(phase, "tx-commit") == 0)
+ return OSMTPD_PHASE_TX_COMMIT;
+ if (strcmp(phase, "tx-rollback") == 0)
+ return OSMTPD_PHASE_TX_ROLLBACK;
+ if (strcmp(phase, "protocol-client") == 0)
+ return OSMTPD_PHASE_PROTOCOL_CLIENT;
+ if (strcmp(phase, "protocol-server") == 0)
+ return OSMTPD_PHASE_PROTOCOL_SERVER;
+ if (strcmp(phase, "filter-response") == 0)
+ return OSMTPD_PHASE_FILTER_RESPONSE;
+ if (strcmp(phase, "timeout") == 0)
+ return OSMTPD_PHASE_TIMEOUT;
+ errx(1, "Invalid line received: invalid phase: %s", linedup);
+}
+
+static const char *
+osmtpd_typetostr(enum osmtpd_type type)
+{
+ switch (type) {
+ case OSMTPD_TYPE_FILTER:
+ return "filter";
+ case OSMTPD_TYPE_REPORT:
+ return "report";
+ }
+ errx(1, "In valid type: %d\n", type);
+}
+
+static const char *
+osmtpd_phasetostr(enum osmtpd_phase phase)
+{
+ switch (phase) {
+ case OSMTPD_PHASE_CONNECT:
+ return "connect";
+ case OSMTPD_PHASE_HELO:
+ return "helo";
+ case OSMTPD_PHASE_EHLO:
+ return "ehlo";
+ case OSMTPD_PHASE_STARTTLS:
+ return "starttls";
+ case OSMTPD_PHASE_AUTH:
+ return "auth";
+ case OSMTPD_PHASE_MAIL_FROM:
+ return "mail-from";
+ case OSMTPD_PHASE_RCPT_TO:
+ return "rcpt-to";
+ case OSMTPD_PHASE_DATA:
+ return "data";
+ case OSMTPD_PHASE_DATA_LINE:
+ return "data-line";
+ case OSMTPD_PHASE_RSET:
+ return "rset";
+ case OSMTPD_PHASE_QUIT:
+ return "quit";
+ case OSMTPD_PHASE_NOOP:
+ return "noop";
+ case OSMTPD_PHASE_HELP:
+ return "help";
+ case OSMTPD_PHASE_WIZ:
+ return "wiz";
+ case OSMTPD_PHASE_COMMIT:
+ return "commit";
+ case OSMTPD_PHASE_LINK_CONNECT:
+ return "link-connect";
+ case OSMTPD_PHASE_LINK_DISCONNECT:
+ return "link-disconnect";
+ case OSMTPD_PHASE_LINK_IDENTIFY:
+ return "link-identify";
+ case OSMTPD_PHASE_LINK_TLS:
+ return "link-tls";
+ case OSMTPD_PHASE_TX_BEGIN:
+ return "tx-begin";
+ case OSMTPD_PHASE_TX_MAIL:
+ return "tx-mail";
+ case OSMTPD_PHASE_TX_RCPT:
+ return "tx-rcpt";
+ case OSMTPD_PHASE_TX_ENVELOPE:
+ return "tx-envelope";
+ case OSMTPD_PHASE_TX_DATA:
+ return "tx-data";
+ case OSMTPD_PHASE_TX_COMMIT:
+ return "tx-commit";
+ case OSMTPD_PHASE_TX_ROLLBACK:
+ return "tx-rollback";
+ case OSMTPD_PHASE_PROTOCOL_CLIENT:
+ return "protocol-client";
+ case OSMTPD_PHASE_PROTOCOL_SERVER:
+ return "protocol-server";
+ case OSMTPD_PHASE_FILTER_RESPONSE:
+ return "filter-response";
+ case OSMTPD_PHASE_TIMEOUT:
+ return "timeout";
+ }
+ errx(1, "In valid phase: %d\n", phase);
+}
+
+static enum
+osmtpd_status osmtpd_strtostatus(const char *status, char *linedup)
+{
+ if (strcmp(status, "ok") == 0)
+ return OSMTPD_STATUS_OK;
+ else if (strcmp(status, "tempfail") == 0)
+ return OSMTPD_STATUS_TEMPFAIL;
+ else if (strcmp(status, "permfail") == 0)
+ return OSMTPD_STATUS_PERMFAIL;
+ errx(1, "Invalid line received: invalid status: %s\n", linedup);
+}
+
+static void
+osmtpd_addrtoss(char *addr, struct sockaddr_storage *ss, int hasport,
+ char *linedup)
+{
+ char *port;
+ const char *errstr = NULL;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_un *sun;
+
+ if (strncasecmp(addr, "ipv6:", 5) == 0) {
+ sin6 = (struct sockaddr_in6 *)ss;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+ if (hasport) {
+ if ((port = strrchr(addr, ':')) == NULL)
+ errx(1, "Invalid line received: invalid "
+ "address (%s): %s", addr, linedup);
+ port++;
+ sin6->sin6_port = htons(strtonum(port, 0, UINT16_MAX,
+ &errstr));
+ if (errstr != NULL)
+ errx(1, "Invalid line received: invalid "
+ "address (%s): %s", addr, linedup);
+ port[-1] = '\0';
+ }
+ switch (inet_pton(AF_INET6, addr + 5, &(sin6->sin6_addr))) {
+ case 1:
+ break;
+ case 0:
+ port[-1] = ':';
+ errx(1, "Invalid line received: invalid address "
+ "(%s): %s", addr, linedup);
+ default:
+ port[-1] = ':';
+ err(1, "Can't parse address (%s): %s", addr, linedup);
+ }
+ } else if (strncasecmp(addr, "unix:", 5) == 0) {
+ sun = (struct sockaddr_un *)ss;
+ sun->sun_len = sizeof(*sun);
+ sun->sun_family = AF_UNIX;
+ if (strlcpy(sun->sun_path, addr,
+ sizeof(sun->sun_path)) >= sizeof(sun->sun_path)) {
+ errx(1, "Invalid line received: address too long (%s): "
+ "%s", addr, linedup);
+ }
+ } else {
+ sin = (struct sockaddr_in *)ss;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_port = 0;
+ if (hasport) {
+ if ((port = strrchr(addr, ':')) == NULL)
+ errx(1, "Invalid line received: invalid "
+ "address (%s): %s", addr, linedup);
+ port++;
+ sin->sin_port = htons(strtonum(port, 0, UINT16_MAX,
+ &errstr));
+ if (errstr != NULL)
+ errx(1, "Invalid line received: invalid "
+ "address (%s): %s", addr, linedup);
+ port[-1] = '\0';
+ }
+ switch (inet_pton(AF_INET, addr, &(sin->sin_addr))) {
+ case 1:
+ break;
+ case 0:
+ port[-1] = ':';
+ errx(1, "Invalid line received: invalid address "
+ "(%s): %s", addr, linedup);
+ default:
+ port[-1] = ':';
+ err(1, "Can't parse address (%s): %s", addr, linedup);
+ }
+ }
+}
+
+static int
+osmtpd_session_cmp(struct osmtpd_session *a, struct osmtpd_session *b)
+{
+ return a->ctx.reqid < b->ctx.reqid ? -1 : a->ctx.reqid > b->ctx.reqid;
+}
+
+RB_GENERATE(osmtpd_sessions, osmtpd_session, entry, osmtpd_session_cmp);
blob - /dev/null
blob + 5117366b3b49a555d29847867705bd42459a06c9 (mode 644)
--- /dev/null
+++ opensmtpd.h
+/*
+ * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.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 <netinet/in.h>
+
+#include <netinet/in.h>
+
+enum osmtpd_status {
+ OSMTPD_STATUS_OK,
+ OSMTPD_STATUS_TEMPFAIL,
+ OSMTPD_STATUS_PERMFAIL
+};
+
+enum osmtpd_type {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_TYPE_REPORT
+};
+
+enum osmtpd_phase {
+ OSMTPD_PHASE_CONNECT,
+ OSMTPD_PHASE_HELO,
+ OSMTPD_PHASE_EHLO,
+ OSMTPD_PHASE_STARTTLS,
+ OSMTPD_PHASE_AUTH,
+ OSMTPD_PHASE_MAIL_FROM,
+ OSMTPD_PHASE_RCPT_TO,
+ OSMTPD_PHASE_DATA,
+ OSMTPD_PHASE_DATA_LINE,
+ OSMTPD_PHASE_RSET,
+ OSMTPD_PHASE_QUIT,
+ OSMTPD_PHASE_NOOP,
+ OSMTPD_PHASE_HELP,
+ OSMTPD_PHASE_WIZ,
+ OSMTPD_PHASE_COMMIT,
+ OSMTPD_PHASE_LINK_CONNECT,
+ OSMTPD_PHASE_LINK_DISCONNECT,
+ OSMTPD_PHASE_LINK_IDENTIFY,
+ OSMTPD_PHASE_LINK_TLS,
+ OSMTPD_PHASE_TX_BEGIN,
+ OSMTPD_PHASE_TX_MAIL,
+ OSMTPD_PHASE_TX_RCPT,
+ OSMTPD_PHASE_TX_ENVELOPE,
+ OSMTPD_PHASE_TX_DATA,
+ OSMTPD_PHASE_TX_COMMIT,
+ OSMTPD_PHASE_TX_ROLLBACK,
+ OSMTPD_PHASE_PROTOCOL_CLIENT,
+ OSMTPD_PHASE_PROTOCOL_SERVER,
+ OSMTPD_PHASE_FILTER_RESPONSE,
+ OSMTPD_PHASE_TIMEOUT
+};
+
+#define OSMTPD_NEED_SRC 1 << 0
+#define OSMTPD_NEED_DST 1 << 1
+#define OSMTPD_NEED_RDNS 1 << 2
+#define OSMTPD_NEED_FRDNS 1 << 3
+#define OSMTPD_NEED_IDENTITY 1 << 4
+#define OSMTPD_NEED_CIPHERS 1 << 5
+#define OSMTPD_NEED_MSGID 1 << 6
+#define OSMTPD_NEED_MAILFROM 1 << 7
+#define OSMTPD_NEED_RCPTTO 1 << 8
+#define OSMTPD_NEED_EVPID 1 << 9
+
+struct osmtpd_ctx {
+ enum osmtpd_type type;
+ int version_major;
+ int version_minor;
+ struct timespec tm;
+ int incoming;
+ enum osmtpd_phase phase;
+ uint64_t reqid;
+ uint64_t token;
+ struct sockaddr_storage src;
+ struct sockaddr_storage dst;
+ char *rdns;
+ char *fcrdns;
+ char *identity;
+ char *ciphers;
+ uint32_t msgid;
+ char *mailfrom;
+ char **rcptto;
+ uint64_t evpid;
+ void *local;
+};
+
+void osmtpd_register_filter_connect(void (*)(struct osmtpd_ctx *, const char *,
+ struct sockaddr_storage *));
+void osmtpd_register_filter_helo(void (*)(struct osmtpd_ctx *, const char *));
+void osmtpd_register_filter_ehlo(void (*)(struct osmtpd_ctx *, const char *));
+void osmtpd_register_filter_starttls(void (*)(struct osmtpd_ctx *));
+void osmtpd_register_filter_auth(void (*)(struct osmtpd_ctx *, const char *));
+void osmtpd_register_filter_mailfrom(void (*)(struct osmtpd_ctx *,
+ const char *));
+void osmtpd_register_filter_rcptto(void (*)(struct osmtpd_ctx *, const char *));
+void osmtpd_register_filter_data(void (*)(struct osmtpd_ctx *));
+void osmtpd_register_filter_dataline(void (*)(struct osmtpd_ctx *,
+ const char *));
+void osmtpd_register_filter_rset(void (*)(struct osmtpd_ctx *));
+void osmtpd_register_filter_quit(void (*)(struct osmtpd_ctx *));
+void osmtpd_register_filter_noop(void (*)(struct osmtpd_ctx *));
+void osmtpd_register_filter_help(void (*)(struct osmtpd_ctx *));
+void osmtpd_register_filter_wiz(void (*)(struct osmtpd_ctx *));
+void osmtpd_register_filter_commit(void (*)(struct osmtpd_ctx *));
+void osmtpd_register_report_connect(int, void (*)(struct osmtpd_ctx *,
+ const char *, const char *, struct sockaddr_storage *,
+ struct sockaddr_storage *));
+void osmtpd_register_report_disconnect(int, void (*)(struct osmtpd_ctx *));
+void osmtpd_register_report_identify(int, void (*)(struct osmtpd_ctx *,
+ const char *));
+void osmtpd_register_report_tls(int, void (*)(struct osmtpd_ctx *,
+ const char *));
+void osmtpd_register_report_begin(int, void (*)(struct osmtpd_ctx *, uint32_t));
+void osmtpd_register_report_mail(int, void (*)(struct osmtpd_ctx *, uint32_t,
+ const char *, enum osmtpd_status));
+void osmtpd_register_report_rcpt(int, void (*)(struct osmtpd_ctx *, uint32_t,
+ const char *, enum osmtpd_status));
+void osmtpd_register_report_envelope(int, void (*)(struct osmtpd_ctx *, uint32_t,
+ uint64_t));
+void osmtpd_register_report_data(int, void (*)(struct osmtpd_ctx *, uint32_t,
+ enum osmtpd_status));
+void osmtpd_register_report_commit(int, void (*)(struct osmtpd_ctx *, uint32_t,
+ size_t));
+void osmtpd_register_report_rollback(int, void (*)(struct osmtpd_ctx *,
+ uint32_t));
+void osmtpd_register_report_client(int, void (*)(struct osmtpd_ctx *,
+ const char *));
+void osmtpd_register_report_server(int, void (*)(struct osmtpd_ctx *,
+ const char *));
+void osmtpd_register_report_response(int, void (*)(struct osmtpd_ctx *,
+ const char *));
+void osmtpd_register_report_timeout(int, void (*)(struct osmtpd_ctx *));
+void osmtpd_localdata(void *(*)(struct osmtpd_ctx *),
+ void (*)(struct osmtpd_ctx *, void *));
+void osmtpd_need(int, int);
+
+void osmtpd_filter_proceed(struct osmtpd_ctx *);
+void osmtpd_filter_reject(struct osmtpd_ctx *, int, const char *, ...)
+ __attribute__((__format__ (printf, 3, 4)));
+void osmtpd_filter_disconnect(struct osmtpd_ctx *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void osmtpd_filter_dataline(struct osmtpd_ctx *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void osmtpd_run(void);
blob - /dev/null
blob + 77e3caf57b2a00d8ffc439f99d39c0db3975040c (mode 644)
--- /dev/null
+++ opensmtpd_priv.h
+/*
+ * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.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.
+ */
+struct osmtpd_callback {
+ enum osmtpd_type type;
+ enum osmtpd_phase phase;
+ int incoming;
+ void (*osmtpd_cb)(struct osmtpd_callback *, struct osmtpd_ctx *, char *,
+ char *);
+ void *cb;
+ int doregister;
+ int storereport;
+};
+
+struct osmtpd_session {
+ struct osmtpd_ctx ctx;
+ RB_ENTRY(osmtpd_session) entry;
+};
+
+static void osmtpd_register(enum osmtpd_type, enum osmtpd_phase, int, int,
+ void *);
+static const char *osmtpd_typetostr(enum osmtpd_type);
+static const char *osmtpd_phasetostr(enum osmtpd_phase);
+static enum osmtpd_phase osmtpd_strtophase(const char *, const char *);
+static void osmtpd_newline(struct io *, int, void *);
+static void osmtpd_outevt(struct io *, int, void *);
+static void osmtpd_noargs(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_onearg(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_connect(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_link_connect(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_link_disconnect(struct osmtpd_callback *,
+ struct osmtpd_ctx *, char *, char *);
+static void osmtpd_link_identify(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_link_tls(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_tx_begin(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_tx_mail(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_tx_rcpt(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_tx_envelope(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_tx_data(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_tx_commit(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_tx_rollback(struct osmtpd_callback *, struct osmtpd_ctx *,
+ char *, char *);
+static void osmtpd_addrtoss(char *, struct sockaddr_storage *, int, char *);
+static enum osmtpd_status osmtpd_strtostatus(const char *, char *);
+static int osmtpd_session_cmp(struct osmtpd_session *, struct osmtpd_session *);
+static void *(*oncreatecb)(struct osmtpd_ctx *) = NULL;
+static void (*ondeletecb)(struct osmtpd_ctx *, void *) = NULL;
+
+
+static struct osmtpd_callback osmtpd_callbacks[] = {
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_CONNECT,
+ 1,
+ osmtpd_connect,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_HELO,
+ 1,
+ osmtpd_onearg,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_EHLO,
+ 1,
+ osmtpd_onearg,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_STARTTLS,
+ 1,
+ osmtpd_noargs,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_AUTH,
+ 1,
+ osmtpd_onearg,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_MAIL_FROM,
+ 1,
+ osmtpd_onearg,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_RCPT_TO,
+ 1,
+ osmtpd_onearg,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_DATA,
+ 1,
+ osmtpd_noargs,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_DATA_LINE,
+ 1,
+ osmtpd_onearg,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_RSET,
+ 1,
+ osmtpd_noargs,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_QUIT,
+ 1,
+ osmtpd_noargs,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_NOOP,
+ 1,
+ osmtpd_noargs,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_HELP,
+ 1,
+ osmtpd_noargs,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_WIZ,
+ 1,
+ osmtpd_noargs,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_FILTER,
+ OSMTPD_PHASE_COMMIT,
+ 1,
+ osmtpd_noargs,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_LINK_CONNECT,
+ 1,
+ osmtpd_link_connect,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_LINK_DISCONNECT,
+ 1,
+ osmtpd_link_disconnect,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_LINK_IDENTIFY,
+ 1,
+ osmtpd_link_identify,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_LINK_TLS,
+ 1,
+ osmtpd_link_tls,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_BEGIN,
+ 1,
+ osmtpd_tx_begin,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_MAIL,
+ 1,
+ osmtpd_tx_mail,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_RCPT,
+ 1,
+ osmtpd_tx_rcpt,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_ENVELOPE,
+ 1,
+ osmtpd_tx_envelope,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_DATA,
+ 1,
+ osmtpd_tx_data,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_COMMIT,
+ 1,
+ osmtpd_tx_commit,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_ROLLBACK,
+ 1,
+ osmtpd_tx_rollback,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_PROTOCOL_CLIENT,
+ 1,
+ osmtpd_onearg,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_PROTOCOL_SERVER,
+ 1,
+ osmtpd_onearg,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_FILTER_RESPONSE,
+ 1,
+ osmtpd_onearg,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TIMEOUT,
+ 1,
+ osmtpd_noargs,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_LINK_CONNECT,
+ 0,
+ osmtpd_link_connect,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_LINK_DISCONNECT,
+ 0,
+ osmtpd_link_disconnect,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_LINK_IDENTIFY,
+ 0,
+ osmtpd_link_identify,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_LINK_TLS,
+ 0,
+ osmtpd_link_tls,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_BEGIN,
+ 0,
+ osmtpd_tx_begin,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_MAIL,
+ 0,
+ osmtpd_tx_mail,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_RCPT,
+ 0,
+ osmtpd_tx_rcpt,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_ENVELOPE,
+ 0,
+ osmtpd_tx_envelope,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_DATA,
+ 0,
+ osmtpd_tx_data,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_COMMIT,
+ 0,
+ osmtpd_tx_commit,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TX_ROLLBACK,
+ 0,
+ osmtpd_tx_rollback,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_PROTOCOL_CLIENT,
+ 0,
+ osmtpd_onearg,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_PROTOCOL_SERVER,
+ 0,
+ osmtpd_onearg,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_FILTER_RESPONSE,
+ 0,
+ osmtpd_onearg,
+ NULL,
+ 0,
+ 0
+ },
+ {
+ OSMTPD_TYPE_REPORT,
+ OSMTPD_PHASE_TIMEOUT,
+ 0,
+ osmtpd_noargs,
+ NULL,
+ 0,
+ 0
+ }
+};
blob - /dev/null
blob + 3d7c908e43d641cb0dcddbee5df35d0ac5910b46 (mode 644)
--- /dev/null
+++ shlib_version
+major=0
+minor=1