aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGilles Chehade <gilles@poolp.org>2020-05-22 14:32:22 +0200
committerGilles Chehade <gilles@poolp.org>2020-05-22 14:32:22 +0200
commit90620a574d8824e5b2aa18709f2d5b5b6bb3cb38 (patch)
tree251ab90cb9264a2cd476a19b33e066868a4f7849
parentmv back (diff)
downloadOpenSMTPD-90620a574d8824e5b2aa18709f2d5b5b6bb3cb38.tar.xz
OpenSMTPD-90620a574d8824e5b2aa18709f2d5b5b6bb3cb38.zip
moving smtpd to usr.sbin/smtpd to ease cherry-picking of upstream
-rw-r--r--contrib/libexec/encrypt/Makefile.am2
-rw-r--r--contrib/libexec/lockspool/Makefile.am2
-rw-r--r--contrib/libexec/mail.local/Makefile.am2
-rw-r--r--mk/pathnames4
-rw-r--r--openbsd-compat/Makefile.am2
-rw-r--r--smtpd/Makefile10
-rw-r--r--smtpd/aliases.5102
-rw-r--r--smtpd/aliases.c234
-rw-r--r--smtpd/bounce.c820
-rw-r--r--smtpd/ca.c777
-rw-r--r--smtpd/cert.c416
-rw-r--r--smtpd/compress_backend.c72
-rw-r--r--smtpd/compress_gzip.c186
-rw-r--r--smtpd/config.c350
-rw-r--r--smtpd/control.c817
-rw-r--r--smtpd/crypto.c400
-rw-r--r--smtpd/dict.c269
-rw-r--r--smtpd/dict.h48
-rw-r--r--smtpd/dns.c379
-rw-r--r--smtpd/enqueue.c932
-rw-r--r--smtpd/envelope.c786
-rw-r--r--smtpd/esc.c116
-rw-r--r--smtpd/expand.c332
-rw-r--r--smtpd/filter.c868
-rw-r--r--smtpd/forward.583
-rw-r--r--smtpd/forward.c104
-rw-r--r--smtpd/iobuf.c462
-rw-r--r--smtpd/iobuf.h67
-rw-r--r--smtpd/ioev.c1064
-rw-r--r--smtpd/ioev.h70
-rw-r--r--smtpd/libressl.c213
-rw-r--r--smtpd/limit.c124
-rw-r--r--smtpd/lka.c914
-rw-r--r--smtpd/lka_filter.c1746
-rw-r--r--smtpd/lka_session.c556
-rw-r--r--smtpd/log.c220
-rw-r--r--smtpd/log.h52
-rw-r--r--smtpd/mail.lmtp.855
-rw-r--r--smtpd/mail.lmtp.c332
-rw-r--r--smtpd/mail.maildir.845
-rw-r--r--smtpd/mail.maildir.c284
-rw-r--r--smtpd/mail.mboxfile.834
-rw-r--r--smtpd/mail.mboxfile.c109
-rw-r--r--smtpd/mail.mda.835
-rw-r--r--smtpd/mail.mda.c70
-rw-r--r--smtpd/mail/Makefile20
-rw-r--r--smtpd/mailaddr.c135
-rw-r--r--smtpd/makemap.8174
-rw-r--r--smtpd/makemap.c521
-rw-r--r--smtpd/mda.c919
-rw-r--r--smtpd/mda_mbox.c94
-rw-r--r--smtpd/mda_unpriv.c110
-rw-r--r--smtpd/mda_variables.c374
-rw-r--r--smtpd/mproc.c676
-rw-r--r--smtpd/mta.c2647
-rw-r--r--smtpd/mta_session.c2008
-rw-r--r--smtpd/newaliases.886
-rw-r--r--smtpd/parse.y3598
-rw-r--r--smtpd/parser.c341
-rw-r--r--smtpd/parser.h43
-rw-r--r--smtpd/pony.c212
-rw-r--r--smtpd/proxy.c387
-rw-r--r--smtpd/queue.c750
-rw-r--r--smtpd/queue_backend.c806
-rw-r--r--smtpd/queue_fs.c695
-rw-r--r--smtpd/queue_null.c120
-rw-r--r--smtpd/queue_proc.c337
-rw-r--r--smtpd/queue_ram.c336
-rw-r--r--smtpd/report_smtp.c335
-rw-r--r--smtpd/resolver.c462
-rw-r--r--smtpd/rfc5322.c266
-rw-r--r--smtpd/rfc5322.h41
-rw-r--r--smtpd/ruleset.c265
-rw-r--r--smtpd/runq.c183
-rw-r--r--smtpd/scheduler.c618
-rw-r--r--smtpd/scheduler_backend.c82
-rw-r--r--smtpd/scheduler_null.c164
-rw-r--r--smtpd/scheduler_proc.c446
-rw-r--r--smtpd/scheduler_ramqueue.c1204
-rw-r--r--smtpd/sendmail.886
-rw-r--r--smtpd/smtp.196
-rw-r--r--smtpd/smtp.c387
-rw-r--r--smtpd/smtp.h95
-rw-r--r--smtpd/smtp/Makefile24
-rw-r--r--smtpd/smtp_client.c923
-rw-r--r--smtpd/smtp_session.c3223
-rw-r--r--smtpd/smtpc.c465
-rw-r--r--smtpd/smtpctl.8336
-rw-r--r--smtpd/smtpctl.c1469
-rw-r--r--smtpd/smtpctl/Makefile56
-rw-r--r--smtpd/smtpd-api.h290
-rw-r--r--smtpd/smtpd-defines.h68
-rw-r--r--smtpd/smtpd-filters.7653
-rw-r--r--smtpd/smtpd.8167
-rw-r--r--smtpd/smtpd.c2328
-rw-r--r--smtpd/smtpd.conf19
-rw-r--r--smtpd/smtpd.conf.51240
-rw-r--r--smtpd/smtpd.h1784
-rw-r--r--smtpd/smtpd/Makefile102
-rw-r--r--smtpd/spfwalk.c391
-rw-r--r--smtpd/srs.c379
-rw-r--r--smtpd/ssl.c458
-rw-r--r--smtpd/ssl.h71
-rw-r--r--smtpd/ssl_smtpd.c105
-rw-r--r--smtpd/ssl_verify.c297
-rw-r--r--smtpd/stat_backend.c124
-rw-r--r--smtpd/stat_ramstat.c162
-rw-r--r--smtpd/table.5258
-rw-r--r--smtpd/table.c709
-rw-r--r--smtpd/table_db.c282
-rw-r--r--smtpd/table_getpwnam.c120
-rw-r--r--smtpd/table_proc.c283
-rw-r--r--smtpd/table_static.c398
-rw-r--r--smtpd/to.c880
-rw-r--r--smtpd/tree.c259
-rw-r--r--smtpd/tree.h48
-rw-r--r--smtpd/unpack_dns.c300
-rw-r--r--smtpd/unpack_dns.h96
-rw-r--r--smtpd/util.c870
-rw-r--r--smtpd/waitq.c104
120 files changed, 6 insertions, 54419 deletions
diff --git a/contrib/libexec/encrypt/Makefile.am b/contrib/libexec/encrypt/Makefile.am
index 6ad7b82d..2f96e60d 100644
--- a/contrib/libexec/encrypt/Makefile.am
+++ b/contrib/libexec/encrypt/Makefile.am
@@ -1,7 +1,7 @@
pkglibexec_PROGRAMS = encrypt
encrypt_SOURCES = encrypt.c
-encrypt_SOURCES += $(top_srcdir)/smtpd/log.c
+encrypt_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c
AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat
diff --git a/contrib/libexec/lockspool/Makefile.am b/contrib/libexec/lockspool/Makefile.am
index dacf5386..2801c101 100644
--- a/contrib/libexec/lockspool/Makefile.am
+++ b/contrib/libexec/lockspool/Makefile.am
@@ -2,7 +2,7 @@ pkglibexec_PROGRAMS = lockspool
lockspool_SOURCES = lockspool.c
lockspool_SOURCES += locking.c
-lockspool_SOURCES += $(top_srcdir)/smtpd/log.c
+lockspool_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c
EXTRA_DIST = mail.local.h pathnames.h
diff --git a/contrib/libexec/mail.local/Makefile.am b/contrib/libexec/mail.local/Makefile.am
index bd5211a2..217659c1 100644
--- a/contrib/libexec/mail.local/Makefile.am
+++ b/contrib/libexec/mail.local/Makefile.am
@@ -2,7 +2,7 @@ pkglibexec_PROGRAMS = mail.local
mail_local_SOURCES = mail.local.c
mail_local_SOURCES += locking.c
-mail_local_SOURCES += $(top_srcdir)/smtpd/log.c
+mail_local_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c
EXTRA_DIST = mail.local.h pathnames.h
diff --git a/mk/pathnames b/mk/pathnames
index b233ec33..efa785ef 100644
--- a/mk/pathnames
+++ b/mk/pathnames
@@ -1,8 +1,8 @@
-smtpd_srcdir = $(top_srcdir)/smtpd
+smtpd_srcdir = $(top_srcdir)/usr.sbin/smtpd
compat_srcdir = $(top_srcdir)/openbsd-compat
regress_srcdir = $(top_srcdir)/regress/bin
-PATHS= -DSMTPD_CONFDIR=\"$(sysconfdir)\" \
+PATHS= -DSMTPD_CONFDIR=\"$(sysconfdir)\" \
-DPATH_CHROOT=\"$(PRIVSEP_PATH)\" \
-DPATH_SMTPCTL=\"$(sbindir)/smtpctl\" \
-DPATH_MAILLOCAL=\"$(pkglibexecdir)/mail.local\" \
diff --git a/openbsd-compat/Makefile.am b/openbsd-compat/Makefile.am
index 01a3efe7..0e704cfd 100644
--- a/openbsd-compat/Makefile.am
+++ b/openbsd-compat/Makefile.am
@@ -1,6 +1,6 @@
noinst_LIBRARIES = libopenbsd.a
-AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/smtpd -I$(top_srcdir)/openbsd-compat
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/usr.sbin/smtpd -I$(top_srcdir)/openbsd-compat
libopenbsd_a_SOURCES = empty.c
diff --git a/smtpd/Makefile b/smtpd/Makefile
deleted file mode 100644
index a3dbc9d1..00000000
--- a/smtpd/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-# $OpenBSD: Makefile,v 1.18 2018/05/24 11:38:24 gilles Exp $
-
-.include <bsd.own.mk>
-
-SUBDIR = smtpd
-SUBDIR+= smtpctl
-SUBDIR+= smtp
-SUBDIR+= mail
-
-.include <bsd.subdir.mk>
diff --git a/smtpd/aliases.5 b/smtpd/aliases.5
deleted file mode 100644
index 7c250c81..00000000
--- a/smtpd/aliases.5
+++ /dev/null
@@ -1,102 +0,0 @@
-.\" $OpenBSD: aliases.5,v 1.16 2020/04/23 21:28:10 jmc Exp $
-.\"
-.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: April 23 2020 $
-.Dt ALIASES 5
-.Os
-.Sh NAME
-.Nm aliases
-.Nd aliases file for smtpd
-.Sh DESCRIPTION
-This manual page describes the format of the
-.Nm
-file, as used by
-.Xr smtpd 8 .
-An alias in its simplest form is used to assign an arbitrary name
-to an email address, or a group of email addresses.
-This provides a convenient way to send mail.
-For example an alias could refer to all users of a group:
-email to that alias would be sent to all members of the group.
-Much more complex aliases can be defined however:
-an alias can refer to other aliases,
-be used to send mail to a file instead of another person,
-or to execute various commands.
-.Pp
-Within the file,
-.Ql #
-is a comment delimiter; anything placed after it is discarded.
-The file consists of key/value mappings of the form:
-.Bd -filled -offset indent
-key: value1, value2, value3, ...
-.Ed
-.Pp
-.Em key
-is always folded to lowercase before alias lookups to ensure that
-there can be no ambiguity.
-The key is expanded to the corresponding values,
-which consist of one or more of the following:
-.Bl -tag -width Ds
-.It Em user
-A user on the host machine.
-The user must have a valid entry in the
-.Xr passwd 5
-database file.
-.It Ar /path/to/file
-Append messages to
-.Ar file ,
-specified by its absolute pathname.
-.It | Ns Ar command
-Pipe the message to
-.Ar command
-on its standard input.
-The command is run under the privileges of the daemon's unprivileged account.
-.It : Ns Ar include : Ns Ar /path/to/file
-Include any definitions in
-.Ar file
-as alias entries.
-The format of the file is identical to this one.
-.It Ar user-part@domain-part
-An email address in RFC 5322 format.
-If an address extension is appended to the user-part,
-it is first compared for an exact match.
-It is then stripped so that an address such as user+ext@example.com
-will only use the part that precedes
-.Sq +
-as a
-.Em key .
-.It Ar error : Ns Ar code message
-A status code and message to return.
-The code must be 3 digits,
-starting 4XX (TempFail) or 5XX (PermFail).
-The message must be present and can be freely chosen.
-.El
-.Sh FILES
-.Bl -tag -width "/etc/mail/aliasesXXX" -compact
-.It Pa /etc/mail/aliases
-Default
-.Nm
-file.
-.El
-.Sh SEE ALSO
-.Xr smtpd.conf 5 ,
-.Xr makemap 8 ,
-.Xr newaliases 8 ,
-.Xr smtpd 8
-.Sh HISTORY
-The
-.Nm
-file format appeared in
-.Bx 4.0 .
diff --git a/smtpd/aliases.c b/smtpd/aliases.c
deleted file mode 100644
index 0f8a5c1e..00000000
--- a/smtpd/aliases.c
+++ /dev/null
@@ -1,234 +0,0 @@
-/* $OpenBSD: aliases.c,v 1.78 2020/04/28 21:46:43 eric Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-#ifdef HAVE_UTIL_H
-#include <util.h>
-#endif
-#ifdef HAVE_LIBUTIL_H
-#include <libutil.h>
-#endif
-
-#include "smtpd.h"
-#include "log.h"
-
-static int aliases_expand_include(struct expand *, const char *);
-
-int
-aliases_get(struct expand *expand, const char *username)
-{
- struct expandnode *xn;
- char buf[SMTPD_MAXLOCALPARTSIZE];
- size_t nbaliases;
- int ret;
- union lookup lk;
- struct dispatcher *dsp;
- struct table *mapping = NULL;
- char *pbuf;
-
- dsp = dict_xget(env->sc_dispatchers, expand->rule->dispatcher);
- mapping = table_find(env, dsp->u.local.table_alias);
-
- xlowercase(buf, username, sizeof(buf));
-
- /* first, check if entry has a user-part tag */
- pbuf = strchr(buf, *env->sc_subaddressing_delim);
- if (pbuf) {
- ret = table_lookup(mapping, K_ALIAS, buf, &lk);
- if (ret < 0)
- return (-1);
- if (ret)
- goto expand;
- *pbuf = '\0';
- }
-
- /* no user-part tag, try looking up user */
- ret = table_lookup(mapping, K_ALIAS, buf, &lk);
- if (ret <= 0)
- return ret;
-
-expand:
- /* foreach node in table_alias expandtree, we merge */
- nbaliases = 0;
- RB_FOREACH(xn, expandtree, &lk.expand->tree) {
- if (xn->type == EXPAND_INCLUDE)
- nbaliases += aliases_expand_include(expand,
- xn->u.buffer);
- else {
- expand_insert(expand, xn);
- nbaliases++;
- }
- }
-
- expand_free(lk.expand);
-
- log_debug("debug: aliases_get: returned %zd aliases", nbaliases);
- return nbaliases;
-}
-
-int
-aliases_virtual_get(struct expand *expand, const struct mailaddr *maddr)
-{
- struct expandnode *xn;
- union lookup lk;
- char buf[LINE_MAX];
- char user[LINE_MAX];
- char tag[LINE_MAX];
- char domain[LINE_MAX];
- char *pbuf;
- int nbaliases;
- int ret;
- struct dispatcher *dsp;
- struct table *mapping = NULL;
-
- dsp = dict_xget(env->sc_dispatchers, expand->rule->dispatcher);
- mapping = table_find(env, dsp->u.local.table_virtual);
-
- if (!bsnprintf(user, sizeof(user), "%s", maddr->user))
- return 0;
- if (!bsnprintf(domain, sizeof(domain), "%s", maddr->domain))
- return 0;
- xlowercase(user, user, sizeof(user));
- xlowercase(domain, domain, sizeof(domain));
-
- memset(tag, '\0', sizeof tag);
- pbuf = strchr(user, *env->sc_subaddressing_delim);
- if (pbuf) {
- if (!bsnprintf(tag, sizeof(tag), "%s", pbuf + 1))
- return 0;
- xlowercase(tag, tag, sizeof(tag));
- *pbuf = '\0';
- }
-
- /* first, check if entry has a user-part tag */
- if (tag[0]) {
- if (!bsnprintf(buf, sizeof(buf), "%s%c%s@%s",
- user, *env->sc_subaddressing_delim, tag, domain))
- return 0;
- ret = table_lookup(mapping, K_ALIAS, buf, &lk);
- if (ret < 0)
- return (-1);
- if (ret)
- goto expand;
- }
-
- /* then, check if entry exists without user-part tag */
- if (!bsnprintf(buf, sizeof(buf), "%s@%s", user, domain))
- return 0;
- ret = table_lookup(mapping, K_ALIAS, buf, &lk);
- if (ret < 0)
- return (-1);
- if (ret)
- goto expand;
-
- if (tag[0]) {
- /* Failed ? We lookup for username + user-part tag */
- if (!bsnprintf(buf, sizeof(buf), "%s%c%s",
- user, *env->sc_subaddressing_delim, tag))
- return 0;
- ret = table_lookup(mapping, K_ALIAS, buf, &lk);
- if (ret < 0)
- return (-1);
- if (ret)
- goto expand;
- }
-
- /* Failed ? We lookup for username only */
- if (!bsnprintf(buf, sizeof(buf), "%s", user))
- return 0;
- ret = table_lookup(mapping, K_ALIAS, buf, &lk);
- if (ret < 0)
- return (-1);
- if (ret)
- goto expand;
-
- /* Do not try catch-all entries if there is no domain */
- if (domain[0] == '\0')
- return 0;
-
- if (!bsnprintf(buf, sizeof(buf), "@%s", domain))
- return 0;
- /* Failed ? We lookup for catch all for virtual domain */
- ret = table_lookup(mapping, K_ALIAS, buf, &lk);
- if (ret < 0)
- return (-1);
- if (ret)
- goto expand;
-
- /* Failed ? We lookup for a *global* catch all */
- ret = table_lookup(mapping, K_ALIAS, "@", &lk);
- if (ret <= 0)
- return (ret);
-
-expand:
- /* foreach node in table_virtual expand, we merge */
- nbaliases = 0;
- RB_FOREACH(xn, expandtree, &lk.expand->tree) {
- if (xn->type == EXPAND_INCLUDE)
- nbaliases += aliases_expand_include(expand,
- xn->u.buffer);
- else {
- expand_insert(expand, xn);
- nbaliases++;
- }
- }
-
- expand_free(lk.expand);
-
- log_debug("debug: aliases_virtual_get: '%s' resolved to %d nodes",
- buf, nbaliases);
-
- return nbaliases;
-}
-
-static int
-aliases_expand_include(struct expand *expand, const char *filename)
-{
- FILE *fp;
- char *line;
- size_t len, lineno = 0;
- char delim[3] = { '\\', '#', '\0' };
-
- fp = fopen(filename, "r");
- if (fp == NULL) {
- log_warn("warn: failed to open include file \"%s\".", filename);
- return 0;
- }
-
- while ((line = fparseln(fp, &len, &lineno, delim, 0)) != NULL) {
- expand_line(expand, line, 0);
- free(line);
- }
-
- fclose(fp);
- return 1;
-}
diff --git a/smtpd/bounce.c b/smtpd/bounce.c
deleted file mode 100644
index 4a4a0992..00000000
--- a/smtpd/bounce.c
+++ /dev/null
@@ -1,820 +0,0 @@
-/* $OpenBSD: bounce.c,v 1.82 2020/04/24 11:34:07 eric Exp $ */
-
-/*
- * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define BOUNCE_MAXRUN 2
-#define BOUNCE_HIWAT 65535
-
-enum {
- BOUNCE_EHLO,
- BOUNCE_MAIL,
- BOUNCE_RCPT,
- BOUNCE_DATA,
- BOUNCE_DATA_NOTICE,
- BOUNCE_DATA_MESSAGE,
- BOUNCE_DATA_END,
- BOUNCE_QUIT,
- BOUNCE_CLOSE,
-};
-
-struct bounce_envelope {
- TAILQ_ENTRY(bounce_envelope) entry;
- uint64_t id;
- struct mailaddr dest;
- char *report;
- uint8_t esc_class;
- uint8_t esc_code;
-};
-
-struct bounce_message {
- SPLAY_ENTRY(bounce_message) sp_entry;
- TAILQ_ENTRY(bounce_message) entry;
- uint32_t msgid;
- struct delivery_bounce bounce;
- char *smtpname;
- char *to;
- time_t timeout;
- TAILQ_HEAD(, bounce_envelope) envelopes;
-};
-
-struct bounce_session {
- char *smtpname;
- struct bounce_message *msg;
- FILE *msgfp;
- int state;
- struct io *io;
- uint64_t boundary;
-};
-
-SPLAY_HEAD(bounce_message_tree, bounce_message);
-static int bounce_message_cmp(const struct bounce_message *,
- const struct bounce_message *);
-SPLAY_PROTOTYPE(bounce_message_tree, bounce_message, sp_entry,
- bounce_message_cmp);
-
-static void bounce_drain(void);
-static void bounce_send(struct bounce_session *, const char *, ...);
-static int bounce_next_message(struct bounce_session *);
-static int bounce_next(struct bounce_session *);
-static void bounce_delivery(struct bounce_message *, int, const char *);
-static void bounce_status(struct bounce_session *, const char *, ...);
-static void bounce_io(struct io *, int, void *);
-static void bounce_timeout(int, short, void *);
-static void bounce_free(struct bounce_session *);
-static const char *action_str(const struct delivery_bounce *);
-
-static struct tree wait_fd;
-static struct bounce_message_tree messages;
-static TAILQ_HEAD(, bounce_message) pending;
-
-static int nmessage = 0;
-static int running = 0;
-static struct event ev_timer;
-
-static void
-bounce_init(void)
-{
- static int init = 0;
-
- if (init == 0) {
- TAILQ_INIT(&pending);
- SPLAY_INIT(&messages);
- tree_init(&wait_fd);
- evtimer_set(&ev_timer, bounce_timeout, NULL);
- init = 1;
- }
-}
-
-void
-bounce_add(uint64_t evpid)
-{
- char buf[LINE_MAX], *line;
- struct envelope evp;
- struct bounce_message key, *msg;
- struct bounce_envelope *be;
-
- bounce_init();
-
- if (queue_envelope_load(evpid, &evp) == 0) {
- m_create(p_scheduler, IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1);
- m_add_evpid(p_scheduler, evpid);
- m_close(p_scheduler);
- return;
- }
-
- if (evp.type != D_BOUNCE)
- errx(1, "bounce: evp:%016" PRIx64 " is not of type D_BOUNCE!",
- evp.id);
-
- key.msgid = evpid_to_msgid(evpid);
- key.bounce = evp.agent.bounce;
- key.smtpname = evp.smtpname;
-
- switch (evp.esc_class) {
- case ESC_STATUS_OK:
- key.bounce.type = B_DELIVERED;
- break;
- case ESC_STATUS_TEMPFAIL:
- key.bounce.type = B_DELAYED;
- break;
- default:
- key.bounce.type = B_FAILED;
- }
-
- key.bounce.dsn_ret = evp.dsn_ret;
- key.bounce.ttl = evp.ttl;
- msg = SPLAY_FIND(bounce_message_tree, &messages, &key);
- if (msg == NULL) {
- msg = xcalloc(1, sizeof(*msg));
- msg->msgid = key.msgid;
- msg->bounce = key.bounce;
-
- TAILQ_INIT(&msg->envelopes);
-
- msg->smtpname = xstrdup(evp.smtpname);
- (void)snprintf(buf, sizeof(buf), "%s@%s", evp.sender.user,
- evp.sender.domain);
- msg->to = xstrdup(buf);
- nmessage += 1;
- SPLAY_INSERT(bounce_message_tree, &messages, msg);
- log_debug("debug: bounce: new message %08" PRIx32,
- msg->msgid);
- stat_increment("bounce.message", 1);
- } else
- TAILQ_REMOVE(&pending, msg, entry);
-
- line = evp.errorline;
- if (strlen(line) > 4 && (*line == '1' || *line == '6'))
- line += 4;
- (void)snprintf(buf, sizeof(buf), "%s@%s: %s", evp.dest.user,
- evp.dest.domain, line);
-
- be = xmalloc(sizeof *be);
- be->id = evpid;
- be->report = xstrdup(buf);
- (void)strlcpy(be->dest.user, evp.dest.user, sizeof(be->dest.user));
- (void)strlcpy(be->dest.domain, evp.dest.domain,
- sizeof(be->dest.domain));
- be->esc_class = evp.esc_class;
- be->esc_code = evp.esc_code;
- TAILQ_INSERT_TAIL(&msg->envelopes, be, entry);
- log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, be->report);
-
- msg->timeout = time(NULL) + 1;
- TAILQ_INSERT_TAIL(&pending, msg, entry);
-
- stat_increment("bounce.envelope", 1);
- bounce_drain();
-}
-
-void
-bounce_fd(int fd)
-{
- struct bounce_session *s;
- struct bounce_message *msg;
-
- log_debug("debug: bounce: got enqueue socket %d", fd);
-
- if (fd == -1 || TAILQ_EMPTY(&pending)) {
- log_debug("debug: bounce: cancelling");
- if (fd != -1)
- close(fd);
- running -= 1;
- bounce_drain();
- return;
- }
-
- msg = TAILQ_FIRST(&pending);
-
- s = xcalloc(1, sizeof(*s));
- s->smtpname = xstrdup(msg->smtpname);
- s->state = BOUNCE_EHLO;
- s->io = io_new();
- io_set_callback(s->io, bounce_io, s);
- io_set_fd(s->io, fd);
- io_set_timeout(s->io, 30000);
- io_set_read(s->io);
- s->boundary = generate_uid();
-
- log_debug("debug: bounce: new session %p", s);
- stat_increment("bounce.session", 1);
-}
-
-static void
-bounce_timeout(int fd, short ev, void *arg)
-{
- log_debug("debug: bounce: timeout");
-
- bounce_drain();
-}
-
-static void
-bounce_drain()
-{
- struct bounce_message *msg;
- struct timeval tv;
- time_t t;
-
- log_debug("debug: bounce: drain: nmessage=%d running=%d",
- nmessage, running);
-
- while (1) {
- if (running >= BOUNCE_MAXRUN) {
- log_debug("debug: bounce: max session reached");
- return;
- }
-
- if (nmessage == 0) {
- log_debug("debug: bounce: no more messages");
- return;
- }
-
- if (running >= nmessage) {
- log_debug("debug: bounce: enough sessions running");
- return;
- }
-
- if ((msg = TAILQ_FIRST(&pending)) == NULL) {
- log_debug("debug: bounce: no more pending messages");
- return;
- }
-
- t = time(NULL);
- if (msg->timeout > t) {
- log_debug("debug: bounce: next message not ready yet");
- if (!evtimer_pending(&ev_timer, NULL)) {
- log_debug("debug: bounce: setting timer");
- tv.tv_sec = msg->timeout - t;
- tv.tv_usec = 0;
- evtimer_add(&ev_timer, &tv);
- }
- return;
- }
-
- log_debug("debug: bounce: requesting new enqueue socket...");
- m_compose(p_pony, IMSG_QUEUE_SMTP_SESSION, 0, 0, -1, NULL, 0);
-
- running += 1;
- }
-}
-
-static void
-bounce_send(struct bounce_session *s, const char *fmt, ...)
-{
- va_list ap;
- char *p;
- int len;
-
- va_start(ap, fmt);
- if ((len = vasprintf(&p, fmt, ap)) == -1)
- fatal("bounce: vasprintf");
- va_end(ap);
-
- log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", s, p);
-
- io_xprintf(s->io, "%s\r\n", p);
-
- free(p);
-}
-
-static const char *
-bounce_duration(long long int d)
-{
- static char buf[32];
-
- if (d < 60) {
- (void)snprintf(buf, sizeof buf, "%lld second%s", d,
- (d == 1) ? "" : "s");
- } else if (d < 3600) {
- d = d / 60;
- (void)snprintf(buf, sizeof buf, "%lld minute%s", d,
- (d == 1) ? "" : "s");
- }
- else if (d < 3600 * 24) {
- d = d / 3600;
- (void)snprintf(buf, sizeof buf, "%lld hour%s", d,
- (d == 1) ? "" : "s");
- }
- else {
- d = d / (3600 * 24);
- (void)snprintf(buf, sizeof buf, "%lld day%s", d,
- (d == 1) ? "" : "s");
- }
- return (buf);
-}
-
-#define NOTICE_INTRO \
- " Hi!\r\n\r\n" \
- " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\r\n"
-
-const char *notice_error =
- " An error has occurred while attempting to deliver a message for\r\n"
- " the following list of recipients:\r\n\r\n";
-
-const char *notice_warning =
- " A message is delayed for more than %s for the following\r\n"
- " list of recipients:\r\n\r\n";
-
-const char *notice_warning2 =
- " Please note that this is only a temporary failure report.\r\n"
- " The message is kept in the queue for up to %s.\r\n"
- " You DO NOT NEED to re-send the message to these recipients.\r\n\r\n";
-
-const char *notice_success =
- " Your message was successfully delivered to these recipients.\r\n\r\n";
-
-const char *notice_relay =
- " Your message was relayed to these recipients.\r\n\r\n";
-
-static int
-bounce_next_message(struct bounce_session *s)
-{
- struct bounce_message *msg;
- char buf[LINE_MAX];
- int fd;
- time_t now;
-
- again:
-
- now = time(NULL);
-
- TAILQ_FOREACH(msg, &pending, entry) {
- if (msg->timeout > now)
- continue;
- if (strcmp(msg->smtpname, s->smtpname))
- continue;
- break;
- }
- if (msg == NULL)
- return (0);
-
- TAILQ_REMOVE(&pending, msg, entry);
- SPLAY_REMOVE(bounce_message_tree, &messages, msg);
-
- if ((fd = queue_message_fd_r(msg->msgid)) == -1) {
- bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL,
- "Could not open message fd");
- goto again;
- }
-
- if ((s->msgfp = fdopen(fd, "r")) == NULL) {
- (void)snprintf(buf, sizeof(buf), "fdopen: %s", strerror(errno));
- log_warn("warn: bounce: fdopen");
- close(fd);
- bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, buf);
- goto again;
- }
-
- s->msg = msg;
- return (1);
-}
-
-static int
-bounce_next(struct bounce_session *s)
-{
- struct bounce_envelope *evp;
- char *line = NULL;
- size_t n, sz = 0;
- ssize_t len;
-
- switch (s->state) {
- case BOUNCE_EHLO:
- bounce_send(s, "EHLO %s", s->smtpname);
- s->state = BOUNCE_MAIL;
- break;
-
- case BOUNCE_MAIL:
- case BOUNCE_DATA_END:
- log_debug("debug: bounce: %p: getting next message...", s);
- if (bounce_next_message(s) == 0) {
- log_debug("debug: bounce: %p: no more messages", s);
- bounce_send(s, "QUIT");
- s->state = BOUNCE_CLOSE;
- break;
- }
- log_debug("debug: bounce: %p: found message %08"PRIx32,
- s, s->msg->msgid);
- bounce_send(s, "MAIL FROM: <>");
- s->state = BOUNCE_RCPT;
- break;
-
- case BOUNCE_RCPT:
- bounce_send(s, "RCPT TO: <%s>", s->msg->to);
- s->state = BOUNCE_DATA;
- break;
-
- case BOUNCE_DATA:
- bounce_send(s, "DATA");
- s->state = BOUNCE_DATA_NOTICE;
- break;
-
- case BOUNCE_DATA_NOTICE:
- /* Construct an appropriate notice. */
-
- io_xprintf(s->io,
- "Subject: Delivery status notification: %s\r\n"
- "From: Mailer Daemon <MAILER-DAEMON@%s>\r\n"
- "To: %s\r\n"
- "Date: %s\r\n"
- "MIME-Version: 1.0\r\n"
- "Content-Type: multipart/mixed;"
- "boundary=\"%16" PRIu64 "/%s\"\r\n"
- "\r\n"
- "This is a MIME-encapsulated message.\r\n"
- "\r\n",
- action_str(&s->msg->bounce),
- s->smtpname,
- s->msg->to,
- time_to_text(time(NULL)),
- s->boundary,
- s->smtpname);
-
- io_xprintf(s->io,
- "--%16" PRIu64 "/%s\r\n"
- "Content-Description: Notification\r\n"
- "Content-Type: text/plain; charset=us-ascii\r\n"
- "\r\n"
- NOTICE_INTRO
- "\r\n",
- s->boundary, s->smtpname);
-
- switch (s->msg->bounce.type) {
- case B_FAILED:
- io_xprint(s->io, notice_error);
- break;
- case B_DELAYED:
- io_xprintf(s->io, notice_warning,
- bounce_duration(s->msg->bounce.delay));
- break;
- case B_DELIVERED:
- io_xprint(s->io, s->msg->bounce.mta_without_dsn ?
- notice_relay : notice_success);
- break;
- default:
- log_warn("warn: bounce: unknown bounce_type");
- }
-
- TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
- io_xprint(s->io, evp->report);
- io_xprint(s->io, "\r\n");
- }
- io_xprint(s->io, "\r\n");
-
- if (s->msg->bounce.type == B_DELAYED)
- io_xprintf(s->io, notice_warning2,
- bounce_duration(s->msg->bounce.ttl));
-
- io_xprintf(s->io,
- " Below is a copy of the original message:\r\n"
- "\r\n");
-
- io_xprintf(s->io,
- "--%16" PRIu64 "/%s\r\n"
- "Content-Description: Delivery Report\r\n"
- "Content-Type: message/delivery-status\r\n"
- "\r\n",
- s->boundary, s->smtpname);
-
- io_xprintf(s->io,
- "Reporting-MTA: dns; %s\r\n"
- "\r\n",
- s->smtpname);
-
- TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
- io_xprintf(s->io,
- "Final-Recipient: rfc822; %s@%s\r\n"
- "Action: %s\r\n"
- "Status: %s\r\n"
- "\r\n",
- evp->dest.user,
- evp->dest.domain,
- action_str(&s->msg->bounce),
- esc_code(evp->esc_class, evp->esc_code));
- }
-
- log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
- s, io_queued(s->io));
-
- s->state = BOUNCE_DATA_MESSAGE;
- break;
-
- case BOUNCE_DATA_MESSAGE:
- io_xprintf(s->io,
- "--%16" PRIu64 "/%s\r\n"
- "Content-Description: Message headers\r\n"
- "Content-Type: text/rfc822-headers\r\n"
- "\r\n",
- s->boundary, s->smtpname);
-
- n = io_queued(s->io);
- while (io_queued(s->io) < BOUNCE_HIWAT) {
- if ((len = getline(&line, &sz, s->msgfp)) == -1)
- break;
- if (len == 1 && line[0] == '\n' && /* end of headers */
- s->msg->bounce.type == B_DELIVERED &&
- s->msg->bounce.dsn_ret == DSN_RETHDRS) {
- free(line);
- fclose(s->msgfp);
- s->msgfp = NULL;
- io_xprintf(s->io,
- "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary,
- s->smtpname);
- bounce_send(s, ".");
- s->state = BOUNCE_DATA_END;
- return (0);
- }
- line[len - 1] = '\0';
- io_xprintf(s->io, "%s%s\r\n",
- (len == 2 && line[0] == '.') ? "." : "", line);
- }
- free(line);
-
- if (ferror(s->msgfp)) {
- fclose(s->msgfp);
- s->msgfp = NULL;
- bounce_delivery(s->msg, IMSG_QUEUE_DELIVERY_TEMPFAIL,
- "Error reading message");
- s->msg = NULL;
- return (-1);
- }
-
- io_xprintf(s->io,
- "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary, s->smtpname);
-
- log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
- s, io_queued(s->io) - n);
-
- if (feof(s->msgfp)) {
- fclose(s->msgfp);
- s->msgfp = NULL;
- bounce_send(s, ".");
- s->state = BOUNCE_DATA_END;
- }
- break;
-
- case BOUNCE_QUIT:
- bounce_send(s, "QUIT");
- s->state = BOUNCE_CLOSE;
- break;
-
- default:
- fatalx("bounce: bad state");
- }
-
- return (0);
-}
-
-
-static void
-bounce_delivery(struct bounce_message *msg, int delivery, const char *status)
-{
- struct bounce_envelope *be;
- struct envelope evp;
- size_t n;
- const char *f;
-
- n = 0;
- while ((be = TAILQ_FIRST(&msg->envelopes))) {
- if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL) {
- if (queue_envelope_load(be->id, &evp) == 0) {
- fatalx("could not reload envelope!");
- }
- evp.retry++;
- evp.lasttry = msg->timeout;
- envelope_set_errormsg(&evp, "%s", status);
- queue_envelope_update(&evp);
- m_create(p_scheduler, delivery, 0, 0, -1);
- m_add_envelope(p_scheduler, &evp);
- m_close(p_scheduler);
- } else {
- m_create(p_scheduler, delivery, 0, 0, -1);
- m_add_evpid(p_scheduler, be->id);
- m_close(p_scheduler);
- queue_envelope_delete(be->id);
- }
- TAILQ_REMOVE(&msg->envelopes, be, entry);
- free(be->report);
- free(be);
- n += 1;
- }
-
-
- if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL)
- f = "TempFail";
- else if (delivery == IMSG_QUEUE_DELIVERY_PERMFAIL)
- f = "PermFail";
- else
- f = NULL;
-
- if (f)
- log_warnx("warn: %s injecting failure report on message %08"
- PRIx32 " to <%s> for %zu envelope%s: %s",
- f, msg->msgid, msg->to, n, n > 1 ? "s":"", status);
-
- nmessage -= 1;
- stat_decrement("bounce.message", 1);
- stat_decrement("bounce.envelope", n);
- free(msg->smtpname);
- free(msg->to);
- free(msg);
-}
-
-static void
-bounce_status(struct bounce_session *s, const char *fmt, ...)
-{
- va_list ap;
- char *status;
- int len, delivery;
-
- /* Ignore if there is no message */
- if (s->msg == NULL)
- return;
-
- va_start(ap, fmt);
- if ((len = vasprintf(&status, fmt, ap)) == -1)
- fatal("bounce: vasprintf");
- va_end(ap);
-
- if (*status == '2')
- delivery = IMSG_QUEUE_DELIVERY_OK;
- else if (*status == '5' || *status == '6')
- delivery = IMSG_QUEUE_DELIVERY_PERMFAIL;
- else
- delivery = IMSG_QUEUE_DELIVERY_TEMPFAIL;
-
- bounce_delivery(s->msg, delivery, status);
- s->msg = NULL;
- if (s->msgfp)
- fclose(s->msgfp);
-
- free(status);
-}
-
-static void
-bounce_free(struct bounce_session *s)
-{
- log_debug("debug: bounce: %p: deleting session", s);
-
- io_free(s->io);
-
- free(s->smtpname);
- free(s);
-
- running -= 1;
- stat_decrement("bounce.session", 1);
- bounce_drain();
-}
-
-static void
-bounce_io(struct io *io, int evt, void *arg)
-{
- struct bounce_session *s = arg;
- const char *error;
- char *line, *msg;
- int cont;
- size_t len;
-
- log_trace(TRACE_IO, "bounce: %p: %s %s", s, io_strevent(evt),
- io_strio(io));
-
- switch (evt) {
- case IO_DATAIN:
- nextline:
- line = io_getline(s->io, &len);
- if (line == NULL && io_datalen(s->io) >= LINE_MAX) {
- bounce_status(s, "Input too long");
- bounce_free(s);
- return;
- }
-
- if (line == NULL)
- break;
-
- /* Strip trailing '\r' */
- if (len && line[len - 1] == '\r')
- line[--len] = '\0';
-
- log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", s, line);
-
- if ((error = parse_smtp_response(line, len, &msg, &cont))) {
- bounce_status(s, "Bad response: %s", error);
- bounce_free(s);
- return;
- }
- if (cont)
- goto nextline;
-
- if (s->state == BOUNCE_CLOSE) {
- bounce_free(s);
- return;
- }
-
- if (line[0] != '2' && line[0] != '3') { /* fail */
- bounce_status(s, "%s", line);
- s->state = BOUNCE_QUIT;
- } else if (s->state == BOUNCE_DATA_END) { /* accepted */
- bounce_status(s, "%s", line);
- }
-
- if (bounce_next(s) == -1) {
- bounce_free(s);
- return;
- }
-
- io_set_write(io);
- break;
-
- case IO_LOWAT:
- if (s->state == BOUNCE_DATA_MESSAGE)
- if (bounce_next(s) == -1) {
- bounce_free(s);
- return;
- }
- if (io_queued(s->io) == 0)
- io_set_read(io);
- break;
-
- default:
- bounce_status(s, "442 i/o error %d", evt);
- bounce_free(s);
- break;
- }
-}
-
-static int
-bounce_message_cmp(const struct bounce_message *a,
- const struct bounce_message *b)
-{
- int r;
-
- if (a->msgid < b->msgid)
- return (-1);
- if (a->msgid > b->msgid)
- return (1);
- if ((r = strcmp(a->smtpname, b->smtpname)))
- return (r);
-
- return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce));
-}
-
-static const char *
-action_str(const struct delivery_bounce *b)
-{
- switch (b->type) {
- case B_FAILED:
- return ("failed");
- case B_DELAYED:
- return ("delayed");
- case B_DELIVERED:
- if (b->mta_without_dsn)
- return ("relayed");
-
- return ("delivered");
- default:
- log_warn("warn: bounce: unknown bounce_type");
- return ("");
- }
-}
-
-SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry,
- bounce_message_cmp);
diff --git a/smtpd/ca.c b/smtpd/ca.c
deleted file mode 100644
index a27db87a..00000000
--- a/smtpd/ca.c
+++ /dev/null
@@ -1,777 +0,0 @@
-/* $OpenBSD: ca.c,v 1.36 2019/09/21 07:46:53 semarie Exp $ */
-
-/*
- * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/socket.h>
-#include <sys/tree.h>
-
-#include <grp.h> /* needed for setgroups */
-#include <err.h>
-#include <imsg.h>
-#include <limits.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <openssl/ssl.h>
-#include <openssl/pem.h>
-#include <openssl/evp.h>
-#include <openssl/ecdsa.h>
-#include <openssl/rsa.h>
-#include <openssl/engine.h>
-#include <openssl/err.h>
-
-#include "smtpd.h"
-#include "log.h"
-#include "ssl.h"
-
-static int ca_verify_cb(int, X509_STORE_CTX *);
-
-static int rsae_send_imsg(int, const unsigned char *, unsigned char *,
- RSA *, int, unsigned int);
-static int rsae_pub_enc(int, const unsigned char *, unsigned char *,
- RSA *, int);
-static int rsae_pub_dec(int,const unsigned char *, unsigned char *,
- RSA *, int);
-static int rsae_priv_enc(int, const unsigned char *, unsigned char *,
- RSA *, int);
-static int rsae_priv_dec(int, const unsigned char *, unsigned char *,
- RSA *, int);
-static int rsae_mod_exp(BIGNUM *, const BIGNUM *, RSA *, BN_CTX *);
-static int rsae_bn_mod_exp(BIGNUM *, const BIGNUM *, const BIGNUM *,
- const BIGNUM *, BN_CTX *, BN_MONT_CTX *);
-static int rsae_init(RSA *);
-static int rsae_finish(RSA *);
-static int rsae_keygen(RSA *, int, BIGNUM *, BN_GENCB *);
-
-#if defined(SUPPORT_ECDSA)
-static ECDSA_SIG *ecdsae_do_sign(const unsigned char *, int, const BIGNUM *,
- const BIGNUM *, EC_KEY *);
-static int ecdsae_sign_setup(EC_KEY *, BN_CTX *, BIGNUM **, BIGNUM **);
-static int ecdsae_do_verify(const unsigned char *, int, const ECDSA_SIG *,
- EC_KEY *);
-#endif
-
-static uint64_t reqid = 0;
-
-static void
-ca_shutdown(void)
-{
- log_debug("debug: ca agent exiting");
- _exit(0);
-}
-
-int
-ca(void)
-{
- struct passwd *pw;
-
- purge_config(PURGE_LISTENERS|PURGE_TABLES|PURGE_RULES|PURGE_DISPATCHERS);
-
- if ((pw = getpwnam(SMTPD_USER)) == NULL)
- fatalx("unknown user " SMTPD_USER);
-
- if (chroot(PATH_CHROOT) == -1)
- fatal("ca: chroot");
- if (chdir("/") == -1)
- fatal("ca: chdir(\"/\")");
-
- config_process(PROC_CA);
-
- if (setgroups(1, &pw->pw_gid) ||
- setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
- setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
- fatal("ca: cannot drop privileges");
-
- imsg_callback = ca_imsg;
- event_init();
-
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, SIG_IGN);
- signal(SIGPIPE, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-
- config_peer(PROC_CONTROL);
- config_peer(PROC_PARENT);
- config_peer(PROC_PONY);
-
- /* Ignore them until we get our config */
- mproc_disable(p_pony);
-
-#if HAVE_PLEDGE
- if (pledge("stdio", NULL) == -1)
- err(1, "pledge");
-#endif
-
- event_dispatch();
- fatalx("exited event loop");
-
- return (0);
-}
-
-void
-ca_init(void)
-{
- BIO *in = NULL;
- EVP_PKEY *pkey = NULL;
- struct pki *pki;
- const char *k;
- void *iter_dict;
-
- log_debug("debug: init private ssl-tree");
- iter_dict = NULL;
- while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) {
- if (pki->pki_key == NULL)
- continue;
-
- if ((in = BIO_new_mem_buf(pki->pki_key,
- pki->pki_key_len)) == NULL)
- fatalx("ca_launch: key");
-
- if ((pkey = PEM_read_bio_PrivateKey(in,
- NULL, NULL, NULL)) == NULL)
- fatalx("ca_launch: PEM");
- BIO_free(in);
-
- pki->pki_pkey = pkey;
-
- freezero(pki->pki_key, pki->pki_key_len);
- pki->pki_key = NULL;
- }
-}
-
-static int
-ca_verify_cb(int ok, X509_STORE_CTX *ctx)
-{
- switch (X509_STORE_CTX_get_error(ctx)) {
- case X509_V_OK:
- break;
- case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
- break;
- case X509_V_ERR_CERT_NOT_YET_VALID:
- case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
- break;
- case X509_V_ERR_CERT_HAS_EXPIRED:
- case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
- break;
- case X509_V_ERR_NO_EXPLICIT_POLICY:
- break;
- }
- return ok;
-}
-
-int
-ca_X509_verify(void *certificate, void *chain, const char *CAfile,
- const char *CRLfile, const char **errstr)
-{
- X509_STORE *store = NULL;
- X509_STORE_CTX *xsc = NULL;
- int ret = 0;
- long error = 0;
-
- if ((store = X509_STORE_new()) == NULL)
- goto end;
-
- if (!X509_STORE_load_locations(store, CAfile, NULL)) {
- log_warn("warn: unable to load CA file %s", CAfile);
- goto end;
- }
- X509_STORE_set_default_paths(store);
-
- if ((xsc = X509_STORE_CTX_new()) == NULL)
- goto end;
-
- if (X509_STORE_CTX_init(xsc, store, certificate, chain) != 1)
- goto end;
-
- X509_STORE_CTX_set_verify_cb(xsc, ca_verify_cb);
-
- ret = X509_verify_cert(xsc);
-
-end:
- *errstr = NULL;
- if (ret != 1) {
- if (xsc) {
- error = X509_STORE_CTX_get_error(xsc);
- *errstr = X509_verify_cert_error_string(error);
- }
- else if (ERR_peek_last_error())
- *errstr = ERR_error_string(ERR_peek_last_error(), NULL);
- }
-
- X509_STORE_CTX_free(xsc);
- X509_STORE_free(store);
-
- return ret > 0 ? 1 : 0;
-}
-
-void
-ca_imsg(struct mproc *p, struct imsg *imsg)
-{
- RSA *rsa = NULL;
-#if defined(SUPPORT_ECDSA)
- EC_KEY *ecdsa = NULL;
-#endif
- const void *from = NULL;
- unsigned char *to = NULL;
- struct msg m;
- const char *pkiname;
- size_t flen, tlen, padding;
-#if defined(SUPPORT_ECDSA)
- int buf_len;
-#endif
- struct pki *pki;
- int ret = 0;
- uint64_t id;
- int v;
-
- if (imsg == NULL)
- ca_shutdown();
-
- switch (imsg->hdr.type) {
- case IMSG_CONF_START:
- return;
- case IMSG_CONF_END:
- ca_init();
-
- /* Start fulfilling requests */
- mproc_enable(p_pony);
- return;
-
- case IMSG_CTL_VERBOSE:
- m_msg(&m, imsg);
- m_get_int(&m, &v);
- m_end(&m);
- log_trace_verbose(v);
- return;
-
- case IMSG_CTL_PROFILE:
- m_msg(&m, imsg);
- m_get_int(&m, &v);
- m_end(&m);
- profiling = v;
- return;
-
- case IMSG_CA_RSA_PRIVENC:
- case IMSG_CA_RSA_PRIVDEC:
- m_msg(&m, imsg);
- m_get_id(&m, &id);
- m_get_string(&m, &pkiname);
- m_get_data(&m, &from, &flen);
- m_get_size(&m, &tlen);
- m_get_size(&m, &padding);
- m_end(&m);
-
- pki = dict_get(env->sc_pki_dict, pkiname);
- if (pki == NULL || pki->pki_pkey == NULL ||
- (rsa = EVP_PKEY_get1_RSA(pki->pki_pkey)) == NULL)
- fatalx("ca_imsg: invalid pki");
-
- if ((to = calloc(1, tlen)) == NULL)
- fatalx("ca_imsg: calloc");
-
- switch (imsg->hdr.type) {
- case IMSG_CA_RSA_PRIVENC:
- ret = RSA_private_encrypt(flen, from, to, rsa,
- padding);
- break;
- case IMSG_CA_RSA_PRIVDEC:
- ret = RSA_private_decrypt(flen, from, to, rsa,
- padding);
- break;
- }
-
- m_create(p, imsg->hdr.type, 0, 0, -1);
- m_add_id(p, id);
- m_add_int(p, ret);
- if (ret > 0)
- m_add_data(p, to, (size_t)ret);
- m_close(p);
-
- free(to);
- RSA_free(rsa);
- return;
-
-#if defined(SUPPORT_ECDSA)
- case IMSG_CA_ECDSA_SIGN:
- m_msg(&m, imsg);
- m_get_id(&m, &id);
- m_get_string(&m, &pkiname);
- m_get_data(&m, &from, &flen);
- m_end(&m);
-
- pki = dict_get(env->sc_pki_dict, pkiname);
- if (pki == NULL || pki->pki_pkey == NULL ||
- (ecdsa = EVP_PKEY_get1_EC_KEY(pki->pki_pkey)) == NULL)
- fatalx("ca_imsg: invalid pki");
-
- buf_len = ECDSA_size(ecdsa);
- if ((to = calloc(1, buf_len)) == NULL)
- fatalx("ca_imsg: calloc");
- ret = ECDSA_sign(0, from, flen, to, &buf_len, ecdsa);
- m_create(p, imsg->hdr.type, 0, 0, -1);
- m_add_id(p, id);
- m_add_int(p, ret);
- if (ret > 0)
- m_add_data(p, to, (size_t)buf_len);
- m_close(p);
- free(to);
- EC_KEY_free(ecdsa);
- return;
-#endif
- }
- errx(1, "ca_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
-}
-
-/*
- * RSA privsep engine (called from unprivileged processes)
- */
-
-const RSA_METHOD *rsa_default = NULL;
-
-static RSA_METHOD *rsae_method = NULL;
-
-static int
-rsae_send_imsg(int flen, const unsigned char *from, unsigned char *to,
- RSA *rsa, int padding, unsigned int cmd)
-{
- int ret = 0;
- struct imsgbuf *ibuf;
- struct imsg imsg;
- int n, done = 0;
- const void *toptr;
- char *pkiname;
- size_t tlen;
- struct msg m;
- uint64_t id;
-
- if ((pkiname = RSA_get_ex_data(rsa, 0)) == NULL)
- return (0);
-
- /*
- * Send a synchronous imsg because we cannot defer the RSA
- * operation in OpenSSL's engine layer.
- */
- m_create(p_ca, cmd, 0, 0, -1);
- reqid++;
- m_add_id(p_ca, reqid);
- m_add_string(p_ca, pkiname);
- m_add_data(p_ca, (const void *)from, (size_t)flen);
- m_add_size(p_ca, (size_t)RSA_size(rsa));
- m_add_size(p_ca, (size_t)padding);
- m_flush(p_ca);
-
- ibuf = &p_ca->imsgbuf;
-
- while (!done) {
- if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
- fatalx("imsg_read");
- if (n == 0)
- fatalx("pipe closed");
-
- while (!done) {
- if ((n = imsg_get(ibuf, &imsg)) == -1)
- fatalx("imsg_get error");
- if (n == 0)
- break;
-
- log_imsg(PROC_PONY, PROC_CA, &imsg);
-
- switch (imsg.hdr.type) {
- case IMSG_CA_RSA_PRIVENC:
- case IMSG_CA_RSA_PRIVDEC:
- break;
- default:
- /* Another imsg is queued up in the buffer */
- pony_imsg(p_ca, &imsg);
- imsg_free(&imsg);
- continue;
- }
-
- m_msg(&m, &imsg);
- m_get_id(&m, &id);
- if (id != reqid)
- fatalx("invalid response id");
- m_get_int(&m, &ret);
- if (ret > 0)
- m_get_data(&m, &toptr, &tlen);
- m_end(&m);
-
- if (ret > 0)
- memcpy(to, toptr, tlen);
- done = 1;
-
- imsg_free(&imsg);
- }
- }
- mproc_event_add(p_ca);
-
- return (ret);
-}
-
-static int
-rsae_pub_enc(int flen,const unsigned char *from, unsigned char *to, RSA *rsa,
- int padding)
-{
- log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
- return (RSA_meth_get_pub_enc(rsa_default)(flen, from, to, rsa, padding));
-}
-
-static int
-rsae_pub_dec(int flen,const unsigned char *from, unsigned char *to, RSA *rsa,
- int padding)
-{
- log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
- return (RSA_meth_get_pub_dec(rsa_default)(flen, from, to, rsa, padding));
-}
-
-static int
-rsae_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,
- int padding)
-{
- log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
- if (RSA_get_ex_data(rsa, 0) != NULL)
- return (rsae_send_imsg(flen, from, to, rsa, padding,
- IMSG_CA_RSA_PRIVENC));
- return (RSA_meth_get_priv_enc(rsa_default)(flen, from, to, rsa, padding));
-}
-
-static int
-rsae_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,
- int padding)
-{
- log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
- if (RSA_get_ex_data(rsa, 0) != NULL)
- return (rsae_send_imsg(flen, from, to, rsa, padding,
- IMSG_CA_RSA_PRIVDEC));
-
- return (RSA_meth_get_priv_dec(rsa_default)(flen, from, to, rsa, padding));
-}
-
-static int
-rsae_mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx)
-{
- log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
- return (RSA_meth_get_mod_exp(rsa_default)(r0, I, rsa, ctx));
-}
-
-static int
-rsae_bn_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
- const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx)
-{
- log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
- return (RSA_meth_get_bn_mod_exp(rsa_default)(r, a, p, m, ctx, m_ctx));
-}
-
-static int
-rsae_init(RSA *rsa)
-{
- log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
- if (RSA_meth_get_init(rsa_default) == NULL)
- return (1);
- return (RSA_meth_get_init(rsa_default)(rsa));
-}
-
-static int
-rsae_finish(RSA *rsa)
-{
- log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
- if (RSA_meth_get_finish(rsa_default) == NULL)
- return (1);
- return (RSA_meth_get_finish(rsa_default)(rsa));
-}
-
-static int
-rsae_keygen(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb)
-{
- log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
- return (RSA_meth_get_keygen(rsa_default)(rsa, bits, e, cb));
-}
-
-
-#if defined(SUPPORT_ECDSA)
-/*
- * ECDSA privsep engine (called from unprivileged processes)
- */
-
-const ECDSA_METHOD *ecdsa_default = NULL;
-
-static ECDSA_METHOD *ecdsae_method = NULL;
-
-ECDSA_METHOD *
-ECDSA_METHOD_new_temporary(const char *name, int);
-
-ECDSA_METHOD *
-ECDSA_METHOD_new_temporary(const char *name, int flags)
-{
- ECDSA_METHOD *ecdsa;
-
- if ((ecdsa = calloc(1, sizeof (*ecdsa))) == NULL)
- return NULL;
-
- if ((ecdsa->name = strdup(name)) == NULL) {
- free(ecdsa);
- return NULL;
- }
-
- ecdsa->flags = flags;
- return ecdsa;
-}
-
-static ECDSA_SIG *
-ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len,
- const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey)
-{
- int ret = 0;
- struct imsgbuf *ibuf;
- struct imsg imsg;
- int n, done = 0;
- const void *toptr;
- char *pkiname;
- size_t tlen;
- struct msg m;
- uint64_t id;
- ECDSA_SIG *sig = NULL;
-
- if ((pkiname = ECDSA_get_ex_data(eckey, 0)) == NULL)
- return (0);
-
- /*
- * Send a synchronous imsg because we cannot defer the ECDSA
- * operation in OpenSSL's engine layer.
- */
- m_create(p_ca, IMSG_CA_ECDSA_SIGN, 0, 0, -1);
- reqid++;
- m_add_id(p_ca, reqid);
- m_add_string(p_ca, pkiname);
- m_add_data(p_ca, (const void *)dgst, (size_t)dgst_len);
- m_flush(p_ca);
-
- ibuf = &p_ca->imsgbuf;
-
- while (!done) {
- if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
- fatalx("imsg_read");
- if (n == 0)
- fatalx("pipe closed");
- while (!done) {
- if ((n = imsg_get(ibuf, &imsg)) == -1)
- fatalx("imsg_get error");
- if (n == 0)
- break;
-
- log_imsg(PROC_PONY, PROC_CA, &imsg);
-
- switch (imsg.hdr.type) {
- case IMSG_CA_ECDSA_SIGN:
- break;
- default:
- /* Another imsg is queued up in the buffer */
- pony_imsg(p_ca, &imsg);
- imsg_free(&imsg);
- continue;
- }
-
- m_msg(&m, &imsg);
- m_get_id(&m, &id);
- if (id != reqid)
- fatalx("invalid response id");
- m_get_int(&m, &ret);
- if (ret > 0)
- m_get_data(&m, &toptr, &tlen);
- m_end(&m);
- done = 1;
-
- if (ret > 0)
- d2i_ECDSA_SIG(&sig, (const unsigned char **)&toptr, tlen);
- imsg_free(&imsg);
- }
- }
- mproc_event_add(p_ca);
-
- return (sig);
-}
-
-ECDSA_SIG *
-ecdsae_do_sign(const unsigned char *dgst, int dgst_len,
- const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey)
-{
- log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
- if (ECDSA_get_ex_data(eckey, 0) != NULL)
- return (ecdsae_send_enc_imsg(dgst, dgst_len, inv, rp, eckey));
- return (ecdsa_default->ecdsa_do_sign(dgst, dgst_len, inv, rp, eckey));
-}
-
-int
-ecdsae_sign_setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv,
- BIGNUM **r)
-{
- log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
- return (ecdsa_default->ecdsa_sign_setup(eckey, ctx, kinv, r));
-}
-
-int
-ecdsae_do_verify(const unsigned char *dgst, int dgst_len,
- const ECDSA_SIG *sig, EC_KEY *eckey)
-{
- log_debug("debug: %s: %s", proc_name(smtpd_process), __func__);
- return (ecdsa_default->ecdsa_do_verify(dgst, dgst_len, sig, eckey));
-}
-#endif
-
-static void
-rsa_engine_init(void)
-{
- ENGINE *e;
- const char *errstr, *name;
-
- if ((rsae_method = RSA_meth_new("RSA privsep engine", 0)) == NULL) {
- errstr = "RSA_meth_new";
- goto fail;
- }
-
- RSA_meth_set_pub_enc(rsae_method, rsae_pub_enc);
- RSA_meth_set_pub_dec(rsae_method, rsae_pub_dec);
- RSA_meth_set_priv_enc(rsae_method, rsae_priv_enc);
- RSA_meth_set_priv_dec(rsae_method, rsae_priv_dec);
- RSA_meth_set_mod_exp(rsae_method, rsae_mod_exp);
- RSA_meth_set_bn_mod_exp(rsae_method, rsae_bn_mod_exp);
- RSA_meth_set_init(rsae_method, rsae_init);
- RSA_meth_set_finish(rsae_method, rsae_finish);
- RSA_meth_set_keygen(rsae_method, rsae_keygen);
-
- if ((e = ENGINE_get_default_RSA()) == NULL) {
- if ((e = ENGINE_new()) == NULL) {
- errstr = "ENGINE_new";
- goto fail;
- }
- if (!ENGINE_set_name(e, RSA_meth_get0_name(rsae_method))) {
- errstr = "ENGINE_set_name";
- goto fail;
- }
- if ((rsa_default = RSA_get_default_method()) == NULL) {
- errstr = "RSA_get_default_method";
- goto fail;
- }
- } else if ((rsa_default = ENGINE_get_RSA(e)) == NULL) {
- errstr = "ENGINE_get_RSA";
- goto fail;
- }
-
- if ((name = ENGINE_get_name(e)) == NULL)
- name = "unknown RSA engine";
-
- log_debug("debug: %s: using %s", __func__, name);
-
- if (RSA_meth_get_mod_exp(rsa_default) == NULL)
- RSA_meth_set_mod_exp(rsae_method, NULL);
- if (RSA_meth_get_bn_mod_exp(rsa_default) == NULL)
- RSA_meth_set_bn_mod_exp(rsae_method, NULL);
- if (RSA_meth_get_keygen(rsa_default) == NULL)
- RSA_meth_set_keygen(rsae_method, NULL);
- RSA_meth_set_flags(rsae_method,
- RSA_meth_get_flags(rsa_default) | RSA_METHOD_FLAG_NO_CHECK);
- RSA_meth_set0_app_data(rsae_method,
- RSA_meth_get0_app_data(rsa_default));
-
- if (!ENGINE_set_RSA(e, rsae_method)) {
- errstr = "ENGINE_set_RSA";
- goto fail;
- }
- if (!ENGINE_set_default_RSA(e)) {
- errstr = "ENGINE_set_default_RSA";
- goto fail;
- }
-
- return;
-
- fail:
- ssl_error(errstr);
- fatalx("%s", errstr);
-}
-
-#if defined(SUPPORT_ECDSA)
-static void
-ecdsa_engine_init(void)
-{
- ENGINE *e;
- const char *errstr, *name;
-
- if ((ecdsae_method = ECDSA_METHOD_new_temporary("ECDSA privsep engine", 0)) == NULL) {
- errstr = "ECDSA_METHOD_new_temporary";
- goto fail;
- }
-
- ecdsae_method->ecdsa_do_sign = ecdsae_do_sign;
- ecdsae_method->ecdsa_sign_setup = ecdsae_sign_setup;
- ecdsae_method->ecdsa_do_verify = ecdsae_do_verify;
-
- if ((e = ENGINE_get_default_ECDSA()) == NULL) {
- if ((e = ENGINE_new()) == NULL) {
- errstr = "ENGINE_new";
- goto fail;
- }
- if (!ENGINE_set_name(e, ecdsae_method->name)) {
- errstr = "ENGINE_set_name";
- goto fail;
- }
- if ((ecdsa_default = ECDSA_get_default_method()) == NULL) {
- errstr = "ECDSA_get_default_method";
- goto fail;
- }
- } else if ((ecdsa_default = ENGINE_get_ECDSA(e)) == NULL) {
- errstr = "ENGINE_get_ECDSA";
- goto fail;
- }
-
- if ((name = ENGINE_get_name(e)) == NULL)
- name = "unknown ECDSA engine";
-
- log_debug("debug: %s: using %s", __func__, name);
-
- if (!ENGINE_set_ECDSA(e, ecdsae_method)) {
- errstr = "ENGINE_set_ECDSA";
- goto fail;
- }
- if (!ENGINE_set_default_ECDSA(e)) {
- errstr = "ENGINE_set_default_ECDSA";
- goto fail;
- }
-
- return;
-
- fail:
- ssl_error(errstr);
- fatalx("%s", errstr);
-}
-#endif
-
-void
-ca_engine_init(void)
-{
- rsa_engine_init();
-#if defined(SUPPORT_ECDSA)
- ecdsa_engine_init();
-#endif
-}
diff --git a/smtpd/cert.c b/smtpd/cert.c
deleted file mode 100644
index 79b1df91..00000000
--- a/smtpd/cert.c
+++ /dev/null
@@ -1,416 +0,0 @@
-/* $OpenBSD: cert.c,v 1.2 2018/12/11 07:25:57 eric Exp $ */
-
-/*
- * Copyright (c) 2018 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/tree.h>
-#include <sys/queue.h>
-#include <netinet/in.h>
-
-#include <imsg.h>
-#include <limits.h>
-#include <openssl/err.h>
-#include <openssl/ssl.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "log.h"
-#include "smtpd.h"
-#include "ssl.h"
-
-#define p_cert p_lka
-
-struct request {
- SPLAY_ENTRY(request) entry;
- uint32_t id;
- void (*cb_get_certificate)(void *, int, const char *,
- const void *, size_t);
- void (*cb_verify)(void *, int);
- void *arg;
-};
-
-#define MAX_CERTS 16
-#define MAX_CERT_LEN (MAX_IMSGSIZE - (IMSG_HEADER_SIZE + sizeof(size_t)))
-
-struct session {
- SPLAY_ENTRY(session) entry;
- uint32_t id;
- struct mproc *proc;
- char *cert[MAX_CERTS];
- size_t cert_len[MAX_CERTS];
- int cert_count;
-};
-
-SPLAY_HEAD(cert_reqtree, request);
-SPLAY_HEAD(cert_sestree, session);
-
-static int request_cmp(struct request *, struct request *);
-static int session_cmp(struct session *, struct session *);
-SPLAY_PROTOTYPE(cert_reqtree, request, entry, request_cmp);
-SPLAY_PROTOTYPE(cert_sestree, session, entry, session_cmp);
-
-static void cert_do_verify(struct session *, const char *, int);
-static int cert_X509_verify(struct session *, const char *, const char *);
-
-static struct cert_reqtree reqs = SPLAY_INITIALIZER(&reqs);
-static struct cert_sestree sess = SPLAY_INITIALIZER(&sess);
-
-int
-cert_init(const char *name, int fallback, void (*cb)(void *, int,
- const char *, const void *, size_t), void *arg)
-{
- struct request *req;
-
- req = calloc(1, sizeof(*req));
- if (req == NULL) {
- cb(arg, CA_FAIL, NULL, NULL, 0);
- return 0;
- }
- while (req->id == 0 || SPLAY_FIND(cert_reqtree, &reqs, req))
- req->id = arc4random();
- req->cb_get_certificate = cb;
- req->arg = arg;
- SPLAY_INSERT(cert_reqtree, &reqs, req);
-
- m_create(p_cert, IMSG_CERT_INIT, req->id, 0, -1);
- m_add_string(p_cert, name);
- m_add_int(p_cert, fallback);
- m_close(p_cert);
-
- return 1;
-}
-
-int
-cert_verify(const void *ssl, const char *name, int fallback,
- void (*cb)(void *, int), void *arg)
-{
- struct request *req;
- X509 *x;
- STACK_OF(X509) *xchain;
- unsigned char *cert_der[MAX_CERTS];
- int cert_len[MAX_CERTS];
- int i, cert_count, ret;
-
- x = SSL_get_peer_certificate(ssl);
- if (x == NULL) {
- cb(arg, CERT_NOCERT);
- return 0;
- }
-
- ret = 0;
- memset(cert_der, 0, sizeof(cert_der));
-
- req = calloc(1, sizeof(*req));
- if (req == NULL)
- goto end;
- while (req->id == 0 || SPLAY_FIND(cert_reqtree, &reqs, req))
- req->id = arc4random();
- req->cb_verify = cb;
- req->arg = arg;
- SPLAY_INSERT(cert_reqtree, &reqs, req);
-
- cert_count = 1;
- if ((xchain = SSL_get_peer_cert_chain(ssl))) {
- cert_count += sk_X509_num(xchain);
- if (cert_count > MAX_CERTS) {
- log_warnx("warn: certificate chain too long");
- goto end;
- }
- }
-
- for (i = 0; i < cert_count; ++i) {
- if (i != 0) {
- if ((x = sk_X509_value(xchain, i - 1)) == NULL) {
- log_warnx("warn: failed to retrieve certificate");
- goto end;
- }
- }
-
- cert_len[i] = i2d_X509(x, &cert_der[i]);
- if (i == 0)
- X509_free(x);
-
- if (cert_len[i] < 0) {
- log_warnx("warn: failed to encode certificate");
- goto end;
- }
-
- log_debug("debug: certificate %i: len=%d", i, cert_len[i]);
- if (cert_len[i] > (int)MAX_CERT_LEN) {
- log_warnx("warn: certificate too long");
- goto end;
- }
- }
-
- /* Send the cert chain, one cert at a time */
- for (i = 0; i < cert_count; ++i) {
- m_create(p_cert, IMSG_CERT_CERTIFICATE, req->id, 0, -1);
- m_add_data(p_cert, cert_der[i], cert_len[i]);
- m_close(p_cert);
- }
-
- /* Tell lookup process that it can start verifying, we're done */
- m_create(p_cert, IMSG_CERT_VERIFY, req->id, 0, -1);
- m_add_string(p_cert, name);
- m_add_int(p_cert, fallback);
- m_close(p_cert);
-
- ret = 1;
-
- end:
- for (i = 0; i < MAX_CERTS; ++i)
- free(cert_der[i]);
-
- if (ret == 0) {
- if (req)
- SPLAY_REMOVE(cert_reqtree, &reqs, req);
- free(req);
- cb(arg, CERT_ERROR);
- }
-
- return ret;
-}
-
-
-void
-cert_dispatch_request(struct mproc *proc, struct imsg *imsg)
-{
- struct pki *pki;
- struct session key, *s;
- const char *name;
- const void *data;
- size_t datalen;
- struct msg m;
- uint32_t reqid;
- char buf[LINE_MAX];
- int fallback;
-
- reqid = imsg->hdr.peerid;
- m_msg(&m, imsg);
-
- switch (imsg->hdr.type) {
-
- case IMSG_CERT_INIT:
- m_get_string(&m, &name);
- m_get_int(&m, &fallback);
- m_end(&m);
-
- xlowercase(buf, name, sizeof(buf));
- log_debug("debug: looking up pki \"%s\"", buf);
- pki = dict_get(env->sc_pki_dict, buf);
- if (pki == NULL && fallback)
- pki = dict_get(env->sc_pki_dict, "*");
-
- m_create(proc, IMSG_CERT_INIT, reqid, 0, -1);
- if (pki) {
- m_add_int(proc, CA_OK);
- m_add_string(proc, pki->pki_name);
- m_add_data(proc, pki->pki_cert, pki->pki_cert_len);
- } else {
- m_add_int(proc, CA_FAIL);
- m_add_string(proc, NULL);
- m_add_data(proc, NULL, 0);
- }
- m_close(proc);
- return;
-
- case IMSG_CERT_CERTIFICATE:
- m_get_data(&m, &data, &datalen);
- m_end(&m);
-
- key.id = reqid;
- key.proc = proc;
- s = SPLAY_FIND(cert_sestree, &sess, &key);
- if (s == NULL) {
- s = calloc(1, sizeof(*s));
- s->proc = proc;
- s->id = reqid;
- SPLAY_INSERT(cert_sestree, &sess, s);
- }
-
- if (s->cert_count == MAX_CERTS)
- fatalx("%s: certificate chain too long", __func__);
-
- s->cert[s->cert_count] = xmemdup(data, datalen);
- s->cert_len[s->cert_count] = datalen;
- s->cert_count++;
- return;
-
- case IMSG_CERT_VERIFY:
- m_get_string(&m, &name);
- m_get_int(&m, &fallback);
- m_end(&m);
-
- key.id = reqid;
- key.proc = proc;
- s = SPLAY_FIND(cert_sestree, &sess, &key);
- if (s == NULL)
- fatalx("%s: no certificate", __func__);
-
- SPLAY_REMOVE(cert_sestree, &sess, s);
- cert_do_verify(s, name, fallback);
- return;
-
- default:
- fatalx("%s: %s", __func__, imsg_to_str(imsg->hdr.type));
- }
-}
-
-void
-cert_dispatch_result(struct mproc *proc, struct imsg *imsg)
-{
- struct request key, *req;
- struct msg m;
- const void *cert;
- const char *name;
- size_t cert_len;
- int res;
-
- key.id = imsg->hdr.peerid;
- req = SPLAY_FIND(cert_reqtree, &reqs, &key);
- if (req == NULL)
- fatalx("%s: unknown request %08x", __func__, imsg->hdr.peerid);
-
- m_msg(&m, imsg);
-
- switch (imsg->hdr.type) {
-
- case IMSG_CERT_INIT:
- m_get_int(&m, &res);
- m_get_string(&m, &name);
- m_get_data(&m, &cert, &cert_len);
- m_end(&m);
- SPLAY_REMOVE(cert_reqtree, &reqs, req);
- req->cb_get_certificate(req->arg, res, name, cert, cert_len);
- free(req);
- break;
-
- case IMSG_CERT_VERIFY:
- m_get_int(&m, &res);
- m_end(&m);
- SPLAY_REMOVE(cert_reqtree, &reqs, req);
- req->cb_verify(req->arg, res);
- free(req);
- break;
- }
-}
-
-static void
-cert_do_verify(struct session *s, const char *name, int fallback)
-{
- struct ca *ca;
- const char *cafile;
- int i, res;
-
- ca = dict_get(env->sc_ca_dict, name);
- if (ca == NULL)
- if (fallback)
- ca = dict_get(env->sc_ca_dict, "*");
- cafile = ca ? ca->ca_cert_file : CA_FILE;
-
- if (ca == NULL && !fallback)
- res = CERT_NOCA;
- else if (!cert_X509_verify(s, cafile, NULL))
- res = CERT_INVALID;
- else
- res = CERT_OK;
-
- for (i = 0; i < s->cert_count; ++i)
- free(s->cert[i]);
-
- m_create(s->proc, IMSG_CERT_VERIFY, s->id, 0, -1);
- m_add_int(s->proc, res);
- m_close(s->proc);
-
- free(s);
-}
-
-static int
-cert_X509_verify(struct session *s, const char *CAfile,
- const char *CRLfile)
-{
- X509 *x509;
- X509 *x509_tmp;
- STACK_OF(X509) *x509_chain;
- const unsigned char *d2i;
- int i, ret = 0;
- const char *errstr;
-
- x509 = NULL;
- x509_tmp = NULL;
- x509_chain = NULL;
-
- d2i = s->cert[0];
- if (d2i_X509(&x509, &d2i, s->cert_len[0]) == NULL) {
- x509 = NULL;
- goto end;
- }
-
- if (s->cert_count > 1) {
- x509_chain = sk_X509_new_null();
- for (i = 1; i < s->cert_count; ++i) {
- d2i = s->cert[i];
- if (d2i_X509(&x509_tmp, &d2i, s->cert_len[i]) == NULL)
- goto end;
- sk_X509_insert(x509_chain, x509_tmp, i);
- x509_tmp = NULL;
- }
- }
- if (!ca_X509_verify(x509, x509_chain, CAfile, NULL, &errstr))
- log_debug("debug: X509 verify: %s", errstr);
- else
- ret = 1;
-
-end:
- X509_free(x509);
- X509_free(x509_tmp);
- if (x509_chain)
- sk_X509_pop_free(x509_chain, X509_free);
-
- return ret;
-}
-
-static int
-request_cmp(struct request *a, struct request *b)
-{
- if (a->id < b->id)
- return -1;
- if (a->id > b->id)
- return 1;
- return 0;
-}
-
-SPLAY_GENERATE(cert_reqtree, request, entry, request_cmp);
-
-static int
-session_cmp(struct session *a, struct session *b)
-{
- if (a->id < b->id)
- return -1;
- if (a->id > b->id)
- return 1;
- if (a->proc < b->proc)
- return -1;
- if (a->proc > b->proc)
- return 1;
- return 0;
-}
-
-SPLAY_GENERATE(cert_sestree, session, entry, session_cmp);
diff --git a/smtpd/compress_backend.c b/smtpd/compress_backend.c
deleted file mode 100644
index 1b974662..00000000
--- a/smtpd/compress_backend.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/* $OpenBSD: compress_backend.c,v 1.9 2015/01/20 17:37:54 deraadt Exp $ */
-
-/*
- * Copyright (c) 2012 Charles Longeau <chl@openbsd.org>
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-
-#define BUFFER_SIZE 16364
-
-extern struct compress_backend compress_gzip;
-
-struct compress_backend *
-compress_backend_lookup(const char *name)
-{
- if (!strcmp(name, "gzip"))
- return &compress_gzip;
-
- return NULL;
-}
-
-size_t
-compress_chunk(void *ib, size_t ibsz, void *ob, size_t obsz)
-{
- return (env->sc_comp->compress_chunk(ib, ibsz, ob, obsz));
-}
-
-size_t
-uncompress_chunk(void *ib, size_t ibsz, void *ob, size_t obsz)
-{
- return (env->sc_comp->uncompress_chunk(ib, ibsz, ob, obsz));
-}
-
-int
-compress_file(FILE *ifile, FILE *ofile)
-{
- return (env->sc_comp->compress_file(ifile, ofile));
-}
-
-int
-uncompress_file(FILE *ifile, FILE *ofile)
-{
- return (env->sc_comp->uncompress_file(ifile, ofile));
-}
diff --git a/smtpd/compress_gzip.c b/smtpd/compress_gzip.c
deleted file mode 100644
index dd60aeec..00000000
--- a/smtpd/compress_gzip.c
+++ /dev/null
@@ -1,186 +0,0 @@
-/* $OpenBSD: compress_gzip.c,v 1.10 2015/12/28 22:08:30 jung Exp $ */
-
-/*
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2012 Charles Longeau <chl@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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include <zlib.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-
-#define GZIP_BUFFER_SIZE 16384
-
-
-static size_t compress_gzip_chunk(void *, size_t, void *, size_t);
-static size_t uncompress_gzip_chunk(void *, size_t, void *, size_t);
-static int compress_gzip_file(FILE *, FILE *);
-static int uncompress_gzip_file(FILE *, FILE *);
-
-
-struct compress_backend compress_gzip = {
- compress_gzip_chunk,
- uncompress_gzip_chunk,
-
- compress_gzip_file,
- uncompress_gzip_file,
-};
-
-static size_t
-compress_gzip_chunk(void *ib, size_t ibsz, void *ob, size_t obsz)
-{
- z_stream *strm;
- size_t ret = 0;
-
- if ((strm = calloc(1, sizeof *strm)) == NULL)
- return 0;
-
- strm->zalloc = Z_NULL;
- strm->zfree = Z_NULL;
- strm->opaque = Z_NULL;
- if (deflateInit2(strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
- (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK)
- goto end;
-
- strm->avail_in = ibsz;
- strm->next_in = (unsigned char *)ib;
- strm->avail_out = obsz;
- strm->next_out = (unsigned char *)ob;
- if (deflate(strm, Z_FINISH) != Z_STREAM_END)
- goto end;
-
- ret = strm->total_out;
-
-end:
- deflateEnd(strm);
- free(strm);
- return ret;
-}
-
-
-static size_t
-uncompress_gzip_chunk(void *ib, size_t ibsz, void *ob, size_t obsz)
-{
- z_stream *strm;
- size_t ret = 0;
-
- if ((strm = calloc(1, sizeof *strm)) == NULL)
- return 0;
-
- strm->zalloc = Z_NULL;
- strm->zfree = Z_NULL;
- strm->opaque = Z_NULL;
- strm->avail_in = 0;
- strm->next_in = Z_NULL;
-
- if (inflateInit2(strm, (15+16)) != Z_OK)
- goto end;
-
- strm->avail_in = ibsz;
- strm->next_in = (unsigned char *)ib;
- strm->avail_out = obsz;
- strm->next_out = (unsigned char *)ob;
-
- if (inflate(strm, Z_FINISH) != Z_STREAM_END)
- goto end;
-
- ret = strm->total_out;
-
-end:
- deflateEnd(strm);
- free(strm);
- return ret;
-}
-
-
-static int
-compress_gzip_file(FILE *in, FILE *out)
-{
- gzFile gzf;
- char ibuf[GZIP_BUFFER_SIZE];
- int r, w;
- int ret = 0;
-
- if (in == NULL || out == NULL)
- return (0);
-
- gzf = gzdopen(fileno(out), "wb");
- if (gzf == NULL)
- return (0);
-
- while ((r = fread(ibuf, 1, GZIP_BUFFER_SIZE, in)) != 0) {
- if ((w = gzwrite(gzf, ibuf, r)) != r)
- goto end;
- }
- if (!feof(in))
- goto end;
-
- ret = 1;
-
-end:
- gzclose(gzf);
- return (ret);
-}
-
-
-static int
-uncompress_gzip_file(FILE *in, FILE *out)
-{
- gzFile gzf;
- char obuf[GZIP_BUFFER_SIZE];
- int r, w;
- int ret = 0;
-
- if (in == NULL || out == NULL)
- return (0);
-
- gzf = gzdopen(fileno(in), "r");
- if (gzf == NULL)
- return (0);
-
- while ((r = gzread(gzf, obuf, sizeof(obuf))) > 0) {
- if ((w = fwrite(obuf, r, 1, out)) != 1)
- goto end;
- }
- if (!gzeof(gzf))
- goto end;
-
- ret = 1;
-
-end:
- gzclose(gzf);
- return (ret);
-}
diff --git a/smtpd/config.c b/smtpd/config.c
deleted file mode 100644
index 8fe983d6..00000000
--- a/smtpd/config.c
+++ /dev/null
@@ -1,350 +0,0 @@
-/* $OpenBSD: config.c,v 1.51 2019/12/18 10:00:39 gilles Exp $ */
-
-/*
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/resource.h>
-
-#include <event.h>
-#include <ifaddrs.h>
-#include <imsg.h>
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <openssl/ssl.h>
-
-#include "smtpd.h"
-#include "log.h"
-#include "ssl.h"
-
-void set_local(struct smtpd *, const char *);
-void set_localaddrs(struct smtpd *, struct table *);
-
-struct smtpd *
-config_default(void)
-{
- struct smtpd *conf = NULL;
- struct mta_limits *limits = NULL;
- struct table *t = NULL;
- char hostname[HOST_NAME_MAX+1];
-
- if (getmailname(hostname, sizeof hostname) == -1)
- return NULL;
-
- if ((conf = calloc(1, sizeof(*conf))) == NULL)
- return conf;
-
- (void)strlcpy(conf->sc_hostname, hostname, sizeof(conf->sc_hostname));
-
- conf->sc_maxsize = DEFAULT_MAX_BODY_SIZE;
- conf->sc_subaddressing_delim = SUBADDRESSING_DELIMITER;
- conf->sc_ttl = SMTPD_QUEUE_EXPIRY;
- conf->sc_srs_ttl = SMTPD_QUEUE_EXPIRY / 86400;
-
- conf->sc_mta_max_deferred = 100;
- conf->sc_scheduler_max_inflight = 5000;
- conf->sc_scheduler_max_schedule = 10;
- conf->sc_scheduler_max_evp_batch_size = 256;
- conf->sc_scheduler_max_msg_batch_size = 1024;
-
- conf->sc_session_max_rcpt = 1000;
- conf->sc_session_max_mails = 100;
-
- conf->sc_mda_max_session = 50;
- conf->sc_mda_max_user_session = 7;
- conf->sc_mda_task_hiwat = 50;
- conf->sc_mda_task_lowat = 30;
- conf->sc_mda_task_release = 10;
-
- /* Report mails delayed for more than 4 hours */
- conf->sc_bounce_warn[0] = 3600 * 4;
-
- conf->sc_tables_dict = calloc(1, sizeof(*conf->sc_tables_dict));
- conf->sc_rules = calloc(1, sizeof(*conf->sc_rules));
- conf->sc_dispatchers = calloc(1, sizeof(*conf->sc_dispatchers));
- conf->sc_listeners = calloc(1, sizeof(*conf->sc_listeners));
- conf->sc_ca_dict = calloc(1, sizeof(*conf->sc_ca_dict));
- conf->sc_pki_dict = calloc(1, sizeof(*conf->sc_pki_dict));
- conf->sc_ssl_dict = calloc(1, sizeof(*conf->sc_ssl_dict));
- conf->sc_limits_dict = calloc(1, sizeof(*conf->sc_limits_dict));
- conf->sc_mda_wrappers = calloc(1, sizeof(*conf->sc_mda_wrappers));
- conf->sc_filter_processes_dict = calloc(1, sizeof(*conf->sc_filter_processes_dict));
- conf->sc_dispatcher_bounce = calloc(1, sizeof(*conf->sc_dispatcher_bounce));
- conf->sc_filters_dict = calloc(1, sizeof(*conf->sc_filters_dict));
- limits = calloc(1, sizeof(*limits));
-
- if (conf->sc_tables_dict == NULL ||
- conf->sc_rules == NULL ||
- conf->sc_dispatchers == NULL ||
- conf->sc_listeners == NULL ||
- conf->sc_ca_dict == NULL ||
- conf->sc_pki_dict == NULL ||
- conf->sc_ssl_dict == NULL ||
- conf->sc_limits_dict == NULL ||
- conf->sc_mda_wrappers == NULL ||
- conf->sc_filter_processes_dict == NULL ||
- conf->sc_dispatcher_bounce == NULL ||
- conf->sc_filters_dict == NULL ||
- limits == NULL)
- goto error;
-
- dict_init(conf->sc_dispatchers);
- dict_init(conf->sc_mda_wrappers);
- dict_init(conf->sc_ca_dict);
- dict_init(conf->sc_pki_dict);
- dict_init(conf->sc_ssl_dict);
- dict_init(conf->sc_tables_dict);
- dict_init(conf->sc_limits_dict);
- dict_init(conf->sc_filter_processes_dict);
-
- limit_mta_set_defaults(limits);
-
- dict_xset(conf->sc_limits_dict, "default", limits);
-
- TAILQ_INIT(conf->sc_listeners);
- TAILQ_INIT(conf->sc_rules);
-
-
- /* bounce dispatcher */
- conf->sc_dispatcher_bounce->type = DISPATCHER_BOUNCE;
-
- /*
- * declare special "localhost", "anyhost" and "localnames" tables
- */
- set_local(conf, conf->sc_hostname);
-
- t = table_create(conf, "static", "<anydestination>", NULL);
- table_add(t, "*", NULL);
-
- hostname[strcspn(hostname, ".")] = '\0';
- if (strcmp(conf->sc_hostname, hostname) != 0)
- table_add(t, hostname, NULL);
-
- table_create(conf, "getpwnam", "<getpwnam>", NULL);
-
- return conf;
-
-error:
- free(conf->sc_tables_dict);
- free(conf->sc_rules);
- free(conf->sc_dispatchers);
- free(conf->sc_listeners);
- free(conf->sc_ca_dict);
- free(conf->sc_pki_dict);
- free(conf->sc_ssl_dict);
- free(conf->sc_limits_dict);
- free(conf->sc_mda_wrappers);
- free(conf->sc_filter_processes_dict);
- free(conf->sc_dispatcher_bounce);
- free(conf->sc_filters_dict);
- free(limits);
- free(conf);
- return NULL;
-}
-
-void
-set_local(struct smtpd *conf, const char *hostname)
-{
- struct table *t;
-
- t = table_create(conf, "static", "<localnames>", NULL);
- table_add(t, "localhost", NULL);
- table_add(t, hostname, NULL);
-
- set_localaddrs(conf, t);
-}
-
-void
-set_localaddrs(struct smtpd *conf, struct table *localnames)
-{
- struct ifaddrs *ifap, *p;
- struct sockaddr_storage ss;
- struct sockaddr_in *sain;
- struct sockaddr_in6 *sin6;
- struct table *t;
- char buf[NI_MAXHOST + 5];
-
- t = table_create(conf, "static", "<anyhost>", NULL);
- table_add(t, "local", NULL);
- table_add(t, "0.0.0.0/0", NULL);
- table_add(t, "::/0", NULL);
-
- if (getifaddrs(&ifap) == -1)
- fatal("getifaddrs");
-
- t = table_create(conf, "static", "<localhost>", NULL);
- table_add(t, "local", NULL);
-
- for (p = ifap; p != NULL; p = p->ifa_next) {
- if (p->ifa_addr == NULL)
- continue;
- switch (p->ifa_addr->sa_family) {
- case AF_INET:
- sain = (struct sockaddr_in *)&ss;
- *sain = *(struct sockaddr_in *)p->ifa_addr;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- sain->sin_len = sizeof(struct sockaddr_in);
-#endif
- table_add(t, ss_to_text(&ss), NULL);
- table_add(localnames, ss_to_text(&ss), NULL);
- (void)snprintf(buf, sizeof buf, "[%s]", ss_to_text(&ss));
- table_add(localnames, buf, NULL);
- break;
-
- case AF_INET6:
- sin6 = (struct sockaddr_in6 *)&ss;
- *sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
-#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
- sin6->sin6_len = sizeof(struct sockaddr_in6);
-#endif
- table_add(t, ss_to_text(&ss), NULL);
- table_add(localnames, ss_to_text(&ss), NULL);
- (void)snprintf(buf, sizeof buf, "[%s]", ss_to_text(&ss));
- table_add(localnames, buf, NULL);
- (void)snprintf(buf, sizeof buf, "[ipv6:%s]", ss_to_text(&ss));
- table_add(localnames, buf, NULL);
- break;
- }
- }
-
- freeifaddrs(ifap);
-}
-
-void
-purge_config(uint8_t what)
-{
- struct dispatcher *d;
- struct listener *l;
- struct table *t;
- struct rule *r;
- struct pki *p;
- const char *k;
- void *iter_dict;
-
- if (what & PURGE_LISTENERS) {
- while ((l = TAILQ_FIRST(env->sc_listeners)) != NULL) {
- TAILQ_REMOVE(env->sc_listeners, l, entry);
- free(l);
- }
- free(env->sc_listeners);
- env->sc_listeners = NULL;
- }
- if (what & PURGE_TABLES) {
- while (dict_root(env->sc_tables_dict, NULL, (void **)&t))
- table_destroy(env, t);
- free(env->sc_tables_dict);
- env->sc_tables_dict = NULL;
- }
- if (what & PURGE_RULES) {
- while ((r = TAILQ_FIRST(env->sc_rules)) != NULL) {
- TAILQ_REMOVE(env->sc_rules, r, r_entry);
- free(r);
- }
- free(env->sc_rules);
- env->sc_rules = NULL;
- }
- if (what & PURGE_DISPATCHERS) {
- while (dict_poproot(env->sc_dispatchers, (void **)&d)) {
- free(d);
- }
- free(env->sc_dispatchers);
- env->sc_dispatchers = NULL;
- }
- if (what & PURGE_PKI) {
- while (dict_poproot(env->sc_pki_dict, (void **)&p)) {
- freezero(p->pki_cert, p->pki_cert_len);
- freezero(p->pki_key, p->pki_key_len);
- EVP_PKEY_free(p->pki_pkey);
- free(p);
- }
- free(env->sc_pki_dict);
- env->sc_pki_dict = NULL;
- } else if (what & PURGE_PKI_KEYS) {
- iter_dict = NULL;
- while (dict_iter(env->sc_pki_dict, &iter_dict, &k,
- (void **)&p)) {
- freezero(p->pki_cert, p->pki_cert_len);
- p->pki_cert = NULL;
- freezero(p->pki_key, p->pki_key_len);
- p->pki_key = NULL;
- EVP_PKEY_free(p->pki_pkey);
- p->pki_pkey = NULL;
- }
- }
-}
-
-#ifndef CONFIG_MINIMUM
-
-void
-config_process(enum smtp_proc_type proc)
-{
- struct rlimit rl;
-
- smtpd_process = proc;
- setproctitle("%s", proc_title(proc));
-
- if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
- fatal("fdlimit: getrlimit");
- rl.rlim_cur = rl.rlim_max;
- if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
- if (errno != EINVAL)
- fatal("fdlimit: setrlimit");
-}
-
-void
-config_peer(enum smtp_proc_type proc)
-{
- struct mproc *p;
-
- if (proc == smtpd_process)
- fatal("config_peers: cannot peer with oneself");
-
- if (proc == PROC_CONTROL)
- p = p_control;
- else if (proc == PROC_LKA)
- p = p_lka;
- else if (proc == PROC_PARENT)
- p = p_parent;
- else if (proc == PROC_QUEUE)
- p = p_queue;
- else if (proc == PROC_SCHEDULER)
- p = p_scheduler;
- else if (proc == PROC_PONY)
- p = p_pony;
- else if (proc == PROC_CA)
- p = p_ca;
- else
- fatalx("bad peer");
-
- mproc_enable(p);
-}
-
-#else
-
-void config_process(enum smtp_proc_type proc) {}
-void config_peer(enum smtp_proc_type proc) {}
-
-#endif
diff --git a/smtpd/control.c b/smtpd/control.c
deleted file mode 100644
index 0e35bbd1..00000000
--- a/smtpd/control.c
+++ /dev/null
@@ -1,817 +0,0 @@
-/* $OpenBSD: control.c,v 1.123 2018/05/31 21:06:12 gilles Exp $ */
-
-/*
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2003, 2004 Henning Brauer <henning@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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <grp.h> /* needed for setgroups */
-#include <imsg.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define CONTROL_BACKLOG 5
-
-struct ctl_conn {
- uint32_t id;
- uint8_t flags;
-#define CTL_CONN_NOTIFY 0x01
- struct mproc mproc;
- uid_t euid;
- gid_t egid;
-};
-
-struct {
- struct event ev;
- int fd;
-} control_state;
-
-static void control_imsg(struct mproc *, struct imsg *);
-static void control_shutdown(void);
-static void control_listen(void);
-static void control_accept(int, short, void *);
-static void control_close(struct ctl_conn *);
-static void control_dispatch_ext(struct mproc *, struct imsg *);
-static void control_digest_update(const char *, size_t, int);
-static void control_broadcast_verbose(int, int);
-
-static struct stat_backend *stat_backend = NULL;
-extern const char *backend_stat;
-
-static uint64_t connid = 0;
-static struct tree ctl_conns;
-static struct tree ctl_count;
-static struct stat_digest digest;
-
-#define CONTROL_FD_RESERVE 5
-#define CONTROL_MAXCONN_PER_CLIENT 32
-
-static void
-control_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct ctl_conn *c;
- struct stat_value val;
- struct msg m;
- const char *key;
- const void *data;
- size_t sz;
-
- if (imsg == NULL) {
- if (p->proc != PROC_CLIENT)
- control_shutdown();
- return;
- }
-
- switch (imsg->hdr.type) {
- case IMSG_CTL_OK:
- case IMSG_CTL_FAIL:
- case IMSG_CTL_LIST_MESSAGES:
- case IMSG_CTL_LIST_ENVELOPES:
- case IMSG_CTL_DISCOVER_EVPID:
- case IMSG_CTL_DISCOVER_MSGID:
- case IMSG_CTL_MTA_SHOW_HOSTS:
- case IMSG_CTL_MTA_SHOW_RELAYS:
- case IMSG_CTL_MTA_SHOW_ROUTES:
- case IMSG_CTL_MTA_SHOW_HOSTSTATS:
- case IMSG_CTL_MTA_SHOW_BLOCK:
- c = tree_get(&ctl_conns, imsg->hdr.peerid);
- if (c == NULL)
- return;
- imsg->hdr.peerid = 0;
- m_forward(&c->mproc, imsg);
- return;
-
- case IMSG_CTL_SMTP_SESSION:
- c = tree_get(&ctl_conns, imsg->hdr.peerid);
- if (c == NULL)
- return;
- m_compose(&c->mproc, IMSG_CTL_OK, 0, 0, imsg->fd, NULL, 0);
- return;
-
- case IMSG_STAT_INCREMENT:
- m_msg(&m, imsg);
- m_get_string(&m, &key);
- m_get_data(&m, &data, &sz);
- m_end(&m);
- if (sz != sizeof(val))
- fatalx("control: IMSG_STAT_INCREMENT size mismatch");
- memmove(&val, data, sz);
- if (stat_backend)
- stat_backend->increment(key, val.u.counter);
- control_digest_update(key, val.u.counter, 1);
- return;
-
- case IMSG_STAT_DECREMENT:
- m_msg(&m, imsg);
- m_get_string(&m, &key);
- m_get_data(&m, &data, &sz);
- m_end(&m);
- if (sz != sizeof(val))
- fatalx("control: IMSG_STAT_DECREMENT size mismatch");
- memmove(&val, data, sz);
- if (stat_backend)
- stat_backend->decrement(key, val.u.counter);
- control_digest_update(key, val.u.counter, 0);
- return;
-
- case IMSG_STAT_SET:
- m_msg(&m, imsg);
- m_get_string(&m, &key);
- m_get_data(&m, &data, &sz);
- m_end(&m);
- if (sz != sizeof(val))
- fatalx("control: IMSG_STAT_SET size mismatch");
- memmove(&val, data, sz);
- if (stat_backend)
- stat_backend->set(key, &val);
- return;
- }
-
- errx(1, "control_imsg: unexpected %s imsg",
- imsg_to_str(imsg->hdr.type));
-}
-
-int
-control_create_socket(void)
-{
- struct sockaddr_un s_un;
- int fd;
- mode_t old_umask;
-
- if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
- fatal("control: socket");
-
- memset(&s_un, 0, sizeof(s_un));
- s_un.sun_family = AF_UNIX;
- if (strlcpy(s_un.sun_path, SMTPD_SOCKET,
- sizeof(s_un.sun_path)) >= sizeof(s_un.sun_path))
- fatal("control: socket name too long");
-
- if (connect(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == 0)
- fatalx("control socket already listening");
-
- if (unlink(SMTPD_SOCKET) == -1)
- if (errno != ENOENT)
- fatal("control: cannot unlink socket");
-
- old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
- if (bind(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) {
- (void)umask(old_umask);
- fatal("control: bind");
- }
- (void)umask(old_umask);
-
- if (chmod(SMTPD_SOCKET,
- S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) {
- (void)unlink(SMTPD_SOCKET);
- fatal("control: chmod");
- }
-
- io_set_nonblocking(fd);
- control_state.fd = fd;
-
- return fd;
-}
-
-int
-control(void)
-{
- struct passwd *pw;
-
- purge_config(PURGE_EVERYTHING);
-
- if ((pw = getpwnam(SMTPD_USER)) == NULL)
- fatalx("unknown user " SMTPD_USER);
-
- stat_backend = env->sc_stat;
- stat_backend->init();
-
- if (chroot(PATH_CHROOT) == -1)
- fatal("control: chroot");
- if (chdir("/") == -1)
- fatal("control: chdir(\"/\")");
-
- config_process(PROC_CONTROL);
-
- if (setgroups(1, &pw->pw_gid) ||
- setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
- setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
- fatal("control: cannot drop privileges");
-
- imsg_callback = control_imsg;
- event_init();
-
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, SIG_IGN);
- signal(SIGPIPE, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-
- tree_init(&ctl_conns);
- tree_init(&ctl_count);
-
- memset(&digest, 0, sizeof digest);
- digest.startup = time(NULL);
-
- config_peer(PROC_SCHEDULER);
- config_peer(PROC_QUEUE);
- config_peer(PROC_PARENT);
- config_peer(PROC_LKA);
- config_peer(PROC_PONY);
- config_peer(PROC_CA);
-
- control_listen();
-
-#if HAVE_PLEDGE
- if (pledge("stdio unix recvfd sendfd", NULL) == -1)
- err(1, "pledge");
-#endif
-
- event_dispatch();
- fatalx("exited event loop");
-
- return (0);
-}
-
-static void
-control_shutdown(void)
-{
- log_debug("debug: control agent exiting");
- _exit(0);
-}
-
-static void
-control_listen(void)
-{
- if (listen(control_state.fd, CONTROL_BACKLOG) == -1)
- fatal("control_listen");
-
- event_set(&control_state.ev, control_state.fd, EV_READ|EV_PERSIST,
- control_accept, NULL);
- event_add(&control_state.ev, NULL);
-}
-
-/* ARGSUSED */
-static void
-control_accept(int listenfd, short event, void *arg)
-{
- int connfd;
- socklen_t len;
- struct sockaddr_un s_un;
- struct ctl_conn *c;
- size_t *count;
- uid_t euid;
- gid_t egid;
-
-#if defined(HAVE_GETDTABLESIZE) && defined(HAVE_GETDTABLECOUNT)
- if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE)
- goto pause;
-#else
- if (available_fds(CONTROL_FD_RESERVE))
- goto pause;
-#endif
-
- len = sizeof(s_un);
- if ((connfd = accept(listenfd, (struct sockaddr *)&s_un, &len)) == -1) {
- if (errno == ENFILE || errno == EMFILE)
- goto pause;
- if (errno == EINTR || errno == EWOULDBLOCK ||
- errno == ECONNABORTED)
- return;
- fatal("control_accept: accept");
- }
-
- io_set_nonblocking(connfd);
-
- if (getpeereid(connfd, &euid, &egid) == -1)
- fatal("getpeereid");
-
- count = tree_get(&ctl_count, euid);
- if (count == NULL) {
- count = xcalloc(1, sizeof *count);
- tree_xset(&ctl_count, euid, count);
- }
-
- if (*count == CONTROL_MAXCONN_PER_CLIENT) {
- close(connfd);
- log_warnx("warn: too many connections to control socket "
- "from user with uid %lu", (unsigned long int)euid);
- return;
- }
- (*count)++;
-
- do {
- ++connid;
- } while (tree_get(&ctl_conns, connid));
-
- c = xcalloc(1, sizeof(*c));
- c->euid = euid;
- c->egid = egid;
- c->id = connid;
- c->mproc.proc = PROC_CLIENT;
- c->mproc.handler = control_dispatch_ext;
- c->mproc.data = c;
- if ((c->mproc.name = strdup(proc_title(c->mproc.proc))) == NULL)
- fatal("strdup");
- mproc_init(&c->mproc, connfd);
- mproc_enable(&c->mproc);
- tree_xset(&ctl_conns, c->id, c);
-
- stat_backend->increment("control.session", 1);
- return;
-
-pause:
- log_warnx("warn: ctl client limit hit, disabling new connections");
- event_del(&control_state.ev);
-}
-
-static void
-control_close(struct ctl_conn *c)
-{
- size_t *count;
-
- count = tree_xget(&ctl_count, c->euid);
- (*count)--;
- if (*count == 0) {
- tree_xpop(&ctl_count, c->euid);
- free(count);
- }
- tree_xpop(&ctl_conns, c->id);
- mproc_clear(&c->mproc);
- free(c);
-
- stat_backend->decrement("control.session", 1);
-
-#if defined(HAVE_GETDTABLESIZE) && defined(HAVE_GETDTABLECOUNT)
- if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE)
- return;
-#else
- if (available_fds(CONTROL_FD_RESERVE))
- return;
-#endif
-
- if (!event_pending(&control_state.ev, EV_READ, NULL)) {
- log_warnx("warn: re-enabling ctl connections");
- event_add(&control_state.ev, NULL);
- }
-}
-
-static void
-control_digest_update(const char *key, size_t value, int incr)
-{
- size_t *p;
-
- p = NULL;
-
- if (!strcmp(key, "smtp.session")) {
- if (incr)
- p = &digest.clt_connect;
- else
- digest.clt_disconnect += value;
- }
- else if (!strcmp(key, "scheduler.envelope")) {
- if (incr)
- p = &digest.evp_enqueued;
- else
- digest.evp_dequeued += value;
- }
- else if (!strcmp(key, "scheduler.envelope.expired"))
- p = &digest.evp_expired;
- else if (!strcmp(key, "scheduler.envelope.removed"))
- p = &digest.evp_removed;
- else if (!strcmp(key, "scheduler.delivery.ok"))
- p = &digest.dlv_ok;
- else if (!strcmp(key, "scheduler.delivery.permfail"))
- p = &digest.dlv_permfail;
- else if (!strcmp(key, "scheduler.delivery.tempfail"))
- p = &digest.dlv_tempfail;
- else if (!strcmp(key, "scheduler.delivery.loop"))
- p = &digest.dlv_loop;
-
- else if (!strcmp(key, "queue.bounce"))
- p = &digest.evp_bounce;
-
- if (p) {
- if (incr)
- *p = *p + value;
- else
- *p = *p - value;
- }
-}
-
-/* ARGSUSED */
-static void
-control_dispatch_ext(struct mproc *p, struct imsg *imsg)
-{
- struct sockaddr_storage ss;
- struct ctl_conn *c;
- int v;
- struct stat_kv *kvp;
- char *key;
- struct stat_value val;
- size_t len;
- uint64_t evpid;
- uint32_t msgid;
-
- c = p->data;
-
- if (imsg == NULL) {
- control_close(c);
- return;
- }
-
- if (imsg->hdr.peerid != IMSG_VERSION) {
- m_compose(p, IMSG_CTL_FAIL, IMSG_VERSION, 0, -1, NULL, 0);
- return;
- }
-
- switch (imsg->hdr.type) {
- case IMSG_CTL_SMTP_SESSION:
- if (env->sc_flags & SMTPD_SMTP_PAUSED) {
- m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
- return;
- }
- m_compose(p_pony, IMSG_CTL_SMTP_SESSION, c->id, 0, -1,
- &c->euid, sizeof(c->euid));
- return;
-
- case IMSG_CTL_GET_DIGEST:
- if (c->euid)
- goto badcred;
- digest.timestamp = time(NULL);
- m_compose(p, IMSG_CTL_GET_DIGEST, 0, 0, -1, &digest, sizeof digest);
- return;
-
- case IMSG_CTL_GET_STATS:
- if (c->euid)
- goto badcred;
- kvp = imsg->data;
- if (!stat_backend->iter(&kvp->iter, &key, &val))
- kvp->iter = NULL;
- else {
- (void)strlcpy(kvp->key, key, sizeof kvp->key);
- kvp->val = val;
- }
- m_compose(p, IMSG_CTL_GET_STATS, 0, 0, -1, kvp, sizeof *kvp);
- return;
-
- case IMSG_CTL_VERBOSE:
- if (c->euid)
- goto badcred;
-
- if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v))
- goto badcred;
-
- memcpy(&v, imsg->data, sizeof(v));
- log_trace_verbose(v);
-
- control_broadcast_verbose(IMSG_CTL_VERBOSE, v);
-
- m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_TRACE_ENABLE:
- if (c->euid)
- goto badcred;
-
- if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v))
- goto badcred;
-
- memcpy(&v, imsg->data, sizeof(v));
- tracing |= v;
- log_trace_verbose(tracing);
-
- control_broadcast_verbose(IMSG_CTL_VERBOSE, tracing);
-
- m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_TRACE_DISABLE:
- if (c->euid)
- goto badcred;
-
- if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v))
- goto badcred;
-
- memcpy(&v, imsg->data, sizeof(v));
- tracing &= ~v;
- log_trace_verbose(tracing);
-
- control_broadcast_verbose(IMSG_CTL_VERBOSE, tracing);
-
- m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_PROFILE_ENABLE:
- if (c->euid)
- goto badcred;
-
- if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v))
- goto badcred;
-
- memcpy(&v, imsg->data, sizeof(v));
- profiling |= v;
-
- control_broadcast_verbose(IMSG_CTL_PROFILE, profiling);
-
- m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_PROFILE_DISABLE:
- if (c->euid)
- goto badcred;
-
- if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v))
- goto badcred;
-
- memcpy(&v, imsg->data, sizeof(v));
- profiling &= ~v;
-
- control_broadcast_verbose(IMSG_CTL_PROFILE, profiling);
-
- m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_PAUSE_EVP:
- if (c->euid)
- goto badcred;
-
- imsg->hdr.peerid = c->id;
- m_forward(p_scheduler, imsg);
- return;
-
- case IMSG_CTL_PAUSE_MDA:
- if (c->euid)
- goto badcred;
-
- if (env->sc_flags & SMTPD_MDA_PAUSED) {
- m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
- return;
- }
- log_info("info: mda paused");
- env->sc_flags |= SMTPD_MDA_PAUSED;
- m_compose(p_queue, IMSG_CTL_PAUSE_MDA, 0, 0, -1, NULL, 0);
- m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_PAUSE_MTA:
- if (c->euid)
- goto badcred;
-
- if (env->sc_flags & SMTPD_MTA_PAUSED) {
- m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
- return;
- }
- log_info("info: mta paused");
- env->sc_flags |= SMTPD_MTA_PAUSED;
- m_compose(p_queue, IMSG_CTL_PAUSE_MTA, 0, 0, -1, NULL, 0);
- m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_PAUSE_SMTP:
- if (c->euid)
- goto badcred;
-
- if (env->sc_flags & SMTPD_SMTP_PAUSED) {
- m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
- return;
- }
- log_info("info: smtp paused");
- env->sc_flags |= SMTPD_SMTP_PAUSED;
- m_compose(p_pony, IMSG_CTL_PAUSE_SMTP, 0, 0, -1, NULL, 0);
- m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_RESUME_EVP:
- if (c->euid)
- goto badcred;
-
- imsg->hdr.peerid = c->id;
- m_forward(p_scheduler, imsg);
- return;
-
- case IMSG_CTL_RESUME_MDA:
- if (c->euid)
- goto badcred;
-
- if (!(env->sc_flags & SMTPD_MDA_PAUSED)) {
- m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
- return;
- }
- log_info("info: mda resumed");
- env->sc_flags &= ~SMTPD_MDA_PAUSED;
- m_compose(p_queue, IMSG_CTL_RESUME_MDA, 0, 0, -1, NULL, 0);
- m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_RESUME_MTA:
- if (c->euid)
- goto badcred;
-
- if (!(env->sc_flags & SMTPD_MTA_PAUSED)) {
- m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
- return;
- }
- log_info("info: mta resumed");
- env->sc_flags &= ~SMTPD_MTA_PAUSED;
- m_compose(p_queue, IMSG_CTL_RESUME_MTA, 0, 0, -1, NULL, 0);
- m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_RESUME_SMTP:
- if (c->euid)
- goto badcred;
-
- if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) {
- m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
- return;
- }
- log_info("info: smtp resumed");
- env->sc_flags &= ~SMTPD_SMTP_PAUSED;
- m_forward(p_pony, imsg);
- m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_RESUME_ROUTE:
- if (c->euid)
- goto badcred;
-
- m_forward(p_pony, imsg);
- m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_LIST_MESSAGES:
- if (c->euid)
- goto badcred;
- m_compose(p_scheduler, IMSG_CTL_LIST_MESSAGES, c->id, 0, -1,
- imsg->data, imsg->hdr.len - sizeof(imsg->hdr));
- return;
-
- case IMSG_CTL_LIST_ENVELOPES:
- if (c->euid)
- goto badcred;
- m_compose(p_scheduler, IMSG_CTL_LIST_ENVELOPES, c->id, 0, -1,
- imsg->data, imsg->hdr.len - sizeof(imsg->hdr));
- return;
-
- case IMSG_CTL_MTA_SHOW_HOSTS:
- case IMSG_CTL_MTA_SHOW_RELAYS:
- case IMSG_CTL_MTA_SHOW_ROUTES:
- case IMSG_CTL_MTA_SHOW_HOSTSTATS:
- case IMSG_CTL_MTA_SHOW_BLOCK:
- if (c->euid)
- goto badcred;
-
- imsg->hdr.peerid = c->id;
- m_forward(p_pony, imsg);
- return;
-
- case IMSG_CTL_SHOW_STATUS:
- if (c->euid)
- goto badcred;
-
- m_compose(p, IMSG_CTL_SHOW_STATUS, 0, 0, -1, &env->sc_flags,
- sizeof(env->sc_flags));
- return;
-
- case IMSG_CTL_MTA_BLOCK:
- case IMSG_CTL_MTA_UNBLOCK:
- if (c->euid)
- goto badcred;
-
- if (imsg->hdr.len - IMSG_HEADER_SIZE <= sizeof(ss))
- goto invalid;
- memmove(&ss, imsg->data, sizeof(ss));
- m_create(p_pony, imsg->hdr.type, c->id, 0, -1);
- m_add_sockaddr(p_pony, (struct sockaddr *)&ss);
- m_add_string(p_pony, (char *)imsg->data + sizeof(ss));
- m_close(p_pony);
- return;
-
- case IMSG_CTL_SCHEDULE:
- if (c->euid)
- goto badcred;
-
- imsg->hdr.peerid = c->id;
- m_forward(p_scheduler, imsg);
- return;
-
- case IMSG_CTL_REMOVE:
- if (c->euid)
- goto badcred;
-
- imsg->hdr.peerid = c->id;
- m_forward(p_scheduler, imsg);
- return;
-
- case IMSG_CTL_UPDATE_TABLE:
- if (c->euid)
- goto badcred;
-
- /* table name too long */
- len = strlen(imsg->data);
- if (len >= LINE_MAX)
- goto invalid;
-
- imsg->hdr.peerid = c->id;
- m_forward(p_lka, imsg);
- return;
-
- case IMSG_CTL_DISCOVER_EVPID:
- if (c->euid)
- goto badcred;
-
- if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof evpid)
- goto invalid;
-
- memmove(&evpid, imsg->data, sizeof evpid);
- m_create(p_queue, imsg->hdr.type, c->id, 0, -1);
- m_add_evpid(p_queue, evpid);
- m_close(p_queue);
- return;
-
- case IMSG_CTL_DISCOVER_MSGID:
- if (c->euid)
- goto badcred;
-
- if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof msgid)
- goto invalid;
-
- memmove(&msgid, imsg->data, sizeof msgid);
- m_create(p_queue, imsg->hdr.type, c->id, 0, -1);
- m_add_msgid(p_queue, msgid);
- m_close(p_queue);
- return;
-
- default:
- log_debug("debug: control_dispatch_ext: "
- "error handling %s imsg",
- imsg_to_str(imsg->hdr.type));
- return;
- }
-badcred:
-invalid:
- m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
-}
-
-static void
-control_broadcast_verbose(int msg, int v)
-{
- m_create(p_lka, msg, 0, 0, -1);
- m_add_int(p_lka, v);
- m_close(p_lka);
-
- m_create(p_pony, msg, 0, 0, -1);
- m_add_int(p_pony, v);
- m_close(p_pony);
-
- m_create(p_queue, msg, 0, 0, -1);
- m_add_int(p_queue, v);
- m_close(p_queue);
-
- m_create(p_ca, msg, 0, 0, -1);
- m_add_int(p_ca, v);
- m_close(p_ca);
-
- m_create(p_scheduler, msg, 0, 0, -1);
- m_add_int(p_scheduler, v);
- m_close(p_scheduler);
-
- m_create(p_parent, msg, 0, 0, -1);
- m_add_int(p_parent, v);
- m_close(p_parent);
-}
diff --git a/smtpd/crypto.c b/smtpd/crypto.c
deleted file mode 100644
index 20a422cd..00000000
--- a/smtpd/crypto.c
+++ /dev/null
@@ -1,400 +0,0 @@
-/* $OpenBSD: crypto.c,v 1.8 2019/06/28 13:32:50 deraadt Exp $ */
-
-/*
- * Copyright (c) 2013 Gilles Chehade <gilles@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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <openssl/evp.h>
-
-
-#define CRYPTO_BUFFER_SIZE 16384
-
-#define GCM_TAG_SIZE 16
-#define IV_SIZE 12
-#define KEY_SIZE 32
-
-/* bump if we ever switch from aes-256-gcm to anything else */
-#define API_VERSION 1
-
-
-int crypto_setup(const char *, size_t);
-int crypto_encrypt_file(FILE *, FILE *);
-int crypto_decrypt_file(FILE *, FILE *);
-size_t crypto_encrypt_buffer(const char *, size_t, char *, size_t);
-size_t crypto_decrypt_buffer(const char *, size_t, char *, size_t);
-
-static struct crypto_ctx {
- unsigned char key[KEY_SIZE];
-} cp;
-
-int
-crypto_setup(const char *key, size_t len)
-{
- if (len != KEY_SIZE)
- return 0;
-
- memset(&cp, 0, sizeof cp);
-
- /* openssl rand -hex 16 */
- memcpy(cp.key, key, sizeof cp.key);
-
- return 1;
-}
-
-int
-crypto_encrypt_file(FILE * in, FILE * out)
-{
- EVP_CIPHER_CTX *ctx;
- uint8_t ibuf[CRYPTO_BUFFER_SIZE];
- uint8_t obuf[CRYPTO_BUFFER_SIZE];
- uint8_t iv[IV_SIZE];
- uint8_t tag[GCM_TAG_SIZE];
- uint8_t version = API_VERSION;
- size_t r, w;
- int len;
- int ret = 0;
- struct stat sb;
-
- /* XXX - Do NOT encrypt files bigger than 64GB */
- if (fstat(fileno(in), &sb) == -1)
- return 0;
- if (sb.st_size >= 0x1000000000LL)
- return 0;
-
- /* prepend version byte*/
- if ((w = fwrite(&version, 1, sizeof version, out)) != sizeof version)
- return 0;
-
- /* generate and prepend IV */
- memset(iv, 0, sizeof iv);
- arc4random_buf(iv, sizeof iv);
- if ((w = fwrite(iv, 1, sizeof iv, out)) != sizeof iv)
- return 0;
-
- ctx = EVP_CIPHER_CTX_new();
- if (ctx == NULL)
- return 0;
-
- EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
-
- /* encrypt until end of file */
- while ((r = fread(ibuf, 1, CRYPTO_BUFFER_SIZE, in)) != 0) {
- if (!EVP_EncryptUpdate(ctx, obuf, &len, ibuf, r))
- goto end;
- if (len && (w = fwrite(obuf, len, 1, out)) != 1)
- goto end;
- }
- if (!feof(in))
- goto end;
-
- /* finalize and write last chunk if any */
- if (!EVP_EncryptFinal_ex(ctx, obuf, &len))
- goto end;
- if (len && (w = fwrite(obuf, len, 1, out)) != 1)
- goto end;
-
- /* get and append tag */
- EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, sizeof tag, tag);
- if ((w = fwrite(tag, sizeof tag, 1, out)) != 1)
- goto end;
-
- fflush(out);
- ret = 1;
-
-end:
- EVP_CIPHER_CTX_free(ctx);
- return ret;
-}
-
-int
-crypto_decrypt_file(FILE * in, FILE * out)
-{
- EVP_CIPHER_CTX *ctx;
- uint8_t ibuf[CRYPTO_BUFFER_SIZE];
- uint8_t obuf[CRYPTO_BUFFER_SIZE];
- uint8_t iv[IV_SIZE];
- uint8_t tag[GCM_TAG_SIZE];
- uint8_t version;
- size_t r, w;
- off_t sz;
- int len;
- int ret = 0;
- struct stat sb;
-
- /* input file too small to be an encrypted file */
- if (fstat(fileno(in), &sb) == -1)
- return 0;
- if (sb.st_size <= (off_t) (sizeof version + sizeof tag + sizeof iv))
- return 0;
- sz = sb.st_size;
-
- /* extract tag */
- if (fseek(in, -sizeof(tag), SEEK_END) == -1)
- return 0;
- if ((r = fread(tag, 1, sizeof tag, in)) != sizeof tag)
- return 0;
-
- if (fseek(in, 0, SEEK_SET) == -1)
- return 0;
-
- /* extract version */
- if ((r = fread(&version, 1, sizeof version, in)) != sizeof version)
- return 0;
- if (version != API_VERSION)
- return 0;
-
- /* extract IV */
- memset(iv, 0, sizeof iv);
- if ((r = fread(iv, 1, sizeof iv, in)) != sizeof iv)
- return 0;
-
- /* real ciphertext length */
- sz -= sizeof version;
- sz -= sizeof iv;
- sz -= sizeof tag;
-
- ctx = EVP_CIPHER_CTX_new();
- if (ctx == NULL)
- return 0;
-
- EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
-
- /* set expected tag */
- EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, sizeof tag, tag);
-
- /* decrypt until end of ciphertext */
- while (sz) {
- if (sz > CRYPTO_BUFFER_SIZE)
- r = fread(ibuf, 1, CRYPTO_BUFFER_SIZE, in);
- else
- r = fread(ibuf, 1, sz, in);
- if (!r)
- break;
- if (!EVP_DecryptUpdate(ctx, obuf, &len, ibuf, r))
- goto end;
- if (len && (w = fwrite(obuf, len, 1, out)) != 1)
- goto end;
- sz -= r;
- }
- if (ferror(in))
- goto end;
-
- /* finalize, write last chunk if any and perform authentication check */
- if (!EVP_DecryptFinal_ex(ctx, obuf, &len))
- goto end;
- if (len && (w = fwrite(obuf, len, 1, out)) != 1)
- goto end;
-
- fflush(out);
- ret = 1;
-
-end:
- EVP_CIPHER_CTX_free(ctx);
- return ret;
-}
-
-size_t
-crypto_encrypt_buffer(const char *in, size_t inlen, char *out, size_t outlen)
-{
- EVP_CIPHER_CTX *ctx;
- uint8_t iv[IV_SIZE];
- uint8_t tag[GCM_TAG_SIZE];
- uint8_t version = API_VERSION;
- off_t sz;
- int olen;
- int len = 0;
- int ret = 0;
-
- /* output buffer does not have enough room */
- if (outlen < inlen + sizeof version + sizeof tag + sizeof iv)
- return 0;
-
- /* input should not exceed 64GB */
- sz = inlen;
- if (sz >= 0x1000000000LL)
- return 0;
-
- /* prepend version */
- *out = version;
- len++;
-
- /* generate IV */
- memset(iv, 0, sizeof iv);
- arc4random_buf(iv, sizeof iv);
- memcpy(out + len, iv, sizeof iv);
- len += sizeof iv;
-
- ctx = EVP_CIPHER_CTX_new();
- if (ctx == NULL)
- return 0;
-
- EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
-
- /* encrypt buffer */
- if (!EVP_EncryptUpdate(ctx, out + len, &olen, in, inlen))
- goto end;
- len += olen;
-
- /* finalize and write last chunk if any */
- if (!EVP_EncryptFinal_ex(ctx, out + len, &olen))
- goto end;
- len += olen;
-
- /* get and append tag */
- EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, sizeof tag, tag);
- memcpy(out + len, tag, sizeof tag);
- ret = len + sizeof tag;
-
-end:
- EVP_CIPHER_CTX_free(ctx);
- return ret;
-}
-
-size_t
-crypto_decrypt_buffer(const char *in, size_t inlen, char *out, size_t outlen)
-{
- EVP_CIPHER_CTX *ctx;
- uint8_t iv[IV_SIZE];
- uint8_t tag[GCM_TAG_SIZE];
- int olen;
- int len = 0;
- int ret = 0;
-
- /* out does not have enough room */
- if (outlen < inlen - sizeof tag + sizeof iv)
- return 0;
-
- /* extract tag */
- memcpy(tag, in + inlen - sizeof tag, sizeof tag);
- inlen -= sizeof tag;
-
- /* check version */
- if (*in != API_VERSION)
- return 0;
- in++;
- inlen--;
-
- /* extract IV */
- memset(iv, 0, sizeof iv);
- memcpy(iv, in, sizeof iv);
- inlen -= sizeof iv;
- in += sizeof iv;
-
- ctx = EVP_CIPHER_CTX_new();
- if (ctx == NULL)
- return 0;
-
- EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, cp.key, iv);
-
- /* set expected tag */
- EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, sizeof tag, tag);
-
- /* decrypt buffer */
- if (!EVP_DecryptUpdate(ctx, out, &olen, in, inlen))
- goto end;
- len += olen;
-
- /* finalize, write last chunk if any and perform authentication check */
- if (!EVP_DecryptFinal_ex(ctx, out + len, &olen))
- goto end;
- ret = len + olen;
-
-end:
- EVP_CIPHER_CTX_free(ctx);
- return ret;
-}
-
-#if 0
-int
-main(int argc, char *argv[])
-{
- if (argc != 3) {
- printf("usage: crypto <key> <buffer>\n");
- return 1;
- }
-
- if (!crypto_setup(argv[1], strlen(argv[1]))) {
- printf("crypto_setup failed\n");
- return 1;
- }
-
- {
- char encbuffer[4096];
- size_t enclen;
- char decbuffer[4096];
- size_t declen;
-
- printf("encrypt/decrypt buffer: ");
- enclen = crypto_encrypt_buffer(argv[2], strlen(argv[2]),
- encbuffer, sizeof encbuffer);
-
- /* uncomment below to provoke integrity check failure */
- /*
- * encbuffer[13] = 0x42;
- * encbuffer[14] = 0x42;
- * encbuffer[15] = 0x42;
- * encbuffer[16] = 0x42;
- */
-
- declen = crypto_decrypt_buffer(encbuffer, enclen,
- decbuffer, sizeof decbuffer);
- if (declen != 0 && !strncmp(argv[2], decbuffer, declen))
- printf("ok\n");
- else
- printf("nope\n");
- }
-
- {
- FILE *fpin;
- FILE *fpout;
- printf("encrypt/decrypt file: ");
-
- fpin = fopen("/etc/passwd", "r");
- fpout = fopen("/tmp/passwd.enc", "w");
- if (!crypto_encrypt_file(fpin, fpout)) {
- printf("encryption failed\n");
- return 1;
- }
- fclose(fpin);
- fclose(fpout);
-
- /* uncomment below to provoke integrity check failure */
- /*
- * fpin = fopen("/tmp/passwd.enc", "a");
- * fprintf(fpin, "borken");
- * fclose(fpin);
- */
- fpin = fopen("/tmp/passwd.enc", "r");
- fpout = fopen("/tmp/passwd.dec", "w");
- if (!crypto_decrypt_file(fpin, fpout))
- printf("nope\n");
- else
- printf("ok\n");
- fclose(fpin);
- fclose(fpout);
- }
-
-
- return 0;
-}
-#endif
diff --git a/smtpd/dict.c b/smtpd/dict.c
deleted file mode 100644
index e660f0a5..00000000
--- a/smtpd/dict.c
+++ /dev/null
@@ -1,269 +0,0 @@
-/* $OpenBSD: dict.c,v 1.6 2018/12/23 16:06:24 gilles Exp $ */
-
-/*
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/tree.h>
-
-#include <err.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#include "dict.h"
-
-struct dictentry {
- SPLAY_ENTRY(dictentry) entry;
- const char *key;
- void *data;
-};
-
-static int dictentry_cmp(struct dictentry *, struct dictentry *);
-
-SPLAY_PROTOTYPE(_dict, dictentry, entry, dictentry_cmp);
-
-int
-dict_check(struct dict *d, const char *k)
-{
- struct dictentry key;
-
- key.key = k;
- return (SPLAY_FIND(_dict, &d->dict, &key) != NULL);
-}
-
-static inline struct dictentry *
-dict_alloc(const char *k, void *data)
-{
- struct dictentry *e;
- size_t s = strlen(k) + 1;
- void *t;
-
- if ((e = malloc(sizeof(*e) + s)) == NULL)
- return NULL;
-
- e->key = t = (char*)(e) + sizeof(*e);
- e->data = data;
- memmove(t, k, s);
-
- return (e);
-}
-
-void *
-dict_set(struct dict *d, const char *k, void *data)
-{
- struct dictentry *entry, key;
- char *old;
-
- key.key = k;
- if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL) {
- if ((entry = dict_alloc(k, data)) == NULL)
- err(1, "dict_set: malloc");
- SPLAY_INSERT(_dict, &d->dict, entry);
- old = NULL;
- d->count += 1;
- } else {
- old = entry->data;
- entry->data = data;
- }
-
- return (old);
-}
-
-void
-dict_xset(struct dict *d, const char * k, void *data)
-{
- struct dictentry *entry;
-
- if ((entry = dict_alloc(k, data)) == NULL)
- err(1, "dict_xset: malloc");
- if (SPLAY_INSERT(_dict, &d->dict, entry))
- errx(1, "dict_xset(%p, %s)", d, k);
- d->count += 1;
-}
-
-void *
-dict_get(struct dict *d, const char *k)
-{
- struct dictentry key, *entry;
-
- key.key = k;
- if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL)
- return (NULL);
-
- return (entry->data);
-}
-
-void *
-dict_xget(struct dict *d, const char *k)
-{
- struct dictentry key, *entry;
-
- key.key = k;
- if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL)
- errx(1, "dict_xget(%p, %s)", d, k);
-
- return (entry->data);
-}
-
-void *
-dict_pop(struct dict *d, const char *k)
-{
- struct dictentry key, *entry;
- void *data;
-
- key.key = k;
- if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL)
- return (NULL);
-
- data = entry->data;
- SPLAY_REMOVE(_dict, &d->dict, entry);
- free(entry);
- d->count -= 1;
-
- return (data);
-}
-
-void *
-dict_xpop(struct dict *d, const char *k)
-{
- struct dictentry key, *entry;
- void *data;
-
- key.key = k;
- if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL)
- errx(1, "dict_xpop(%p, %s)", d, k);
-
- data = entry->data;
- SPLAY_REMOVE(_dict, &d->dict, entry);
- free(entry);
- d->count -= 1;
-
- return (data);
-}
-
-int
-dict_poproot(struct dict *d, void **data)
-{
- struct dictentry *entry;
-
- entry = SPLAY_ROOT(&d->dict);
- if (entry == NULL)
- return (0);
- if (data)
- *data = entry->data;
- SPLAY_REMOVE(_dict, &d->dict, entry);
- free(entry);
- d->count -= 1;
-
- return (1);
-}
-
-int
-dict_root(struct dict *d, const char **k, void **data)
-{
- struct dictentry *entry;
-
- entry = SPLAY_ROOT(&d->dict);
- if (entry == NULL)
- return (0);
- if (k)
- *k = entry->key;
- if (data)
- *data = entry->data;
- return (1);
-}
-
-int
-dict_iter(struct dict *d, void **hdl, const char **k, void **data)
-{
- struct dictentry *curr = *hdl;
-
- if (curr == NULL)
- curr = SPLAY_MIN(_dict, &d->dict);
- else
- curr = SPLAY_NEXT(_dict, &d->dict, curr);
-
- if (curr) {
- *hdl = curr;
- if (k)
- *k = curr->key;
- if (data)
- *data = curr->data;
- return (1);
- }
-
- return (0);
-}
-
-int
-dict_iterfrom(struct dict *d, void **hdl, const char *kfrom, const char **k,
- void **data)
-{
- struct dictentry *curr = *hdl, key;
-
- if (curr == NULL) {
- if (kfrom == NULL)
- curr = SPLAY_MIN(_dict, &d->dict);
- else {
- key.key = kfrom;
- curr = SPLAY_FIND(_dict, &d->dict, &key);
- if (curr == NULL) {
- SPLAY_INSERT(_dict, &d->dict, &key);
- curr = SPLAY_NEXT(_dict, &d->dict, &key);
- SPLAY_REMOVE(_dict, &d->dict, &key);
- }
- }
- } else
- curr = SPLAY_NEXT(_dict, &d->dict, curr);
-
- if (curr) {
- *hdl = curr;
- if (k)
- *k = curr->key;
- if (data)
- *data = curr->data;
- return (1);
- }
-
- return (0);
-}
-
-void
-dict_merge(struct dict *dst, struct dict *src)
-{
- struct dictentry *entry;
-
- while (!SPLAY_EMPTY(&src->dict)) {
- entry = SPLAY_ROOT(&src->dict);
- SPLAY_REMOVE(_dict, &src->dict, entry);
- if (SPLAY_INSERT(_dict, &dst->dict, entry))
- errx(1, "dict_merge: duplicate");
- }
- dst->count += src->count;
- src->count = 0;
-}
-
-static int
-dictentry_cmp(struct dictentry *a, struct dictentry *b)
-{
- return strcmp(a->key, b->key);
-}
-
-SPLAY_GENERATE(_dict, dictentry, entry, dictentry_cmp);
diff --git a/smtpd/dict.h b/smtpd/dict.h
deleted file mode 100644
index c5d47e1a..00000000
--- a/smtpd/dict.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* $OpenBSD: dict.h,v 1.1 2018/12/23 16:06:24 gilles Exp $ */
-
-/*
- * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
- * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _DICT_H_
-#define _DICT_H_
-
-SPLAY_HEAD(_dict, dictentry);
-
-struct dict {
- struct _dict dict;
- size_t count;
-};
-
-
-/* dict.c */
-#define dict_init(d) do { SPLAY_INIT(&((d)->dict)); (d)->count = 0; } while(0)
-#define dict_empty(d) SPLAY_EMPTY(&((d)->dict))
-#define dict_count(d) ((d)->count)
-int dict_check(struct dict *, const char *);
-void *dict_set(struct dict *, const char *, void *);
-void dict_xset(struct dict *, const char *, void *);
-void *dict_get(struct dict *, const char *);
-void *dict_xget(struct dict *, const char *);
-void *dict_pop(struct dict *, const char *);
-void *dict_xpop(struct dict *, const char *);
-int dict_poproot(struct dict *, void **);
-int dict_root(struct dict *, const char **, void **);
-int dict_iter(struct dict *, void **, const char **, void **);
-int dict_iterfrom(struct dict *, void **, const char *, const char **, void **);
-void dict_merge(struct dict *, struct dict *);
-
-#endif
diff --git a/smtpd/dns.c b/smtpd/dns.c
deleted file mode 100644
index a3107e89..00000000
--- a/smtpd/dns.c
+++ /dev/null
@@ -1,379 +0,0 @@
-/* $OpenBSD: dns.c,v 1.89 2019/09/18 11:26:30 eric Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * Copyright (c) 2011-2014 Eric Faurot <eric@faurot.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/tree.h>
-#include <sys/queue.h>
-#include <sys/uio.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-#ifdef HAVE_ARPA_NAMESER_COMPAT_H
-#include <arpa/nameser_compat.h>
-#endif
-#include <netdb.h>
-
-#include <asr.h>
-#include <event.h>
-#include <netdb.h>
-#include <resolv.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-#include "unpack_dns.h"
-
-/* On OpenBSD, this function is not needed because we don't free addrinfo */
-#if defined(NOOP_ASR_FREEADDRINFO)
-#define asr_freeaddrinfo(x) do { } while(0);
-#endif
-
-struct dns_lookup {
- struct dns_session *session;
- char *host;
- int preference;
-};
-
-struct dns_session {
- struct mproc *p;
- uint64_t reqid;
- int type;
- char name[HOST_NAME_MAX+1];
- size_t mxfound;
- int error;
- int refcount;
-};
-
-static void dns_lookup_host(struct dns_session *, const char *, int);
-static void dns_dispatch_host(struct asr_result *, void *);
-static void dns_dispatch_mx(struct asr_result *, void *);
-static void dns_dispatch_mx_preference(struct asr_result *, void *);
-
-static int
-domainname_is_addr(const char *s, struct sockaddr *sa, socklen_t *sl)
-{
- struct addrinfo hints, *res;
- socklen_t sl2;
- size_t l;
- char buf[SMTPD_MAXDOMAINPARTSIZE];
- int i6, error;
-
- if (*s != '[')
- return (0);
-
- i6 = (strncasecmp("[IPv6:", s, 6) == 0);
- s += i6 ? 6 : 1;
-
- l = strlcpy(buf, s, sizeof(buf));
- if (l >= sizeof(buf) || l == 0 || buf[l - 1] != ']')
- return (0);
-
- buf[l - 1] = '\0';
- memset(&hints, 0, sizeof(hints));
- hints.ai_flags = AI_NUMERICHOST;
- hints.ai_socktype = SOCK_STREAM;
- if (i6)
- hints.ai_family = AF_INET6;
-
- res = NULL;
- if ((error = getaddrinfo(buf, NULL, &hints, &res))) {
- log_warnx("getaddrinfo: %s", gai_strerror(error));
- }
-
- if (!res)
- return (0);
-
- if (sa && sl) {
- sl2 = *sl;
- if (sl2 > res->ai_addrlen)
- sl2 = res->ai_addrlen;
- memmove(sa, res->ai_addr, sl2);
- *sl = res->ai_addrlen;
- }
-
- freeaddrinfo(res);
- return (1);
-}
-
-void
-dns_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct sockaddr_storage ss;
- struct dns_session *s;
- struct sockaddr *sa;
- struct asr_query *as;
- struct msg m;
- const char *domain, *mx, *host;
- socklen_t sl;
-
- s = xcalloc(1, sizeof *s);
- s->type = imsg->hdr.type;
- s->p = p;
-
- m_msg(&m, imsg);
- m_get_id(&m, &s->reqid);
-
- switch (s->type) {
-
- case IMSG_MTA_DNS_HOST:
- m_get_string(&m, &host);
- m_end(&m);
- dns_lookup_host(s, host, -1);
- return;
-
- case IMSG_MTA_DNS_MX:
- m_get_string(&m, &domain);
- m_end(&m);
- (void)strlcpy(s->name, domain, sizeof(s->name));
-
- sa = (struct sockaddr *)&ss;
- sl = sizeof(ss);
-
- if (domainname_is_addr(domain, sa, &sl)) {
- m_create(s->p, IMSG_MTA_DNS_HOST, 0, 0, -1);
- m_add_id(s->p, s->reqid);
- m_add_string(s->p, sockaddr_to_text(sa));
- m_add_sockaddr(s->p, sa);
- m_add_int(s->p, -1);
- m_close(s->p);
-
- m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1);
- m_add_id(s->p, s->reqid);
- m_add_int(s->p, DNS_OK);
- m_close(s->p);
- free(s);
- return;
- }
-
- as = res_query_async(s->name, C_IN, T_MX, NULL);
- if (as == NULL) {
- log_warn("warn: res_query_async: %s", s->name);
- m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1);
- m_add_id(s->p, s->reqid);
- m_add_int(s->p, DNS_EINVAL);
- m_close(s->p);
- free(s);
- return;
- }
-
- event_asr_run(as, dns_dispatch_mx, s);
- return;
-
- case IMSG_MTA_DNS_MX_PREFERENCE:
- m_get_string(&m, &domain);
- m_get_string(&m, &mx);
- m_end(&m);
- (void)strlcpy(s->name, mx, sizeof(s->name));
-
- as = res_query_async(domain, C_IN, T_MX, NULL);
- if (as == NULL) {
- m_create(s->p, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1);
- m_add_id(s->p, s->reqid);
- m_add_int(s->p, DNS_ENOTFOUND);
- m_close(s->p);
- free(s);
- return;
- }
-
- event_asr_run(as, dns_dispatch_mx_preference, s);
- return;
-
- default:
- log_warnx("warn: bad dns request %d", s->type);
- fatal(NULL);
- }
-}
-
-static void
-dns_dispatch_host(struct asr_result *ar, void *arg)
-{
- struct dns_session *s;
- struct dns_lookup *lookup = arg;
- struct addrinfo *ai;
-
- s = lookup->session;
-
- for (ai = ar->ar_addrinfo; ai; ai = ai->ai_next) {
- s->mxfound++;
- m_create(s->p, IMSG_MTA_DNS_HOST, 0, 0, -1);
- m_add_id(s->p, s->reqid);
- m_add_string(s->p, lookup->host);
- m_add_sockaddr(s->p, ai->ai_addr);
- m_add_int(s->p, lookup->preference);
- m_close(s->p);
- }
- free(lookup->host);
- free(lookup);
- if (ar->ar_addrinfo)
- asr_freeaddrinfo(ar->ar_addrinfo);
-
- if (ar->ar_gai_errno)
- s->error = ar->ar_gai_errno;
-
- if (--s->refcount)
- return;
-
- m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1);
- m_add_id(s->p, s->reqid);
- m_add_int(s->p, s->mxfound ? DNS_OK : DNS_ENOTFOUND);
- m_close(s->p);
- free(s);
-}
-
-static void
-dns_dispatch_mx(struct asr_result *ar, void *arg)
-{
- struct dns_session *s = arg;
- struct unpack pack;
- struct dns_header h;
- struct dns_query q;
- struct dns_rr rr;
- char buf[512];
- size_t found;
-
- if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA &&
- ar->ar_h_errno != NOTIMP) {
-
- m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1);
- m_add_id(s->p, s->reqid);
- if (ar->ar_rcode == NXDOMAIN)
- m_add_int(s->p, DNS_ENONAME);
- else if (ar->ar_h_errno == NO_RECOVERY)
- m_add_int(s->p, DNS_EINVAL);
- else
- m_add_int(s->p, DNS_RETRY);
- m_close(s->p);
- free(s);
- free(ar->ar_data);
- return;
- }
-
- unpack_init(&pack, ar->ar_data, ar->ar_datalen);
- unpack_header(&pack, &h);
- unpack_query(&pack, &q);
-
- found = 0;
- for (; h.ancount; h.ancount--) {
- unpack_rr(&pack, &rr);
- if (rr.rr_type != T_MX)
- continue;
- print_dname(rr.rr.mx.exchange, buf, sizeof(buf));
- buf[strlen(buf) - 1] = '\0';
- dns_lookup_host(s, buf, rr.rr.mx.preference);
- found++;
- }
- free(ar->ar_data);
-
- /* fallback to host if no MX is found. */
- if (found == 0)
- dns_lookup_host(s, s->name, 0);
-}
-
-static void
-dns_dispatch_mx_preference(struct asr_result *ar, void *arg)
-{
- struct dns_session *s = arg;
- struct unpack pack;
- struct dns_header h;
- struct dns_query q;
- struct dns_rr rr;
- char buf[512];
- int error;
-
- if (ar->ar_h_errno) {
- if (ar->ar_rcode == NXDOMAIN)
- error = DNS_ENONAME;
- else if (ar->ar_h_errno == NO_RECOVERY
- || ar->ar_h_errno == NO_DATA)
- error = DNS_EINVAL;
- else
- error = DNS_RETRY;
- }
- else {
- error = DNS_ENOTFOUND;
- unpack_init(&pack, ar->ar_data, ar->ar_datalen);
- unpack_header(&pack, &h);
- unpack_query(&pack, &q);
- for (; h.ancount; h.ancount--) {
- unpack_rr(&pack, &rr);
- if (rr.rr_type != T_MX)
- continue;
- print_dname(rr.rr.mx.exchange, buf, sizeof(buf));
- buf[strlen(buf) - 1] = '\0';
- if (!strcasecmp(s->name, buf)) {
- error = DNS_OK;
- break;
- }
- }
- }
-
- free(ar->ar_data);
-
- m_create(s->p, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1);
- m_add_id(s->p, s->reqid);
- m_add_int(s->p, error);
- if (error == DNS_OK)
- m_add_int(s->p, rr.rr.mx.preference);
- m_close(s->p);
- free(s);
-}
-
-static void
-dns_lookup_host(struct dns_session *s, const char *host, int preference)
-{
- struct dns_lookup *lookup;
- struct addrinfo hints;
- char hostcopy[HOST_NAME_MAX+1];
- char *p;
- void *as;
-
- lookup = xcalloc(1, sizeof *lookup);
- lookup->preference = preference;
- lookup->host = xstrdup(host);
- lookup->session = s;
- s->refcount++;
-
- if (*host == '[') {
- if (strncasecmp("[IPv6:", host, 6) == 0)
- host += 6;
- else
- host += 1;
- (void)strlcpy(hostcopy, host, sizeof hostcopy);
- p = strchr(hostcopy, ']');
- if (p)
- *p = 0;
- host = hostcopy;
- }
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_flags = AI_ADDRCONFIG;
- hints.ai_family = PF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- as = getaddrinfo_async(host, NULL, &hints, NULL);
- event_asr_run(as, dns_dispatch_host, lookup);
-}
diff --git a/smtpd/enqueue.c b/smtpd/enqueue.c
deleted file mode 100644
index 0ef694b5..00000000
--- a/smtpd/enqueue.c
+++ /dev/null
@@ -1,932 +0,0 @@
-/* $OpenBSD: enqueue.c,v 1.118 2020/03/18 20:17:14 eric Exp $ */
-
-/*
- * Copyright (c) 2005 Henning Brauer <henning@bulabula.org>
- * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/socket.h>
-#include <sys/tree.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <grp.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-
-extern struct imsgbuf *ibuf;
-
-void usage(void);
-static void build_from(char *, struct passwd *);
-static int parse_message(FILE *, int, int, FILE *);
-static void parse_addr(char *, size_t, int);
-static void parse_addr_terminal(int);
-static char *qualify_addr(char *);
-static void rcpt_add(char *);
-static int open_connection(void);
-static int get_responses(FILE *, int);
-static int send_line(FILE *, int, char *, ...);
-static int enqueue_offline(int, char *[], FILE *, FILE *);
-static int savedeadletter(struct passwd *, FILE *);
-
-extern int srv_connected(void);
-
-enum headerfields {
- HDR_NONE,
- HDR_FROM,
- HDR_TO,
- HDR_CC,
- HDR_BCC,
- HDR_SUBJECT,
- HDR_DATE,
- HDR_MSGID,
- HDR_MIME_VERSION,
- HDR_CONTENT_TYPE,
- HDR_CONTENT_DISPOSITION,
- HDR_CONTENT_TRANSFER_ENCODING,
- HDR_USER_AGENT
-};
-
-struct {
- char *word;
- enum headerfields type;
-} keywords[] = {
- { "From:", HDR_FROM },
- { "To:", HDR_TO },
- { "Cc:", HDR_CC },
- { "Bcc:", HDR_BCC },
- { "Subject:", HDR_SUBJECT },
- { "Date:", HDR_DATE },
- { "Message-Id:", HDR_MSGID },
- { "MIME-Version:", HDR_MIME_VERSION },
- { "Content-Type:", HDR_CONTENT_TYPE },
- { "Content-Disposition:", HDR_CONTENT_DISPOSITION },
- { "Content-Transfer-Encoding:", HDR_CONTENT_TRANSFER_ENCODING },
- { "User-Agent:", HDR_USER_AGENT },
-};
-
-#define LINESPLIT 990
-#define SMTP_LINELEN 1000
-#define TIMEOUTMSG "Timeout\n"
-
-#define WSP(c) (c == ' ' || c == '\t')
-
-int verbose = 0;
-static char host[HOST_NAME_MAX+1];
-char *user = NULL;
-time_t timestamp;
-
-struct {
- int fd;
- char *from;
- char *fromname;
- char **rcpts;
- char *dsn_notify;
- char *dsn_ret;
- char *dsn_envid;
- int rcpt_cnt;
- int need_linesplit;
- int saw_date;
- int saw_msgid;
- int saw_from;
- int saw_mime_version;
- int saw_content_type;
- int saw_content_disposition;
- int saw_content_transfer_encoding;
- int saw_user_agent;
- int noheader;
-} msg;
-
-struct {
- uint quote;
- uint comment;
- uint esc;
- uint brackets;
- size_t wpos;
- char buf[SMTP_LINELEN];
-} pstate;
-
-#define QP_TEST_WRAP(fp, buf, linelen, size) do { \
- if (((linelen) += (size)) + 1 > 76) { \
- fprintf((fp), "=\r\n"); \
- if (buf[0] == '.') \
- fprintf((fp), "."); \
- (linelen) = (size); \
- } \
-} while (0)
-
-/* RFC 2045 section 6.7 */
-static void
-qp_encoded_write(FILE *fp, char *buf)
-{
- size_t linelen = 0;
-
- for (;buf[0] != '\0' && buf[0] != '\n'; buf++) {
- /*
- * Point 3: Any TAB (HT) or SPACE characters on an encoded line
- * MUST thus be followed on that line by a printable character.
- *
- * Ergo, only encode if the next character is EOL.
- */
- if (buf[0] == ' ' || buf[0] == '\t') {
- if (buf[1] == '\n') {
- QP_TEST_WRAP(fp, buf, linelen, 3);
- fprintf(fp, "=%2X", *buf & 0xff);
- } else {
- QP_TEST_WRAP(fp, buf, linelen, 1);
- fprintf(fp, "%c", *buf & 0xff);
- }
- /*
- * Point 1, with exclusion of point 2, skip EBCDIC NOTE.
- * Do this after whitespace check, else they would match here.
- */
- } else if (!((buf[0] >= 33 && buf[0] <= 60) ||
- (buf[0] >= 62 && buf[0] <= 126))) {
- QP_TEST_WRAP(fp, buf, linelen, 3);
- fprintf(fp, "=%2X", *buf & 0xff);
- /* Point 2: 33 through 60 inclusive, and 62 through 126 */
- } else {
- QP_TEST_WRAP(fp, buf, linelen, 1);
- fprintf(fp, "%c", *buf);
- }
- }
- fprintf(fp, "\r\n");
-}
-
-int
-enqueue(int argc, char *argv[], FILE *ofp)
-{
- int i, ch, tflag = 0;
- char *fake_from = NULL, *buf = NULL;
- struct passwd *pw;
- FILE *fp = NULL, *fout;
- size_t sz = 0, envid_sz = 0;
- ssize_t len;
- char *line;
- int inheaders = 1;
- int save_argc;
- char **save_argv;
- int no_getlogin = 0;
-
- memset(&msg, 0, sizeof(msg));
- time(&timestamp);
-
- save_argc = argc;
- save_argv = argv;
-
- while ((ch = getopt(argc, argv,
- "A:B:b:E::e:F:f:iJ::L:mN:o:p:qr:R:StvV:x")) != -1) {
- switch (ch) {
- case 'f':
- fake_from = optarg;
- break;
- case 'F':
- msg.fromname = optarg;
- break;
- case 'N':
- msg.dsn_notify = optarg;
- break;
- case 'r':
- fake_from = optarg;
- break;
- case 'R':
- msg.dsn_ret = optarg;
- break;
- case 'S':
- no_getlogin = 1;
- break;
- case 't':
- tflag = 1;
- break;
- case 'v':
- verbose = 1;
- break;
- case 'V':
- msg.dsn_envid = optarg;
- break;
- /* all remaining: ignored, sendmail compat */
- case 'A':
- case 'B':
- case 'b':
- case 'E':
- case 'e':
- case 'i':
- case 'L':
- case 'm':
- case 'o':
- case 'p':
- case 'x':
- break;
- case 'q':
- /* XXX: implement "process all now" */
- return (EX_SOFTWARE);
- default:
- usage();
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (getmailname(host, sizeof(host)) == -1)
- errx(EX_NOHOST, "getmailname");
- if (no_getlogin) {
- if ((pw = getpwuid(getuid())) == NULL)
- user = "anonymous";
- if (pw != NULL)
- user = xstrdup(pw->pw_name);
- }
- else {
- uid_t ruid = getuid();
-
- if ((user = getlogin()) != NULL && *user != '\0') {
- if ((pw = getpwnam(user)) == NULL ||
- (ruid != 0 && ruid != pw->pw_uid))
- pw = getpwuid(ruid);
- } else if ((pw = getpwuid(ruid)) == NULL) {
- user = "anonymous";
- }
- user = xstrdup(pw ? pw->pw_name : user);
- }
-
- build_from(fake_from, pw);
-
- while (argc > 0) {
- rcpt_add(argv[0]);
- argv++;
- argc--;
- }
-
- if ((fp = tmpfile()) == NULL)
- err(EX_UNAVAILABLE, "tmpfile");
-
- msg.noheader = parse_message(stdin, fake_from == NULL, tflag, fp);
-
- if (msg.rcpt_cnt == 0)
- errx(EX_SOFTWARE, "no recipients");
-
- /* init session */
- rewind(fp);
-
- /* check if working in offline mode */
- /* If the server is not running, enqueue the message offline */
-
- if (!srv_connected()) {
-#if HAVE_PLEDGE
- if (pledge("stdio", NULL) == -1)
- err(1, "pledge");
-#endif
- return (enqueue_offline(save_argc, save_argv, fp, ofp));
- }
-
- if ((msg.fd = open_connection()) == -1)
- errx(EX_UNAVAILABLE, "server too busy");
-
-#if HAVE_PLEDGE
- if (pledge("stdio wpath cpath", NULL) == -1)
- err(1, "pledge");
-#endif
-
- fout = fdopen(msg.fd, "a+");
- if (fout == NULL)
- err(EX_UNAVAILABLE, "fdopen");
-
- /*
- * We need to call get_responses after every command because we don't
- * support PIPELINING on the server-side yet.
- */
-
- /* banner */
- if (!get_responses(fout, 1))
- goto fail;
-
- if (!send_line(fout, verbose, "EHLO localhost\r\n"))
- goto fail;
- if (!get_responses(fout, 1))
- goto fail;
-
- if (msg.dsn_envid != NULL)
- envid_sz = strlen(msg.dsn_envid);
-
- if (!send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\r\n",
- msg.from,
- msg.dsn_ret ? "RET=" : "",
- msg.dsn_ret ? msg.dsn_ret : "",
- envid_sz ? "ENVID=" : "",
- envid_sz ? msg.dsn_envid : ""))
- goto fail;
- if (!get_responses(fout, 1))
- goto fail;
-
- for (i = 0; i < msg.rcpt_cnt; i++) {
- if (!send_line(fout, verbose, "RCPT TO:<%s> %s%s\r\n",
- msg.rcpts[i],
- msg.dsn_notify ? "NOTIFY=" : "",
- msg.dsn_notify ? msg.dsn_notify : ""))
- goto fail;
- if (!get_responses(fout, 1))
- goto fail;
- }
-
- if (!send_line(fout, verbose, "DATA\r\n"))
- goto fail;
- if (!get_responses(fout, 1))
- goto fail;
-
- /* add From */
- if (!msg.saw_from && !send_line(fout, 0, "From: %s%s<%s>\r\n",
- msg.fromname ? msg.fromname : "", msg.fromname ? " " : "",
- msg.from))
- goto fail;
-
- /* add Date */
- if (!msg.saw_date && !send_line(fout, 0, "Date: %s\r\n",
- time_to_text(timestamp)))
- goto fail;
-
- if (msg.need_linesplit) {
- /* we will always need to mime encode for long lines */
- if (!msg.saw_mime_version && !send_line(fout, 0,
- "MIME-Version: 1.0\r\n"))
- goto fail;
- if (!msg.saw_content_type && !send_line(fout, 0,
- "Content-Type: text/plain; charset=unknown-8bit\r\n"))
- goto fail;
- if (!msg.saw_content_disposition && !send_line(fout, 0,
- "Content-Disposition: inline\r\n"))
- goto fail;
- if (!msg.saw_content_transfer_encoding && !send_line(fout, 0,
- "Content-Transfer-Encoding: quoted-printable\r\n"))
- goto fail;
- }
-
- /* add separating newline */
- if (msg.noheader) {
- if (!send_line(fout, 0, "\r\n"))
- goto fail;
- inheaders = 0;
- }
-
- for (;;) {
- if ((len = getline(&buf, &sz, fp)) == -1) {
- if (feof(fp))
- break;
- else
- err(EX_UNAVAILABLE, "getline");
- }
-
- /* newlines have been normalized on first parsing */
- if (buf[len-1] != '\n')
- errx(EX_SOFTWARE, "expect EOL");
- len--;
-
- if (buf[0] == '.') {
- if (fputc('.', fout) == EOF)
- goto fail;
- }
-
- line = buf;
-
- if (inheaders) {
- if (strncasecmp("from ", line, 5) == 0)
- continue;
- if (strncasecmp("return-path: ", line, 13) == 0)
- continue;
- }
-
- if (msg.saw_content_transfer_encoding || msg.noheader ||
- inheaders || !msg.need_linesplit) {
- if (!send_line(fout, 0, "%.*s\r\n", (int)len, line))
- goto fail;
- if (inheaders && buf[0] == '\n')
- inheaders = 0;
- continue;
- }
-
- /* we don't have a content transfer encoding, use our default */
- qp_encoded_write(fout, line);
- }
- free(buf);
- if (!send_line(fout, verbose, ".\r\n"))
- goto fail;
- if (!get_responses(fout, 1))
- goto fail;
-
- if (!send_line(fout, verbose, "QUIT\r\n"))
- goto fail;
- if (!get_responses(fout, 1))
- goto fail;
-
- fclose(fp);
- fclose(fout);
-
- exit(EX_OK);
-
-fail:
- if (pw)
- savedeadletter(pw, fp);
- exit(EX_SOFTWARE);
-}
-
-static int
-get_responses(FILE *fin, int n)
-{
- char *buf = NULL;
- size_t sz = 0;
- ssize_t len;
- int e, ret = 0;
-
- fflush(fin);
- if ((e = ferror(fin))) {
- warnx("ferror: %d", e);
- goto err;
- }
-
- while (n) {
- if ((len = getline(&buf, &sz, fin)) == -1) {
- if (ferror(fin)) {
- warn("getline");
- goto err;
- } else if (feof(fin))
- break;
- else
- err(EX_UNAVAILABLE, "getline");
- }
-
- /* account for \r\n linebreaks */
- if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n')
- buf[--len - 1] = '\n';
-
- if (len < 4) {
- warnx("bad response");
- goto err;
- }
-
- if (verbose)
- printf("<<< %.*s", (int)len, buf);
-
- if (buf[3] == '-')
- continue;
- if (buf[0] != '2' && buf[0] != '3') {
- warnx("command failed: %.*s", (int)len, buf);
- goto err;
- }
- n--;
- }
-
- ret = 1;
-err:
- free(buf);
- return ret;
-}
-
-static int
-send_line(FILE *fp, int v, char *fmt, ...)
-{
- int ret = 0;
- va_list ap;
-
- va_start(ap, fmt);
- if (vfprintf(fp, fmt, ap) >= 0)
- ret = 1;
- va_end(ap);
-
- if (ret && v) {
- printf(">>> ");
- va_start(ap, fmt);
- vprintf(fmt, ap);
- va_end(ap);
- }
-
- return (ret);
-}
-
-static void
-build_from(char *fake_from, struct passwd *pw)
-{
- char *p;
-
- if (fake_from == NULL)
- msg.from = qualify_addr(user);
- else {
- if (fake_from[0] == '<') {
- if (fake_from[strlen(fake_from) - 1] != '>')
- errx(1, "leading < but no trailing >");
- fake_from[strlen(fake_from) - 1] = 0;
- p = xstrdup(fake_from + 1);
-
- msg.from = qualify_addr(p);
- free(p);
- } else
- msg.from = qualify_addr(fake_from);
- }
-
- if (msg.fromname == NULL && fake_from == NULL && pw != NULL) {
- int len, apos;
-
- len = strcspn(pw->pw_gecos, ",");
- if ((p = memchr(pw->pw_gecos, '&', len))) {
- apos = p - pw->pw_gecos;
- if (asprintf(&msg.fromname, "%.*s%s%.*s",
- apos, pw->pw_gecos,
- pw->pw_name,
- len - apos - 1, p + 1) == -1)
- err(1, "asprintf");
- msg.fromname[apos] = toupper((unsigned char)msg.fromname[apos]);
- } else {
- if (asprintf(&msg.fromname, "%.*s", len,
- pw->pw_gecos) == -1)
- err(1, "asprintf");
- }
- }
-}
-
-static int
-parse_message(FILE *fin, int get_from, int tflag, FILE *fout)
-{
- char *buf = NULL;
- size_t sz = 0;
- ssize_t len;
- uint i, cur = HDR_NONE;
- uint header_seen = 0, header_done = 0;
-
- memset(&pstate, 0, sizeof(pstate));
- for (;;) {
- if ((len = getline(&buf, &sz, fin)) == -1) {
- if (feof(fin))
- break;
- else
- err(EX_UNAVAILABLE, "getline");
- }
-
- /* account for \r\n linebreaks */
- if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n')
- buf[--len - 1] = '\n';
-
- if (len == 1 && buf[0] == '\n') /* end of header */
- header_done = 1;
-
- if (!WSP(buf[0])) { /* whitespace -> continuation */
- if (cur == HDR_FROM)
- parse_addr_terminal(1);
- if (cur == HDR_TO || cur == HDR_CC || cur == HDR_BCC)
- parse_addr_terminal(0);
- cur = HDR_NONE;
- }
-
- /* not really exact, if we are still in headers */
- if (len + (buf[len - 1] == '\n' ? 0 : 1) >= LINESPLIT)
- msg.need_linesplit = 1;
-
- for (i = 0; !header_done && cur == HDR_NONE &&
- i < nitems(keywords); i++)
- if ((size_t)len > strlen(keywords[i].word) &&
- !strncasecmp(buf, keywords[i].word,
- strlen(keywords[i].word)))
- cur = keywords[i].type;
-
- if (cur != HDR_NONE)
- header_seen = 1;
-
- if (cur != HDR_BCC) {
- if (!send_line(fout, 0, "%.*s", (int)len, buf))
- err(1, "write error");
- if (buf[len - 1] != '\n') {
- if (fputc('\n', fout) == EOF)
- err(1, "write error");
- }
- }
-
- /*
- * using From: as envelope sender is not sendmail compatible,
- * but I really want it that way - maybe needs a knob
- */
- if (cur == HDR_FROM) {
- msg.saw_from++;
- if (get_from)
- parse_addr(buf, len, 1);
- }
-
- if (tflag && (cur == HDR_TO || cur == HDR_CC || cur == HDR_BCC))
- parse_addr(buf, len, 0);
-
- if (cur == HDR_DATE)
- msg.saw_date++;
- if (cur == HDR_MSGID)
- msg.saw_msgid++;
- if (cur == HDR_MIME_VERSION)
- msg.saw_mime_version = 1;
- if (cur == HDR_CONTENT_TYPE)
- msg.saw_content_type = 1;
- if (cur == HDR_CONTENT_DISPOSITION)
- msg.saw_content_disposition = 1;
- if (cur == HDR_CONTENT_TRANSFER_ENCODING)
- msg.saw_content_transfer_encoding = 1;
- if (cur == HDR_USER_AGENT)
- msg.saw_user_agent = 1;
- }
-
- free(buf);
- return (!header_seen);
-}
-
-static void
-parse_addr(char *s, size_t len, int is_from)
-{
- size_t pos = 0;
- int terminal = 0;
-
- /* unless this is a continuation... */
- if (!WSP(s[pos]) && s[pos] != ',' && s[pos] != ';') {
- /* ... skip over everything before the ':' */
- for (; pos < len && s[pos] != ':'; pos++)
- ; /* nothing */
- /* ... and check & reset parser state */
- parse_addr_terminal(is_from);
- }
-
- /* skip over ':' ',' ';' and whitespace */
- for (; pos < len && !pstate.quote && (WSP(s[pos]) || s[pos] == ':' ||
- s[pos] == ',' || s[pos] == ';'); pos++)
- ; /* nothing */
-
- for (; pos < len; pos++) {
- if (!pstate.esc && !pstate.quote && s[pos] == '(')
- pstate.comment++;
- if (!pstate.comment && !pstate.esc && s[pos] == '"')
- pstate.quote = !pstate.quote;
-
- if (!pstate.comment && !pstate.quote && !pstate.esc) {
- if (s[pos] == ':') { /* group */
- for (pos++; pos < len && WSP(s[pos]); pos++)
- ; /* nothing */
- pstate.wpos = 0;
- }
- if (s[pos] == '\n' || s[pos] == '\r')
- break;
- if (s[pos] == ',' || s[pos] == ';') {
- terminal = 1;
- break;
- }
- if (s[pos] == '<') {
- pstate.brackets = 1;
- pstate.wpos = 0;
- }
- if (pstate.brackets && s[pos] == '>')
- terminal = 1;
- }
-
- if (!pstate.comment && !terminal && (!(!(pstate.quote ||
- pstate.esc) && (s[pos] == '<' || WSP(s[pos]))))) {
- if (pstate.wpos >= sizeof(pstate.buf))
- errx(1, "address exceeds buffer size");
- pstate.buf[pstate.wpos++] = s[pos];
- }
-
- if (!pstate.quote && pstate.comment && s[pos] == ')')
- pstate.comment--;
-
- if (!pstate.esc && !pstate.comment && s[pos] == '\\')
- pstate.esc = 1;
- else
- pstate.esc = 0;
- }
-
- if (terminal)
- parse_addr_terminal(is_from);
-
- for (; pos < len && (s[pos] == '\r' || s[pos] == '\n'); pos++)
- ; /* nothing */
-
- if (pos < len)
- parse_addr(s + pos, len - pos, is_from);
-}
-
-static void
-parse_addr_terminal(int is_from)
-{
- if (pstate.comment || pstate.quote || pstate.esc)
- errx(1, "syntax error in address");
- if (pstate.wpos) {
- if (pstate.wpos >= sizeof(pstate.buf))
- errx(1, "address exceeds buffer size");
- pstate.buf[pstate.wpos] = '\0';
- if (is_from)
- msg.from = qualify_addr(pstate.buf);
- else
- rcpt_add(pstate.buf);
- pstate.wpos = 0;
- }
-}
-
-static char *
-qualify_addr(char *in)
-{
- char *out;
-
- if (strlen(in) > 0 && strchr(in, '@') == NULL) {
- if (asprintf(&out, "%s@%s", in, host) == -1)
- err(1, "qualify asprintf");
- } else
- out = xstrdup(in);
-
- return (out);
-}
-
-static void
-rcpt_add(char *addr)
-{
- void *nrcpts;
- char *p;
- int n;
-
- n = 1;
- p = addr;
- while ((p = strchr(p, ',')) != NULL) {
- n++;
- p++;
- }
-
- if ((nrcpts = reallocarray(msg.rcpts,
- msg.rcpt_cnt + n, sizeof(char *))) == NULL)
- err(1, "rcpt_add realloc");
- msg.rcpts = nrcpts;
-
- while (n--) {
- if ((p = strchr(addr, ',')) != NULL)
- *p++ = '\0';
- msg.rcpts[msg.rcpt_cnt++] = qualify_addr(addr);
- if (p == NULL)
- break;
- addr = p;
- }
-}
-
-static int
-open_connection(void)
-{
- struct imsg imsg;
- int fd;
- int n;
-
- imsg_compose(ibuf, IMSG_CTL_SMTP_SESSION, IMSG_VERSION, 0, -1, NULL, 0);
-
- while (ibuf->w.queued)
- if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
- err(1, "write error");
-
- while (1) {
- if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
- errx(1, "imsg_read error");
- if (n == 0)
- errx(1, "pipe closed");
-
- if ((n = imsg_get(ibuf, &imsg)) == -1)
- errx(1, "imsg_get error");
- if (n == 0)
- continue;
-
- switch (imsg.hdr.type) {
- case IMSG_CTL_OK:
- break;
- case IMSG_CTL_FAIL:
- errx(1, "server disallowed submission request");
- default:
- errx(1, "unexpected imsg reply type");
- }
-
- fd = imsg.fd;
- imsg_free(&imsg);
-
- break;
- }
-
- return fd;
-}
-
-static int
-enqueue_offline(int argc, char *argv[], FILE *ifile, FILE *ofile)
-{
- int i, ch;
-
- for (i = 1; i < argc; i++) {
- if (strchr(argv[i], '|') != NULL) {
- warnx("%s contains illegal character", argv[i]);
- ftruncate(fileno(ofile), 0);
- exit(EX_SOFTWARE);
- }
- if (fprintf(ofile, "%s%s", i == 1 ? "" : "|", argv[i]) < 0)
- goto write_error;
- }
-
- if (fputc('\n', ofile) == EOF)
- goto write_error;
-
- while ((ch = fgetc(ifile)) != EOF) {
- if (fputc(ch, ofile) == EOF)
- goto write_error;
- }
-
- if (ferror(ifile)) {
- warn("read error");
- ftruncate(fileno(ofile), 0);
- exit(EX_UNAVAILABLE);
- }
-
- if (fclose(ofile) == EOF)
- goto write_error;
-
- return (EX_TEMPFAIL);
-write_error:
- warn("write error");
- ftruncate(fileno(ofile), 0);
- exit(EX_UNAVAILABLE);
-}
-
-static int
-savedeadletter(struct passwd *pw, FILE *in)
-{
- char buffer[PATH_MAX];
- FILE *fp;
- char *buf = NULL;
- size_t sz = 0;
- ssize_t len;
-
- (void)snprintf(buffer, sizeof buffer, "%s/dead.letter", pw->pw_dir);
-
- if (fseek(in, 0, SEEK_SET) != 0)
- return 0;
-
- if ((fp = fopen(buffer, "w")) == NULL)
- return 0;
-
- /* add From */
- if (!msg.saw_from)
- fprintf(fp, "From: %s%s<%s>\n",
- msg.fromname ? msg.fromname : "",
- msg.fromname ? " " : "",
- msg.from);
-
- /* add Date */
- if (!msg.saw_date)
- fprintf(fp, "Date: %s\n", time_to_text(timestamp));
-
- if (msg.need_linesplit) {
- /* we will always need to mime encode for long lines */
- if (!msg.saw_mime_version)
- fprintf(fp, "MIME-Version: 1.0\n");
- if (!msg.saw_content_type)
- fprintf(fp, "Content-Type: text/plain; "
- "charset=unknown-8bit\n");
- if (!msg.saw_content_disposition)
- fprintf(fp, "Content-Disposition: inline\n");
- if (!msg.saw_content_transfer_encoding)
- fprintf(fp, "Content-Transfer-Encoding: "
- "quoted-printable\n");
- }
-
- /* add separating newline */
- if (msg.noheader)
- fprintf(fp, "\n");
-
- while ((len = getline(&buf, &sz, in)) != -1) {
- if (buf[len - 1] == '\n')
- buf[len - 1] = '\0';
- fprintf(fp, "%s\n", buf);
- }
-
- free(buf);
- fprintf(fp, "\n");
- fclose(fp);
- return 1;
-}
diff --git a/smtpd/envelope.c b/smtpd/envelope.c
deleted file mode 100644
index 35d98b79..00000000
--- a/smtpd/envelope.c
+++ /dev/null
@@ -1,786 +0,0 @@
-/* $OpenBSD: envelope.c,v 1.47 2019/11/25 14:18:32 gilles Exp $ */
-
-/*
- * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
- * Copyright (c) 2011-2013 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static int envelope_ascii_load(struct envelope *, struct dict *);
-static void envelope_ascii_dump(const struct envelope *, char **, size_t *,
- const char *);
-
-void
-envelope_set_errormsg(struct envelope *e, char *fmt, ...)
-{
- int ret;
- va_list ap;
-
- va_start(ap, fmt);
- ret = vsnprintf(e->errorline, sizeof(e->errorline), fmt, ap);
- va_end(ap);
-
- /* this should not happen */
- if (ret < 0)
- err(1, "vsnprintf");
-
- if ((size_t)ret >= sizeof(e->errorline))
- (void)strlcpy(e->errorline + (sizeof(e->errorline) - 4),
- "...", 4);
-}
-
-void
-envelope_set_esc_class(struct envelope *e, enum enhanced_status_class class)
-{
- e->esc_class = class;
-}
-
-void
-envelope_set_esc_code(struct envelope *e, enum enhanced_status_code code)
-{
- e->esc_code = code;
-}
-
-static int
-envelope_buffer_to_dict(struct dict *d, const char *ibuf, size_t buflen)
-{
- static char lbuf[sizeof(struct envelope)];
- size_t len;
- char *buf, *field, *nextline;
-
- memset(lbuf, 0, sizeof lbuf);
- if (strlcpy(lbuf, ibuf, sizeof lbuf) >= sizeof lbuf)
- goto err;
- buf = lbuf;
-
- while (buflen > 0) {
- len = strcspn(buf, "\n");
- buf[len] = '\0';
- nextline = buf + len + 1;
- buflen -= (nextline - buf);
-
- field = buf;
- while (*buf && (isalnum((unsigned char)*buf) || *buf == '-'))
- buf++;
- if (!*buf)
- goto err;
-
- /* skip whitespaces before separator */
- while (*buf && isspace((unsigned char)*buf))
- *buf++ = 0;
-
- /* we *want* ':' */
- if (*buf != ':')
- goto err;
- *buf++ = 0;
-
- /* skip whitespaces after separator */
- while (*buf && isspace((unsigned char)*buf))
- *buf++ = 0;
- dict_set(d, field, buf);
- buf = nextline;
- }
-
- return (1);
-
-err:
- return (0);
-}
-
-int
-envelope_load_buffer(struct envelope *ep, const char *ibuf, size_t buflen)
-{
- struct dict d;
- const char *val, *errstr;
- long long version;
- int ret = 0;
-
- dict_init(&d);
- if (!envelope_buffer_to_dict(&d, ibuf, buflen)) {
- log_debug("debug: cannot parse envelope to dict");
- goto end;
- }
-
- val = dict_get(&d, "version");
- if (val == NULL) {
- log_debug("debug: envelope version not found");
- goto end;
- }
- version = strtonum(val, 1, 64, &errstr);
- if (errstr) {
- log_debug("debug: cannot parse envelope version: %s", val);
- goto end;
- }
-
- if (version != SMTPD_ENVELOPE_VERSION) {
- log_debug("debug: bad envelope version %lld", version);
- goto end;
- }
-
- memset(ep, 0, sizeof *ep);
- ret = envelope_ascii_load(ep, &d);
- if (ret)
- ep->version = SMTPD_ENVELOPE_VERSION;
-end:
- while (dict_poproot(&d, NULL))
- ;
- return (ret);
-}
-
-int
-envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len)
-{
- char *p = dest;
-
- envelope_ascii_dump(ep, &dest, &len, "version");
- envelope_ascii_dump(ep, &dest, &len, "dispatcher");
- envelope_ascii_dump(ep, &dest, &len, "tag");
- envelope_ascii_dump(ep, &dest, &len, "type");
- envelope_ascii_dump(ep, &dest, &len, "smtpname");
- envelope_ascii_dump(ep, &dest, &len, "helo");
- envelope_ascii_dump(ep, &dest, &len, "hostname");
- envelope_ascii_dump(ep, &dest, &len, "username");
- envelope_ascii_dump(ep, &dest, &len, "errorline");
- envelope_ascii_dump(ep, &dest, &len, "sockaddr");
- envelope_ascii_dump(ep, &dest, &len, "sender");
- envelope_ascii_dump(ep, &dest, &len, "rcpt");
- envelope_ascii_dump(ep, &dest, &len, "dest");
- envelope_ascii_dump(ep, &dest, &len, "ctime");
- envelope_ascii_dump(ep, &dest, &len, "last-try");
- envelope_ascii_dump(ep, &dest, &len, "last-bounce");
- envelope_ascii_dump(ep, &dest, &len, "ttl");
- envelope_ascii_dump(ep, &dest, &len, "retry");
- envelope_ascii_dump(ep, &dest, &len, "flags");
- envelope_ascii_dump(ep, &dest, &len, "dsn-notify");
- envelope_ascii_dump(ep, &dest, &len, "dsn-ret");
- envelope_ascii_dump(ep, &dest, &len, "dsn-envid");
- envelope_ascii_dump(ep, &dest, &len, "dsn-orcpt");
- envelope_ascii_dump(ep, &dest, &len, "esc-class");
- envelope_ascii_dump(ep, &dest, &len, "esc-code");
-
- switch (ep->type) {
- case D_MDA:
- envelope_ascii_dump(ep, &dest, &len, "mda-exec");
- envelope_ascii_dump(ep, &dest, &len, "mda-subaddress");
- envelope_ascii_dump(ep, &dest, &len, "mda-user");
- break;
- case D_MTA:
- break;
- case D_BOUNCE:
- envelope_ascii_dump(ep, &dest, &len, "bounce-ttl");
- envelope_ascii_dump(ep, &dest, &len, "bounce-delay");
- envelope_ascii_dump(ep, &dest, &len, "bounce-type");
- break;
- default:
- return (0);
- }
-
- if (dest == NULL)
- return (0);
-
- return (dest - p);
-}
-
-static int
-ascii_load_uint8(uint8_t *dest, char *buf)
-{
- const char *errstr;
-
- *dest = strtonum(buf, 0, 0xff, &errstr);
- if (errstr)
- return 0;
- return 1;
-}
-
-static int
-ascii_load_uint16(uint16_t *dest, char *buf)
-{
- const char *errstr;
-
- *dest = strtonum(buf, 0, 0xffff, &errstr);
- if (errstr)
- return 0;
- return 1;
-}
-
-static int
-ascii_load_uint32(uint32_t *dest, char *buf)
-{
- const char *errstr;
-
- *dest = strtonum(buf, 0, 0xffffffff, &errstr);
- if (errstr)
- return 0;
- return 1;
-}
-
-static int
-ascii_load_time(time_t *dest, char *buf)
-{
- const char *errstr;
-
- *dest = strtonum(buf, 0, LLONG_MAX, &errstr);
- if (errstr)
- return 0;
- return 1;
-}
-
-static int
-ascii_load_type(enum delivery_type *dest, char *buf)
-{
- if (strcasecmp(buf, "mda") == 0)
- *dest = D_MDA;
- else if (strcasecmp(buf, "mta") == 0)
- *dest = D_MTA;
- else if (strcasecmp(buf, "bounce") == 0)
- *dest = D_BOUNCE;
- else
- return 0;
- return 1;
-}
-
-static int
-ascii_load_string(char *dest, char *buf, size_t len)
-{
- if (strlcpy(dest, buf, len) >= len)
- return 0;
- return 1;
-}
-
-static int
-ascii_load_sockaddr(struct sockaddr_storage *ss, char *buf)
-{
- struct sockaddr_in6 ssin6;
- struct sockaddr_in ssin;
-
- memset(&ssin, 0, sizeof ssin);
- memset(&ssin6, 0, sizeof ssin6);
-
- if (!strcmp("local", buf)) {
- ss->ss_family = AF_LOCAL;
- }
- else if (strncasecmp("IPv6:", buf, 5) == 0) {
- /* XXX - remove this after 6.6 release */
- if (inet_pton(AF_INET6, buf + 5, &ssin6.sin6_addr) != 1)
- return 0;
- ssin6.sin6_family = AF_INET6;
- memcpy(ss, &ssin6, sizeof(ssin6));
-#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
- ss->ss_len = sizeof(struct sockaddr_in6);
-#endif
- }
- else if (buf[0] == '[' && buf[strlen(buf)-1] == ']') {
- buf[strlen(buf)-1] = '\0';
- if (inet_pton(AF_INET6, buf+1, &ssin6.sin6_addr) != 1)
- return 0;
- ssin6.sin6_family = AF_INET6;
- memcpy(ss, &ssin6, sizeof(ssin6));
-#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
- ss->ss_len = sizeof(struct sockaddr_in6);
-#endif
- }
- else {
- if (inet_pton(AF_INET, buf, &ssin.sin_addr) != 1)
- return 0;
- ssin.sin_family = AF_INET;
- memcpy(ss, &ssin, sizeof(ssin));
-#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
- ss->ss_len = sizeof(struct sockaddr_in);
-#endif
- }
- return 1;
-}
-
-static int
-ascii_load_mailaddr(struct mailaddr *dest, char *buf)
-{
- if (!text_to_mailaddr(dest, buf))
- return 0;
- return 1;
-}
-
-static int
-ascii_load_flags(enum envelope_flags *dest, char *buf)
-{
- char *flag;
-
- while ((flag = strsep(&buf, " ,|")) != NULL) {
- if (strcasecmp(flag, "authenticated") == 0)
- *dest |= EF_AUTHENTICATED;
- else if (strcasecmp(flag, "enqueued") == 0)
- ;
- else if (strcasecmp(flag, "bounce") == 0)
- *dest |= EF_BOUNCE;
- else if (strcasecmp(flag, "internal") == 0)
- *dest |= EF_INTERNAL;
- else
- return 0;
- }
- return 1;
-}
-
-static int
-ascii_load_bounce_type(enum bounce_type *dest, char *buf)
-{
- if (strcasecmp(buf, "error") == 0 || strcasecmp(buf, "failed") == 0)
- *dest = B_FAILED;
- else if (strcasecmp(buf, "warn") == 0 ||
- strcasecmp(buf, "delayed") == 0)
- *dest = B_DELAYED;
- else if (strcasecmp(buf, "dsn") == 0 ||
- strcasecmp(buf, "delivered") == 0)
- *dest = B_DELIVERED;
- else
- return 0;
- return 1;
-}
-
-static int
-ascii_load_dsn_ret(enum dsn_ret *ret, char *buf)
-{
- if (strcasecmp(buf, "HDRS") == 0)
- *ret = DSN_RETHDRS;
- else if (strcasecmp(buf, "FULL") == 0)
- *ret = DSN_RETFULL;
- else
- return 0;
- return 1;
-}
-
-static int
-ascii_load_field(const char *field, struct envelope *ep, char *buf)
-{
- if (strcasecmp("dispatcher", field) == 0)
- return ascii_load_string(ep->dispatcher, buf,
- sizeof ep->dispatcher);
-
- if (strcasecmp("bounce-delay", field) == 0)
- return ascii_load_time(&ep->agent.bounce.delay, buf);
-
- if (strcasecmp("bounce-ttl", field) == 0)
- return ascii_load_time(&ep->agent.bounce.ttl, buf);
-
- if (strcasecmp("bounce-type", field) == 0)
- return ascii_load_bounce_type(&ep->agent.bounce.type, buf);
-
- if (strcasecmp("ctime", field) == 0)
- return ascii_load_time(&ep->creation, buf);
-
- if (strcasecmp("dest", field) == 0)
- return ascii_load_mailaddr(&ep->dest, buf);
-
- if (strcasecmp("username", field) == 0)
- return ascii_load_string(ep->username, buf, sizeof(ep->username));
-
- if (strcasecmp("errorline", field) == 0)
- return ascii_load_string(ep->errorline, buf,
- sizeof ep->errorline);
-
- if (strcasecmp("ttl", field) == 0)
- return ascii_load_time(&ep->ttl, buf);
-
- if (strcasecmp("flags", field) == 0)
- return ascii_load_flags(&ep->flags, buf);
-
- if (strcasecmp("helo", field) == 0)
- return ascii_load_string(ep->helo, buf, sizeof ep->helo);
-
- if (strcasecmp("hostname", field) == 0)
- return ascii_load_string(ep->hostname, buf,
- sizeof ep->hostname);
-
- if (strcasecmp("last-bounce", field) == 0)
- return ascii_load_time(&ep->lastbounce, buf);
-
- if (strcasecmp("last-try", field) == 0)
- return ascii_load_time(&ep->lasttry, buf);
-
- if (strcasecmp("retry", field) == 0)
- return ascii_load_uint16(&ep->retry, buf);
-
- if (strcasecmp("rcpt", field) == 0)
- return ascii_load_mailaddr(&ep->rcpt, buf);
-
- if (strcasecmp("mda-exec", field) == 0)
- return ascii_load_string(ep->mda_exec, buf, sizeof(ep->mda_exec));
-
- if (strcasecmp("mda-subaddress", field) == 0)
- return ascii_load_string(ep->mda_subaddress, buf, sizeof(ep->mda_subaddress));
-
- if (strcasecmp("mda-user", field) == 0)
- return ascii_load_string(ep->mda_user, buf, sizeof(ep->mda_user));
-
- if (strcasecmp("sender", field) == 0)
- return ascii_load_mailaddr(&ep->sender, buf);
-
- if (strcasecmp("smtpname", field) == 0)
- return ascii_load_string(ep->smtpname, buf,
- sizeof(ep->smtpname));
-
- if (strcasecmp("sockaddr", field) == 0)
- return ascii_load_sockaddr(&ep->ss, buf);
-
- if (strcasecmp("tag", field) == 0)
- return ascii_load_string(ep->tag, buf, sizeof ep->tag);
-
- if (strcasecmp("type", field) == 0)
- return ascii_load_type(&ep->type, buf);
-
- if (strcasecmp("version", field) == 0)
- return ascii_load_uint32(&ep->version, buf);
-
- if (strcasecmp("dsn-notify", field) == 0)
- return ascii_load_uint8(&ep->dsn_notify, buf);
-
- if (strcasecmp("dsn-orcpt", field) == 0)
- return ascii_load_mailaddr(&ep->dsn_orcpt, buf);
-
- if (strcasecmp("dsn-ret", field) == 0)
- return ascii_load_dsn_ret(&ep->dsn_ret, buf);
-
- if (strcasecmp("dsn-envid", field) == 0)
- return ascii_load_string(ep->dsn_envid, buf,
- sizeof(ep->dsn_envid));
-
- if (strcasecmp("esc-class", field) == 0)
- return ascii_load_uint8(&ep->esc_class, buf);
-
- if (strcasecmp("esc-code", field) == 0)
- return ascii_load_uint8(&ep->esc_code, buf);
-
- return (0);
-}
-
-static int
-envelope_ascii_load(struct envelope *ep, struct dict *d)
-{
- const char *field;
- char *value;
- void *hdl;
-
- hdl = NULL;
- while (dict_iter(d, &hdl, &field, (void **)&value))
- if (!ascii_load_field(field, ep, value))
- goto err;
-
- return (1);
-
-err:
- log_warnx("envelope: invalid field \"%s\"", field);
- return (0);
-}
-
-
-static int
-ascii_dump_uint8(uint8_t src, char *dest, size_t len)
-{
- return bsnprintf(dest, len, "%d", src);
-}
-
-static int
-ascii_dump_uint16(uint16_t src, char *dest, size_t len)
-{
- return bsnprintf(dest, len, "%d", src);
-}
-
-static int
-ascii_dump_uint32(uint32_t src, char *dest, size_t len)
-{
- return bsnprintf(dest, len, "%d", src);
-}
-
-static int
-ascii_dump_time(time_t src, char *dest, size_t len)
-{
- return bsnprintf(dest, len, "%lld", (long long) src);
-}
-
-static int
-ascii_dump_string(const char *src, char *dest, size_t len)
-{
- return bsnprintf(dest, len, "%s", src);
-}
-
-static int
-ascii_dump_type(enum delivery_type type, char *dest, size_t len)
-{
- char *p = NULL;
-
- switch (type) {
- case D_MDA:
- p = "mda";
- break;
- case D_MTA:
- p = "mta";
- break;
- case D_BOUNCE:
- p = "bounce";
- break;
- default:
- return 0;
- }
-
- return bsnprintf(dest, len, "%s", p);
-}
-
-static int
-ascii_dump_mailaddr(const struct mailaddr *addr, char *dest, size_t len)
-{
- return bsnprintf(dest, len, "%s@%s",
- addr->user, addr->domain);
-}
-
-static int
-ascii_dump_flags(enum envelope_flags flags, char *buf, size_t len)
-{
- size_t cpylen = 0;
-
- buf[0] = '\0';
- if (flags) {
- if (flags & EF_AUTHENTICATED)
- cpylen = strlcat(buf, "authenticated", len);
- if (flags & EF_BOUNCE) {
- if (buf[0] != '\0')
- (void)strlcat(buf, " ", len);
- cpylen = strlcat(buf, "bounce", len);
- }
- if (flags & EF_INTERNAL) {
- if (buf[0] != '\0')
- (void)strlcat(buf, " ", len);
- cpylen = strlcat(buf, "internal", len);
- }
- }
-
- return cpylen < len ? 1 : 0;
-}
-
-static int
-ascii_dump_bounce_type(enum bounce_type type, char *dest, size_t len)
-{
- char *p = NULL;
-
- switch (type) {
- case B_FAILED:
- p = "failed";
- break;
- case B_DELAYED:
- p = "delayed";
- break;
- case B_DELIVERED:
- p = "delivered";
- break;
- default:
- return 0;
- }
- return bsnprintf(dest, len, "%s", p);
-}
-
-
-static int
-ascii_dump_dsn_ret(enum dsn_ret flag, char *dest, size_t len)
-{
- size_t cpylen = 0;
-
- dest[0] = '\0';
- if (flag == DSN_RETFULL)
- cpylen = strlcat(dest, "FULL", len);
- else if (flag == DSN_RETHDRS)
- cpylen = strlcat(dest, "HDRS", len);
-
- return cpylen < len ? 1 : 0;
-}
-
-static int
-ascii_dump_field(const char *field, const struct envelope *ep,
- char *buf, size_t len)
-{
- if (strcasecmp(field, "dispatcher") == 0)
- return ascii_dump_string(ep->dispatcher, buf, len);
-
- if (strcasecmp(field, "bounce-delay") == 0) {
- if (ep->agent.bounce.type != B_DELAYED)
- return (1);
- return ascii_dump_time(ep->agent.bounce.delay, buf, len);
- }
-
- if (strcasecmp(field, "bounce-ttl") == 0) {
- if (ep->agent.bounce.type != B_DELAYED)
- return (1);
- return ascii_dump_time(ep->agent.bounce.ttl, buf, len);
- }
-
- if (strcasecmp(field, "bounce-type") == 0)
- return ascii_dump_bounce_type(ep->agent.bounce.type, buf, len);
-
- if (strcasecmp(field, "ctime") == 0)
- return ascii_dump_time(ep->creation, buf, len);
-
- if (strcasecmp(field, "dest") == 0)
- return ascii_dump_mailaddr(&ep->dest, buf, len);
-
- if (strcasecmp(field, "username") == 0) {
- if (ep->username[0])
- return ascii_dump_string(ep->username, buf, len);
- return 1;
- }
-
- if (strcasecmp(field, "errorline") == 0)
- return ascii_dump_string(ep->errorline, buf, len);
-
- if (strcasecmp(field, "ttl") == 0)
- return ascii_dump_time(ep->ttl, buf, len);
-
- if (strcasecmp(field, "flags") == 0)
- return ascii_dump_flags(ep->flags, buf, len);
-
- if (strcasecmp(field, "helo") == 0)
- return ascii_dump_string(ep->helo, buf, len);
-
- if (strcasecmp(field, "hostname") == 0)
- return ascii_dump_string(ep->hostname, buf, len);
-
- if (strcasecmp(field, "last-bounce") == 0)
- return ascii_dump_time(ep->lastbounce, buf, len);
-
- if (strcasecmp(field, "last-try") == 0)
- return ascii_dump_time(ep->lasttry, buf, len);
-
- if (strcasecmp(field, "retry") == 0)
- return ascii_dump_uint16(ep->retry, buf, len);
-
- if (strcasecmp(field, "rcpt") == 0)
- return ascii_dump_mailaddr(&ep->rcpt, buf, len);
-
- if (strcasecmp(field, "mda-exec") == 0) {
- if (ep->mda_exec[0])
- return ascii_dump_string(ep->mda_exec, buf, len);
- return 1;
- }
-
- if (strcasecmp(field, "mda-subaddress") == 0) {
- if (ep->mda_subaddress[0])
- return ascii_dump_string(ep->mda_subaddress, buf, len);
- return 1;
- }
-
- if (strcasecmp(field, "mda-user") == 0) {
- if (ep->mda_user[0])
- return ascii_dump_string(ep->mda_user, buf, len);
- return 1;
- }
-
- if (strcasecmp(field, "sender") == 0)
- return ascii_dump_mailaddr(&ep->sender, buf, len);
-
- if (strcasecmp(field, "smtpname") == 0)
- return ascii_dump_string(ep->smtpname, buf, len);
-
- if (strcasecmp(field, "sockaddr") == 0)
- return ascii_dump_string(ss_to_text(&ep->ss), buf, len);
-
- if (strcasecmp(field, "tag") == 0)
- return ascii_dump_string(ep->tag, buf, len);
-
- if (strcasecmp(field, "type") == 0)
- return ascii_dump_type(ep->type, buf, len);
-
- if (strcasecmp(field, "version") == 0)
- return ascii_dump_uint32(SMTPD_ENVELOPE_VERSION, buf, len);
-
- if (strcasecmp(field, "dsn-notify") == 0)
- return ascii_dump_uint8(ep->dsn_notify, buf, len);
-
- if (strcasecmp(field, "dsn-ret") == 0)
- return ascii_dump_dsn_ret(ep->dsn_ret, buf, len);
-
- if (strcasecmp(field, "dsn-orcpt") == 0) {
- if (ep->dsn_orcpt.user[0] && ep->dsn_orcpt.domain[0])
- return ascii_dump_mailaddr(&ep->dsn_orcpt, buf, len);
- return 1;
- }
-
- if (strcasecmp(field, "dsn-envid") == 0)
- return ascii_dump_string(ep->dsn_envid, buf, len);
-
- if (strcasecmp(field, "esc-class") == 0) {
- if (ep->esc_class)
- return ascii_dump_uint8(ep->esc_class, buf, len);
- return 1;
- }
-
- if (strcasecmp(field, "esc-code") == 0) {
- /* this is not a pasto, we dump esc_code if esc_class is !0 */
- if (ep->esc_class)
- return ascii_dump_uint8(ep->esc_code, buf, len);
- return 1;
- }
-
- return (0);
-}
-
-static void
-envelope_ascii_dump(const struct envelope *ep, char **dest, size_t *len,
- const char *field)
-{
- char buf[8192];
- int l;
-
- if (*dest == NULL)
- return;
-
- memset(buf, 0, sizeof buf);
- if (!ascii_dump_field(field, ep, buf, sizeof buf))
- goto err;
- if (buf[0] == '\0')
- return;
-
- l = snprintf(*dest, *len, "%s: %s\n", field, buf);
- if (l < 0 || (size_t) l >= *len)
- goto err;
- *dest += l;
- *len -= l;
-
- return;
-err:
- *dest = NULL;
-}
diff --git a/smtpd/esc.c b/smtpd/esc.c
deleted file mode 100644
index 64a44c79..00000000
--- a/smtpd/esc.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/* $OpenBSD: esc.c,v 1.5 2016/09/03 22:16:39 gilles Exp $ */
-
-/*
- * Copyright (c) 2014 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <stdio.h>
-#include <limits.h>
-
-#include "smtpd-defines.h"
-#include "smtpd-api.h"
-
-static struct escode {
- enum enhanced_status_code code;
- const char *description;
-} esc[] = {
- /* 0.0 */
- { ESC_OTHER_STATUS, "Other/Undefined" },
-
- /* 1.x */
- { ESC_OTHER_ADDRESS_STATUS, "Other/Undefined address status" },
- { ESC_BAD_DESTINATION_MAILBOX_ADDRESS, "Bad destination mailbox address" },
- { ESC_BAD_DESTINATION_SYSTEM_ADDRESS, "Bad destination system address" },
- { ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX, "Bad destination mailbox address syntax" },
- { ESC_DESTINATION_MAILBOX_ADDRESS_AMBIGUOUS, "Destination mailbox address ambiguous" },
- { ESC_DESTINATION_ADDRESS_VALID, "Destination address valid" },
- { ESC_DESTINATION_MAILBOX_HAS_MOVED, "Destination mailbox has moved, No forwarding address" },
- { ESC_BAD_SENDER_MAILBOX_ADDRESS_SYNTAX, "Bad sender's mailbox address syntax" },
- { ESC_BAD_SENDER_SYSTEM_ADDRESS, "Bad sender's system address syntax" },
-
- /* 2.x */
- { ESC_OTHER_MAILBOX_STATUS, "Other/Undefined mailbox status" },
- { ESC_MAILBOX_DISABLED, "Mailbox disabled, not accepting messages" },
- { ESC_MAILBOX_FULL, "Mailbox full" },
- { ESC_MESSAGE_LENGTH_TOO_LARGE, "Message length exceeds administrative limit" },
- { ESC_MAILING_LIST_EXPANSION_PROBLEM, "Mailing list expansion problem" },
-
- /* 3.x */
- { ESC_OTHER_MAIL_SYSTEM_STATUS, "Other/Undefined mail system status" },
- { ESC_MAIL_SYSTEM_FULL, "Mail system full" },
- { ESC_SYSTEM_NOT_ACCEPTING_MESSAGES, "System not accepting network messages" },
- { ESC_SYSTEM_NOT_CAPABLE_OF_SELECTED_FEATURES, "System not capable of selected features" },
- { ESC_MESSAGE_TOO_BIG_FOR_SYSTEM, "Message too big for system" },
- { ESC_SYSTEM_INCORRECTLY_CONFIGURED, "System incorrectly configured" },
-
- /* 4.x */
- { ESC_OTHER_NETWORK_ROUTING_STATUS, "Other/Undefined network or routing status" },
- { ESC_NO_ANSWER_FROM_HOST, "No answer from host" },
- { ESC_BAD_CONNECTION, "Bad connection" },
- { ESC_DIRECTORY_SERVER_FAILURE, "Directory server failure" },
- { ESC_UNABLE_TO_ROUTE, "Unable to route" },
- { ESC_MAIL_SYSTEM_CONGESTION, "Mail system congestion" },
- { ESC_ROUTING_LOOP_DETECTED, "Routing loop detected" },
- { ESC_DELIVERY_TIME_EXPIRED, "Delivery time expired" },
-
- /* 5.x */
- { ESC_INVALID_RECIPIENT, "Invalid recipient" },
- { ESC_INVALID_COMMAND, "Invalid command" },
- { ESC_SYNTAX_ERROR, "Syntax error" },
- { ESC_TOO_MANY_RECIPIENTS, "Too many recipients" },
- { ESC_INVALID_COMMAND_ARGUMENTS, "Invalid command arguments" },
- { ESC_WRONG_PROTOCOL_VERSION, "Wrong protocol version" },
-
- /* 6.x */
- { ESC_OTHER_MEDIA_ERROR, "Other/Undefined media error" },
- { ESC_MEDIA_NOT_SUPPORTED, "Media not supported" },
- { ESC_CONVERSION_REQUIRED_AND_PROHIBITED, "Conversion required and prohibited" },
- { ESC_CONVERSION_REQUIRED_BUT_NOT_SUPPORTED, "Conversion required but not supported" },
- { ESC_CONVERSION_WITH_LOSS_PERFORMED, "Conversion with loss performed" },
- { ESC_CONVERSION_FAILED, "Conversion failed" },
-
- /* 7.x */
- { ESC_OTHER_SECURITY_STATUS, "Other/Undefined security status" },
- { ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED, "Delivery not authorized, message refused" },
- { ESC_MAILING_LIST_EXPANSION_PROHIBITED, "Mailing list expansion prohibited" },
- { ESC_SECURITY_CONVERSION_REQUIRED_NOT_POSSIBLE,"Security conversion required but not possible" },
- { ESC_SECURITY_FEATURES_NOT_SUPPORTED, "Security features not supported" },
- { ESC_CRYPTOGRAPHIC_FAILURE, "Cryptographic failure" },
- { ESC_CRYPTOGRAPHIC_ALGORITHM_NOT_SUPPORTED, "Cryptographic algorithm not supported" },
- { ESC_MESSAGE_TOO_BIG_FOR_SYSTEM, "Message integrity failure" },
-};
-
-const char *
-esc_code(enum enhanced_status_class class, enum enhanced_status_code code)
-{
- static char buffer[6];
-
- (void)snprintf(buffer, sizeof buffer, "%d.%d.%d", class, code / 10, code % 10);
- return buffer;
-
-}
-
-const char *
-esc_description(enum enhanced_status_code code)
-{
- uint32_t i;
-
- for (i = 0; i < nitems(esc); ++i)
- if (code == esc[i].code)
- return esc[i].description;
- return "Other/Undefined";
-}
diff --git a/smtpd/expand.c b/smtpd/expand.c
deleted file mode 100644
index a4306fc0..00000000
--- a/smtpd/expand.c
+++ /dev/null
@@ -1,332 +0,0 @@
-/* $OpenBSD: expand.c,v 1.31 2018/05/31 21:06:12 gilles Exp $ */
-
-/*
- * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <event.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_UTIL_H
-#include <util.h>
-#endif
-#ifdef HAVE_LIBUTIL_H
-#include <libutil.h>
-#endif
-
-#include "smtpd.h"
-#include "log.h"
-
-static const char *expandnode_info(struct expandnode *);
-
-struct expandnode *
-expand_lookup(struct expand *expand, struct expandnode *key)
-{
- return RB_FIND(expandtree, &expand->tree, key);
-}
-
-int
-expand_to_text(struct expand *expand, char *buf, size_t sz)
-{
- struct expandnode *xn;
-
- buf[0] = '\0';
-
- RB_FOREACH(xn, expandtree, &expand->tree) {
- if (buf[0])
- (void)strlcat(buf, ", ", sz);
- if (strlcat(buf, expandnode_to_text(xn), sz) >= sz)
- return 0;
- }
-
- return 1;
-}
-
-void
-expand_insert(struct expand *expand, struct expandnode *node)
-{
- struct expandnode *xn;
-
- node->rule = expand->rule;
- node->parent = expand->parent;
-
- log_trace(TRACE_EXPAND, "expand: %p: expand_insert() called for %s",
- expand, expandnode_info(node));
- if (node->type == EXPAND_USERNAME &&
- expand->parent &&
- expand->parent->type == EXPAND_USERNAME &&
- !strcmp(expand->parent->u.user, node->u.user)) {
- log_trace(TRACE_EXPAND, "expand: %p: setting sameuser = 1",
- expand);
- node->sameuser = 1;
- }
-
- if (expand_lookup(expand, node)) {
- log_trace(TRACE_EXPAND, "expand: %p: node found, discarding",
- expand);
- return;
- }
-
- xn = xmemdup(node, sizeof *xn);
- xn->rule = expand->rule;
- xn->parent = expand->parent;
- if (xn->parent)
- xn->depth = xn->parent->depth + 1;
- else
- xn->depth = 0;
- RB_INSERT(expandtree, &expand->tree, xn);
- if (expand->queue)
- TAILQ_INSERT_TAIL(expand->queue, xn, tq_entry);
- expand->nb_nodes++;
- log_trace(TRACE_EXPAND, "expand: %p: inserted node %p", expand, xn);
-}
-
-void
-expand_clear(struct expand *expand)
-{
- struct expandnode *xn;
-
- log_trace(TRACE_EXPAND, "expand: %p: clearing expand tree", expand);
- if (expand->queue)
- while ((xn = TAILQ_FIRST(expand->queue)))
- TAILQ_REMOVE(expand->queue, xn, tq_entry);
-
- while ((xn = RB_ROOT(&expand->tree)) != NULL) {
- RB_REMOVE(expandtree, &expand->tree, xn);
- free(xn);
- }
-}
-
-void
-expand_free(struct expand *expand)
-{
- expand_clear(expand);
-
- log_trace(TRACE_EXPAND, "expand: %p: freeing expand tree", expand);
- free(expand);
-}
-
-int
-expand_cmp(struct expandnode *e1, struct expandnode *e2)
-{
- struct expandnode *p1, *p2;
- int r;
-
- if (e1->type < e2->type)
- return -1;
- if (e1->type > e2->type)
- return 1;
- if (e1->sameuser < e2->sameuser)
- return -1;
- if (e1->sameuser > e2->sameuser)
- return 1;
- if (e1->realuser < e2->realuser)
- return -1;
- if (e1->realuser > e2->realuser)
- return 1;
-
- r = memcmp(&e1->u, &e2->u, sizeof(e1->u));
- if (r)
- return (r);
-
- if (e1->parent == e2->parent)
- return (0);
-
- if (e1->parent == NULL)
- return (-1);
- if (e2->parent == NULL)
- return (1);
-
- /*
- * The same node can be expanded in for different dest context.
- * Wen need to distinguish between those.
- */
- for(p1 = e1->parent; p1->type != EXPAND_ADDRESS; p1 = p1->parent)
- ;
- for(p2 = e2->parent; p2->type != EXPAND_ADDRESS; p2 = p2->parent)
- ;
- if (p1 < p2)
- return (-1);
- if (p1 > p2)
- return (1);
-
- if (e1->type != EXPAND_FILENAME && e1->type != EXPAND_FILTER)
- return (0);
-
- /*
- * For external delivery, we need to distinguish between users.
- * If we can't find a username, we assume it is _smtpd.
- */
- for(p1 = e1->parent; p1 && p1->type != EXPAND_USERNAME; p1 = p1->parent)
- ;
- for(p2 = e2->parent; p2 && p2->type != EXPAND_USERNAME; p2 = p2->parent)
- ;
- if (p1 < p2)
- return (-1);
- if (p1 > p2)
- return (1);
-
- return (0);
-}
-
-static int
-expand_line_split(char **line, char **ret)
-{
- static char buffer[LINE_MAX];
- int esc, dq, sq;
- size_t i;
- char *s;
-
- memset(buffer, 0, sizeof buffer);
- esc = dq = sq = 0;
- i = 0;
- for (s = *line; (*s) && (i < sizeof(buffer)); ++s) {
- if (esc) {
- buffer[i++] = *s;
- esc = 0;
- continue;
- }
- if (*s == '\\') {
- esc = 1;
- continue;
- }
- if (*s == ',' && !dq && !sq) {
- *ret = buffer;
- *line = s+1;
- return (1);
- }
-
- buffer[i++] = *s;
- esc = 0;
-
- if (*s == '"' && !sq)
- dq ^= 1;
- if (*s == '\'' && !dq)
- sq ^= 1;
- }
-
- if (esc || dq || sq || i == sizeof(buffer))
- return (-1);
-
- *ret = buffer;
- *line = s;
- return (i ? 1 : 0);
-}
-
-int
-expand_line(struct expand *expand, const char *s, int do_includes)
-{
- struct expandnode xn;
- char buffer[LINE_MAX];
- char *p, *subrcpt;
- int ret;
-
- memset(buffer, 0, sizeof buffer);
- if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
- return 0;
-
- p = buffer;
- while ((ret = expand_line_split(&p, &subrcpt)) > 0) {
- subrcpt = strip(subrcpt);
- if (subrcpt[0] == '\0')
- continue;
- if (!text_to_expandnode(&xn, subrcpt))
- return 0;
- if (!do_includes)
- if (xn.type == EXPAND_INCLUDE)
- continue;
- expand_insert(expand, &xn);
- }
-
- if (ret >= 0)
- return 1;
-
- /* expand_line_split() returned < 0 */
- return 0;
-}
-
-static const char *
-expandnode_info(struct expandnode *e)
-{
- static char buffer[1024];
- const char *type = NULL;
- const char *value = NULL;
- char tmp[64];
-
- switch (e->type) {
- case EXPAND_FILTER:
- type = "filter";
- break;
- case EXPAND_FILENAME:
- type = "filename";
- break;
- case EXPAND_INCLUDE:
- type = "include";
- break;
- case EXPAND_USERNAME:
- type = "username";
- break;
- case EXPAND_ADDRESS:
- type = "address";
- break;
- case EXPAND_ERROR:
- type = "error";
- break;
- case EXPAND_INVALID:
- default:
- return NULL;
- }
-
- if ((value = expandnode_to_text(e)) == NULL)
- return NULL;
-
- (void)strlcpy(buffer, type, sizeof buffer);
- (void)strlcat(buffer, ":", sizeof buffer);
- if (strlcat(buffer, value, sizeof buffer) >= sizeof buffer)
- return NULL;
-
- (void)snprintf(tmp, sizeof(tmp), "[parent=%p", e->parent);
- if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
- return NULL;
-
- (void)snprintf(tmp, sizeof(tmp), ", rule=%p", e->rule);
- if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
- return NULL;
-
- if (e->rule) {
- (void)snprintf(tmp, sizeof(tmp), ", dispatcher=%p", e->rule->dispatcher);
- if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer)
- return NULL;
- }
-
- if (strlcat(buffer, "]", sizeof buffer) >= sizeof buffer)
- return NULL;
-
- return buffer;
-}
-
-RB_GENERATE(expandtree, expandnode, entry, expand_cmp);
diff --git a/smtpd/filter.c b/smtpd/filter.c
deleted file mode 100644
index 614486b7..00000000
--- a/smtpd/filter.c
+++ /dev/null
@@ -1,868 +0,0 @@
-/* $OpenBSD: filter.c,v 1.25 2017/01/09 09:53:23 reyk Exp $ */
-
-/*
- * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-
-#include <netinet/in.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <resolv.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-enum {
- QUERY_READY,
- QUERY_RUNNING,
- QUERY_DONE
-};
-
-
-struct filter_proc {
- TAILQ_ENTRY(filter_proc) entry;
- struct mproc mproc;
- int hooks;
- int flags;
- int ready;
-};
-
-struct filter {
- TAILQ_ENTRY(filter) entry;
- struct filter_proc *proc;
-};
-TAILQ_HEAD(filter_lst, filter);
-
-TAILQ_HEAD(filter_query_lst, filter_query);
-struct filter_session {
- uint64_t id;
- int terminate;
- struct filter_lst *filters;
- struct filter *fcurr;
-
- int error;
- struct io *iev;
- size_t idatalen;
- FILE *ofile;
-
- struct filter_query *eom;
-};
-
-struct filter_query {
- uint64_t qid;
- int type;
- struct filter_session *session;
-
- int state;
- struct filter *current;
-
- /* current data */
- union {
- struct {
- struct sockaddr_storage local;
- struct sockaddr_storage remote;
- char hostname[HOST_NAME_MAX+1];
- } connect;
- char line[LINE_MAX];
- struct mailaddr maddr;
- size_t datalen;
- } u;
-
- /* current response */
- struct {
- int status;
- int code;
- char *response;
- } smtp;
-};
-
-static void filter_imsg(struct mproc *, struct imsg *);
-static void filter_post_event(uint64_t, int, struct filter *, struct filter *);
-static struct filter_query *filter_query(struct filter_session *, int);
-static void filter_drain_query(struct filter_query *);
-static void filter_run_query(struct filter *, struct filter_query *);
-static void filter_end_query(struct filter_query *);
-static void filter_set_sink(struct filter_session *, int);
-static int filter_tx(struct filter_session *, int);
-static void filter_tx_io(struct io *, int, void *);
-
-static TAILQ_HEAD(, filter_proc) procs;
-struct dict chains;
-
-static const char * filter_session_to_text(struct filter_session *);
-static const char * filter_query_to_text(struct filter_query *);
-static const char * filter_to_text(struct filter *);
-static const char * filter_proc_to_text(struct filter_proc *);
-static const char * query_to_str(int);
-static const char * event_to_str(int);
-static const char * status_to_str(int);
-static const char * filterimsg_to_str(int);
-
-struct tree sessions;
-struct tree queries;
-
-static void
-filter_add_arg(struct filter_conf *filter, char *arg)
-{
- if (filter->argc == MAX_FILTER_ARGS) {
- log_warnx("warn: filter \"%s\" is full", filter->name);
- fatalx("exiting");
- }
- filter->argv[filter->argc++] = arg;
-}
-
-static void
-filter_extend_chain(struct filter_lst *chain, const char *name)
-{
- struct filter *n;
- struct filter_lst *fchain;
- struct filter_conf *fconf;
- int i;
-
- fconf = dict_xget(&env->sc_filters, name);
- if (fconf->chain) {
- log_debug("filter: extending with \"%s\"", name);
- for (i = 0; i < fconf->argc; i++)
- filter_extend_chain(chain, fconf->argv[i]);
- }
- else {
- log_debug("filter: adding filter \"%s\"", name);
- n = xcalloc(1, sizeof(*n), "filter_extend_chain");
- fchain = dict_get(&chains, name);
- n->proc = TAILQ_FIRST(fchain)->proc;
- TAILQ_INSERT_TAIL(chain, n, entry);
- }
-}
-
-void
-filter_postfork(void)
-{
- static int prepare = 0;
- struct filter_conf *filter;
- void *iter;
- struct filter_proc *proc;
- struct filter_lst *fchain;
- struct filter *f;
- struct mproc *p;
- int done, i;
-
- if (prepare)
- return;
- prepare = 1;
-
- TAILQ_INIT(&procs);
- dict_init(&chains);
-
- log_debug("filter: building simple chains...");
-
- /* create all filter proc and associated chains */
- iter = NULL;
- while (dict_iter(&env->sc_filters, &iter, NULL, (void **)&filter)) {
- if (filter->chain)
- continue;
-
- log_debug("filter: building simple chain \"%s\"", filter->name);
- proc = xcalloc(1, sizeof(*proc), "filter_postfork");
- p = &proc->mproc;
- p->handler = filter_imsg;
- p->proc = PROC_FILTER;
- p->name = xstrdup(filter->name, "filter_postfork");
- p->data = proc;
- if (tracing & TRACE_DEBUG)
- filter_add_arg(filter, "-v");
- if (foreground_log)
- filter_add_arg(filter, "-d");
- if (mproc_fork(p, filter->path, filter->argv) < 0)
- fatalx("filter_postfork");
-
- log_debug("filter: registering proc \"%s\"", filter->name);
- f = xcalloc(1, sizeof(*f), "filter_postfork");
- f->proc = proc;
-
- TAILQ_INSERT_TAIL(&procs, proc, entry);
- fchain = xcalloc(1, sizeof(*fchain), "filter_postfork");
- TAILQ_INIT(fchain);
- TAILQ_INSERT_TAIL(fchain, f, entry);
- dict_xset(&chains, filter->name, fchain);
- filter->done = 1;
- }
-
- log_debug("filter: building complex chains...");
-
- /* resolve all chains */
- done = 0;
- while (!done) {
- done = 1;
- iter = NULL;
- while (dict_iter(&env->sc_filters, &iter, NULL,
- (void **)&filter)) {
- if (filter->done)
- continue;
- done = 0;
- filter->done = 1;
- for (i = 0; i < filter->argc; i++) {
- if (!dict_get(&chains, filter->argv[i])) {
- filter->done = 0;
- break;
- }
- }
- if (filter->done == 0)
- continue;
- fchain = xcalloc(1, sizeof(*fchain), "filter_postfork");
- TAILQ_INIT(fchain);
- log_debug("filter: building chain \"%s\"...",
- filter->name);
- for (i = 0; i < filter->argc; i++)
- filter_extend_chain(fchain, filter->argv[i]);
- log_debug("filter: done building chain \"%s\"",
- filter->name);
- dict_xset(&chains, filter->name, fchain);
- }
- }
- log_debug("filter: done building complex chains");
-
- fchain = xcalloc(1, sizeof(*fchain), "filter_postfork");
- TAILQ_INIT(fchain);
- dict_xset(&chains, "<no-filter>", fchain);
-}
-
-void
-filter_configure(void)
-{
- static int init = 0;
- struct filter_proc *p;
-
- if (init)
- return;
- init = 1;
-
- tree_init(&sessions);
- tree_init(&queries);
-
- TAILQ_FOREACH(p, &procs, entry) {
- m_create(&p->mproc, IMSG_FILTER_REGISTER, 0, 0, -1);
- m_add_u32(&p->mproc, FILTER_API_VERSION);
- m_add_string(&p->mproc, p->mproc.name);
- m_close(&p->mproc);
- mproc_enable(&p->mproc);
- }
-
- if (TAILQ_FIRST(&procs) == NULL)
- smtp_configure();
-}
-
-void
-filter_event(uint64_t id, int event)
-{
- struct filter_session *s;
-
- if (event == EVENT_DISCONNECT)
- /* On disconnect, the session is virtualy dead */
- s = tree_xpop(&sessions, id);
- else
- s = tree_xget(&sessions, id);
-
- filter_post_event(id, event, TAILQ_FIRST(s->filters), NULL);
-
- if (event == EVENT_DISCONNECT) {
- if (s->iev)
- io_free(s->iev);
- if (s->ofile)
- fclose(s->ofile);
- free(s);
- }
-}
-
-void
-filter_connect(uint64_t id, const struct sockaddr *local,
- const struct sockaddr *remote, const char *host, const char *filter)
-{
- struct filter_session *s;
- struct filter_query *q;
-
- s = xcalloc(1, sizeof(*s), "filter_event");
- s->id = id;
- if (filter == NULL)
- filter = "<no-filter>";
- s->filters = dict_xget(&chains, filter);
- tree_xset(&sessions, s->id, s);
-
- filter_event(id, EVENT_CONNECT);
- q = filter_query(s, QUERY_CONNECT);
-
- memmove(&q->u.connect.local, local, SA_LEN(local));
- memmove(&q->u.connect.remote, remote, SA_LEN(remote));
- strlcpy(q->u.connect.hostname, host, sizeof(q->u.connect.hostname));
-
- q->smtp.status = FILTER_OK;
- q->smtp.code = 0;
- q->smtp.response = NULL;
-
- filter_drain_query(q);
-}
-
-void
-filter_mailaddr(uint64_t id, int type, const struct mailaddr *maddr)
-{
- struct filter_session *s;
- struct filter_query *q;
-
- s = tree_xget(&sessions, id);
- q = filter_query(s, type);
-
- strlcpy(q->u.maddr.user, maddr->user, sizeof(q->u.maddr.user));
- strlcpy(q->u.maddr.domain, maddr->domain, sizeof(q->u.maddr.domain));
-
- filter_drain_query(q);
-}
-
-void
-filter_line(uint64_t id, int type, const char *line)
-{
- struct filter_session *s;
- struct filter_query *q;
-
- s = tree_xget(&sessions, id);
- q = filter_query(s, type);
-
- if (line)
- strlcpy(q->u.line, line, sizeof(q->u.line));
-
- filter_drain_query(q);
-}
-
-void
-filter_eom(uint64_t id, int type, size_t datalen)
-{
- struct filter_session *s;
- struct filter_query *q;
-
- s = tree_xget(&sessions, id);
- q = filter_query(s, type);
- q->u.datalen = datalen;
-
- filter_drain_query(q);
-}
-
-static void
-filter_set_sink(struct filter_session *s, int sink)
-{
- struct mproc *p;
-
- while (s->fcurr) {
- if (s->fcurr->proc->hooks & HOOK_DATALINE) {
- log_trace(TRACE_FILTERS, "filter: sending fd %d to %s",
- sink, filter_to_text(s->fcurr));
- p = &s->fcurr->proc->mproc;
- m_create(p, IMSG_FILTER_PIPE, 0, 0, sink);
- m_add_id(p, s->id);
- m_close(p);
- return;
- }
- s->fcurr = TAILQ_PREV(s->fcurr, filter_lst, entry);
- }
-
- log_trace(TRACE_FILTERS, "filter: chain input is %d", sink);
- smtp_filter_fd(s->id, sink);
-}
-
-void
-filter_build_fd_chain(uint64_t id, int sink)
-{
- struct filter_session *s;
- int fd;
-
- s = tree_xget(&sessions, id);
- s->fcurr = TAILQ_LAST(s->filters, filter_lst);
-
- fd = filter_tx(s, sink);
- filter_set_sink(s, fd);
-}
-
-void
-filter_post_event(uint64_t id, int event, struct filter *f, struct filter *end)
-{
- for(; f && f != end; f = TAILQ_NEXT(f, entry)) {
- log_trace(TRACE_FILTERS, "filter: post-event event=%s filter=%s",
- event_to_str(event), f->proc->mproc.name);
-
- m_create(&f->proc->mproc, IMSG_FILTER_EVENT, 0, 0, -1);
- m_add_id(&f->proc->mproc, id);
- m_add_int(&f->proc->mproc, event);
- m_close(&f->proc->mproc);
- }
-}
-
-static struct filter_query *
-filter_query(struct filter_session *s, int type)
-{
- struct filter_query *q;
-
- q = xcalloc(1, sizeof(*q), "filter_query");
- q->qid = generate_uid();
- q->session = s;
- q->type = type;
-
- q->state = QUERY_READY;
- q->current = TAILQ_FIRST(s->filters);
-
- log_trace(TRACE_FILTERS, "filter: new query %s", query_to_str(type));
-
- return (q);
-}
-
-static void
-filter_drain_query(struct filter_query *q)
-{
- log_trace(TRACE_FILTERS, "filter: filter_drain_query %s",
- filter_query_to_text(q));
-
- /*
- * The query must be passed through all filters that registered
- * a hook, until one rejects it.
- */
- while (q->state != QUERY_DONE) {
- /* Walk over all filters */
- while (q->current) {
- filter_run_query(q->current, q);
- if (q->state == QUERY_RUNNING) {
- log_trace(TRACE_FILTERS,
- "filter: waiting for running query %s",
- filter_query_to_text(q));
- return;
- }
- }
- q->state = QUERY_DONE;
- }
-
- /* Defer the response if the file is not closed yet. */
- if (q->type == QUERY_EOM && q->session->ofile && q->smtp.status == FILTER_OK) {
- log_debug("filter: deferring eom query...");
- q->session->eom = q;
- return;
- }
-
- filter_end_query(q);
-}
-
-static void
-filter_run_query(struct filter *f, struct filter_query *q)
-{
- log_trace(TRACE_FILTERS,
- "filter: running filter %s for query %s",
- filter_to_text(f), filter_query_to_text(q));
-
- m_create(&f->proc->mproc, IMSG_FILTER_QUERY, 0, 0, -1);
- m_add_id(&f->proc->mproc, q->session->id);
- m_add_id(&f->proc->mproc, q->qid);
- m_add_int(&f->proc->mproc, q->type);
-
- switch (q->type) {
- case QUERY_CONNECT:
- m_add_sockaddr(&f->proc->mproc,
- (struct sockaddr *)&q->u.connect.local);
- m_add_sockaddr(&f->proc->mproc,
- (struct sockaddr *)&q->u.connect.remote);
- m_add_string(&f->proc->mproc, q->u.connect.hostname);
- break;
- case QUERY_HELO:
- m_add_string(&f->proc->mproc, q->u.line);
- break;
- case QUERY_MAIL:
- case QUERY_RCPT:
- m_add_mailaddr(&f->proc->mproc, &q->u.maddr);
- break;
- case QUERY_EOM:
- m_add_u32(&f->proc->mproc, q->u.datalen);
- break;
- default:
- break;
- }
- m_close(&f->proc->mproc);
-
- tree_xset(&queries, q->qid, q);
- q->state = QUERY_RUNNING;
-}
-
-static void
-filter_end_query(struct filter_query *q)
-{
- struct filter_session *s = q->session;
- const char *response = q->smtp.response;
-
- log_trace(TRACE_FILTERS, "filter: filter_end_query %s",
- filter_query_to_text(q));
-
- if (q->type == QUERY_EOM && q->smtp.status == FILTER_OK) {
- if (s->error || q->u.datalen != s->idatalen) {
- response = "Internal error";
- q->smtp.code = 451;
- q->smtp.status = FILTER_FAIL;
- if (!s->error)
- log_warnx("filter: datalen mismatch on session %" PRIx64
- ": %zu/%zu", s->id, s->idatalen, q->u.datalen);
- }
- }
-
- log_trace(TRACE_FILTERS,
- "filter: query %016"PRIx64" done: "
- "status=%s code=%d response=\"%s\"",
- q->qid,
- status_to_str(q->smtp.status),
- q->smtp.code,
- response);
-
- smtp_filter_response(s->id, q->type, q->smtp.status, q->smtp.code,
- response);
- free(q->smtp.response);
- free(q);
-}
-
-static void
-filter_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct filter_proc *proc = p->data;
- struct filter_session *s;
- struct filter_query *q;
- struct msg m;
- const char *line;
- uint64_t qid;
- uint32_t datalen;
- int type, status, code;
-
- if (imsg == NULL) {
- log_warnx("warn: filter \"%s\" closed unexpectedly", p->name);
- fatalx("exiting");
- }
-
- log_trace(TRACE_FILTERS, "filter: imsg %s from procfilter %s",
- filterimsg_to_str(imsg->hdr.type),
- filter_proc_to_text(proc));
-
- switch (imsg->hdr.type) {
-
- case IMSG_FILTER_REGISTER:
- if (proc->ready) {
- log_warnx("warn: filter \"%s\" already registered",
- proc->mproc.name);
- exit(1);
- }
-
- m_msg(&m, imsg);
- m_get_int(&m, &proc->hooks);
- m_get_int(&m, &proc->flags);
- m_end(&m);
- proc->ready = 1;
-
- log_debug("debug: filter \"%s\": hooks 0x%08x flags 0x%04x",
- proc->mproc.name, proc->hooks, proc->flags);
-
- TAILQ_FOREACH(proc, &procs, entry)
- if (!proc->ready)
- return;
-
- smtp_configure();
- break;
-
- case IMSG_FILTER_RESPONSE:
- m_msg(&m, imsg);
- m_get_id(&m, &qid);
- m_get_int(&m, &type);
- if (type == QUERY_EOM)
- m_get_u32(&m, &datalen);
- m_get_int(&m, &status);
- m_get_int(&m, &code);
- if (m_is_eom(&m))
- line = NULL;
- else
- m_get_string(&m, &line);
- m_end(&m);
-
- q = tree_xpop(&queries, qid);
- if (q->type != type) {
- log_warnx("warn: filter: type mismatch %d != %d",
- q->type, type);
- fatalx("exiting");
- }
- q->smtp.status = status;
- if (code)
- q->smtp.code = code;
- if (line) {
- free(q->smtp.response);
- q->smtp.response = xstrdup(line, "filter_imsg");
- }
- q->state = (status == FILTER_OK) ? QUERY_READY : QUERY_DONE;
- if (type == QUERY_EOM)
- q->u.datalen = datalen;
-
- q->current = TAILQ_NEXT(q->current, entry);
- filter_drain_query(q);
- break;
-
- case IMSG_FILTER_PIPE:
- m_msg(&m, imsg);
- m_get_id(&m, &qid);
- m_end(&m);
-
- s = tree_xget(&sessions, qid);
- s->fcurr = TAILQ_PREV(s->fcurr, filter_lst, entry);
- filter_set_sink(s, imsg->fd);
- break;
-
- default:
- log_warnx("warn: bad imsg from filter %s", p->name);
- exit(1);
- }
-}
-
-static int
-filter_tx(struct filter_session *s, int sink)
-{
- int sp[2];
-
- s->idatalen = 0;
- s->eom = NULL;
- s->error = 0;
-
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) {
- log_warn("warn: filter: socketpair");
- return (-1);
- }
-
- if ((s->ofile = fdopen(sink, "w")) == NULL) {
- log_warn("warn: filter: fdopen");
- close(sp[0]);
- close(sp[1]);
- return (-1);
- }
-
- io_set_nonblocking(sp[0]);
- io_set_nonblocking(sp[1]);
-
- s->iev = io_new();
- io_set_callback(s->iev, filter_tx_io, s);
- io_set_fd(s->iev, sp[0]);
- io_set_read(s->iev);
-
- return (sp[1]);
-}
-
-static void
-filter_tx_io(struct io *io, int evt, void *arg)
-{
- struct filter_session *s = arg;
- size_t len, n;
- char *data;
-
- log_trace(TRACE_FILTERS, "filter: filter_tx_io(%p, %s)",
- s, io_strevent(evt));
-
- switch (evt) {
- case IO_DATAIN:
- data = io_data(s->iev);
- len = io_datalen(s->iev);
-
- log_trace(TRACE_FILTERS,
- "filter: filter_tx_io: datain (%zu) for req %016"PRIx64"",
- len, s->id);
-
- n = fwrite(data, 1, len, s->ofile);
- if (n != len) {
- log_warnx("warn: filter_tx_io: fwrite %zu/%zu", n, len);
- s->error = 1;
- break;
- }
- s->idatalen += n;
- io_drop(s->iev, n);
- return;
-
- case IO_DISCONNECTED:
- log_trace(TRACE_FILTERS,
- "debug: filter: tx done (%zu) for req %016"PRIx64,
- s->idatalen, s->id);
- break;
-
- default:
- log_warn("warn: filter_tx_io: bad evt (%d) for req %016"PRIx64,
- evt, s->id);
- s->error = 1;
- break;
- }
-
- io_free(s->iev);
- s->iev = NULL;
- fclose(s->ofile);
- s->ofile = NULL;
-
- /* deferred eom request */
- if (s->eom) {
- log_debug("filter: running eom query...");
- filter_end_query(s->eom);
- } else {
- log_debug("filter: eom not received yet");
- }
-}
-
-static const char *
-filter_query_to_text(struct filter_query *q)
-{
- static char buf[1024];
- char tmp[1024];
-
- tmp[0] = '\0';
-
- switch (q->type) {
- case QUERY_CONNECT:
- strlcat(tmp, "=", sizeof tmp);
- strlcat(tmp, ss_to_text(&q->u.connect.local),
- sizeof tmp);
- strlcat(tmp, " <-> ", sizeof tmp);
- strlcat(tmp, ss_to_text(&q->u.connect.remote),
- sizeof tmp);
- strlcat(tmp, "(", sizeof tmp);
- strlcat(tmp, q->u.connect.hostname, sizeof tmp);
- strlcat(tmp, ")", sizeof tmp);
- break;
- case QUERY_MAIL:
- case QUERY_RCPT:
- snprintf(tmp, sizeof tmp, "=%s@%s",
- q->u.maddr.user, q->u.maddr.domain);
- break;
- case QUERY_HELO:
- snprintf(tmp, sizeof tmp, "=%s", q->u.line);
- break;
- default:
- break;
- }
- snprintf(buf, sizeof buf, "%016"PRIx64"[%s%s,%s]",
- q->qid, query_to_str(q->type), tmp,
- filter_session_to_text(q->session));
-
- return (buf);
-}
-
-static const char *
-filter_session_to_text(struct filter_session *s)
-{
- static char buf[1024];
-
- if (s == NULL)
- return "filter_session@NULL";
-
- snprintf(buf, sizeof(buf),
- "filter_session@%p[datalen=%zu,eom=%p,ofile=%p]",
- s, s->idatalen, s->eom, s->ofile);
-
- return buf;
-}
-
-static const char *
-filter_to_text(struct filter *f)
-{
- static char buf[1024];
-
- snprintf(buf, sizeof buf, "filter:%s", filter_proc_to_text(f->proc));
-
- return (buf);
-}
-
-static const char *
-filter_proc_to_text(struct filter_proc *proc)
-{
- static char buf[1024];
-
- snprintf(buf, sizeof buf, "%s[hooks=0x%08x,flags=0x%04x]",
- proc->mproc.name, proc->hooks, proc->flags);
-
- return (buf);
-}
-
-#define CASE(x) case x : return #x
-
-static const char *
-filterimsg_to_str(int imsg)
-{
- switch (imsg) {
- CASE(IMSG_FILTER_REGISTER);
- CASE(IMSG_FILTER_EVENT);
- CASE(IMSG_FILTER_QUERY);
- CASE(IMSG_FILTER_PIPE);
- CASE(IMSG_FILTER_RESPONSE);
- default:
- return "IMSG_FILTER_???";
- }
-}
-
-static const char *
-query_to_str(int query)
-{
- switch (query) {
- CASE(QUERY_CONNECT);
- CASE(QUERY_HELO);
- CASE(QUERY_MAIL);
- CASE(QUERY_RCPT);
- CASE(QUERY_DATA);
- CASE(QUERY_EOM);
- CASE(QUERY_DATALINE);
- default:
- return "QUERY_???";
- }
-}
-
-static const char *
-event_to_str(int event)
-{
- switch (event) {
- CASE(EVENT_CONNECT);
- CASE(EVENT_RESET);
- CASE(EVENT_DISCONNECT);
- CASE(EVENT_TX_BEGIN);
- CASE(EVENT_TX_COMMIT);
- CASE(EVENT_TX_ROLLBACK);
- default:
- return "EVENT_???";
- }
-}
-
-static const char *
-status_to_str(int status)
-{
- switch (status) {
- CASE(FILTER_OK);
- CASE(FILTER_FAIL);
- CASE(FILTER_CLOSE);
- default:
- return "FILTER_???";
- }
-}
diff --git a/smtpd/forward.5 b/smtpd/forward.5
deleted file mode 100644
index 5a68f229..00000000
--- a/smtpd/forward.5
+++ /dev/null
@@ -1,83 +0,0 @@
-.\" $OpenBSD: forward.5,v 1.9 2015/03/13 22:41:54 eric Exp $
-.\"
-.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: March 13 2015 $
-.Dt FORWARD 5
-.Os
-.Sh NAME
-.Nm forward
-.Nd email forwarding information file
-.Sh DESCRIPTION
-Users may put a
-.Nm .forward
-file in their home directory.
-If this file exists,
-.Xr smtpd 8
-forwards email to the destinations specified therein.
-.Pp
-A
-.Nm .forward
-file contains a list of expansion values, as described in
-.Xr aliases 5 .
-Each expansion value should be on a line by itself.
-However, the
-.Nm .forward
-mechanism differs from the aliases mechanism in that it disallows
-file inclusion
-.Pq :include:
-and it performs expansion under the user ID of the
-.Nm .forward
-file owner.
-.Pp
-Permissions on the
-.Nm .forward
-file are very strict and expansion is rejected if the file is
-group or world-writable;
-if the home directory is group writeable;
-or if the file is not owned by the user.
-.Pp
-Users should avoid editing directly the
-.Nm .forward
-file to prevent delivery failures from occurring if a message
-arrives while the file is not fully written.
-The best option is to use a temporary file and use the
-.Xr mv 1
-command to atomically overwrite the former
-.Nm .forward .
-Alternatively, setting the
-.Xr sticky 8
-bit on the home directory will cause the
-.Nm .forward
-lookup to return a temporary failure, causing mails to be deferred.
-.Sh FILES
-.Bl -tag -width "~/.forwardXXX" -compact
-.It Pa ~/.forward
-Email forwarding information.
-.El
-.Sh EXAMPLES
-The following file forwards mail to
-.Dq user@example.com ,
-and pipes the same mail to
-.Dq examplemda .
-.Bd -literal -offset indent
-# empty lines are ignored
-
-user@example.com # anything after # is ignored
-"|/path/to/examplemda"
-.Ed
-.Sh SEE ALSO
-.Xr aliases 5 ,
-.Xr smtpd 8
diff --git a/smtpd/forward.c b/smtpd/forward.c
deleted file mode 100644
index 7494c6ce..00000000
--- a/smtpd/forward.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* $OpenBSD: forward.c,v 1.39 2015/12/28 22:08:30 jung Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <event.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_UTIL_H
-#include <util.h>
-#endif
-#ifdef HAVE_LIBUTIL_H
-#include <libutil.h>
-#endif
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define MAX_FORWARD_SIZE (4 * 1024)
-#define MAX_EXPAND_NODES (100)
-
-int
-forwards_get(int fd, struct expand *expand)
-{
- FILE *fp = NULL;
- char *line = NULL;
- size_t len;
- size_t lineno;
- size_t save;
- int ret;
- struct stat sb;
-
- ret = -1;
- if (fstat(fd, &sb) == -1)
- goto end;
-
- /* if it's empty just pretend that no expansion took place */
- if (sb.st_size == 0) {
- log_info("info: forward file is empty");
- ret = 0;
- goto end;
- }
-
- /* over MAX_FORWARD_SIZE, temporarily fail */
- if (sb.st_size >= MAX_FORWARD_SIZE) {
- log_info("info: forward file exceeds max size");
- goto end;
- }
-
- if ((fp = fdopen(fd, "r")) == NULL) {
- log_warn("warn: fdopen failure in forwards_get()");
- goto end;
- }
-
- lineno = 0;
- save = expand->nb_nodes;
- while ((line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL) {
- if (!expand_line(expand, line, 0)) {
- log_info("info: parse error in forward file");
- goto end;
- }
- if (expand->nb_nodes > MAX_EXPAND_NODES) {
- log_info("info: forward file expanded too many nodes");
- goto end;
- }
- free(line);
- }
-
- ret = expand->nb_nodes > save ? 1 : 0;
-
-end:
- free(line);
- if (fp)
- fclose(fp);
- else
- close(fd);
- return ret;
-}
diff --git a/smtpd/iobuf.c b/smtpd/iobuf.c
deleted file mode 100644
index dec10660..00000000
--- a/smtpd/iobuf.c
+++ /dev/null
@@ -1,462 +0,0 @@
-/* $OpenBSD: iobuf.c,v 1.13 2020/04/24 11:34:07 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-
-#include <errno.h>
-#include <limits.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);
- buf[i] = '\0';
- if (rlen)
- *rlen = i;
- 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);
- 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);
- return (IOBUF_ERROR);
- default:
- return (IOBUF_TLSERROR);
- }
- } else if (n == 0)
- return (IOBUF_CLOSED);
-
- io->wpos += n;
-
- return (n);
-}
-
-#endif /* IO_TLS */
diff --git a/smtpd/iobuf.h b/smtpd/iobuf.h
deleted file mode 100644
index c454d0a1..00000000
--- a/smtpd/iobuf.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/* $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 *);
diff --git a/smtpd/ioev.c b/smtpd/ioev.c
deleted file mode 100644
index e0a8a096..00000000
--- a/smtpd/ioev.c
+++ /dev/null
@@ -1,1064 +0,0 @@
-/* $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 "includes.h"
-
-#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, SA_LEN(bsa)) == -1)
- goto fail;
-
- if (connect(sock, sa, SA_LEN(sa)) == -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 */
diff --git a/smtpd/ioev.h b/smtpd/ioev.h
deleted file mode 100644
index f155a7fc..00000000
--- a/smtpd/ioev.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* $OpenBSD: ioev.h,v 1.18 2019/09/11 04:19:19 martijn 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 *, ...)
- __attribute__((__format__ (printf, 2, 3)));
-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);
diff --git a/smtpd/libressl.c b/smtpd/libressl.c
deleted file mode 100644
index 57d74389..00000000
--- a/smtpd/libressl.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to. The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code. The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * "This product includes cryptographic software written by
- * Eric Young (eay@cryptsoft.com)"
- * The word 'cryptographic' can be left out if the rouines from the library
- * being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- * the apps directory (application code) you must include an acknowledgement:
- * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed. i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.]
- */
-
-/*
- * SSL operations needed when running in a privilege separated environment.
- * Adapted from openssl's ssl_rsa.c by Pierre-Yves Ritschard .
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-
-#include <limits.h>
-#include <unistd.h>
-#include <stdio.h>
-
-#include <openssl/err.h>
-#include <openssl/bio.h>
-#include <openssl/objects.h>
-#include <openssl/evp.h>
-#include <openssl/x509.h>
-#include <openssl/pem.h>
-#include <openssl/ssl.h>
-
-#include "log.h"
-#include "ssl.h"
-
-#define SSL_ECDH_CURVE "prime256v1"
-
-/*
- * Read a bio that contains our certificate in "PEM" format,
- * possibly followed by a sequence of CA certificates that should be
- * sent to the peer in the Certificate message.
- */
-static int
-ssl_ctx_use_certificate_chain_bio(SSL_CTX *ctx, BIO *in)
-{
- int ret = 0;
- X509 *x = NULL;
-
- ERR_clear_error(); /* clear error stack for SSL_CTX_use_certificate() */
-
- x = PEM_read_bio_X509_AUX(in, NULL, ctx->default_passwd_callback,
- ctx->default_passwd_callback_userdata);
- if (x == NULL) {
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB);
- goto end;
- }
-
- ret = SSL_CTX_use_certificate(ctx, x);
-
- if (ERR_peek_error() != 0)
- ret = 0;
- /* Key/certificate mismatch doesn't imply ret==0 ... */
- if (ret) {
- /*
- * If we could set up our certificate, now proceed to
- * the CA certificates.
- */
- X509 *ca;
- int r;
- unsigned long err;
-
- if (ctx->extra_certs != NULL) {
- sk_X509_pop_free(ctx->extra_certs, X509_free);
- ctx->extra_certs = NULL;
- }
-
- while ((ca = PEM_read_bio_X509(in, NULL,
- ctx->default_passwd_callback,
- ctx->default_passwd_callback_userdata)) != NULL) {
- r = SSL_CTX_add_extra_chain_cert(ctx, ca);
- if (!r) {
- X509_free(ca);
- ret = 0;
- goto end;
- }
- /*
- * Note that we must not free r if it was successfully
- * added to the chain (while we must free the main
- * certificate, since its reference count is increased
- * by SSL_CTX_use_certificate).
- */
- }
-
- /* When the while loop ends, it's usually just EOF. */
- err = ERR_peek_last_error();
- if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
- ERR_GET_REASON(err) == PEM_R_NO_START_LINE)
- ERR_clear_error();
- else
- ret = 0; /* some real error */
- }
-
-end:
- if (x != NULL)
- X509_free(x);
- return (ret);
-}
-
-int
-SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *buf, int len)
-{
- BIO *in;
- int ret = 0;
-
- in = BIO_new_mem_buf(buf, len);
- if (in == NULL) {
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_BUF_LIB);
- goto end;
- }
-
- ret = ssl_ctx_use_certificate_chain_bio(ctx, in);
-
-end:
- BIO_free(in);
- return (ret);
-}
-
-#ifndef HAVE_SSL_CTX_SET_ECDH_AUTO
-void
-SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int enable)
-{
- int nid;
- EC_KEY *ecdh;
-
- if (!enable)
- return;
-
- if ((nid = OBJ_sn2nid(SSL_ECDH_CURVE)) == 0) {
- ssl_error("ssl_set_ecdh_auto");
- fatal("ssl_set_ecdh_auto: unknown curve name "
- SSL_ECDH_CURVE);
- }
-
- if ((ecdh = EC_KEY_new_by_curve_name(nid)) == NULL) {
- ssl_error("ssl_set_ecdh_auto");
- fatal("ssl_set_ecdh_auto: unable to create curve "
- SSL_ECDH_CURVE);
- }
-
- SSL_CTX_set_tmp_ecdh(ctx, ecdh);
- SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
- EC_KEY_free(ecdh);
-}
-#endif
-
-#ifndef HAVE_SSL_CTX_SET_DH_AUTO
-void
-SSL_CTX_set_dh_auto(SSL_CTX *ctx, int enable)
-{
- if (!enable)
- return;
-
- /* stub until OpenSSL catches up with this ... */
- log_warnx("OpenSSL does not support SSL_CTX_set_dh_auto (yet ?)");
- return;
-}
-#endif
diff --git a/smtpd/limit.c b/smtpd/limit.c
deleted file mode 100644
index 25e7a026..00000000
--- a/smtpd/limit.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/* $OpenBSD: limit.c,v 1.5 2016/06/15 19:59:03 gilles Exp $ */
-
-/*
- * Copyright (c) 2013 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <string.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-void
-limit_mta_set_defaults(struct mta_limits *limits)
-{
- limits->maxconn_per_host = 10;
- limits->maxconn_per_route = 5;
- limits->maxconn_per_source = 100;
- limits->maxconn_per_connector = 20;
- limits->maxconn_per_relay = 100;
- limits->maxconn_per_domain = 100;
-
- limits->conndelay_host = 0;
- limits->conndelay_route = 5;
- limits->conndelay_source = 0;
- limits->conndelay_connector = 0;
- limits->conndelay_relay = 2;
- limits->conndelay_domain = 0;
-
- limits->discdelay_route = 3;
-
- limits->max_mail_per_session = 100;
- limits->sessdelay_transaction = 0;
- limits->sessdelay_keepalive = 10;
-
- limits->max_failures_per_session = 25;
-
- limits->family = AF_UNSPEC;
-
- limits->task_hiwat = 50;
- limits->task_lowat = 30;
- limits->task_release = 10;
-}
-
-int
-limit_mta_set(struct mta_limits *limits, const char *key, int64_t value)
-{
- if (!strcmp(key, "max-conn-per-host"))
- limits->maxconn_per_host = value;
- else if (!strcmp(key, "max-conn-per-route"))
- limits->maxconn_per_route = value;
- else if (!strcmp(key, "max-conn-per-source"))
- limits->maxconn_per_source = value;
- else if (!strcmp(key, "max-conn-per-connector"))
- limits->maxconn_per_connector = value;
- else if (!strcmp(key, "max-conn-per-relay"))
- limits->maxconn_per_relay = value;
- else if (!strcmp(key, "max-conn-per-domain"))
- limits->maxconn_per_domain = value;
-
- else if (!strcmp(key, "conn-delay-host"))
- limits->conndelay_host = value;
- else if (!strcmp(key, "conn-delay-route"))
- limits->conndelay_route = value;
- else if (!strcmp(key, "conn-delay-source"))
- limits->conndelay_source = value;
- else if (!strcmp(key, "conn-delay-connector"))
- limits->conndelay_connector = value;
- else if (!strcmp(key, "conn-delay-relay"))
- limits->conndelay_relay = value;
- else if (!strcmp(key, "conn-delay-domain"))
- limits->conndelay_domain = value;
-
- else if (!strcmp(key, "reconn-delay-route"))
- limits->discdelay_route = value;
-
- else if (!strcmp(key, "session-mail-max"))
- limits->max_mail_per_session = value;
- else if (!strcmp(key, "session-transaction-delay"))
- limits->sessdelay_transaction = value;
- else if (!strcmp(key, "session-keepalive"))
- limits->sessdelay_keepalive = value;
-
- else if (!strcmp(key, "max-failures-per-session"))
- limits->max_failures_per_session = value;
-
- else if (!strcmp(key, "task-hiwat"))
- limits->task_hiwat = value;
- else if (!strcmp(key, "task-lowat"))
- limits->task_lowat = value;
- else if (!strcmp(key, "task-release"))
- limits->task_release = value;
-
- else
- return (0);
-
- return (1);
-}
diff --git a/smtpd/lka.c b/smtpd/lka.c
deleted file mode 100644
index 6ac21245..00000000
--- a/smtpd/lka.c
+++ /dev/null
@@ -1,914 +0,0 @@
-/* $OpenBSD: lka.c,v 1.243 2019/12/21 10:23:37 gilles Exp $ */
-
-/*
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2012 Eric Faurot <eric@faurot.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/uio.h>
-
-#include <netinet/in.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <netdb.h>
-#include <grp.h> /* needed for setgroups */
-#include <imsg.h>
-#include <openssl/err.h>
-#include <openssl/ssl.h>
-#include <pwd.h>
-#include <resolv.h>
-#include <limits.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-#include "ssl.h"
-
-static void lka_imsg(struct mproc *, struct imsg *);
-static void lka_shutdown(void);
-static void lka_sig_handler(int, short, void *);
-static int lka_authenticate(const char *, const char *, const char *);
-static int lka_credentials(const char *, const char *, char *, size_t);
-static int lka_userinfo(const char *, const char *, struct userinfo *);
-static int lka_addrname(const char *, const struct sockaddr *,
- struct addrname *);
-static int lka_mailaddrmap(const char *, const char *, const struct mailaddr *);
-
-static void proc_timeout(int fd, short event, void *p);
-
-struct event ev_proc_ready;
-
-static void
-lka_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct table *table;
- int ret;
- struct sockaddr_storage ss;
- struct userinfo userinfo;
- struct addrname addrname;
- struct envelope evp;
- struct mailaddr maddr;
- struct msg m;
- union lookup lk;
- char buf[LINE_MAX];
- const char *tablename, *username, *password, *label, *procname;
- uint64_t reqid;
- int v;
- struct timeval tv;
- const char *direction;
- const char *rdns;
- const char *command;
- const char *response;
- const char *ciphers;
- const char *address;
- const char *domain;
- const char *helomethod;
- const char *heloname;
- const char *filter_name;
- const char *result;
- struct sockaddr_storage ss_src, ss_dest;
- int filter_response;
- int filter_phase;
- const char *filter_param;
- uint32_t msgid;
- uint32_t subsystems;
- uint64_t evpid;
- size_t msgsz;
- int ok;
- int fcrdns;
-
- if (imsg == NULL)
- lka_shutdown();
-
- switch (imsg->hdr.type) {
-
- case IMSG_GETADDRINFO:
- case IMSG_GETNAMEINFO:
- case IMSG_RES_QUERY:
- resolver_dispatch_request(p, imsg);
- return;
-
- case IMSG_CERT_INIT:
- case IMSG_CERT_CERTIFICATE:
- case IMSG_CERT_VERIFY:
- cert_dispatch_request(p, imsg);
- return;
-
- case IMSG_MTA_DNS_HOST:
- case IMSG_MTA_DNS_MX:
- case IMSG_MTA_DNS_MX_PREFERENCE:
- dns_imsg(p, imsg);
- return;
-
- case IMSG_SMTP_CHECK_SENDER:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &tablename);
- m_get_string(&m, &username);
- m_get_mailaddr(&m, &maddr);
- m_end(&m);
-
- ret = lka_mailaddrmap(tablename, username, &maddr);
-
- m_create(p, IMSG_SMTP_CHECK_SENDER, 0, 0, -1);
- m_add_id(p, reqid);
- m_add_int(p, ret);
- m_close(p);
- return;
-
- case IMSG_SMTP_EXPAND_RCPT:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_envelope(&m, &evp);
- m_end(&m);
- lka_session(reqid, &evp);
- return;
-
- case IMSG_SMTP_LOOKUP_HELO:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &tablename);
- m_get_sockaddr(&m, (struct sockaddr *)&ss);
- m_end(&m);
-
- ret = lka_addrname(tablename, (struct sockaddr*)&ss,
- &addrname);
-
- m_create(p, IMSG_SMTP_LOOKUP_HELO, 0, 0, -1);
- m_add_id(p, reqid);
- m_add_int(p, ret);
- if (ret == LKA_OK)
- m_add_string(p, addrname.name);
- m_close(p);
- return;
-
- case IMSG_SMTP_AUTHENTICATE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &tablename);
- m_get_string(&m, &username);
- m_get_string(&m, &password);
- m_end(&m);
-
- if (!tablename[0]) {
- m_create(p_parent, IMSG_LKA_AUTHENTICATE,
- 0, 0, -1);
- m_add_id(p_parent, reqid);
- m_add_string(p_parent, username);
- m_add_string(p_parent, password);
- m_close(p_parent);
- return;
- }
-
- ret = lka_authenticate(tablename, username, password);
-
- m_create(p, IMSG_SMTP_AUTHENTICATE, 0, 0, -1);
- m_add_id(p, reqid);
- m_add_int(p, ret);
- m_close(p);
- return;
-
- case IMSG_MDA_LOOKUP_USERINFO:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &tablename);
- m_get_string(&m, &username);
- m_end(&m);
-
- ret = lka_userinfo(tablename, username, &userinfo);
-
- m_create(p, IMSG_MDA_LOOKUP_USERINFO, 0, 0, -1);
- m_add_id(p, reqid);
- m_add_int(p, ret);
- if (ret == LKA_OK)
- m_add_data(p, &userinfo, sizeof(userinfo));
- m_close(p);
- return;
-
- case IMSG_MTA_LOOKUP_CREDENTIALS:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &tablename);
- m_get_string(&m, &label);
- m_end(&m);
-
- lka_credentials(tablename, label, buf, sizeof(buf));
-
- m_create(p, IMSG_MTA_LOOKUP_CREDENTIALS, 0, 0, -1);
- m_add_id(p, reqid);
- m_add_string(p, buf);
- m_close(p);
- return;
-
- case IMSG_MTA_LOOKUP_SOURCE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &tablename);
- m_end(&m);
-
- table = table_find(env, tablename);
-
- m_create(p, IMSG_MTA_LOOKUP_SOURCE, 0, 0, -1);
- m_add_id(p, reqid);
-
- if (table == NULL) {
- log_warn("warn: source address table %s missing",
- tablename);
- m_add_int(p, LKA_TEMPFAIL);
- }
- else {
- ret = table_fetch(table, K_SOURCE, &lk);
- if (ret == -1)
- m_add_int(p, LKA_TEMPFAIL);
- else if (ret == 0)
- m_add_int(p, LKA_PERMFAIL);
- else {
- m_add_int(p, LKA_OK);
- m_add_sockaddr(p,
- (struct sockaddr *)&lk.source.addr);
- }
- }
- m_close(p);
- return;
-
- case IMSG_MTA_LOOKUP_HELO:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &tablename);
- m_get_sockaddr(&m, (struct sockaddr *)&ss);
- m_end(&m);
-
- ret = lka_addrname(tablename, (struct sockaddr*)&ss,
- &addrname);
-
- m_create(p, IMSG_MTA_LOOKUP_HELO, 0, 0, -1);
- m_add_id(p, reqid);
- m_add_int(p, ret);
- if (ret == LKA_OK)
- m_add_string(p, addrname.name);
- m_close(p);
- return;
-
- case IMSG_MTA_LOOKUP_SMARTHOST:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &domain);
- m_get_string(&m, &tablename);
- m_end(&m);
-
- table = table_find(env, tablename);
-
- m_create(p, IMSG_MTA_LOOKUP_SMARTHOST, 0, 0, -1);
- m_add_id(p, reqid);
-
- if (table == NULL) {
- log_warn("warn: smarthost table %s missing", tablename);
- m_add_int(p, LKA_TEMPFAIL);
- }
- else {
- if (domain == NULL)
- ret = table_fetch(table, K_RELAYHOST, &lk);
- else
- ret = table_lookup(table, K_RELAYHOST, domain, &lk);
-
- if (ret == -1)
- m_add_int(p, LKA_TEMPFAIL);
- else if (ret == 0)
- m_add_int(p, LKA_PERMFAIL);
- else {
- m_add_int(p, LKA_OK);
- m_add_string(p, lk.relayhost);
- }
- }
- m_close(p);
- return;
-
- case IMSG_CONF_START:
- return;
-
- case IMSG_CONF_END:
- if (tracing & TRACE_TABLES)
- table_dump_all(env);
-
- /* fork & exec tables that need it */
- table_open_all(env);
-
-#if HAVE_PLEDGE
- /* revoke proc & exec */
- if (pledge("stdio rpath inet dns getpw recvfd sendfd",
- NULL) == -1)
- err(1, "pledge");
-#endif
-
- /* setup proc registering task */
- evtimer_set(&ev_proc_ready, proc_timeout, &ev_proc_ready);
- tv.tv_sec = 0;
- tv.tv_usec = 10;
- evtimer_add(&ev_proc_ready, &tv);
- return;
-
- case IMSG_LKA_OPEN_FORWARD:
- lka_session_forward_reply(imsg->data, imsg->fd);
- return;
-
- case IMSG_LKA_AUTHENTICATE:
- imsg->hdr.type = IMSG_SMTP_AUTHENTICATE;
- m_forward(p_pony, imsg);
- return;
-
- case IMSG_CTL_VERBOSE:
- m_msg(&m, imsg);
- m_get_int(&m, &v);
- m_end(&m);
- log_trace_verbose(v);
- return;
-
- case IMSG_CTL_PROFILE:
- m_msg(&m, imsg);
- m_get_int(&m, &v);
- m_end(&m);
- profiling = v;
- return;
-
- case IMSG_CTL_UPDATE_TABLE:
- ret = 0;
- table = table_find(env, imsg->data);
- if (table == NULL) {
- log_warnx("warn: Lookup table not found: "
- "\"%s\"", (char *)imsg->data);
- } else
- ret = table_update(table);
-
- m_compose(p_control,
- (ret == 1) ? IMSG_CTL_OK : IMSG_CTL_FAIL,
- imsg->hdr.peerid, 0, -1, NULL, 0);
- return;
-
- case IMSG_LKA_PROCESSOR_FORK:
- m_msg(&m, imsg);
- m_get_string(&m, &procname);
- m_get_u32(&m, &subsystems);
- m_end(&m);
-
- m_create(p, IMSG_LKA_PROCESSOR_ERRFD, 0, 0, -1);
- m_add_string(p, procname);
- m_close(p);
-
- lka_proc_forked(procname, subsystems, imsg->fd);
- return;
-
- case IMSG_LKA_PROCESSOR_ERRFD:
- m_msg(&m, imsg);
- m_get_string(&m, &procname);
- m_end(&m);
-
- lka_proc_errfd(procname, imsg->fd);
- shutdown(imsg->fd, SHUT_WR);
- return;
-
- case IMSG_REPORT_SMTP_LINK_CONNECT:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_string(&m, &rdns);
- m_get_int(&m, &fcrdns);
- m_get_sockaddr(&m, (struct sockaddr *)&ss_src);
- m_get_sockaddr(&m, (struct sockaddr *)&ss_dest);
- m_end(&m);
-
- lka_report_smtp_link_connect(direction, &tv, reqid, rdns, fcrdns, &ss_src, &ss_dest);
- return;
-
- case IMSG_REPORT_SMTP_LINK_GREETING:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_string(&m, &domain);
- m_end(&m);
-
- lka_report_smtp_link_greeting(direction, reqid, &tv, domain);
- return;
-
- case IMSG_REPORT_SMTP_LINK_DISCONNECT:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_end(&m);
-
- lka_report_smtp_link_disconnect(direction, &tv, reqid);
- return;
-
- case IMSG_REPORT_SMTP_LINK_IDENTIFY:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_string(&m, &helomethod);
- m_get_string(&m, &heloname);
- m_end(&m);
-
- lka_report_smtp_link_identify(direction, &tv, reqid, helomethod, heloname);
- return;
-
- case IMSG_REPORT_SMTP_LINK_TLS:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_string(&m, &ciphers);
- m_end(&m);
-
- lka_report_smtp_link_tls(direction, &tv, reqid, ciphers);
- return;
-
- case IMSG_REPORT_SMTP_LINK_AUTH:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_string(&m, &username);
- m_get_string(&m, &result);
- m_end(&m);
-
- lka_report_smtp_link_auth(direction, &tv, reqid, username, result);
- return;
-
- case IMSG_REPORT_SMTP_TX_RESET:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_u32(&m, &msgid);
- m_end(&m);
-
- lka_report_smtp_tx_reset(direction, &tv, reqid, msgid);
- return;
-
- case IMSG_REPORT_SMTP_TX_BEGIN:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_u32(&m, &msgid);
- m_end(&m);
-
- lka_report_smtp_tx_begin(direction, &tv, reqid, msgid);
- return;
-
- case IMSG_REPORT_SMTP_TX_MAIL:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_u32(&m, &msgid);
- m_get_string(&m, &address);
- m_get_int(&m, &ok);
- m_end(&m);
-
- lka_report_smtp_tx_mail(direction, &tv, reqid, msgid, address, ok);
- return;
-
- case IMSG_REPORT_SMTP_TX_RCPT:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_u32(&m, &msgid);
- m_get_string(&m, &address);
- m_get_int(&m, &ok);
- m_end(&m);
-
- lka_report_smtp_tx_rcpt(direction, &tv, reqid, msgid, address, ok);
- return;
-
- case IMSG_REPORT_SMTP_TX_ENVELOPE:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_u32(&m, &msgid);
- m_get_id(&m, &evpid);
- m_end(&m);
-
- lka_report_smtp_tx_envelope(direction, &tv, reqid, msgid, evpid);
- return;
-
- case IMSG_REPORT_SMTP_TX_DATA:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_u32(&m, &msgid);
- m_get_int(&m, &ok);
- m_end(&m);
-
- lka_report_smtp_tx_data(direction, &tv, reqid, msgid, ok);
- return;
-
- case IMSG_REPORT_SMTP_TX_COMMIT:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_u32(&m, &msgid);
- m_get_size(&m, &msgsz);
- m_end(&m);
-
- lka_report_smtp_tx_commit(direction, &tv, reqid, msgid, msgsz);
- return;
-
- case IMSG_REPORT_SMTP_TX_ROLLBACK:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_u32(&m, &msgid);
- m_end(&m);
-
- lka_report_smtp_tx_rollback(direction, &tv, reqid, msgid);
- return;
-
- case IMSG_REPORT_SMTP_PROTOCOL_CLIENT:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_string(&m, &command);
- m_end(&m);
-
- lka_report_smtp_protocol_client(direction, &tv, reqid, command);
- return;
-
- case IMSG_REPORT_SMTP_PROTOCOL_SERVER:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_string(&m, &response);
- m_end(&m);
-
- lka_report_smtp_protocol_server(direction, &tv, reqid, response);
- return;
-
- case IMSG_REPORT_SMTP_FILTER_RESPONSE:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_get_int(&m, &filter_phase);
- m_get_int(&m, &filter_response);
- m_get_string(&m, &filter_param);
- m_end(&m);
-
- lka_report_smtp_filter_response(direction, &tv, reqid,
- filter_phase, filter_response, filter_param);
- return;
-
- case IMSG_REPORT_SMTP_TIMEOUT:
- m_msg(&m, imsg);
- m_get_string(&m, &direction);
- m_get_timeval(&m, &tv);
- m_get_id(&m, &reqid);
- m_end(&m);
-
- lka_report_smtp_timeout(direction, &tv, reqid);
- return;
-
- case IMSG_FILTER_SMTP_PROTOCOL:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &filter_phase);
- m_get_string(&m, &filter_param);
- m_end(&m);
-
- lka_filter_protocol(reqid, filter_phase, filter_param);
- return;
-
- case IMSG_FILTER_SMTP_BEGIN:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &filter_name);
- m_end(&m);
-
- lka_filter_begin(reqid, filter_name);
- return;
-
- case IMSG_FILTER_SMTP_END:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_end(&m);
-
- lka_filter_end(reqid);
- return;
-
- case IMSG_FILTER_SMTP_DATA_BEGIN:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_end(&m);
-
- lka_filter_data_begin(reqid);
- return;
-
- case IMSG_FILTER_SMTP_DATA_END:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_end(&m);
-
- lka_filter_data_end(reqid);
- return;
-
- }
-
- errx(1, "lka_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
-}
-
-static void
-lka_sig_handler(int sig, short event, void *p)
-{
- int status;
- pid_t pid;
-
- switch (sig) {
- case SIGCHLD:
- do {
- pid = waitpid(-1, &status, WNOHANG);
- } while (pid > 0 || (pid == -1 && errno == EINTR));
- break;
- default:
- fatalx("lka_sig_handler: unexpected signal");
- }
-}
-
-void
-lka_shutdown(void)
-{
- log_debug("debug: lookup agent exiting");
- _exit(0);
-}
-
-int
-lka(void)
-{
- struct passwd *pw;
- struct event ev_sigchld;
-
- purge_config(PURGE_LISTENERS);
-
- if ((pw = getpwnam(SMTPD_USER)) == NULL)
- fatalx("unknown user " SMTPD_USER);
-
- config_process(PROC_LKA);
-
- if (initgroups(pw->pw_name, pw->pw_gid) ||
- setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
- setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
- fatal("lka: cannot drop privileges");
-
- imsg_callback = lka_imsg;
- event_init();
-
- signal_set(&ev_sigchld, SIGCHLD, lka_sig_handler, NULL);
- signal_add(&ev_sigchld, NULL);
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, SIG_IGN);
- signal(SIGPIPE, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-
- config_peer(PROC_PARENT);
- config_peer(PROC_QUEUE);
- config_peer(PROC_CONTROL);
- config_peer(PROC_PONY);
-
- /* Ignore them until we get our config */
- mproc_disable(p_pony);
-
- lka_report_init();
- lka_filter_init();
-
-#if HAVE_PLEDGE
- /* proc & exec will be revoked before serving requests */
- if (pledge("stdio rpath inet dns getpw recvfd sendfd proc exec", NULL) == -1)
- err(1, "pledge");
-#endif
-
- event_dispatch();
- fatalx("exited event loop");
-
- return (0);
-}
-
-static void
-proc_timeout(int fd, short event, void *p)
-{
- struct event *ev = p;
- struct timeval tv;
-
- if (!lka_proc_ready())
- goto reset;
-
- lka_filter_ready();
- mproc_enable(p_pony);
- return;
-
-reset:
- tv.tv_sec = 0;
- tv.tv_usec = 10;
- evtimer_add(ev, &tv);
-}
-
-
-static int
-lka_authenticate(const char *tablename, const char *user, const char *password)
-{
- struct table *table;
- union lookup lk;
-
- log_debug("debug: lka: authenticating for %s:%s", tablename, user);
- table = table_find(env, tablename);
- if (table == NULL) {
- log_warnx("warn: could not find table %s needed for authentication",
- tablename);
- return (LKA_TEMPFAIL);
- }
-
- switch (table_lookup(table, K_CREDENTIALS, user, &lk)) {
- case -1:
- log_warnx("warn: user credentials lookup fail for %s:%s",
- tablename, user);
- return (LKA_TEMPFAIL);
- case 0:
- return (LKA_PERMFAIL);
- default:
- if (crypt_checkpass(password, lk.creds.password) == 0)
- return (LKA_OK);
- return (LKA_PERMFAIL);
- }
-}
-
-static int
-lka_credentials(const char *tablename, const char *label, char *dst, size_t sz)
-{
- struct table *table;
- union lookup lk;
- char *buf;
- int buflen, r;
-
- table = table_find(env, tablename);
- if (table == NULL) {
- log_warnx("warn: credentials table %s missing", tablename);
- return (LKA_TEMPFAIL);
- }
-
- dst[0] = '\0';
-
- switch (table_lookup(table, K_CREDENTIALS, label, &lk)) {
- case -1:
- log_warnx("warn: credentials lookup fail for %s:%s",
- tablename, label);
- return (LKA_TEMPFAIL);
- case 0:
- log_warnx("warn: credentials not found for %s:%s",
- tablename, label);
- return (LKA_PERMFAIL);
- default:
- if ((buflen = asprintf(&buf, "%c%s%c%s", '\0',
- lk.creds.username, '\0', lk.creds.password)) == -1) {
- log_warn("warn");
- return (LKA_TEMPFAIL);
- }
-
- r = base64_encode((unsigned char *)buf, buflen, dst, sz);
- free(buf);
-
- if (r == -1) {
- log_warnx("warn: credentials parse error for %s:%s",
- tablename, label);
- return (LKA_TEMPFAIL);
- }
- return (LKA_OK);
- }
-}
-
-static int
-lka_userinfo(const char *tablename, const char *username, struct userinfo *res)
-{
- struct table *table;
- union lookup lk;
-
- log_debug("debug: lka: userinfo %s:%s", tablename, username);
- table = table_find(env, tablename);
- if (table == NULL) {
- log_warnx("warn: cannot find user table %s", tablename);
- return (LKA_TEMPFAIL);
- }
-
- switch (table_lookup(table, K_USERINFO, username, &lk)) {
- case -1:
- log_warnx("warn: failure during userinfo lookup %s:%s",
- tablename, username);
- return (LKA_TEMPFAIL);
- case 0:
- return (LKA_PERMFAIL);
- default:
- *res = lk.userinfo;
- return (LKA_OK);
- }
-}
-
-static int
-lka_addrname(const char *tablename, const struct sockaddr *sa,
- struct addrname *res)
-{
- struct table *table;
- union lookup lk;
- const char *source;
-
- source = sa_to_text(sa);
-
- log_debug("debug: lka: helo %s:%s", tablename, source);
- table = table_find(env, tablename);
- if (table == NULL) {
- log_warnx("warn: cannot find helo table %s", tablename);
- return (LKA_TEMPFAIL);
- }
-
- switch (table_lookup(table, K_ADDRNAME, source, &lk)) {
- case -1:
- log_warnx("warn: failure during helo lookup %s:%s",
- tablename, source);
- return (LKA_TEMPFAIL);
- case 0:
- return (LKA_PERMFAIL);
- default:
- *res = lk.addrname;
- return (LKA_OK);
- }
-}
-
-static int
-lka_mailaddrmap(const char *tablename, const char *username, const struct mailaddr *maddr)
-{
- struct table *table;
- struct maddrnode *mn;
- union lookup lk;
- int found;
-
- log_debug("debug: lka: mailaddrmap %s:%s", tablename, username);
- table = table_find(env, tablename);
- if (table == NULL) {
- log_warnx("warn: cannot find mailaddrmap table %s", tablename);
- return (LKA_TEMPFAIL);
- }
-
- switch (table_lookup(table, K_MAILADDRMAP, username, &lk)) {
- case -1:
- log_warnx("warn: failure during mailaddrmap lookup %s:%s",
- tablename, username);
- return (LKA_TEMPFAIL);
- case 0:
- return (LKA_PERMFAIL);
- default:
- found = 0;
- TAILQ_FOREACH(mn, &lk.maddrmap->queue, entries) {
- if (!mailaddr_match(maddr, &mn->mailaddr))
- continue;
- found = 1;
- break;
- }
- maddrmap_free(lk.maddrmap);
- if (found)
- return (LKA_OK);
- return (LKA_PERMFAIL);
- }
- return (LKA_OK);
-}
diff --git a/smtpd/lka_filter.c b/smtpd/lka_filter.c
deleted file mode 100644
index 2dc66057..00000000
--- a/smtpd/lka_filter.c
+++ /dev/null
@@ -1,1746 +0,0 @@
-/* $OpenBSD: lka_filter.c,v 1.62 2020/04/24 11:34:07 eric Exp $ */
-
-/*
- * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <netinet/in.h>
-
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define PROTOCOL_VERSION "0.6"
-
-struct filter;
-struct filter_session;
-static void filter_protocol_internal(struct filter_session *, uint64_t *, uint64_t, enum filter_phase, const char *);
-static void filter_protocol(uint64_t, enum filter_phase, const char *);
-static void filter_protocol_next(uint64_t, uint64_t, enum filter_phase);
-static void filter_protocol_query(struct filter *, uint64_t, uint64_t, const char *, const char *);
-
-static void filter_data_internal(struct filter_session *, uint64_t, uint64_t, const char *);
-static void filter_data(uint64_t, const char *);
-static void filter_data_next(uint64_t, uint64_t, const char *);
-static void filter_data_query(struct filter *, uint64_t, uint64_t, const char *);
-
-static int filter_builtins_notimpl(struct filter_session *, struct filter *, uint64_t, const char *);
-static int filter_builtins_connect(struct filter_session *, struct filter *, uint64_t, const char *);
-static int filter_builtins_helo(struct filter_session *, struct filter *, uint64_t, const char *);
-static int filter_builtins_mail_from(struct filter_session *, struct filter *, uint64_t, const char *);
-static int filter_builtins_rcpt_to(struct filter_session *, struct filter *, uint64_t, const char *);
-static int filter_builtins_data(struct filter_session *, struct filter *, uint64_t, const char *);
-static int filter_builtins_commit(struct filter_session *, struct filter *, uint64_t, const char *);
-
-static void filter_result_proceed(uint64_t);
-static void filter_result_junk(uint64_t);
-static void filter_result_rewrite(uint64_t, const char *);
-static void filter_result_reject(uint64_t, const char *);
-static void filter_result_disconnect(uint64_t, const char *);
-
-static void filter_session_io(struct io *, int, void *);
-void lka_filter_process_response(const char *, const char *);
-
-
-struct filter_session {
- uint64_t id;
- struct io *io;
-
- char *lastparam;
-
- char *filter_name;
- struct sockaddr_storage ss_src;
- struct sockaddr_storage ss_dest;
- char *rdns;
- int fcrdns;
-
- char *helo;
- char *username;
- char *mail_from;
-
- enum filter_phase phase;
-};
-
-static struct filter_exec {
- enum filter_phase phase;
- const char *phase_name;
- int (*func)(struct filter_session *, struct filter *, uint64_t, const char *);
-} filter_execs[FILTER_PHASES_COUNT] = {
- { FILTER_CONNECT, "connect", filter_builtins_connect },
- { FILTER_HELO, "helo", filter_builtins_helo },
- { FILTER_EHLO, "ehlo", filter_builtins_helo },
- { FILTER_STARTTLS, "starttls", filter_builtins_notimpl },
- { FILTER_AUTH, "auth", filter_builtins_notimpl },
- { FILTER_MAIL_FROM, "mail-from", filter_builtins_mail_from },
- { FILTER_RCPT_TO, "rcpt-to", filter_builtins_rcpt_to },
- { FILTER_DATA, "data", filter_builtins_data },
- { FILTER_DATA_LINE, "data-line", filter_builtins_notimpl },
- { FILTER_RSET, "rset", filter_builtins_notimpl },
- { FILTER_QUIT, "quit", filter_builtins_notimpl },
- { FILTER_NOOP, "noop", filter_builtins_notimpl },
- { FILTER_HELP, "help", filter_builtins_notimpl },
- { FILTER_WIZ, "wiz", filter_builtins_notimpl },
- { FILTER_COMMIT, "commit", filter_builtins_commit },
-};
-
-struct filter {
- uint64_t id;
- uint32_t phases;
- const char *name;
- const char *proc;
- struct filter **chain;
- size_t chain_size;
- struct filter_config *config;
-};
-static struct dict filters;
-
-struct filter_entry {
- TAILQ_ENTRY(filter_entry) entries;
- uint64_t id;
- const char *name;
-};
-
-struct filter_chain {
- TAILQ_HEAD(, filter_entry) chain[nitems(filter_execs)];
-};
-
-static struct dict filter_smtp_in;
-
-static struct tree sessions;
-static int filters_inited;
-
-static struct dict filter_chains;
-
-struct reporter_proc {
- TAILQ_ENTRY(reporter_proc) entries;
- const char *name;
-};
-TAILQ_HEAD(reporters, reporter_proc);
-
-static struct dict report_smtp_in;
-static struct dict report_smtp_out;
-
-static struct smtp_events {
- const char *event;
-} smtp_events[] = {
- { "link-connect" },
- { "link-disconnect" },
- { "link-greeting" },
- { "link-identify" },
- { "link-tls" },
- { "link-auth" },
-
- { "tx-reset" },
- { "tx-begin" },
- { "tx-mail" },
- { "tx-rcpt" },
- { "tx-envelope" },
- { "tx-data" },
- { "tx-commit" },
- { "tx-rollback" },
-
- { "protocol-client" },
- { "protocol-server" },
-
- { "filter-report" },
- { "filter-response" },
-
- { "timeout" },
-};
-
-static int processors_inited = 0;
-static struct dict processors;
-
-struct processor_instance {
- char *name;
- struct io *io;
- struct io *errfd;
- int ready;
- uint32_t subsystems;
-};
-
-static void processor_io(struct io *, int, void *);
-static void processor_errfd(struct io *, int, void *);
-void lka_filter_process_response(const char *, const char *);
-
-int
-lka_proc_ready(void)
-{
- void *iter;
- struct processor_instance *pi;
-
- iter = NULL;
- while (dict_iter(&processors, &iter, NULL, (void **)&pi))
- if (!pi->ready)
- return 0;
- return 1;
-}
-
-static void
-lka_proc_config(struct processor_instance *pi)
-{
- io_printf(pi->io, "config|smtpd-version|%s\n", SMTPD_VERSION);
- io_printf(pi->io, "config|smtp-session-timeout|%d\n", SMTPD_SESSION_TIMEOUT);
- if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_IN)
- io_printf(pi->io, "config|subsystem|smtp-in\n");
- if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_OUT)
- io_printf(pi->io, "config|subsystem|smtp-out\n");
- io_printf(pi->io, "config|ready\n");
-}
-
-void
-lka_proc_forked(const char *name, uint32_t subsystems, int fd)
-{
- struct processor_instance *processor;
-
- if (!processors_inited) {
- dict_init(&processors);
- processors_inited = 1;
- }
-
- processor = xcalloc(1, sizeof *processor);
- processor->name = xstrdup(name);
- processor->io = io_new();
- processor->subsystems = subsystems;
-
- io_set_nonblocking(fd);
-
- io_set_fd(processor->io, fd);
- io_set_callback(processor->io, processor_io, processor->name);
- dict_xset(&processors, name, processor);
-}
-
-void
-lka_proc_errfd(const char *name, int fd)
-{
- struct processor_instance *processor;
-
- processor = dict_xget(&processors, name);
-
- io_set_nonblocking(fd);
-
- processor->errfd = io_new();
- io_set_fd(processor->errfd, fd);
- io_set_callback(processor->errfd, processor_errfd, processor->name);
-
- lka_proc_config(processor);
-}
-
-struct io *
-lka_proc_get_io(const char *name)
-{
- struct processor_instance *processor;
-
- processor = dict_xget(&processors, name);
-
- return processor->io;
-}
-
-static void
-processor_register(const char *name, const char *line)
-{
- struct processor_instance *processor;
-
- processor = dict_xget(&processors, name);
-
- if (strcmp(line, "register|ready") == 0) {
- processor->ready = 1;
- return;
- }
-
- if (strncmp(line, "register|report|", 16) == 0) {
- lka_report_register_hook(name, line+16);
- return;
- }
-
- if (strncmp(line, "register|filter|", 16) == 0) {
- lka_filter_register_hook(name, line+16);
- return;
- }
-
- fatalx("Invalid register line received: %s", line);
-}
-
-static void
-processor_io(struct io *io, int evt, void *arg)
-{
- struct processor_instance *processor;
- const char *name = arg;
- char *line = NULL;
- ssize_t len;
-
- switch (evt) {
- case IO_DATAIN:
- while ((line = io_getline(io, &len)) != NULL) {
- if (strncmp("register|", line, 9) == 0) {
- processor_register(name, line);
- continue;
- }
-
- processor = dict_xget(&processors, name);
- if (!processor->ready)
- fatalx("Non-register message before register|"
- "ready: %s", line);
- else if (strncmp(line, "filter-result|", 14) == 0 ||
- strncmp(line, "filter-dataline|", 16) == 0)
- lka_filter_process_response(name, line);
- else if (strncmp(line, "report|", 7) == 0)
- lka_report_proc(name, line);
- else
- fatalx("Invalid filter message type: %s", line);
- }
- }
-}
-
-static void
-processor_errfd(struct io *io, int evt, void *arg)
-{
- const char *name = arg;
- char *line = NULL;
- ssize_t len;
-
- switch (evt) {
- case IO_DATAIN:
- while ((line = io_getline(io, &len)) != NULL)
- log_warnx("%s: %s", name, line);
- }
-}
-
-void
-lka_filter_init(void)
-{
- void *iter;
- const char *name;
- struct filter *filter;
- struct filter_config *filter_config;
- size_t i;
- char buffer[LINE_MAX]; /* for traces */
-
- dict_init(&filters);
- dict_init(&filter_chains);
-
- /* first pass, allocate and init individual filters */
- iter = NULL;
- while (dict_iter(env->sc_filters_dict, &iter, &name, (void **)&filter_config)) {
- switch (filter_config->filter_type) {
- case FILTER_TYPE_BUILTIN:
- filter = xcalloc(1, sizeof(*filter));
- filter->name = name;
- filter->phases |= (1<<filter_config->phase);
- filter->config = filter_config;
- dict_set(&filters, name, filter);
- log_trace(TRACE_FILTERS, "filters init type=builtin, name=%s, hooks=%08x",
- name, filter->phases);
- break;
-
- case FILTER_TYPE_PROC:
- filter = xcalloc(1, sizeof(*filter));
- filter->name = name;
- filter->proc = filter_config->proc;
- filter->config = filter_config;
- dict_set(&filters, name, filter);
- log_trace(TRACE_FILTERS, "filters init type=proc, name=%s, proc=%s",
- name, filter_config->proc);
- break;
-
- case FILTER_TYPE_CHAIN:
- break;
- }
- }
-
- /* second pass, allocate and init filter chains but don't build yet */
- iter = NULL;
- while (dict_iter(env->sc_filters_dict, &iter, &name, (void **)&filter_config)) {
- switch (filter_config->filter_type) {
- case FILTER_TYPE_CHAIN:
- filter = xcalloc(1, sizeof(*filter));
- filter->name = name;
- filter->chain = xcalloc(filter_config->chain_size, sizeof(void **));
- filter->chain_size = filter_config->chain_size;
- filter->config = filter_config;
-
- buffer[0] = '\0';
- for (i = 0; i < filter->chain_size; ++i) {
- filter->chain[i] = dict_xget(&filters, filter_config->chain[i]);
- if (i)
- (void)strlcat(buffer, ", ", sizeof buffer);
- (void)strlcat(buffer, filter->chain[i]->name, sizeof buffer);
- }
- log_trace(TRACE_FILTERS, "filters init type=chain, name=%s { %s }", name, buffer);
-
- dict_set(&filters, name, filter);
- break;
-
- case FILTER_TYPE_BUILTIN:
- case FILTER_TYPE_PROC:
- break;
- }
- }
-}
-
-void
-lka_filter_register_hook(const char *name, const char *hook)
-{
- struct dict *subsystem;
- struct filter *filter;
- const char *filter_name;
- void *iter;
- size_t i;
-
- if (strncasecmp(hook, "smtp-in|", 8) == 0) {
- subsystem = &filter_smtp_in;
- hook += 8;
- }
- else
- fatalx("Invalid message direction: %s", hook);
-
- for (i = 0; i < nitems(filter_execs); i++)
- if (strcmp(hook, filter_execs[i].phase_name) == 0)
- break;
- if (i == nitems(filter_execs))
- fatalx("Unrecognized report name: %s", hook);
-
- iter = NULL;
- while (dict_iter(&filters, &iter, &filter_name, (void **)&filter))
- if (filter->proc && strcmp(name, filter->proc) == 0)
- filter->phases |= (1<<filter_execs[i].phase);
-}
-
-void
-lka_filter_ready(void)
-{
- struct filter *filter;
- struct filter *subfilter;
- const char *filter_name;
- struct filter_entry *filter_entry;
- struct filter_chain *filter_chain;
- void *iter;
- size_t i;
- size_t j;
-
- /* all filters are ready, actually build the filter chains */
- iter = NULL;
- while (dict_iter(&filters, &iter, &filter_name, (void **)&filter)) {
- filter_chain = xcalloc(1, sizeof *filter_chain);
- for (i = 0; i < nitems(filter_execs); i++)
- TAILQ_INIT(&filter_chain->chain[i]);
- dict_set(&filter_chains, filter_name, filter_chain);
-
- if (filter->chain) {
- for (i = 0; i < filter->chain_size; i++) {
- subfilter = filter->chain[i];
- for (j = 0; j < nitems(filter_execs); ++j) {
- if (subfilter->phases & (1<<j)) {
- filter_entry = xcalloc(1, sizeof *filter_entry);
- filter_entry->id = generate_uid();
- filter_entry->name = subfilter->name;
- TAILQ_INSERT_TAIL(&filter_chain->chain[j],
- filter_entry, entries);
- }
- }
- }
- continue;
- }
-
- for (i = 0; i < nitems(filter_execs); ++i) {
- if (filter->phases & (1<<i)) {
- filter_entry = xcalloc(1, sizeof *filter_entry);
- filter_entry->id = generate_uid();
- filter_entry->name = filter_name;
- TAILQ_INSERT_TAIL(&filter_chain->chain[i],
- filter_entry, entries);
- }
- }
- }
-}
-
-int
-lka_filter_proc_in_session(uint64_t reqid, const char *proc)
-{
- struct filter_session *fs;
- struct filter *filter;
- size_t i;
-
- if ((fs = tree_get(&sessions, reqid)) == NULL)
- return 0;
-
- filter = dict_get(&filters, fs->filter_name);
- if (filter == NULL || (filter->proc == NULL && filter->chain == NULL))
- return 0;
-
- if (filter->proc)
- return strcmp(filter->proc, proc) == 0 ? 1 : 0;
-
- for (i = 0; i < filter->chain_size; i++)
- if (filter->chain[i]->proc &&
- strcmp(filter->chain[i]->proc, proc) == 0)
- return 1;
-
- return 0;
-}
-
-void
-lka_filter_begin(uint64_t reqid, const char *filter_name)
-{
- struct filter_session *fs;
-
- if (!filters_inited) {
- tree_init(&sessions);
- filters_inited = 1;
- }
-
- fs = xcalloc(1, sizeof (struct filter_session));
- fs->id = reqid;
- fs->filter_name = xstrdup(filter_name);
- tree_xset(&sessions, fs->id, fs);
-
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-begin", reqid);
-}
-
-void
-lka_filter_end(uint64_t reqid)
-{
- struct filter_session *fs;
-
- fs = tree_xpop(&sessions, reqid);
- free(fs->rdns);
- free(fs->helo);
- free(fs->mail_from);
- free(fs->username);
- free(fs->lastparam);
- free(fs);
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-end", reqid);
-}
-
-void
-lka_filter_data_begin(uint64_t reqid)
-{
- struct filter_session *fs;
- int sp[2];
- int fd = -1;
-
- fs = tree_xget(&sessions, reqid);
-
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1)
- goto end;
- io_set_nonblocking(sp[0]);
- io_set_nonblocking(sp[1]);
- fd = sp[0];
- fs->io = io_new();
- io_set_fd(fs->io, sp[1]);
- io_set_callback(fs->io, filter_session_io, fs);
-
-end:
- m_create(p_pony, IMSG_FILTER_SMTP_DATA_BEGIN, 0, 0, fd);
- m_add_id(p_pony, reqid);
- m_add_int(p_pony, fd != -1 ? 1 : 0);
- m_close(p_pony);
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters data-begin fd=%d", reqid, fd);
-}
-
-void
-lka_filter_data_end(uint64_t reqid)
-{
- struct filter_session *fs;
-
- fs = tree_xget(&sessions, reqid);
- if (fs->io) {
- io_free(fs->io);
- fs->io = NULL;
- }
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters data-end", reqid);
-}
-
-static void
-filter_session_io(struct io *io, int evt, void *arg)
-{
- struct filter_session *fs = arg;
- char *line = NULL;
- ssize_t len;
-
- log_trace(TRACE_IO, "filter session: %p: %s %s", fs, io_strevent(evt),
- io_strio(io));
-
- switch (evt) {
- case IO_DATAIN:
- nextline:
- line = io_getline(fs->io, &len);
- /* No complete line received */
- if (line == NULL)
- return;
-
- filter_data(fs->id, line);
-
- goto nextline;
-
- case IO_DISCONNECTED:
- io_free(fs->io);
- fs->io = NULL;
- break;
- }
-}
-
-void
-lka_filter_process_response(const char *name, const char *line)
-{
- uint64_t reqid;
- uint64_t token;
- char buffer[LINE_MAX];
- char *ep = NULL;
- char *kind = NULL;
- char *qid = NULL;
- /*char *phase = NULL;*/
- char *response = NULL;
- char *parameter = NULL;
- struct filter_session *fs;
-
- (void)strlcpy(buffer, line, sizeof buffer);
- if ((ep = strchr(buffer, '|')) == NULL)
- fatalx("Missing token: %s", line);
- ep[0] = '\0';
-
- kind = buffer;
-
- qid = ep+1;
- if ((ep = strchr(qid, '|')) == NULL)
- fatalx("Missing reqid: %s", line);
- ep[0] = '\0';
-
- reqid = strtoull(qid, &ep, 16);
- if (qid[0] == '\0' || *ep != '\0')
- fatalx("Invalid reqid: %s", line);
- if (errno == ERANGE && reqid == ULLONG_MAX)
- fatal("Invalid reqid: %s", line);
-
- qid = ep+1;
- if ((ep = strchr(qid, '|')) == NULL)
- fatal("Missing directive: %s", line);
- ep[0] = '\0';
-
- token = strtoull(qid, &ep, 16);
- if (qid[0] == '\0' || *ep != '\0')
- fatalx("Invalid token: %s", line);
- if (errno == ERANGE && token == ULLONG_MAX)
- fatal("Invalid token: %s", line);
-
- response = ep+1;
-
- /* session can legitimately disappear on a resume */
- if ((fs = tree_get(&sessions, reqid)) == NULL)
- return;
-
- if (strcmp(kind, "filter-dataline") == 0) {
- if (fs->phase != FILTER_DATA_LINE)
- fatalx("filter-dataline out of dataline phase");
- filter_data_next(token, reqid, response);
- return;
- }
- if (fs->phase == FILTER_DATA_LINE)
- fatalx("filter-result in dataline phase");
-
- if ((ep = strchr(response, '|'))) {
- parameter = ep + 1;
- ep[0] = '\0';
- }
-
- if (strcmp(response, "proceed") == 0) {
- if (parameter != NULL)
- fatalx("Unexpected parameter after proceed: %s", line);
- filter_protocol_next(token, reqid, 0);
- return;
- } else if (strcmp(response, "junk") == 0) {
- if (parameter != NULL)
- fatalx("Unexpected parameter after junk: %s", line);
- if (fs->phase == FILTER_COMMIT)
- fatalx("filter-reponse junk after DATA");
- filter_result_junk(reqid);
- return;
- } else {
- if (parameter == NULL)
- fatalx("Missing parameter: %s", line);
-
- if (strcmp(response, "rewrite") == 0)
- filter_result_rewrite(reqid, parameter);
- else if (strcmp(response, "reject") == 0)
- filter_result_reject(reqid, parameter);
- else if (strcmp(response, "disconnect") == 0)
- filter_result_disconnect(reqid, parameter);
- else
- fatalx("Invalid directive: %s", line);
- }
-}
-
-void
-lka_filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param)
-{
- filter_protocol(reqid, phase, param);
-}
-
-static void
-filter_protocol_internal(struct filter_session *fs, uint64_t *token, uint64_t reqid, enum filter_phase phase, const char *param)
-{
- struct filter_chain *filter_chain;
- struct filter_entry *filter_entry;
- struct filter *filter;
- struct timeval tv;
- const char *phase_name = filter_execs[phase].phase_name;
- int resume = 1;
-
- if (!*token) {
- fs->phase = phase;
- resume = 0;
- }
-
- /* XXX - this sanity check requires a protocol change, stub for now */
- phase = fs->phase;
- if (fs->phase != phase)
- fatalx("misbehaving filter");
-
- /* based on token, identify the filter_entry we should apply */
- filter_chain = dict_get(&filter_chains, fs->filter_name);
- filter_entry = TAILQ_FIRST(&filter_chain->chain[fs->phase]);
- if (*token) {
- TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries)
- if (filter_entry->id == *token)
- break;
- if (filter_entry == NULL)
- fatalx("misbehaving filter");
- filter_entry = TAILQ_NEXT(filter_entry, entries);
- }
-
- /* no filter_entry, we either had none or reached end of chain */
- if (filter_entry == NULL) {
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, resume=%s, "
- "action=proceed",
- fs->id, phase_name, resume ? "y" : "n");
- filter_result_proceed(reqid);
- return;
- }
-
- /* process param with current filter_entry */
- *token = filter_entry->id;
- filter = dict_get(&filters, filter_entry->name);
- if (filter->proc) {
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
- "resume=%s, action=deferred, filter=%s",
- fs->id, phase_name, resume ? "y" : "n",
- filter->name);
- filter_protocol_query(filter, filter_entry->id, reqid,
- filter_execs[fs->phase].phase_name, param);
- return; /* deferred response */
- }
-
- if (filter_execs[fs->phase].func(fs, filter, reqid, param)) {
- if (filter->config->rewrite) {
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
- "resume=%s, action=rewrite, filter=%s, query=%s, response=%s",
- fs->id, phase_name, resume ? "y" : "n",
- filter->name,
- param,
- filter->config->rewrite);
- filter_result_rewrite(reqid, filter->config->rewrite);
- return;
- }
- else if (filter->config->disconnect) {
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
- "resume=%s, action=disconnect, filter=%s, query=%s, response=%s",
- fs->id, phase_name, resume ? "y" : "n",
- filter->name,
- param,
- filter->config->disconnect);
- filter_result_disconnect(reqid, filter->config->disconnect);
- return;
- }
- else if (filter->config->junk) {
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
- "resume=%s, action=junk, filter=%s, query=%s",
- fs->id, phase_name, resume ? "y" : "n",
- filter->name,
- param);
- filter_result_junk(reqid);
- return;
- } else if (filter->config->report) {
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
- "resume=%s, action=report, filter=%s, query=%s response=%s",
- fs->id, phase_name, resume ? "y" : "n",
- filter->name,
- param, filter->config->report);
-
- gettimeofday(&tv, NULL);
- lka_report_filter_report(fs->id, filter->name, 1,
- "smtp-in", &tv, filter->config->report);
- } else if (filter->config->bypass) {
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
- "resume=%s, action=bypass, filter=%s, query=%s",
- fs->id, phase_name, resume ? "y" : "n",
- filter->name,
- param);
- filter_result_proceed(reqid);
- return;
- } else {
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
- "resume=%s, action=reject, filter=%s, query=%s, response=%s",
- fs->id, phase_name, resume ? "y" : "n",
- filter->name,
- param,
- filter->config->reject);
- filter_result_reject(reqid, filter->config->reject);
- return;
- }
- }
-
- log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
- "resume=%s, action=proceed, filter=%s, query=%s",
- fs->id, phase_name, resume ? "y" : "n",
- filter->name,
- param);
-
- /* filter_entry resulted in proceed, try next filter */
- filter_protocol_internal(fs, token, reqid, phase, param);
- return;
-}
-
-static void
-filter_data_internal(struct filter_session *fs, uint64_t token, uint64_t reqid, const char *line)
-{
- struct filter_chain *filter_chain;
- struct filter_entry *filter_entry;
- struct filter *filter;
-
- if (!token)
- fs->phase = FILTER_DATA_LINE;
- if (fs->phase != FILTER_DATA_LINE)
- fatalx("misbehaving filter");
-
- /* based on token, identify the filter_entry we should apply */
- filter_chain = dict_get(&filter_chains, fs->filter_name);
- filter_entry = TAILQ_FIRST(&filter_chain->chain[fs->phase]);
- if (token) {
- TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries)
- if (filter_entry->id == token)
- break;
- if (filter_entry == NULL)
- fatalx("misbehaving filter");
- filter_entry = TAILQ_NEXT(filter_entry, entries);
- }
-
- /* no filter_entry, we either had none or reached end of chain */
- if (filter_entry == NULL) {
- io_printf(fs->io, "%s\n", line);
- return;
- }
-
- /* pass data to the filter */
- filter = dict_get(&filters, filter_entry->name);
- filter_data_query(filter, filter_entry->id, reqid, line);
-}
-
-static void
-filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param)
-{
- struct filter_session *fs;
- uint64_t token = 0;
- char *nparam = NULL;
-
- fs = tree_xget(&sessions, reqid);
-
- switch (phase) {
- case FILTER_HELO:
- case FILTER_EHLO:
- free(fs->helo);
- fs->helo = xstrdup(param);
- break;
- case FILTER_MAIL_FROM:
- free(fs->mail_from);
- fs->mail_from = xstrdup(param + 1);
- *strchr(fs->mail_from, '>') = '\0';
- param = fs->mail_from;
-
- break;
- case FILTER_RCPT_TO:
- nparam = xstrdup(param + 1);
- *strchr(nparam, '>') = '\0';
- param = nparam;
- break;
- case FILTER_STARTTLS:
- /* TBD */
- break;
- default:
- break;
- }
-
- free(fs->lastparam);
- fs->lastparam = xstrdup(param);
-
- filter_protocol_internal(fs, &token, reqid, phase, param);
- if (nparam)
- free(nparam);
-}
-
-static void
-filter_protocol_next(uint64_t token, uint64_t reqid, enum filter_phase phase)
-{
- struct filter_session *fs;
-
- /* session can legitimately disappear on a resume */
- if ((fs = tree_get(&sessions, reqid)) == NULL)
- return;
-
- filter_protocol_internal(fs, &token, reqid, phase, fs->lastparam);
-}
-
-static void
-filter_data(uint64_t reqid, const char *line)
-{
- struct filter_session *fs;
-
- fs = tree_xget(&sessions, reqid);
-
- filter_data_internal(fs, 0, reqid, line);
-}
-
-static void
-filter_data_next(uint64_t token, uint64_t reqid, const char *line)
-{
- struct filter_session *fs;
-
- /* session can legitimately disappear on a resume */
- if ((fs = tree_get(&sessions, reqid)) == NULL)
- return;
-
- filter_data_internal(fs, token, reqid, line);
-}
-
-static void
-filter_protocol_query(struct filter *filter, uint64_t token, uint64_t reqid, const char *phase, const char *param)
-{
- int n;
- struct filter_session *fs;
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- fs = tree_xget(&sessions, reqid);
- if (strcmp(phase, "connect") == 0)
- n = io_printf(lka_proc_get_io(filter->proc),
- "filter|%s|%lld.%06ld|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s|%s\n",
- PROTOCOL_VERSION,
- (long long int)tv.tv_sec, tv.tv_usec,
- phase, reqid, token, fs->rdns, param);
- else
- n = io_printf(lka_proc_get_io(filter->proc),
- "filter|%s|%lld.%06ld|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s\n",
- PROTOCOL_VERSION,
- (long long int)tv.tv_sec, tv.tv_usec,
- phase, reqid, token, param);
- if (n == -1)
- fatalx("failed to write to processor");
-}
-
-static void
-filter_data_query(struct filter *filter, uint64_t token, uint64_t reqid, const char *line)
-{
- int n;
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- n = io_printf(lka_proc_get_io(filter->proc),
- "filter|%s|%lld.%06ld|smtp-in|data-line|"
- "%016"PRIx64"|%016"PRIx64"|%s\n",
- PROTOCOL_VERSION,
- (long long int)tv.tv_sec, tv.tv_usec,
- reqid, token, line);
- if (n == -1)
- fatalx("failed to write to processor");
-}
-
-static void
-filter_result_proceed(uint64_t reqid)
-{
- m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
- m_add_id(p_pony, reqid);
- m_add_int(p_pony, FILTER_PROCEED);
- m_close(p_pony);
-}
-
-static void
-filter_result_junk(uint64_t reqid)
-{
- m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
- m_add_id(p_pony, reqid);
- m_add_int(p_pony, FILTER_JUNK);
- m_close(p_pony);
-}
-
-static void
-filter_result_rewrite(uint64_t reqid, const char *param)
-{
- m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
- m_add_id(p_pony, reqid);
- m_add_int(p_pony, FILTER_REWRITE);
- m_add_string(p_pony, param);
- m_close(p_pony);
-}
-
-static void
-filter_result_reject(uint64_t reqid, const char *message)
-{
- m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
- m_add_id(p_pony, reqid);
- m_add_int(p_pony, FILTER_REJECT);
- m_add_string(p_pony, message);
- m_close(p_pony);
-}
-
-static void
-filter_result_disconnect(uint64_t reqid, const char *message)
-{
- m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
- m_add_id(p_pony, reqid);
- m_add_int(p_pony, FILTER_DISCONNECT);
- m_add_string(p_pony, message);
- m_close(p_pony);
-}
-
-
-/* below is code for builtin filters */
-
-static int
-filter_check_rdns_table(struct filter *filter, enum table_service kind, const char *key)
-{
- int ret = 0;
-
- if (filter->config->rdns_table == NULL)
- return 0;
-
- if (table_match(filter->config->rdns_table, kind, key) > 0)
- ret = 1;
-
- return filter->config->not_rdns_table < 0 ? !ret : ret;
-}
-
-static int
-filter_check_rdns_regex(struct filter *filter, const char *key)
-{
- int ret = 0;
-
- if (filter->config->rdns_regex == NULL)
- return 0;
-
- if (table_match(filter->config->rdns_regex, K_REGEX, key) > 0)
- ret = 1;
- return filter->config->not_rdns_regex < 0 ? !ret : ret;
-}
-
-static int
-filter_check_src_table(struct filter *filter, enum table_service kind, const char *key)
-{
- int ret = 0;
-
- if (filter->config->src_table == NULL)
- return 0;
-
- if (table_match(filter->config->src_table, kind, key) > 0)
- ret = 1;
- return filter->config->not_src_table < 0 ? !ret : ret;
-}
-
-static int
-filter_check_src_regex(struct filter *filter, const char *key)
-{
- int ret = 0;
-
- if (filter->config->src_regex == NULL)
- return 0;
-
- if (table_match(filter->config->src_regex, K_REGEX, key) > 0)
- ret = 1;
- return filter->config->not_src_regex < 0 ? !ret : ret;
-}
-
-static int
-filter_check_helo_table(struct filter *filter, enum table_service kind, const char *key)
-{
- int ret = 0;
-
- if (filter->config->helo_table == NULL)
- return 0;
-
- if (table_match(filter->config->helo_table, kind, key) > 0)
- ret = 1;
- return filter->config->not_helo_table < 0 ? !ret : ret;
-}
-
-static int
-filter_check_helo_regex(struct filter *filter, const char *key)
-{
- int ret = 0;
-
- if (filter->config->helo_regex == NULL)
- return 0;
-
- if (table_match(filter->config->helo_regex, K_REGEX, key) > 0)
- ret = 1;
- return filter->config->not_helo_regex < 0 ? !ret : ret;
-}
-
-static int
-filter_check_auth(struct filter *filter, const char *username)
-{
- int ret = 0;
-
- if (!filter->config->auth)
- return 0;
-
- ret = username ? 1 : 0;
-
- return filter->config->not_auth < 0 ? !ret : ret;
-}
-
-static int
-filter_check_auth_table(struct filter *filter, enum table_service kind, const char *key)
-{
- int ret = 0;
-
- if (filter->config->auth_table == NULL)
- return 0;
-
- if (key && table_match(filter->config->auth_table, kind, key) > 0)
- ret = 1;
-
- return filter->config->not_auth_table < 0 ? !ret : ret;
-}
-
-static int
-filter_check_auth_regex(struct filter *filter, const char *key)
-{
- int ret = 0;
-
- if (filter->config->auth_regex == NULL)
- return 0;
-
- if (key && table_match(filter->config->auth_regex, K_REGEX, key) > 0)
- ret = 1;
- return filter->config->not_auth_regex < 0 ? !ret : ret;
-}
-
-
-static int
-filter_check_mail_from_table(struct filter *filter, enum table_service kind, const char *key)
-{
- int ret = 0;
-
- if (filter->config->mail_from_table == NULL)
- return 0;
-
- if (table_match(filter->config->mail_from_table, kind, key) > 0)
- ret = 1;
- return filter->config->not_mail_from_table < 0 ? !ret : ret;
-}
-
-static int
-filter_check_mail_from_regex(struct filter *filter, const char *key)
-{
- int ret = 0;
-
- if (filter->config->mail_from_regex == NULL)
- return 0;
-
- if (table_match(filter->config->mail_from_regex, K_REGEX, key) > 0)
- ret = 1;
- return filter->config->not_mail_from_regex < 0 ? !ret : ret;
-}
-
-static int
-filter_check_rcpt_to_table(struct filter *filter, enum table_service kind, const char *key)
-{
- int ret = 0;
-
- if (filter->config->rcpt_to_table == NULL)
- return 0;
-
- if (table_match(filter->config->rcpt_to_table, kind, key) > 0)
- ret = 1;
- return filter->config->not_rcpt_to_table < 0 ? !ret : ret;
-}
-
-static int
-filter_check_rcpt_to_regex(struct filter *filter, const char *key)
-{
- int ret = 0;
-
- if (filter->config->rcpt_to_regex == NULL)
- return 0;
-
- if (table_match(filter->config->rcpt_to_regex, K_REGEX, key) > 0)
- ret = 1;
- return filter->config->not_rcpt_to_regex < 0 ? !ret : ret;
-}
-
-static int
-filter_check_fcrdns(struct filter *filter, int fcrdns)
-{
- int ret = 0;
-
- if (!filter->config->fcrdns)
- return 0;
-
- ret = fcrdns == 1;
- return filter->config->not_fcrdns < 0 ? !ret : ret;
-}
-
-static int
-filter_check_rdns(struct filter *filter, const char *hostname)
-{
- int ret = 0;
- struct netaddr netaddr;
-
- if (!filter->config->rdns)
- return 0;
-
- /* this is a hack until smtp session properly deals with lack of rdns */
- ret = strcmp("<unknown>", hostname);
- if (ret == 0)
- return filter->config->not_rdns < 0 ? !ret : ret;
-
- /* if text_to_netaddress succeeds,
- * we don't have an rDNS so the filter should match
- */
- ret = !text_to_netaddr(&netaddr, hostname);
- return filter->config->not_rdns < 0 ? !ret : ret;
-}
-
-static int
-filter_builtins_notimpl(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
-{
- return 0;
-}
-
-static int
-filter_builtins_global(struct filter_session *fs, struct filter *filter, uint64_t reqid)
-{
- return filter_check_fcrdns(filter, fs->fcrdns) ||
- filter_check_rdns(filter, fs->rdns) ||
- filter_check_rdns_table(filter, K_DOMAIN, fs->rdns) ||
- filter_check_rdns_regex(filter, fs->rdns) ||
- filter_check_src_table(filter, K_NETADDR, ss_to_text(&fs->ss_src)) ||
- filter_check_src_regex(filter, ss_to_text(&fs->ss_src)) ||
- filter_check_helo_table(filter, K_DOMAIN, fs->helo) ||
- filter_check_helo_regex(filter, fs->helo) ||
- filter_check_auth(filter, fs->username) ||
- filter_check_auth_table(filter, K_STRING, fs->username) ||
- filter_check_auth_table(filter, K_CREDENTIALS, fs->username) ||
- filter_check_auth_regex(filter, fs->username) ||
- filter_check_mail_from_table(filter, K_MAILADDR, fs->mail_from) ||
- filter_check_mail_from_regex(filter, fs->mail_from);
-}
-
-static int
-filter_builtins_connect(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
-{
- return filter_builtins_global(fs, filter, reqid);
-}
-
-static int
-filter_builtins_helo(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
-{
- return filter_builtins_global(fs, filter, reqid);
-}
-
-static int
-filter_builtins_mail_from(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
-{
- return filter_builtins_global(fs, filter, reqid);
-}
-
-static int
-filter_builtins_rcpt_to(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
-{
- return filter_builtins_global(fs, filter, reqid) ||
- filter_check_rcpt_to_table(filter, K_MAILADDR, param) ||
- filter_check_rcpt_to_regex(filter, param);
-}
-
-static int
-filter_builtins_data(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
-{
- return filter_builtins_global(fs, filter, reqid);
-}
-
-static int
-filter_builtins_commit(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
-{
- return filter_builtins_global(fs, filter, reqid);
-}
-
-static void
-report_smtp_broadcast(uint64_t, const char *, struct timeval *, const char *,
- const char *, ...) __attribute__((__format__ (printf, 5, 6)));
-
-void
-lka_report_init(void)
-{
- struct reporters *tailq;
- size_t i;
-
- dict_init(&report_smtp_in);
- dict_init(&report_smtp_out);
-
- for (i = 0; i < nitems(smtp_events); ++i) {
- tailq = xcalloc(1, sizeof (struct reporters));
- TAILQ_INIT(tailq);
- dict_xset(&report_smtp_in, smtp_events[i].event, tailq);
-
- tailq = xcalloc(1, sizeof (struct reporters));
- TAILQ_INIT(tailq);
- dict_xset(&report_smtp_out, smtp_events[i].event, tailq);
- }
-}
-
-void
-lka_report_register_hook(const char *name, const char *hook)
-{
- struct dict *subsystem;
- struct reporter_proc *rp;
- struct reporters *tailq;
- void *iter;
- size_t i;
-
- if (strncmp(hook, "smtp-in|", 8) == 0) {
- subsystem = &report_smtp_in;
- hook += 8;
- }
- else if (strncmp(hook, "smtp-out|", 9) == 0) {
- subsystem = &report_smtp_out;
- hook += 9;
- }
- else
- fatalx("Invalid message direction: %s", hook);
-
- if (strcmp(hook, "*") == 0) {
- iter = NULL;
- while (dict_iter(subsystem, &iter, NULL, (void **)&tailq)) {
- rp = xcalloc(1, sizeof *rp);
- rp->name = xstrdup(name);
- TAILQ_INSERT_TAIL(tailq, rp, entries);
- }
- return;
- }
-
- for (i = 0; i < nitems(smtp_events); i++)
- if (strcmp(hook, smtp_events[i].event) == 0)
- break;
- if (i == nitems(smtp_events))
- fatalx("Unrecognized report name: %s", hook);
-
- tailq = dict_get(subsystem, hook);
- rp = xcalloc(1, sizeof *rp);
- rp->name = xstrdup(name);
- TAILQ_INSERT_TAIL(tailq, rp, entries);
-}
-
-static void
-report_smtp_broadcast(uint64_t reqid, const char *direction, struct timeval *tv, const char *event,
- const char *format, ...)
-{
- va_list ap;
- struct dict *d;
- struct reporters *tailq;
- struct reporter_proc *rp;
-
- if (strcmp("smtp-in", direction) == 0)
- d = &report_smtp_in;
-
- else if (strcmp("smtp-out", direction) == 0)
- d = &report_smtp_out;
-
- else
- fatalx("unexpected direction: %s", direction);
-
- tailq = dict_xget(d, event);
- TAILQ_FOREACH(rp, tailq, entries) {
- if (!lka_filter_proc_in_session(reqid, rp->name))
- continue;
-
- va_start(ap, format);
- if (io_printf(lka_proc_get_io(rp->name),
- "report|%s|%lld.%06ld|%s|%s|%016"PRIx64"%s",
- PROTOCOL_VERSION, (long long int)tv->tv_sec, tv->tv_usec, direction,
- event, reqid, format[0] != '\n' ? "|" : "") == -1 ||
- io_vprintf(lka_proc_get_io(rp->name), format, ap) == -1)
- fatalx("failed to write to processor");
- va_end(ap);
- }
-}
-
-void
-lka_report_smtp_link_connect(const char *direction, struct timeval *tv, uint64_t reqid, const char *rdns,
- int fcrdns,
- const struct sockaddr_storage *ss_src,
- const struct sockaddr_storage *ss_dest)
-{
- struct filter_session *fs;
- char src[NI_MAXHOST + 5];
- char dest[NI_MAXHOST + 5];
- uint16_t src_port = 0;
- uint16_t dest_port = 0;
- const char *fcrdns_str;
-
- if (ss_src->ss_family == AF_INET)
- src_port = ntohs(((const struct sockaddr_in *)ss_src)->sin_port);
- else if (ss_src->ss_family == AF_INET6)
- src_port = ntohs(((const struct sockaddr_in6 *)ss_src)->sin6_port);
-
- if (ss_dest->ss_family == AF_INET)
- dest_port = ntohs(((const struct sockaddr_in *)ss_dest)->sin_port);
- else if (ss_dest->ss_family == AF_INET6)
- dest_port = ntohs(((const struct sockaddr_in6 *)ss_dest)->sin6_port);
-
- if (strcmp(ss_to_text(ss_src), "local") == 0) {
- (void)snprintf(src, sizeof src, "unix:%s", SMTPD_SOCKET);
- (void)snprintf(dest, sizeof dest, "unix:%s", SMTPD_SOCKET);
- } else {
- (void)snprintf(src, sizeof src, "%s:%d", ss_to_text(ss_src), src_port);
- (void)snprintf(dest, sizeof dest, "%s:%d", ss_to_text(ss_dest), dest_port);
- }
-
- switch (fcrdns) {
- case 1:
- fcrdns_str = "pass";
- break;
- case 0:
- fcrdns_str = "fail";
- break;
- default:
- fcrdns_str = "error";
- break;
- }
-
- fs = tree_xget(&sessions, reqid);
- fs->rdns = xstrdup(rdns);
- fs->fcrdns = fcrdns;
- fs->ss_src = *ss_src;
- fs->ss_dest = *ss_dest;
-
- report_smtp_broadcast(reqid, direction, tv, "link-connect",
- "%s|%s|%s|%s\n", rdns, fcrdns_str, src, dest);
-}
-
-void
-lka_report_smtp_link_disconnect(const char *direction, struct timeval *tv, uint64_t reqid)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-disconnect", "\n");
-}
-
-void
-lka_report_smtp_link_greeting(const char *direction, uint64_t reqid,
- struct timeval *tv, const char *domain)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-greeting", "%s\n",
- domain);
-}
-
-void
-lka_report_smtp_link_auth(const char *direction, struct timeval *tv, uint64_t reqid,
- const char *username, const char *result)
-{
- struct filter_session *fs;
-
- if (strcmp(result, "pass") == 0) {
- fs = tree_xget(&sessions, reqid);
- fs->username = xstrdup(username);
- }
- report_smtp_broadcast(reqid, direction, tv, "link-auth", "%s|%s\n",
- username, result);
-}
-
-void
-lka_report_smtp_link_identify(const char *direction, struct timeval *tv,
- uint64_t reqid, const char *method, const char *heloname)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-identify", "%s|%s\n",
- method, heloname);
-}
-
-void
-lka_report_smtp_link_tls(const char *direction, struct timeval *tv, uint64_t reqid, const char *ciphers)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-tls", "%s\n",
- ciphers);
-}
-
-void
-lka_report_smtp_tx_reset(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-reset", "%08x\n",
- msgid);
-}
-
-void
-lka_report_smtp_tx_begin(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-begin", "%08x\n",
- msgid);
-}
-
-void
-lka_report_smtp_tx_mail(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok)
-{
- const char *result;
-
- switch (ok) {
- case 1:
- result = "ok";
- break;
- case 0:
- result = "permfail";
- break;
- default:
- result = "tempfail";
- break;
- }
- report_smtp_broadcast(reqid, direction, tv, "tx-mail", "%08x|%s|%s\n",
- msgid, result, address);
-}
-
-void
-lka_report_smtp_tx_rcpt(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok)
-{
- const char *result;
-
- switch (ok) {
- case 1:
- result = "ok";
- break;
- case 0:
- result = "permfail";
- break;
- default:
- result = "tempfail";
- break;
- }
- report_smtp_broadcast(reqid, direction, tv, "tx-rcpt", "%08x|%s|%s\n",
- msgid, result, address);
-}
-
-void
-lka_report_smtp_tx_envelope(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, uint64_t evpid)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-envelope",
- "%08x|%016"PRIx64"\n", msgid, evpid);
-}
-
-void
-lka_report_smtp_tx_data(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, int ok)
-{
- const char *result;
-
- switch (ok) {
- case 1:
- result = "ok";
- break;
- case 0:
- result = "permfail";
- break;
- default:
- result = "tempfail";
- break;
- }
- report_smtp_broadcast(reqid, direction, tv, "tx-data", "%08x|%s\n",
- msgid, result);
-}
-
-void
-lka_report_smtp_tx_commit(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, size_t msgsz)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-commit", "%08x|%zd\n",
- msgid, msgsz);
-}
-
-void
-lka_report_smtp_tx_rollback(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-rollback", "%08x\n",
- msgid);
-}
-
-void
-lka_report_smtp_protocol_client(const char *direction, struct timeval *tv, uint64_t reqid, const char *command)
-{
- report_smtp_broadcast(reqid, direction, tv, "protocol-client", "%s\n",
- command);
-}
-
-void
-lka_report_smtp_protocol_server(const char *direction, struct timeval *tv, uint64_t reqid, const char *response)
-{
- report_smtp_broadcast(reqid, direction, tv, "protocol-server", "%s\n",
- response);
-}
-
-void
-lka_report_smtp_filter_response(const char *direction, struct timeval *tv, uint64_t reqid,
- int phase, int response, const char *param)
-{
- const char *phase_name;
- const char *response_name;
-
- switch (phase) {
- case FILTER_CONNECT:
- phase_name = "connected";
- break;
- case FILTER_HELO:
- phase_name = "helo";
- break;
- case FILTER_EHLO:
- phase_name = "ehlo";
- break;
- case FILTER_STARTTLS:
- phase_name = "tls";
- break;
- case FILTER_AUTH:
- phase_name = "auth";
- break;
- case FILTER_MAIL_FROM:
- phase_name = "mail-from";
- break;
- case FILTER_RCPT_TO:
- phase_name = "rcpt-to";
- break;
- case FILTER_DATA:
- phase_name = "data";
- break;
- case FILTER_DATA_LINE:
- phase_name = "data-line";
- break;
- case FILTER_RSET:
- phase_name = "rset";
- break;
- case FILTER_QUIT:
- phase_name = "quit";
- break;
- case FILTER_NOOP:
- phase_name = "noop";
- break;
- case FILTER_HELP:
- phase_name = "help";
- break;
- case FILTER_WIZ:
- phase_name = "wiz";
- break;
- case FILTER_COMMIT:
- phase_name = "commit";
- break;
- default:
- phase_name = "";
- }
-
- switch (response) {
- case FILTER_PROCEED:
- response_name = "proceed";
- break;
- case FILTER_JUNK:
- response_name = "junk";
- break;
- case FILTER_REWRITE:
- response_name = "rewrite";
- break;
- case FILTER_REJECT:
- response_name = "reject";
- break;
- case FILTER_DISCONNECT:
- response_name = "disconnect";
- break;
- default:
- response_name = "";
- }
-
- report_smtp_broadcast(reqid, direction, tv, "filter-response",
- "%s|%s%s%s\n", phase_name, response_name, param ? "|" : "",
- param ? param : "");
-}
-
-void
-lka_report_smtp_timeout(const char *direction, struct timeval *tv, uint64_t reqid)
-{
- report_smtp_broadcast(reqid, direction, tv, "timeout", "\n");
-}
-
-void
-lka_report_filter_report(uint64_t reqid, const char *name, int builtin,
- const char *direction, struct timeval *tv, const char *message)
-{
- report_smtp_broadcast(reqid, direction, tv, "filter-report",
- "%s|%s|%s\n", builtin ? "builtin" : "proc",
- name, message);
-}
-
-void
-lka_report_proc(const char *name, const char *line)
-{
- char buffer[LINE_MAX];
- struct timeval tv;
- char *ep, *sp, *direction;
- uint64_t reqid;
-
- if (strlcpy(buffer, line + 7, sizeof(buffer)) >= sizeof(buffer))
- fatalx("Invalid report: line too long: %s", line);
-
- errno = 0;
- tv.tv_sec = strtoll(buffer, &ep, 10);
- if (ep[0] != '.' || errno != 0)
- fatalx("Invalid report: invalid time: %s", line);
- sp = ep + 1;
- tv.tv_usec = strtol(sp, &ep, 10);
- if (ep[0] != '|' || errno != 0)
- fatalx("Invalid report: invalid time: %s", line);
- if (ep - sp != 6)
- fatalx("Invalid report: invalid time: %s", line);
-
- direction = ep + 1;
- if (strncmp(direction, "smtp-in|", 8) == 0) {
- direction[7] = '\0';
- direction += 7;
-#if 0
- } else if (strncmp(direction, "smtp-out|", 9) == 0) {
- direction[8] = '\0';
- direction += 8;
-#endif
- } else
- fatalx("Invalid report: invalid direction: %s", line);
-
- reqid = strtoull(sp, &ep, 16);
- if (ep[0] != '|' || errno != 0)
- fatalx("Invalid report: invalid reqid: %s", line);
- sp = ep + 1;
-
- lka_report_filter_report(reqid, name, 0, direction, &tv, sp);
-}
diff --git a/smtpd/lka_session.c b/smtpd/lka_session.c
deleted file mode 100644
index 999e01d6..00000000
--- a/smtpd/lka_session.c
+++ /dev/null
@@ -1,556 +0,0 @@
-/* $OpenBSD: lka_session.c,v 1.93 2019/09/20 17:46:05 gilles Exp $ */
-
-/*
- * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-
-#include <netinet/in.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <resolv.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define EXPAND_DEPTH 10
-
-#define F_WAITING 0x01
-
-struct lka_session {
- uint64_t id; /* given by smtp */
-
- TAILQ_HEAD(, envelope) deliverylist;
- struct expand expand;
-
- int flags;
- int error;
- const char *errormsg;
- struct envelope envelope;
- struct xnodes nodes;
- /* waiting for fwdrq */
- struct rule *rule;
- struct expandnode *node;
-};
-
-static void lka_expand(struct lka_session *, struct rule *,
- struct expandnode *);
-static void lka_submit(struct lka_session *, struct rule *,
- struct expandnode *);
-static void lka_resume(struct lka_session *);
-
-static int init;
-static struct tree sessions;
-
-void
-lka_session(uint64_t id, struct envelope *envelope)
-{
- struct lka_session *lks;
- struct expandnode xn;
-
- if (init == 0) {
- init = 1;
- tree_init(&sessions);
- }
-
- lks = xcalloc(1, sizeof(*lks));
- lks->id = id;
- RB_INIT(&lks->expand.tree);
- TAILQ_INIT(&lks->deliverylist);
- tree_xset(&sessions, lks->id, lks);
-
- lks->envelope = *envelope;
-
- TAILQ_INIT(&lks->nodes);
- memset(&xn, 0, sizeof xn);
- xn.type = EXPAND_ADDRESS;
- xn.u.mailaddr = lks->envelope.rcpt;
- lks->expand.parent = NULL;
- lks->expand.rule = NULL;
- lks->expand.queue = &lks->nodes;
- expand_insert(&lks->expand, &xn);
- lka_resume(lks);
-}
-
-void
-lka_session_forward_reply(struct forward_req *fwreq, int fd)
-{
- struct lka_session *lks;
- struct dispatcher *dsp;
- struct rule *rule;
- struct expandnode *xn;
- int ret;
-
- lks = tree_xget(&sessions, fwreq->id);
- xn = lks->node;
- rule = lks->rule;
-
- lks->flags &= ~F_WAITING;
-
- switch (fwreq->status) {
- case 0:
- /* permanent failure while lookup ~/.forward */
- log_trace(TRACE_EXPAND, "expand: ~/.forward failed for user %s",
- fwreq->user);
- lks->error = LKA_PERMFAIL;
- break;
- case 1:
- if (fd == -1) {
- dsp = dict_get(env->sc_dispatchers, lks->rule->dispatcher);
- if (dsp->u.local.forward_only) {
- log_trace(TRACE_EXPAND, "expand: no .forward "
- "for user %s on forward-only rule", fwreq->user);
- lks->error = LKA_TEMPFAIL;
- }
- else if (dsp->u.local.expand_only) {
- log_trace(TRACE_EXPAND, "expand: no .forward "
- "for user %s and no default action on rule", fwreq->user);
- lks->error = LKA_PERMFAIL;
- }
- else {
- log_trace(TRACE_EXPAND, "expand: no .forward for "
- "user %s, just deliver", fwreq->user);
- lka_submit(lks, rule, xn);
- }
- }
- else {
- dsp = dict_get(env->sc_dispatchers, rule->dispatcher);
-
- /* expand for the current user and rule */
- lks->expand.rule = rule;
- lks->expand.parent = xn;
-
- /* forwards_get() will close the descriptor no matter what */
- ret = forwards_get(fd, &lks->expand);
- if (ret == -1) {
- log_trace(TRACE_EXPAND, "expand: temporary "
- "forward error for user %s", fwreq->user);
- lks->error = LKA_TEMPFAIL;
- }
- else if (ret == 0) {
- if (dsp->u.local.forward_only) {
- log_trace(TRACE_EXPAND, "expand: empty .forward "
- "for user %s on forward-only rule", fwreq->user);
- lks->error = LKA_TEMPFAIL;
- }
- else if (dsp->u.local.expand_only) {
- log_trace(TRACE_EXPAND, "expand: empty .forward "
- "for user %s and no default action on rule", fwreq->user);
- lks->error = LKA_PERMFAIL;
- }
- else {
- log_trace(TRACE_EXPAND, "expand: empty .forward "
- "for user %s, just deliver", fwreq->user);
- lka_submit(lks, rule, xn);
- }
- }
- }
- break;
- default:
- /* temporary failure while looking up ~/.forward */
- lks->error = LKA_TEMPFAIL;
- }
-
- if (lks->error == LKA_TEMPFAIL && lks->errormsg == NULL)
- lks->errormsg = "424 4.2.4 Mailing list expansion problem";
- if (lks->error == LKA_PERMFAIL && lks->errormsg == NULL)
- lks->errormsg = "524 5.2.4 Mailing list expansion problem";
-
- lka_resume(lks);
-}
-
-static void
-lka_resume(struct lka_session *lks)
-{
- struct envelope *ep;
- struct expandnode *xn;
-
- if (lks->error)
- goto error;
-
- /* pop next node and expand it */
- while ((xn = TAILQ_FIRST(&lks->nodes))) {
- TAILQ_REMOVE(&lks->nodes, xn, tq_entry);
- lka_expand(lks, xn->rule, xn);
- if (lks->flags & F_WAITING)
- return;
- if (lks->error)
- goto error;
- }
-
- /* delivery list is empty, reject */
- if (TAILQ_FIRST(&lks->deliverylist) == NULL) {
- log_trace(TRACE_EXPAND, "expand: lka_done: expanded to empty "
- "delivery list");
- lks->error = LKA_PERMFAIL;
- lks->errormsg = "524 5.2.4 Mailing list expansion problem";
- }
- error:
- if (lks->error) {
- m_create(p_pony, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1);
- m_add_id(p_pony, lks->id);
- m_add_int(p_pony, lks->error);
-
- if (lks->errormsg)
- m_add_string(p_pony, lks->errormsg);
- else {
- if (lks->error == LKA_PERMFAIL)
- m_add_string(p_pony, "550 Invalid recipient");
- else if (lks->error == LKA_TEMPFAIL)
- m_add_string(p_pony, "451 Temporary failure");
- }
-
- m_close(p_pony);
- while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) {
- TAILQ_REMOVE(&lks->deliverylist, ep, entry);
- free(ep);
- }
- }
- else {
- /* Process the delivery list and submit envelopes to queue */
- while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) {
- TAILQ_REMOVE(&lks->deliverylist, ep, entry);
- m_create(p_queue, IMSG_LKA_ENVELOPE_SUBMIT, 0, 0, -1);
- m_add_id(p_queue, lks->id);
- m_add_envelope(p_queue, ep);
- m_close(p_queue);
- free(ep);
- }
-
- m_create(p_queue, IMSG_LKA_ENVELOPE_COMMIT, 0, 0, -1);
- m_add_id(p_queue, lks->id);
- m_close(p_queue);
- }
-
- expand_clear(&lks->expand);
- tree_xpop(&sessions, lks->id);
- free(lks);
-}
-
-static void
-lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn)
-{
- struct forward_req fwreq;
- struct envelope ep;
- struct expandnode node;
- struct mailaddr maddr;
- struct dispatcher *dsp;
- struct table *userbase;
- int r;
- union lookup lk;
- char *tag;
- const char *srs_decoded;
-
- if (xn->depth >= EXPAND_DEPTH) {
- log_trace(TRACE_EXPAND, "expand: lka_expand: node too deep.");
- lks->error = LKA_PERMFAIL;
- lks->errormsg = "524 5.2.4 Mailing list expansion problem";
- return;
- }
-
- switch (xn->type) {
- case EXPAND_INVALID:
- case EXPAND_INCLUDE:
- fatalx("lka_expand: unexpected type");
- break;
-
- case EXPAND_ADDRESS:
-
- log_trace(TRACE_EXPAND, "expand: lka_expand: address: %s@%s "
- "[depth=%d]",
- xn->u.mailaddr.user, xn->u.mailaddr.domain, xn->depth);
-
-
- ep = lks->envelope;
- ep.dest = xn->u.mailaddr;
- if (xn->parent) /* nodes with parent are forward addresses */
- ep.flags |= EF_INTERNAL;
-
- /* handle SRS */
- if (env->sc_srs_key != NULL &&
- ep.sender.user[0] == '\0' &&
- (strncasecmp(ep.rcpt.user, "SRS0=", 5) == 0 ||
- strncasecmp(ep.rcpt.user, "SRS1=", 5) == 0)) {
- srs_decoded = srs_decode(mailaddr_to_text(&ep.rcpt));
- if (srs_decoded &&
- text_to_mailaddr(&ep.rcpt, srs_decoded)) {
- /* flag envelope internal and override rcpt */
- ep.flags |= EF_INTERNAL;
- xn->u.mailaddr = ep.rcpt;
- lks->envelope = ep;
- }
- else {
- log_warn("SRS failed to decode: %s",
- mailaddr_to_text(&ep.rcpt));
- }
- }
-
- /* Pass the node through the ruleset */
- rule = ruleset_match(&ep);
- if (rule == NULL || rule->reject) {
- lks->error = (errno == EAGAIN) ?
- LKA_TEMPFAIL : LKA_PERMFAIL;
- break;
- }
-
- dsp = dict_xget(env->sc_dispatchers, rule->dispatcher);
- if (dsp->type == DISPATCHER_REMOTE) {
- lka_submit(lks, rule, xn);
- }
- else if (dsp->u.local.table_virtual) {
- /* expand */
- lks->expand.rule = rule;
- lks->expand.parent = xn;
-
- /* temporary replace the mailaddr with a copy where
- * we eventually strip the '+'-part before lookup.
- */
- maddr = xn->u.mailaddr;
- xlowercase(maddr.user, xn->u.mailaddr.user,
- sizeof maddr.user);
- r = aliases_virtual_get(&lks->expand, &maddr);
- if (r == -1) {
- lks->error = LKA_TEMPFAIL;
- log_trace(TRACE_EXPAND, "expand: lka_expand: "
- "error in virtual alias lookup");
- }
- else if (r == 0) {
- lks->error = LKA_PERMFAIL;
- log_trace(TRACE_EXPAND, "expand: lka_expand: "
- "no aliases for virtual");
- }
- if (lks->error == LKA_TEMPFAIL && lks->errormsg == NULL)
- lks->errormsg = "424 4.2.4 Mailing list expansion problem";
- if (lks->error == LKA_PERMFAIL && lks->errormsg == NULL)
- lks->errormsg = "524 5.2.4 Mailing list expansion problem";
- }
- else {
- lks->expand.rule = rule;
- lks->expand.parent = xn;
- xn->rule = rule;
-
- memset(&node, 0, sizeof node);
- node.type = EXPAND_USERNAME;
- xlowercase(node.u.user, xn->u.mailaddr.user,
- sizeof node.u.user);
- expand_insert(&lks->expand, &node);
- }
- break;
-
- case EXPAND_USERNAME:
- log_trace(TRACE_EXPAND, "expand: lka_expand: username: %s "
- "[depth=%d, sameuser=%d]",
- xn->u.user, xn->depth, xn->sameuser);
-
- /* expand aliases with the given rule */
- dsp = dict_xget(env->sc_dispatchers, rule->dispatcher);
-
- lks->expand.rule = rule;
- lks->expand.parent = xn;
-
- if (!xn->sameuser &&
- (dsp->u.local.table_alias || dsp->u.local.table_virtual)) {
- if (dsp->u.local.table_alias)
- r = aliases_get(&lks->expand, xn->u.user);
- if (dsp->u.local.table_virtual)
- r = aliases_virtual_get(&lks->expand, &xn->u.mailaddr);
- if (r == -1) {
- log_trace(TRACE_EXPAND, "expand: lka_expand: "
- "error in alias lookup");
- lks->error = LKA_TEMPFAIL;
- if (lks->errormsg == NULL)
- lks->errormsg = "424 4.2.4 Mailing list expansion problem";
- }
- if (r)
- break;
- }
-
- /* gilles+hackers@ -> gilles@ */
- if ((tag = strchr(xn->u.user, *env->sc_subaddressing_delim)) != NULL) {
- *tag++ = '\0';
- (void)strlcpy(xn->subaddress, tag, sizeof xn->subaddress);
- }
-
- userbase = table_find(env, dsp->u.local.table_userbase);
- r = table_lookup(userbase, K_USERINFO, xn->u.user, &lk);
- if (r == -1) {
- log_trace(TRACE_EXPAND, "expand: lka_expand: "
- "backend error while searching user");
- lks->error = LKA_TEMPFAIL;
- break;
- }
- if (r == 0) {
- log_trace(TRACE_EXPAND, "expand: lka_expand: "
- "user-part does not match system user");
- lks->error = LKA_PERMFAIL;
- break;
- }
- xn->realuser = 1;
-
- if (xn->sameuser && xn->parent->forwarded) {
- log_trace(TRACE_EXPAND, "expand: lka_expand: same "
- "user, submitting");
- lka_submit(lks, rule, xn);
- break;
- }
-
- /* no aliases found, query forward file */
- lks->rule = rule;
- lks->node = xn;
- xn->forwarded = 1;
-
- memset(&fwreq, 0, sizeof(fwreq));
- fwreq.id = lks->id;
- (void)strlcpy(fwreq.user, lk.userinfo.username, sizeof(fwreq.user));
- (void)strlcpy(fwreq.directory, lk.userinfo.directory, sizeof(fwreq.directory));
- fwreq.uid = lk.userinfo.uid;
- fwreq.gid = lk.userinfo.gid;
-
- m_compose(p_parent, IMSG_LKA_OPEN_FORWARD, 0, 0, -1,
- &fwreq, sizeof(fwreq));
- lks->flags |= F_WAITING;
- break;
-
- case EXPAND_FILENAME:
- dsp = dict_xget(env->sc_dispatchers, rule->dispatcher);
- if (dsp->u.local.forward_only) {
- log_trace(TRACE_EXPAND, "expand: filename matched on forward-only rule");
- lks->error = LKA_TEMPFAIL;
- break;
- }
- log_trace(TRACE_EXPAND, "expand: lka_expand: filename: %s "
- "[depth=%d]", xn->u.buffer, xn->depth);
- lka_submit(lks, rule, xn);
- break;
-
- case EXPAND_ERROR:
- dsp = dict_xget(env->sc_dispatchers, rule->dispatcher);
- if (dsp->u.local.forward_only) {
- log_trace(TRACE_EXPAND, "expand: error matched on forward-only rule");
- lks->error = LKA_TEMPFAIL;
- break;
- }
- log_trace(TRACE_EXPAND, "expand: lka_expand: error: %s "
- "[depth=%d]", xn->u.buffer, xn->depth);
- if (xn->u.buffer[0] == '4')
- lks->error = LKA_TEMPFAIL;
- else if (xn->u.buffer[0] == '5')
- lks->error = LKA_PERMFAIL;
- lks->errormsg = xn->u.buffer;
- break;
-
- case EXPAND_FILTER:
- dsp = dict_xget(env->sc_dispatchers, rule->dispatcher);
- if (dsp->u.local.forward_only) {
- log_trace(TRACE_EXPAND, "expand: filter matched on forward-only rule");
- lks->error = LKA_TEMPFAIL;
- break;
- }
- log_trace(TRACE_EXPAND, "expand: lka_expand: filter: %s "
- "[depth=%d]", xn->u.buffer, xn->depth);
- lka_submit(lks, rule, xn);
- break;
- }
-}
-
-static struct expandnode *
-lka_find_ancestor(struct expandnode *xn, enum expand_type type)
-{
- while (xn && (xn->type != type))
- xn = xn->parent;
- if (xn == NULL) {
- log_warnx("warn: lka_find_ancestor: no ancestors of type %d",
- type);
- fatalx(NULL);
- }
- return (xn);
-}
-
-static void
-lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn)
-{
- struct envelope *ep;
- struct dispatcher *dsp;
- const char *user;
- const char *format;
-
- ep = xmemdup(&lks->envelope, sizeof *ep);
- (void)strlcpy(ep->dispatcher, rule->dispatcher, sizeof ep->dispatcher);
-
- dsp = dict_xget(env->sc_dispatchers, ep->dispatcher);
-
- switch (dsp->type) {
- case DISPATCHER_REMOTE:
- if (xn->type != EXPAND_ADDRESS)
- fatalx("lka_deliver: expect address");
- ep->type = D_MTA;
- ep->dest = xn->u.mailaddr;
- break;
-
- case DISPATCHER_BOUNCE:
- case DISPATCHER_LOCAL:
- if (xn->type != EXPAND_USERNAME &&
- xn->type != EXPAND_FILENAME &&
- xn->type != EXPAND_FILTER)
- fatalx("lka_deliver: wrong type: %d", xn->type);
-
- ep->type = D_MDA;
- ep->dest = lka_find_ancestor(xn, EXPAND_ADDRESS)->u.mailaddr;
- if (xn->type == EXPAND_USERNAME) {
- (void)strlcpy(ep->mda_user, xn->u.user, sizeof(ep->mda_user));
- (void)strlcpy(ep->mda_subaddress, xn->subaddress, sizeof(ep->mda_subaddress));
- }
- else {
- user = !xn->parent->realuser ?
- SMTPD_USER :
- xn->parent->u.user;
- (void)strlcpy(ep->mda_user, user, sizeof (ep->mda_user));
-
- /* this battle needs to be fought ... */
- if (xn->type == EXPAND_FILTER &&
- strcmp(ep->mda_user, SMTPD_USER) == 0)
- log_warnx("commands executed from aliases "
- "run with %s privileges", SMTPD_USER);
-
- if (xn->type == EXPAND_FILENAME)
- format = PATH_LIBEXEC"/mail.mboxfile -f %%{mbox.from} %s";
- else if (xn->type == EXPAND_FILTER)
- format = "%s";
- (void)snprintf(ep->mda_exec, sizeof(ep->mda_exec),
- format, xn->u.buffer);
- }
- break;
- }
-
- TAILQ_INSERT_TAIL(&lks->deliverylist, ep, entry);
-}
diff --git a/smtpd/log.c b/smtpd/log.c
deleted file mode 100644
index 14f681e3..00000000
--- a/smtpd/log.c
+++ /dev/null
@@ -1,220 +0,0 @@
-/* $OpenBSD: log.c,v 1.20 2017/03/21 12:06:56 bluhm Exp $ */
-
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@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 "includes.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <syslog.h>
-#include <errno.h>
-#include <time.h>
-
-static int debug;
-static int verbose;
-const char *log_procname;
-
-void log_init(int, int);
-void log_procinit(const char *);
-void log_setverbose(int);
-int log_getverbose(void);
-void log_warn(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-void log_warnx(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-void log_info(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-void log_debug(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-void logit(int, const char *, ...)
- __attribute__((__format__ (printf, 2, 3)));
-void vlog(int, const char *, va_list)
- __attribute__((__format__ (printf, 2, 0)));
-__dead void fatal(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-__dead void fatalx(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-
-void
-log_init(int n_debug, int facility)
-{
- extern char *__progname;
-
- debug = n_debug;
- verbose = n_debug;
- log_procinit(__progname);
-
- if (!debug)
- openlog(__progname, LOG_PID | LOG_NDELAY, facility);
-
- tzset();
-}
-
-void
-log_procinit(const char *procname)
-{
- if (procname != NULL)
- log_procname = procname;
-}
-
-void
-log_setverbose(int v)
-{
- verbose = v;
-}
-
-int
-log_getverbose(void)
-{
- return (verbose);
-}
-
-void
-logit(int pri, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vlog(pri, fmt, ap);
- va_end(ap);
-}
-
-void
-vlog(int pri, const char *fmt, va_list ap)
-{
- char *nfmt;
- int saved_errno = errno;
-
- if (debug) {
- /* best effort in out of mem situations */
- if (asprintf(&nfmt, "%s\n", fmt) == -1) {
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- } else {
- vfprintf(stderr, nfmt, ap);
- free(nfmt);
- }
- fflush(stderr);
- } else
- vsyslog(pri, fmt, ap);
-
- errno = saved_errno;
-}
-
-void
-log_warn(const char *emsg, ...)
-{
- char *nfmt;
- va_list ap;
- int saved_errno = errno;
-
- /* best effort to even work in out of memory situations */
- if (emsg == NULL)
- logit(LOG_ERR, "%s", strerror(saved_errno));
- else {
- va_start(ap, emsg);
-
- if (asprintf(&nfmt, "%s: %s", emsg,
- strerror(saved_errno)) == -1) {
- /* we tried it... */
- vlog(LOG_ERR, emsg, ap);
- logit(LOG_ERR, "%s", strerror(saved_errno));
- } else {
- vlog(LOG_ERR, nfmt, ap);
- free(nfmt);
- }
- va_end(ap);
- }
-
- errno = saved_errno;
-}
-
-void
-log_warnx(const char *emsg, ...)
-{
- va_list ap;
-
- va_start(ap, emsg);
- vlog(LOG_ERR, emsg, ap);
- va_end(ap);
-}
-
-void
-log_info(const char *emsg, ...)
-{
- va_list ap;
-
- va_start(ap, emsg);
- vlog(LOG_INFO, emsg, ap);
- va_end(ap);
-}
-
-void
-log_debug(const char *emsg, ...)
-{
- va_list ap;
-
- if (verbose > 1) {
- va_start(ap, emsg);
- vlog(LOG_DEBUG, emsg, ap);
- va_end(ap);
- }
-}
-
-static void
-vfatalc(int code, const char *emsg, va_list ap)
-{
- static char s[BUFSIZ];
- const char *sep;
-
- if (emsg != NULL) {
- (void)vsnprintf(s, sizeof(s), emsg, ap);
- sep = ": ";
- } else {
- s[0] = '\0';
- sep = "";
- }
- if (code)
- logit(LOG_CRIT, "%s: %s%s%s",
- log_procname, s, sep, strerror(code));
- else
- logit(LOG_CRIT, "%s%s%s", log_procname, sep, s);
-}
-
-void
-fatal(const char *emsg, ...)
-{
- va_list ap;
-
- va_start(ap, emsg);
- vfatalc(errno, emsg, ap);
- va_end(ap);
- exit(1);
-}
-
-void
-fatalx(const char *emsg, ...)
-{
- va_list ap;
-
- va_start(ap, emsg);
- vfatalc(0, emsg, ap);
- va_end(ap);
- exit(1);
-}
diff --git a/smtpd/log.h b/smtpd/log.h
deleted file mode 100644
index 81d0973c..00000000
--- a/smtpd/log.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* $OpenBSD: log.h,v 1.8 2018/04/26 20:57:59 eric Exp $ */
-
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@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.
- */
-
-#ifndef LOG_H
-#define LOG_H
-
-#include "openbsd-compat.h"
-
-#include <syslog.h>
-
-#include <stdarg.h>
-#ifdef HAVE_SYS_CDEFS_H
-#include <sys/cdefs.h>
-#endif
-
-void log_init(int, int);
-void log_procinit(const char *);
-void log_setverbose(int);
-int log_getverbose(void);
-void log_warn(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-void log_warnx(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-void log_info(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-void log_debug(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-void logit(int, const char *, ...)
- __attribute__((__format__ (printf, 2, 3)));
-void vlog(int, const char *, va_list)
- __attribute__((__format__ (printf, 2, 0)));
-__dead void fatal(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-__dead void fatalx(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)));
-
-#endif /* LOG_H */
diff --git a/smtpd/mail.lmtp.8 b/smtpd/mail.lmtp.8
deleted file mode 100644
index 98dee00d..00000000
--- a/smtpd/mail.lmtp.8
+++ /dev/null
@@ -1,55 +0,0 @@
-.\" $OpenBSD: mail.lmtp.8,v 1.1 2017/02/14 15:16:34 gilles Exp $
-.\"
-.\" Copyright (c) 2017 Gilles Chehade <gilles@poolp.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: February 14 2017 $
-.Dt MAIL.LMTP 8
-.Os
-.Sh NAME
-.Nm mail.lmtp
-.Nd deliver mail through LMTP
-.Sh SYNOPSIS
-.Nm mail.lmtp
-.Op Fl d Ar destination
-.Op Fl f Ar from
-.Op Fl l Ar lhlo
-.Ar user ...
-.Sh DESCRIPTION
-.Nm
-reads the standard input up to an end-of-file and delivers it to
-an LMTP server for each
-.Ar user Ns 's
-address.
-The
-.Ar user
-must be a valid user name or email address.
-.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl d Ar destination
-Specify the destination LMTP address.
-.It Fl f Ar from
-Specify the sender's name or email address.
-.It Fl l Ar lhlo
-Specify the LHLO argument used in the LMTP session.
-By default,
-.Nm mail.lmtp
-will default to "localhost".
-.El
-.Sh EXIT STATUS
-.Ex -std mail.lmtp
-.Sh SEE ALSO
-.Xr mail 1 ,
-.Xr smtpd 8
diff --git a/smtpd/mail.lmtp.c b/smtpd/mail.lmtp.c
deleted file mode 100644
index 90b89990..00000000
--- a/smtpd/mail.lmtp.c
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright (c) 2017 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sysexits.h>
-#include <unistd.h>
-
-enum phase {
- PHASE_BANNER,
- PHASE_HELO,
- PHASE_MAILFROM,
- PHASE_RCPTTO,
- PHASE_DATA,
- PHASE_EOM,
- PHASE_QUIT
-};
-
-struct session {
- const char *lhlo;
- const char *mailfrom;
- char *rcptto;
-
- char **rcpts;
- int n_rcpts;
-};
-
-static int lmtp_connect(const char *);
-static void lmtp_engine(int, struct session *);
-static void stream_file(FILE *);
-
-int
-main(int argc, char *argv[])
-{
- int ch;
- int conn;
- const char *destination = "localhost";
- struct session session;
-
- if (! geteuid())
- errx(EX_TEMPFAIL, "mail.lmtp: may not be executed as root");
-
- session.lhlo = "localhost";
- session.mailfrom = getenv("SENDER");
- session.rcptto = NULL;
-
- while ((ch = getopt(argc, argv, "d:l:f:ru")) != -1) {
- switch (ch) {
- case 'd':
- destination = optarg;
- break;
- case 'l':
- session.lhlo = optarg;
- break;
- case 'f':
- session.mailfrom = optarg;
- break;
-
- case 'r':
- session.rcptto = getenv("RECIPIENT");
- break;
-
- case 'u':
- session.rcptto = getenv("USER");
- break;
-
- default:
- break;
- }
- }
- argc -= optind;
- argv += optind;
-
- if (session.mailfrom == NULL)
- errx(EX_TEMPFAIL, "sender must be specified with -f");
-
- if (argc == 0 && session.rcptto == NULL)
- errx(EX_TEMPFAIL, "no recipient was specified");
-
- if (session.rcptto) {
- session.rcpts = &session.rcptto;
- session.n_rcpts = 1;
- }
- else {
- session.rcpts = argv;
- session.n_rcpts = argc;
- }
-
- conn = lmtp_connect(destination);
- lmtp_engine(conn, &session);
-
- return (0);
-}
-
-static int
-lmtp_connect_inet(const char *destination)
-{
- struct addrinfo hints, *res, *res0;
- char *destcopy = NULL;
- const char *hostname = NULL;
- const char *servname = NULL;
- const char *cause = NULL;
- char *p;
- int n, s = -1, save_errno;
-
- if ((destcopy = strdup(destination)) == NULL)
- err(EX_TEMPFAIL, NULL);
-
- servname = "25";
- hostname = destcopy;
- p = destcopy;
- if (*p == '[') {
- if ((p = strchr(destcopy, ']')) == NULL)
- errx(EX_TEMPFAIL, "inet: invalid address syntax");
-
- /* remove [ and ] */
- *p = '\0';
- hostname++;
- if (strncasecmp(hostname, "IPv6:", 5) == 0)
- hostname += 5;
-
- /* extract port if any */
- switch (*(p+1)) {
- case ':':
- servname = p+2;
- break;
- case '\0':
- break;
- default:
- errx(EX_TEMPFAIL, "inet: invalid address syntax");
- }
- }
- else if ((p = strchr(destcopy, ':')) != NULL) {
- *p++ = '\0';
- servname = p;
- }
-
- memset(&hints, 0, sizeof hints);
- hints.ai_family = PF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_NUMERICSERV;
- n = getaddrinfo(hostname, servname, &hints, &res0);
- if (n)
- errx(EX_TEMPFAIL, "inet: %s", gai_strerror(n));
-
- for (res = res0; res; res = res->ai_next) {
- s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
- if (s == -1) {
- cause = "socket";
- continue;
- }
-
- if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
- cause = "connect";
- save_errno = errno;
- close(s);
- errno = save_errno;
- s = -1;
- continue;
- }
- break;
- }
-
- freeaddrinfo(res0);
- if (s == -1)
- errx(EX_TEMPFAIL, "%s", cause);
-
- free(destcopy);
- return s;
-}
-
-static int
-lmtp_connect_unix(const char *destination)
-{
- struct sockaddr_un addr;
- int s;
-
- if (*destination != '/')
- errx(EX_TEMPFAIL, "unix: path must be absolute");
-
- if ((s = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1)
- err(EX_TEMPFAIL, NULL);
-
- memset(&addr, 0, sizeof addr);
- addr.sun_family = AF_UNIX;
- if (strlcpy(addr.sun_path, destination, sizeof addr.sun_path)
- >= sizeof addr.sun_path)
- errx(EX_TEMPFAIL, "unix: socket path is too long");
-
- if (connect(s, (struct sockaddr *)&addr, sizeof addr) == -1)
- err(EX_TEMPFAIL, "connect");
-
- return s;
-}
-
-static int
-lmtp_connect(const char *destination)
-{
- if (destination[0] == '/')
- return lmtp_connect_unix(destination);
- return lmtp_connect_inet(destination);
-}
-
-static void
-lmtp_engine(int fd_read, struct session *session)
-{
- int fd_write = 0;
- FILE *file_read = 0;
- FILE *file_write = 0;
- char *line = NULL;
- size_t linesize = 0;
- ssize_t linelen;
- enum phase phase = PHASE_BANNER;
-
- if ((fd_write = dup(fd_read)) == -1)
- err(EX_TEMPFAIL, "dup");
-
- if ((file_read = fdopen(fd_read, "r")) == NULL)
- err(EX_TEMPFAIL, "fdopen");
-
- if ((file_write = fdopen(fd_write, "w")) == NULL)
- err(EX_TEMPFAIL, "fdopen");
-
- do {
- fflush(file_write);
-
- if ((linelen = getline(&line, &linesize, file_read)) == -1) {
- if (ferror(file_read))
- err(EX_TEMPFAIL, "getline");
- else
- errx(EX_TEMPFAIL, "unexpected EOF from LMTP server");
- }
- line[strcspn(line, "\n")] = '\0';
- line[strcspn(line, "\r")] = '\0';
-
- if (linelen < 4 ||
- !isdigit((unsigned char)line[0]) ||
- !isdigit((unsigned char)line[1]) ||
- !isdigit((unsigned char)line[2]) ||
- (line[3] != ' ' && line[3] != '-'))
- errx(EX_TEMPFAIL, "LMTP server sent an invalid line");
-
- if (line[0] != (phase == PHASE_DATA ? '3' : '2'))
- errx(EX_TEMPFAIL, "LMTP server error: %s", line);
-
- if (line[3] == '-')
- continue;
-
- switch (phase) {
-
- case PHASE_BANNER:
- fprintf(file_write, "LHLO %s\r\n", session->lhlo);
- phase++;
- break;
-
- case PHASE_HELO:
- fprintf(file_write, "MAIL FROM:<%s>\r\n", session->mailfrom);
- phase++;
- break;
-
- case PHASE_MAILFROM:
- fprintf(file_write, "RCPT TO:<%s>\r\n", session->rcpts[session->n_rcpts - 1]);
- if (session->n_rcpts - 1 == 0) {
- phase++;
- break;
- }
- session->n_rcpts--;
- break;
-
- case PHASE_RCPTTO:
- fprintf(file_write, "DATA\r\n");
- phase++;
- break;
-
- case PHASE_DATA:
- stream_file(file_write);
- fprintf(file_write, ".\r\n");
- phase++;
- break;
-
- case PHASE_EOM:
- fprintf(file_write, "QUIT\r\n");
- phase++;
- break;
-
- case PHASE_QUIT:
- exit(0);
- }
- } while (1);
-}
-
-static void
-stream_file(FILE *conn)
-{
- char *line = NULL;
- size_t linesize = 0;
- ssize_t linelen;
-
- while ((linelen = getline(&line, &linesize, stdin)) != -1) {
- line[strcspn(line, "\n")] = '\0';
- if (line[0] == '.')
- fprintf(conn, ".");
- fprintf(conn, "%s\r\n", line);
- }
- free(line);
- if (ferror(stdin))
- err(EX_TEMPFAIL, "getline");
-}
diff --git a/smtpd/mail.maildir.8 b/smtpd/mail.maildir.8
deleted file mode 100644
index ce822698..00000000
--- a/smtpd/mail.maildir.8
+++ /dev/null
@@ -1,45 +0,0 @@
-.\" $OpenBSD: mail.maildir.8,v 1.5 2018/05/30 12:37:57 jmc Exp $
-.\"
-.\" Copyright (c) 2017 Gilles Chehade <gilles@poolp.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: May 30 2018 $
-.Dt MAIL.MAILDIR 8
-.Os
-.Sh NAME
-.Nm mail.maildir
-.Nd store mail in a maildir
-.Sh SYNOPSIS
-.Nm mail.maildir
-.Op Fl j
-.Op Ar pathname
-.Sh DESCRIPTION
-.Nm
-reads the standard input up to an end-of-file and adds it to the
-mail directory located in
-.Ar pathname
-or to the mail directory
-.Pa Maildir
-located in the user's home directory.
-.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl j
-Scan message for X-Spam and move to Junk folder if result is positive.
-.El
-.Sh EXIT STATUS
-.Ex -std mail.maildir
-.Sh SEE ALSO
-.Xr mail 1 ,
-.Xr smtpd 8
diff --git a/smtpd/mail.maildir.c b/smtpd/mail.maildir.c
deleted file mode 100644
index fe6adba6..00000000
--- a/smtpd/mail.maildir.c
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (c) 2017 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#ifndef nitems
-#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
-#endif
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <sysexits.h>
-#include <unistd.h>
-
-#define MAILADDR_ESCAPE "!#$%&'*/?^`{|}~"
-
-static int maildir_subdir(const char *, char *, size_t);
-static void maildir_mkdirs(const char *);
-static void maildir_engine(const char *, int);
-static int mkdirs_component(const char *, mode_t);
-static int mkdirs(const char *, mode_t);
-
-int
-main(int argc, char *argv[])
-{
- int ch;
- int junk = 0;
-
- if (! geteuid())
- errx(1, "mail.maildir: may not be executed as root");
-
- while ((ch = getopt(argc, argv, "j")) != -1) {
- switch (ch) {
- case 'j':
- junk = 1;
- break;
- default:
- break;
- }
- }
- argc -= optind;
- argv += optind;
-
- if (argc > 1)
- errx(1, "mail.maildir: only one maildir is allowed");
-
- maildir_engine(argv[0], junk);
-
- return (0);
-}
-
-static int
-maildir_subdir(const char *extension, char *dest, size_t len)
-{
- char *sanitized;
-
- if (strlcpy(dest, extension, len) >= len)
- return 0;
-
- for (sanitized = dest; *sanitized; sanitized++)
- if (strchr(MAILADDR_ESCAPE, *sanitized))
- *sanitized = ':';
-
- return 1;
-}
-
-static void
-maildir_mkdirs(const char *dirname)
-{
- uint i;
- int ret;
- char pathname[PATH_MAX];
- char *subdirs[] = { "cur", "tmp", "new" };
-
- if (mkdirs(dirname, 0700) == -1 && errno != EEXIST) {
- if (errno == EINVAL || errno == ENAMETOOLONG)
- err(1, NULL);
- err(EX_TEMPFAIL, NULL);
- }
-
- for (i = 0; i < nitems(subdirs); ++i) {
- ret = snprintf(pathname, sizeof pathname, "%s/%s", dirname,
- subdirs[i]);
- if (ret < 0 || (size_t)ret >= sizeof pathname)
- errc(1, ENAMETOOLONG, "%s/%s", dirname, subdirs[i]);
- if (mkdir(pathname, 0700) == -1 && errno != EEXIST)
- err(EX_TEMPFAIL, NULL);
- }
-}
-
-static void
-maildir_engine(const char *dirname, int junk)
-{
- char rootpath[PATH_MAX];
- char junkpath[PATH_MAX];
- char extpath[PATH_MAX];
- char subdir[PATH_MAX];
- char filename[PATH_MAX];
- char hostname[HOST_NAME_MAX+1];
-
- char tmp[PATH_MAX];
- char new[PATH_MAX];
-
- int ret;
-
- int fd;
- FILE *fp;
- char *line = NULL;
- size_t linesize = 0;
- ssize_t linelen;
- struct stat sb;
- char *home;
- char *extension;
-
- int is_junk = 0;
- int in_hdr = 1;
-
- if (dirname == NULL) {
- if ((home = getenv("HOME")) == NULL)
- err(1, NULL);
- ret = snprintf(rootpath, sizeof rootpath, "%s/Maildir", home);
- if (ret < 0 || (size_t)ret >= sizeof rootpath)
- errc(1, ENAMETOOLONG, "%s/Maildir", home);
- dirname = rootpath;
- }
- maildir_mkdirs(dirname);
-
- if (junk) {
- /* create Junk subdirectory */
- ret = snprintf(junkpath, sizeof junkpath, "%s/.Junk", dirname);
- if (ret < 0 || (size_t)ret >= sizeof junkpath)
- errc(1, ENAMETOOLONG, "%s/.Junk", dirname);
- maildir_mkdirs(junkpath);
- }
-
- if ((extension = getenv("EXTENSION")) != NULL) {
- if (maildir_subdir(extension, subdir, sizeof(subdir)) &&
- subdir[0]) {
- ret = snprintf(extpath, sizeof extpath, "%s/.%s",
- dirname, subdir);
- if (ret < 0 || (size_t)ret >= sizeof extpath)
- errc(1, ENAMETOOLONG, "%s/.%s",
- dirname, subdir);
- if (stat(extpath, &sb) != -1) {
- dirname = extpath;
- maildir_mkdirs(dirname);
- }
- }
- }
-
- if (gethostname(hostname, sizeof hostname) != 0)
- (void)strlcpy(hostname, "localhost", sizeof hostname);
-
- (void)snprintf(filename, sizeof filename, "%lld.%08x.%s",
- (long long int) time(NULL),
- arc4random(),
- hostname);
-
- (void)snprintf(tmp, sizeof tmp, "%s/tmp/%s", dirname, filename);
-
- fd = open(tmp, O_CREAT | O_EXCL | O_WRONLY, 0600);
- if (fd == -1)
- err(EX_TEMPFAIL, NULL);
- if ((fp = fdopen(fd, "w")) == NULL)
- err(EX_TEMPFAIL, NULL);
-
- while ((linelen = getline(&line, &linesize, stdin)) != -1) {
- line[strcspn(line, "\n")] = '\0';
- if (line[0] == '\0')
- in_hdr = 0;
- if (junk && in_hdr &&
- (strcasecmp(line, "x-spam: yes") == 0 ||
- strcasecmp(line, "x-spam-flag: yes") == 0))
- is_junk = 1;
- fprintf(fp, "%s\n", line);
- }
- free(line);
- if (ferror(stdin))
- err(EX_TEMPFAIL, NULL);
-
- if (fflush(fp) == EOF ||
- ferror(fp) ||
- fsync(fd) == -1 ||
- fclose(fp) == EOF)
- err(EX_TEMPFAIL, NULL);
-
- (void)snprintf(new, sizeof new, "%s/new/%s",
- is_junk ? junkpath : dirname, filename);
-
- if (rename(tmp, new) == -1)
- err(EX_TEMPFAIL, NULL);
-
- exit(0);
-}
-
-
-static int
-mkdirs_component(const char *path, mode_t mode)
-{
- struct stat sb;
-
- if (stat(path, &sb) == -1) {
- if (errno != ENOENT)
- return 0;
- if (mkdir(path, mode | S_IWUSR | S_IXUSR) == -1)
- return 0;
- }
- else if (!S_ISDIR(sb.st_mode)) {
- errno = ENOTDIR;
- return 0;
- }
-
- return 1;
-}
-
-static int
-mkdirs(const char *path, mode_t mode)
-{
- char buf[PATH_MAX];
- int i = 0;
- int done = 0;
- const char *p;
-
- /* absolute path required */
- if (*path != '/') {
- errno = EINVAL;
- return 0;
- }
-
- /* make sure we don't exceed PATH_MAX */
- if (strlen(path) >= sizeof buf) {
- errno = ENAMETOOLONG;
- return 0;
- }
-
- memset(buf, 0, sizeof buf);
- for (p = path; *p; p++) {
- if (*p == '/') {
- if (buf[0] != '\0')
- if (!mkdirs_component(buf, mode))
- return 0;
- while (*p == '/')
- p++;
- buf[i++] = '/';
- buf[i++] = *p;
- if (*p == '\0' && ++done)
- break;
- continue;
- }
- buf[i++] = *p;
- }
- if (!done)
- if (!mkdirs_component(buf, mode))
- return 0;
-
- if (chmod(path, mode) == -1)
- return 0;
-
- return 1;
-}
diff --git a/smtpd/mail.mboxfile.8 b/smtpd/mail.mboxfile.8
deleted file mode 100644
index 015adcb5..00000000
--- a/smtpd/mail.mboxfile.8
+++ /dev/null
@@ -1,34 +0,0 @@
-.\" $OpenBSD: mail.mboxfile.8,v 1.1 2018/07/25 10:19:28 gilles Exp $
-.\"
-.\" Copyright (c) 2017 Gilles Chehade <gilles@poolp.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: July 25 2018 $
-.Dt MAIL.MDA 8
-.Os
-.Sh NAME
-.Nm mail.mboxfile
-.Nd deliver mail to a file in mbox format
-.Sh SYNOPSIS
-.Nm mail.mboxfile
-.Ar file
-.Sh DESCRIPTION
-.Nm
-appends mail to a file in mbox format and acknowledges delivery success or failure
-with its exit status.
-.Sh EXIT STATUS
-.Ex -std mail.mboxfile
-.Sh SEE ALSO
-.Xr mail 1 ,
-.Xr smtpd 8
diff --git a/smtpd/mail.mboxfile.c b/smtpd/mail.mboxfile.c
deleted file mode 100644
index 097a8d96..00000000
--- a/smtpd/mail.mboxfile.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sysexits.h>
-#include <time.h>
-#include <unistd.h>
-
-static void mboxfile_engine(const char *sender, const char *filename);
-
-int
-main(int argc, char *argv[])
-{
- int ch;
- char *sender = "<unknown>";
-
- if (! geteuid())
- errx(1, "mail.mboxfile: may not be executed as root");
-
- while ((ch = getopt(argc, argv, "f:")) != -1) {
- switch (ch) {
- case 'f':
- sender = optarg;
- break;
- default:
- break;
- }
- }
- argc -= optind;
- argv += optind;
-
- if (argc > 1)
- errx(1, "mail.mboxfile: only one mboxfile is allowed");
-
- mboxfile_engine(sender, argv[0]);
-
- return (0);
-}
-
-static void
-mboxfile_engine(const char *sender, const char *filename)
-{
- int fd;
- FILE *fp;
- char *line = NULL;
- size_t linesize = 0;
- ssize_t linelen;
- time_t now;
-
- time(&now);
-
-#ifndef O_EXLOCK
-#define O_EXLOCK 0
-#endif
- fd = open(filename, O_CREAT | O_APPEND | O_WRONLY | O_EXLOCK, 0600);
-#ifndef HAVE_O_EXLOCK
- /* XXX : do something! */
-#endif
- if (fd == -1)
- err(EX_TEMPFAIL, NULL);
-
- if ((fp = fdopen(fd, "w")) == NULL)
- err(EX_TEMPFAIL, NULL);
-
- fprintf(fp, "From %s %s", sender, ctime(&now));
- while ((linelen = getline(&line, &linesize, stdin)) != -1) {
- line[strcspn(line, "\n")] = '\0';
- if (strncmp(line, "From ", 5) == 0)
- fprintf(fp, ">%s\n", line);
- else
- fprintf(fp, "%s\n", line);
- }
- fprintf(fp, "\n");
- free(line);
- if (ferror(stdin))
- err(EX_TEMPFAIL, NULL);
-
- if (fflush(fp) == EOF ||
- ferror(fp) ||
- (fsync(fd) == -1 && errno != EINVAL) ||
- fclose(fp) == EOF)
- err(EX_TEMPFAIL, NULL);
-}
diff --git a/smtpd/mail.mda.8 b/smtpd/mail.mda.8
deleted file mode 100644
index 61fed733..00000000
--- a/smtpd/mail.mda.8
+++ /dev/null
@@ -1,35 +0,0 @@
-.\" $OpenBSD: mail.mda.8,v 1.1 2017/08/09 07:56:10 gilles Exp $
-.\"
-.\" Copyright (c) 2017 Gilles Chehade <gilles@poolp.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: August 9 2017 $
-.Dt MAIL.MDA 8
-.Os
-.Sh NAME
-.Nm mail.mda
-.Nd deliver mail to a program
-.Sh SYNOPSIS
-.Nm mail.mda
-.Ar program
-.Sh DESCRIPTION
-.Nm
-executes the program and its parameters.
-The program must read from the standard input up to an end-of-file
-and acknowledge delivery success or failure with its exit status.
-.Sh EXIT STATUS
-.Ex -std mail.mda
-.Sh SEE ALSO
-.Xr mail 1 ,
-.Xr smtpd 8
diff --git a/smtpd/mail.mda.c b/smtpd/mail.mda.c
deleted file mode 100644
index 23958071..00000000
--- a/smtpd/mail.mda.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) 2017 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sysexits.h>
-#include <unistd.h>
-
-int
-main(int argc, char *argv[])
-{
- int ch;
- int ret;
-
- if (! geteuid())
- errx(1, "mail.mda: may not be executed as root");
-
- while ((ch = getopt(argc, argv, "")) != -1) {
- switch (ch) {
- default:
- break;
- }
- }
- argc -= optind;
- argv += optind;
-
- if (argc == 0)
- errx(1, "mail.mda: command required");
-
- if (argc > 1)
- errx(1, "mail.mda: only one command is supported");
-
- /* could not obtain a shell or could not obtain wait status,
- * tempfail */
- if ((ret = system(argv[0])) == -1)
- errx(EX_TEMPFAIL, "%s", strerror(errno));
-
- /* not exited properly but we have no details,
- * tempfail */
- if (! WIFEXITED(ret))
- exit(EX_TEMPFAIL);
-
- exit(WEXITSTATUS(ret));
-}
diff --git a/smtpd/mail/Makefile b/smtpd/mail/Makefile
deleted file mode 100644
index b2bc2a26..00000000
--- a/smtpd/mail/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# $OpenBSD: Makefile,v 1.8 2018/07/25 10:19:28 gilles Exp $
-.PATH: ${.CURDIR}/..
-
-PROGS= mail.lmtp mail.maildir mail.mboxfile mail.mda
-
-MAN= mail.lmtp.8 mail.maildir.8 mail.mboxfile.8 mail.mda.8
-
-BINOWN= root
-BINGRP= wheel
-
-BINDIR= /usr/libexec
-
-CFLAGS+= -fstack-protector-all
-CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS+= -Wmissing-declarations
-CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
-CFLAGS+= -Wsign-compare
-CFLAGS+= -Werror-implicit-function-declaration
-
-.include <bsd.prog.mk>
diff --git a/smtpd/mailaddr.c b/smtpd/mailaddr.c
deleted file mode 100644
index 4346e3dc..00000000
--- a/smtpd/mailaddr.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/* $OpenBSD: mailaddr.c,v 1.3 2018/05/31 21:06:12 gilles Exp $ */
-
-/*
- * Copyright (c) 2015 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <event.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static int
-mailaddr_line_split(char **line, char **ret)
-{
- static char buffer[LINE_MAX];
- int esc, dq, sq;
- size_t i;
- char *s;
-
- memset(buffer, 0, sizeof buffer);
- esc = dq = sq = 0;
- i = 0;
- for (s = *line; (*s) && (i < sizeof(buffer)); ++s) {
- if (esc) {
- buffer[i++] = *s;
- esc = 0;
- continue;
- }
- if (*s == '\\') {
- esc = 1;
- continue;
- }
- if (*s == ',' && !dq && !sq) {
- *ret = buffer;
- *line = s+1;
- return (1);
- }
-
- buffer[i++] = *s;
- esc = 0;
-
- if (*s == '"' && !sq)
- dq ^= 1;
- if (*s == '\'' && !dq)
- sq ^= 1;
- }
-
- if (esc || dq || sq || i == sizeof(buffer))
- return (-1);
-
- *ret = buffer;
- *line = s;
- return (i ? 1 : 0);
-}
-
-int
-mailaddr_line(struct maddrmap *maddrmap, const char *s)
-{
- struct maddrnode mn;
- char buffer[LINE_MAX];
- char *p, *subrcpt;
- int ret;
-
- memset(buffer, 0, sizeof buffer);
- if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
- return 0;
-
- p = buffer;
- while ((ret = mailaddr_line_split(&p, &subrcpt)) > 0) {
- subrcpt = strip(subrcpt);
- if (subrcpt[0] == '\0')
- continue;
- if (!text_to_mailaddr(&mn.mailaddr, subrcpt))
- return 0;
- log_debug("subrcpt: [%s]", subrcpt);
- maddrmap_insert(maddrmap, &mn);
- }
-
- if (ret >= 0)
- return 1;
- /* expand_line_split() returned < 0 */
- return 0;
-}
-
-void
-maddrmap_init(struct maddrmap *maddrmap)
-{
- TAILQ_INIT(&maddrmap->queue);
-}
-
-void
-maddrmap_insert(struct maddrmap *maddrmap, struct maddrnode *maddrnode)
-{
- struct maddrnode *mn;
-
- mn = xmemdup(maddrnode, sizeof *maddrnode);
- TAILQ_INSERT_TAIL(&maddrmap->queue, mn, entries);
-}
-
-void
-maddrmap_free(struct maddrmap *maddrmap)
-{
- struct maddrnode *mn;
-
- while ((mn = TAILQ_FIRST(&maddrmap->queue))) {
- TAILQ_REMOVE(&maddrmap->queue, mn, entries);
- free(mn);
- }
- free(maddrmap);
-}
diff --git a/smtpd/makemap.8 b/smtpd/makemap.8
deleted file mode 100644
index 674bef6f..00000000
--- a/smtpd/makemap.8
+++ /dev/null
@@ -1,174 +0,0 @@
-.\" $OpenBSD: makemap.8,v 1.30 2018/11/25 14:41:16 gilles Exp $
-.\"
-.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@openbsd.org>
-.\" Copyright (c) 2008-2009 Gilles Chehade <gilles@poolp.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: November 25 2018 $
-.Dt MAKEMAP 8
-.Os
-.Sh NAME
-.Nm makemap
-.Nd create database maps for smtpd
-.Sh SYNOPSIS
-.Nm makemap
-.Op Fl U
-.Op Fl d Ar dbtype
-.Op Fl o Ar dbfile
-.Op Fl t Ar type
-.Ar file
-.Sh DESCRIPTION
-Maps provide a generic interface for associating textual key to a value.
-Such associations may be accessed through a plaintext file, database, or DNS.
-The format of these file types is described below.
-.Nm
-itself creates the database maps used by keyed map lookups specified in
-.Xr smtpd.conf 5 .
-.Pp
-.Nm
-reads input from
-.Ar file
-and writes data to a file whose name is made by adding a
-.Dq .db
-suffix to
-.Ar file .
-The current line can be extended over multiple lines using a backslash
-.Pq Sq \e .
-Comments can be put anywhere in the file using a hash mark
-.Pq Sq # ,
-and extend to the end of the current line.
-Care should be taken when commenting out multi-line text:
-the comment is effective until the end of the entire block.
-In all cases,
-.Nm
-reads lines consisting of words separated by whitespace.
-The first word of a line is the database key;
-the remainder represents the mapped value.
-The database key and value may optionally be separated
-by the colon character.
-.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl d Ar dbtype
-Specify the format of the database.
-Available formats are
-.Ar hash
-and
-.Ar btree .
-The default value is
-.Ar hash .
-.It Fl o Ar dbfile
-Write the generated database to
-.Ar dbfile .
-.It Fl t Ar type
-Specify the format of the resulting map file.
-The default map format is suitable for storing simple, unstructured,
-key-to-value string associations.
-However, if the mapped value has special meaning,
-as in the case of the virtual domains file,
-a suitable
-.Ar type
-must be provided.
-The available output types are:
-.Bl -tag -width "aliases"
-.It Cm aliases
-The mapped value is a comma-separated list of mail destinations.
-This format can be used for building user aliases and
-user mappings for virtual domain files.
-.It Cm set
-There is no mapped value \(en a map of this type will only allow for
-the lookup of keys.
-This format can be used for building primary domain maps.
-.El
-.It Fl U
-Instead of generating a database map from text input,
-dump the contents of a database map as text
-with the key and value separated with a tab.
-.El
-.Sh PRIMARY DOMAINS
-Primary domains can be kept in tables.
-To create a primary domain table, add each primary domain on a
-single line by itself.
-.Pp
-In addition to adding an entry to the primary domain map,
-one must add a filter rule that accepts mail for the domain
-map, for example:
-.Bd -literal -offset indent
-table domains db:/etc/mail/domains.db
-
-action "local" mbox
-
-match for domain <domains> action "local"
-.Ed
-.Sh VIRTUAL DOMAINS
-Virtual domains may also be kept in tables.
-To create a virtual domain table, add each virtual domain on a
-single line by itself.
-.Pp
-Virtual domains expect a mapping of virtual users to real users
-in order to determine if a recipient is accepted or not.
-The mapping format is an extension to
-.Xr aliases 5 ,
-which allows the use of
-.Dq user@domain.tld
-to accept user only on the specified domain,
-.Dq user
-to accept the user for any of the virtual domains,
-.Dq @domain.tld
-to provide a catch-all for the specified domain and
-.Dq @
-to provide a global catch-all for all domains.
-.Xr smtpd 8
-will perform the lookups in that specific order.
-.Pp
-To create single virtual address, add
-.Dq user@example.com user
-to the users map.
-To handle all mail destined to any user at example.com, add
-.Dq @example.com user
-to the virtual map.
-.Pp
-In addition to adding an entry to the virtual map,
-one must add a filter rule that accepts mail for virtual domains,
-for example:
-.Bd -literal -offset indent
-table vdomains db:/etc/mail/vdomains.db
-table vusers db:/etc/mail/users.db
-
-action "local" mbox virtual <vusers>
-
-match for domain <vdomains> action "local"
-match for domain "example.org" action "local"
-.Ed
-.Sh FILES
-.Bl -tag -width "/etc/mail/aliasesXXX" -compact
-.It Pa /etc/mail/aliases
-List of user mail aliases.
-.It Pa /etc/mail/secrets
-List of remote host credentials.
-.El
-.Sh EXIT STATUS
-.Ex -std makemap
-.Sh SEE ALSO
-.Xr aliases 5 ,
-.Xr smtpd.conf 5 ,
-.Xr table 5 ,
-.Xr newaliases 8 ,
-.Xr smtpd 8
-.Sh HISTORY
-The
-.Nm
-command first appeared in
-.Ox 4.6
-as a replacement for the equivalent command shipped with sendmail.
diff --git a/smtpd/makemap.c b/smtpd/makemap.c
deleted file mode 100644
index 10e3f555..00000000
--- a/smtpd/makemap.c
+++ /dev/null
@@ -1,521 +0,0 @@
-/* $OpenBSD: makemap.c,v 1.73 2020/02/24 16:16:07 millert Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2008-2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#ifdef HAVE_SYS_FILE_H
-#include <sys/file.h> /* Needed for flock */
-#endif
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/tree.h>
-#include <sys/queue.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#ifdef HAVE_DB_H
-#include <db.h>
-#elif defined(HAVE_DB1_DB_H)
-#include <db1/db.h>
-#elif defined(HAVE_DB_185_H)
-#include <db_185.h>
-#endif
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-#include <limits.h>
-#ifdef HAVE_UTIL_H
-#include <util.h>
-#endif
-#ifdef HAVE_LIBUTIL_H
-#include <libutil.h>
-#endif
-
-#include "smtpd.h"
-#include "log.h"
-
-#define PATH_ALIASES SMTPD_CONFDIR "/aliases"
-
-static void usage(void);
-static int parse_map(DB *, int *, char *);
-static int parse_entry(DB *, int *, char *, size_t, size_t);
-static int parse_mapentry(DB *, int *, char *, size_t, size_t);
-static int parse_setentry(DB *, int *, char *, size_t, size_t);
-static int make_plain(DBT *, char *);
-static int make_aliases(DBT *, char *);
-static char *conf_aliases(char *);
-static int dump_db(const char *, DBTYPE);
-
-char *source;
-static int mode;
-
-enum output_type {
- T_PLAIN,
- T_ALIASES,
- T_SET
-} type;
-
-/*
- * Stub functions so that makemap compiles using minimum object files.
- */
-int
-fork_proc_backend(const char *backend, const char *conf, const char *procname)
-{
- return (-1);
-}
-
-int
-makemap(int prog_mode, int argc, char *argv[])
-{
- struct stat sb;
- char dbname[PATH_MAX];
- DB *db;
- const char *opts;
- char *conf, *oflag = NULL;
- int ch, dbputs = 0, Uflag = 0;
- DBTYPE dbtype = DB_HASH;
- char *p;
- gid_t gid;
- int fd = -1;
-
- gid = getgid();
- if (setresgid(gid, gid, gid) == -1)
- err(1, "setresgid");
-
- if ((env = config_default()) == NULL)
- err(1, NULL);
-
- log_init(1, LOG_MAIL);
-
- mode = prog_mode;
- conf = CONF_FILE;
- type = T_PLAIN;
- opts = "b:C:d:ho:O:t:U";
- if (mode == P_NEWALIASES)
- opts = "f:h";
-
- while ((ch = getopt(argc, argv, opts)) != -1) {
- switch (ch) {
- case 'b':
- if (optarg && strcmp(optarg, "i") == 0)
- mode = P_NEWALIASES;
- break;
- case 'C':
- break; /* for compatibility */
- case 'd':
- if (strcmp(optarg, "hash") == 0)
- dbtype = DB_HASH;
- else if (strcmp(optarg, "btree") == 0)
- dbtype = DB_BTREE;
- else
- errx(1, "unsupported DB type '%s'", optarg);
- break;
- case 'f':
- conf = optarg;
- break;
- case 'o':
- oflag = optarg;
- break;
- case 'O':
- if (strncmp(optarg, "AliasFile=", 10) != 0)
- break;
- type = T_ALIASES;
- p = strchr(optarg, '=');
- source = ++p;
- break;
- case 't':
- if (strcmp(optarg, "aliases") == 0)
- type = T_ALIASES;
- else if (strcmp(optarg, "set") == 0)
- type = T_SET;
- else
- errx(1, "unsupported type '%s'", optarg);
- break;
- case 'U':
- Uflag = 1;
- break;
- default:
- usage();
- }
- }
- argc -= optind;
- argv += optind;
-
- /* sendmail-compat makemap ... re-execute using proper interface */
- if (argc == 2) {
- if (oflag)
- usage();
-
- p = strstr(argv[1], ".db");
- if (p == NULL || strcmp(p, ".db") != 0) {
- if (!bsnprintf(dbname, sizeof dbname, "%s.db",
- argv[1]))
- errx(1, "database name too long");
- }
- else {
- if (strlcpy(dbname, argv[1], sizeof dbname)
- >= sizeof dbname)
- errx(1, "database name too long");
- }
-
- execl(PATH_MAKEMAP, "makemap", "-d", argv[0], "-o", dbname,
- "-", (char *)NULL);
- err(1, "execl");
- }
-
- if (mode == P_NEWALIASES) {
- if (geteuid())
- errx(1, "need root privileges");
- if (argc != 0)
- usage();
- type = T_ALIASES;
- if (source == NULL)
- source = conf_aliases(conf);
- } else {
- if (argc != 1)
- usage();
- source = argv[0];
- }
-
- if (Uflag)
- return dump_db(source, dbtype);
-
- if (oflag == NULL && asprintf(&oflag, "%s.db", source) == -1)
- err(1, "asprintf");
-
- if (strcmp(source, "-") != 0)
- if (stat(source, &sb) == -1)
- err(1, "stat: %s", source);
-
- if (!bsnprintf(dbname, sizeof(dbname), "%s.XXXXXXXXXXX", oflag))
- errx(1, "path too long");
- if ((fd = mkstemp(dbname)) == -1)
- err(1, "mkstemp");
-
- db = dbopen(dbname, O_TRUNC|O_RDWR, 0644, dbtype, NULL);
- if (db == NULL) {
- warn("dbopen: %s", dbname);
- goto bad;
- }
-
- if (strcmp(source, "-") != 0)
- if (fchmod(db->fd(db), sb.st_mode) == -1 ||
- fchown(db->fd(db), sb.st_uid, sb.st_gid) == -1) {
- warn("couldn't carry ownership and perms to %s",
- dbname);
- goto bad;
- }
-
- if (!parse_map(db, &dbputs, source))
- goto bad;
-
- if (db->close(db) == -1) {
- warn("dbclose: %s", dbname);
- goto bad;
- }
-
- /* force to disk before renaming over an existing file */
- if (fsync(fd) == -1) {
- warn("fsync: %s", dbname);
- goto bad;
- }
- if (close(fd) == -1) {
- fd = -1;
- warn("close: %s", dbname);
- goto bad;
- }
- fd = -1;
-
- if (rename(dbname, oflag) == -1) {
- warn("rename");
- goto bad;
- }
-
- if (mode == P_NEWALIASES)
- printf("%s: %d aliases\n", source, dbputs);
- else if (dbputs == 0)
- warnx("warning: empty map created: %s", oflag);
-
- return 0;
-bad:
- if (fd != -1)
- close(fd);
- unlink(dbname);
- return 1;
-}
-
-static int
-parse_map(DB *db, int *dbputs, char *filename)
-{
- FILE *fp;
- char *line;
- size_t len;
- size_t lineno = 0;
-
- if (strcmp(filename, "-") == 0)
- fp = fdopen(0, "r");
- else
- fp = fopen(filename, "r");
- if (fp == NULL) {
- warn("%s", filename);
- return 0;
- }
-
- if (!isatty(fileno(fp)) && flock(fileno(fp), LOCK_SH|LOCK_NB) == -1) {
- if (errno == EWOULDBLOCK)
- warnx("%s is locked", filename);
- else
- warn("%s: flock", filename);
- fclose(fp);
- return 0;
- }
-
- while ((line = fparseln(fp, &len, &lineno,
- NULL, FPARSELN_UNESCCOMM)) != NULL) {
- if (!parse_entry(db, dbputs, line, len, lineno)) {
- free(line);
- fclose(fp);
- return 0;
- }
- free(line);
- }
-
- fclose(fp);
- return 1;
-}
-
-static int
-parse_entry(DB *db, int *dbputs, char *line, size_t len, size_t lineno)
-{
- switch (type) {
- case T_PLAIN:
- case T_ALIASES:
- return parse_mapentry(db, dbputs, line, len, lineno);
- case T_SET:
- return parse_setentry(db, dbputs, line, len, lineno);
- }
- return 0;
-}
-
-static int
-parse_mapentry(DB *db, int *dbputs, char *line, size_t len, size_t lineno)
-{
- DBT key;
- DBT val;
- char *keyp;
- char *valp;
-
- keyp = line;
- while (isspace((unsigned char)*keyp))
- keyp++;
- if (*keyp == '\0')
- return 1;
-
- valp = keyp;
- strsep(&valp, " \t:");
- if (valp == NULL || valp == keyp)
- goto bad;
- while (*valp == ':' || isspace((unsigned char)*valp))
- valp++;
- if (*valp == '\0')
- goto bad;
-
- /* Check for dups. */
- key.data = keyp;
- key.size = strlen(keyp) + 1;
-
- xlowercase(key.data, key.data, strlen(key.data) + 1);
- if (db->get(db, &key, &val, 0) == 0) {
- warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp);
- return 0;
- }
-
- if (type == T_PLAIN) {
- if (!make_plain(&val, valp))
- goto bad;
- }
- else if (type == T_ALIASES) {
- if (!make_aliases(&val, valp))
- goto bad;
- }
-
- if (db->put(db, &key, &val, 0) == -1) {
- warn("dbput");
- return 0;
- }
-
- (*dbputs)++;
-
- free(val.data);
-
- return 1;
-
-bad:
- warnx("%s:%zd: invalid entry", source, lineno);
- return 0;
-}
-
-static int
-parse_setentry(DB *db, int *dbputs, char *line, size_t len, size_t lineno)
-{
- DBT key;
- DBT val;
- char *keyp;
-
- keyp = line;
- while (isspace((unsigned char)*keyp))
- keyp++;
- if (*keyp == '\0')
- return 1;
-
- val.data = "<set>";
- val.size = strlen(val.data) + 1;
-
- /* Check for dups. */
- key.data = keyp;
- key.size = strlen(keyp) + 1;
- xlowercase(key.data, key.data, strlen(key.data) + 1);
- if (db->get(db, &key, &val, 0) == 0) {
- warnx("%s:%zd: duplicate entry for %s", source, lineno, keyp);
- return 0;
- }
-
- if (db->put(db, &key, &val, 0) == -1) {
- warn("dbput");
- return 0;
- }
-
- (*dbputs)++;
-
- return 1;
-}
-
-static int
-make_plain(DBT *val, char *text)
-{
- val->data = xstrdup(text);
- val->size = strlen(text) + 1;
-
- return (val->size);
-}
-
-static int
-make_aliases(DBT *val, char *text)
-{
- struct expandnode xn;
- char *subrcpt;
- char *origtext;
-
- val->data = NULL;
- val->size = 0;
-
- origtext = xstrdup(text);
-
- while ((subrcpt = strsep(&text, ",")) != NULL) {
- /* subrcpt: strip initial and trailing whitespace. */
- subrcpt = strip(subrcpt);
- if (*subrcpt == '\0')
- goto error;
-
- if (!text_to_expandnode(&xn, subrcpt))
- goto error;
- }
-
- val->data = origtext;
- val->size = strlen(origtext) + 1;
- return (val->size);
-
-error:
- free(origtext);
-
- return 0;
-}
-
-static char *
-conf_aliases(char *cfgpath)
-{
- struct table *table;
- char *path;
- char *p;
-
- if (parse_config(env, cfgpath, 0))
- exit(1);
-
- table = table_find(env, "aliases");
- if (table == NULL)
- return (PATH_ALIASES);
-
- path = xstrdup(table->t_config);
- p = strstr(path, ".db");
- if (p == NULL || strcmp(p, ".db") != 0) {
- return (path);
- }
- *p = '\0';
- return (path);
-}
-
-static int
-dump_db(const char *dbname, DBTYPE dbtype)
-{
- DB *db;
- DBT key, val;
- char *keystr, *valstr;
- int r;
-
- db = dbopen(dbname, O_RDONLY, 0644, dbtype, NULL);
- if (db == NULL)
- err(1, "dbopen: %s", dbname);
-
- for (r = db->seq(db, &key, &val, R_FIRST); r == 0;
- r = db->seq(db, &key, &val, R_NEXT)) {
- keystr = key.data;
- valstr = val.data;
- if (keystr[key.size - 1] == '\0')
- key.size--;
- if (valstr[val.size - 1] == '\0')
- val.size--;
- printf("%.*s\t%.*s\n", (int)key.size, keystr,
- (int)val.size, valstr);
- }
- if (r == -1)
- err(1, "db->seq: %s", dbname);
-
- if (db->close(db) == -1)
- err(1, "dbclose: %s", dbname);
-
- return 0;
-}
-
-static void
-usage(void)
-{
- if (mode == P_NEWALIASES)
- fprintf(stderr, "usage: newaliases [-f file]\n");
- else
- fprintf(stderr, "usage: makemap [-U] [-d dbtype] [-o dbfile] "
- "[-t type] file\n");
- exit(1);
-}
diff --git a/smtpd/mda.c b/smtpd/mda.c
deleted file mode 100644
index 5e8fec19..00000000
--- a/smtpd/mda.c
+++ /dev/null
@@ -1,919 +0,0 @@
-/* $OpenBSD: mda.c,v 1.141 2019/10/03 08:50:08 gilles Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <grp.h> /* needed for setgroups */
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sysexits.h>
-#include <time.h>
-#include <unistd.h>
-#include <limits.h>
-#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
-#include <vis.h>
-#else
-#include "bsd-vis.h"
-#endif
-
-#include "smtpd.h"
-#include "log.h"
-
-#define MDA_HIWAT 65536
-
-struct mda_envelope {
- TAILQ_ENTRY(mda_envelope) entry;
- uint64_t session_id;
- uint64_t id;
- time_t creation;
- char *sender;
- char *rcpt;
- char *dest;
- char *user;
- char *dispatcher;
- char *mda_subaddress;
- char *mda_exec;
-};
-
-#define USER_WAITINFO 0x01
-#define USER_RUNNABLE 0x02
-#define USER_ONHOLD 0x04
-#define USER_HOLDQ 0x08
-
-struct mda_user {
- uint64_t id;
- TAILQ_ENTRY(mda_user) entry;
- TAILQ_ENTRY(mda_user) entry_runnable;
- char name[LOGIN_NAME_MAX];
- char usertable[PATH_MAX];
- size_t evpcount;
- TAILQ_HEAD(, mda_envelope) envelopes;
- int flags;
- size_t running;
- struct userinfo userinfo;
-};
-
-struct mda_session {
- uint64_t id;
- struct mda_user *user;
- struct mda_envelope *evp;
- struct io *io;
- FILE *datafp;
-};
-
-static void mda_io(struct io *, int, void *);
-static int mda_check_loop(FILE *, struct mda_envelope *);
-static int mda_getlastline(int, char *, size_t);
-static void mda_done(struct mda_session *);
-static void mda_fail(struct mda_user *, int, const char *,
- enum enhanced_status_code);
-static void mda_drain(void);
-static void mda_log(const struct mda_envelope *, const char *, const char *);
-static void mda_queue_ok(uint64_t);
-static void mda_queue_tempfail(uint64_t, const char *,
- enum enhanced_status_code);
-static void mda_queue_permfail(uint64_t, const char *, enum enhanced_status_code);
-static void mda_queue_loop(uint64_t);
-static struct mda_user *mda_user(const struct envelope *);
-static void mda_user_free(struct mda_user *);
-static const char *mda_user_to_text(const struct mda_user *);
-static struct mda_envelope *mda_envelope(uint64_t, const struct envelope *);
-static void mda_envelope_free(struct mda_envelope *);
-static struct mda_session * mda_session(struct mda_user *);
-static const char *mda_sysexit_to_str(int);
-
-static struct tree sessions;
-static struct tree users;
-
-static TAILQ_HEAD(, mda_user) runnable;
-
-void
-mda_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct mda_session *s;
- struct mda_user *u;
- struct mda_envelope *e;
- struct envelope evp;
- struct deliver deliver;
- struct msg m;
- const void *data;
- const char *error, *parent_error, *syserror;
- uint64_t reqid;
- size_t sz;
- char out[256], buf[LINE_MAX];
- int n;
- enum lka_resp_status status;
- enum mda_resp_status mda_status;
- int mda_sysexit;
-
- switch (imsg->hdr.type) {
- case IMSG_MDA_LOOKUP_USERINFO:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, (int *)&status);
- if (status == LKA_OK)
- m_get_data(&m, &data, &sz);
- m_end(&m);
-
- u = tree_xget(&users, reqid);
-
- if (status == LKA_TEMPFAIL)
- mda_fail(u, 0,
- "Temporary failure in user lookup",
- ESC_OTHER_ADDRESS_STATUS);
- else if (status == LKA_PERMFAIL)
- mda_fail(u, 1,
- "Permanent failure in user lookup",
- ESC_DESTINATION_MAILBOX_HAS_MOVED);
- else {
- if (sz != sizeof(u->userinfo))
- fatalx("mda: userinfo size mismatch");
- memmove(&u->userinfo, data, sz);
- u->flags &= ~USER_WAITINFO;
- u->flags |= USER_RUNNABLE;
- TAILQ_INSERT_TAIL(&runnable, u, entry_runnable);
- mda_drain();
- }
- return;
-
- case IMSG_QUEUE_DELIVER:
- m_msg(&m, imsg);
- m_get_envelope(&m, &evp);
- m_end(&m);
-
- u = mda_user(&evp);
-
- if (u->evpcount >= env->sc_mda_task_hiwat) {
- if (!(u->flags & USER_ONHOLD)) {
- log_debug("debug: mda: hiwat reached for "
- "user \"%s\": holding envelopes",
- mda_user_to_text(u));
- u->flags |= USER_ONHOLD;
- }
- }
-
- if (u->flags & USER_ONHOLD) {
- u->flags |= USER_HOLDQ;
- m_create(p_queue, IMSG_MDA_DELIVERY_HOLD,
- 0, 0, -1);
- m_add_evpid(p_queue, evp.id);
- m_add_id(p_queue, u->id);
- m_close(p_queue);
- return;
- }
-
- e = mda_envelope(u->id, &evp);
- TAILQ_INSERT_TAIL(&u->envelopes, e, entry);
- u->evpcount += 1;
- stat_increment("mda.pending", 1);
-
- if (!(u->flags & USER_RUNNABLE) &&
- !(u->flags & USER_WAITINFO)) {
- u->flags |= USER_RUNNABLE;
- TAILQ_INSERT_TAIL(&runnable, u, entry_runnable);
- }
-
- mda_drain();
- return;
-
- case IMSG_MDA_OPEN_MESSAGE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_end(&m);
-
- s = tree_xget(&sessions, reqid);
- e = s->evp;
-
- if (imsg->fd == -1) {
- log_debug("debug: mda: cannot get message fd");
- mda_queue_tempfail(e->id,
- "Cannot get message fd",
- ESC_OTHER_MAIL_SYSTEM_STATUS);
- mda_log(e, "TempFail", "Cannot get message fd");
- mda_done(s);
- return;
- }
-
- log_debug("debug: mda: got message fd %d "
- "for session %016"PRIx64 " evpid %016"PRIx64,
- imsg->fd, s->id, e->id);
-
- if ((s->datafp = fdopen(imsg->fd, "r")) == NULL) {
- log_warn("warn: mda: fdopen");
- close(imsg->fd);
- mda_queue_tempfail(e->id, "fdopen failed",
- ESC_OTHER_MAIL_SYSTEM_STATUS);
- mda_log(e, "TempFail", "fdopen failed");
- mda_done(s);
- return;
- }
-
- /* check delivery loop */
- if (mda_check_loop(s->datafp, e)) {
- log_debug("debug: mda: loop detected");
- mda_queue_loop(e->id);
- mda_log(e, "PermFail", "Loop detected");
- mda_done(s);
- return;
- }
-
- /* start queueing delivery headers */
- if (e->sender[0])
- /*
- * XXX: remove existing Return-Path,
- * if any
- */
- n = io_printf(s->io,
- "Return-Path: <%s>\n"
- "Delivered-To: %s\n",
- e->sender,
- e->rcpt ? e->rcpt : e->dest);
- else
- n = io_printf(s->io,
- "Delivered-To: %s\n",
- e->rcpt ? e->rcpt : e->dest);
- if (n == -1) {
- log_warn("warn: mda: "
- "fail to write delivery info");
- mda_queue_tempfail(e->id, "Out of memory",
- ESC_OTHER_MAIL_SYSTEM_STATUS);
- mda_log(e, "TempFail", "Out of memory");
- mda_done(s);
- return;
- }
-
- /* request parent to fork a helper process */
- memset(&deliver, 0, sizeof deliver);
- (void)text_to_mailaddr(&deliver.sender, s->evp->sender);
- (void)text_to_mailaddr(&deliver.rcpt, s->evp->rcpt);
- (void)text_to_mailaddr(&deliver.dest, s->evp->dest);
- if (s->evp->mda_exec)
- (void)strlcpy(deliver.mda_exec, s->evp->mda_exec, sizeof deliver.mda_exec);
- if (s->evp->mda_subaddress)
- (void)strlcpy(deliver.mda_subaddress, s->evp->mda_subaddress, sizeof deliver.mda_subaddress);
- (void)strlcpy(deliver.dispatcher, s->evp->dispatcher, sizeof deliver.dispatcher);
- deliver.userinfo = s->user->userinfo;
-
- log_debug("debug: mda: querying mda fd "
- "for session %016"PRIx64 " evpid %016"PRIx64,
- s->id, s->evp->id);
-
- m_create(p_parent, IMSG_MDA_FORK, 0, 0, -1);
- m_add_id(p_parent, reqid);
- m_add_data(p_parent, &deliver, sizeof(deliver));
- m_close(p_parent);
- return;
-
- case IMSG_MDA_FORK:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_end(&m);
-
- s = tree_xget(&sessions, reqid);
- e = s->evp;
- if (imsg->fd == -1) {
- log_warn("warn: mda: fail to retrieve mda fd");
- mda_queue_tempfail(e->id, "Cannot get mda fd",
- ESC_OTHER_MAIL_SYSTEM_STATUS);
- mda_log(e, "TempFail", "Cannot get mda fd");
- mda_done(s);
- return;
- }
-
- log_debug("debug: mda: got mda fd %d "
- "for session %016"PRIx64 " evpid %016"PRIx64,
- imsg->fd, s->id, s->evp->id);
-
- io_set_nonblocking(imsg->fd);
- io_set_fd(s->io, imsg->fd);
- io_set_write(s->io);
- return;
-
- case IMSG_MDA_DONE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, (int *)&mda_status);
- m_get_int(&m, (int *)&mda_sysexit);
- m_get_string(&m, &parent_error);
- m_end(&m);
-
- s = tree_xget(&sessions, reqid);
- e = s->evp;
- /*
- * Grab last line of mda stdout/stderr if available.
- */
- out[0] = '\0';
- if (imsg->fd != -1)
- mda_getlastline(imsg->fd, out, sizeof(out));
-
- /*
- * Choose between parent's description of error and
- * child's output, the latter having preference over
- * the former.
- */
- error = NULL;
- if (mda_status == MDA_OK) {
- if (s->datafp || (s->io && io_queued(s->io))) {
- error = "mda exited prematurely";
- mda_status = MDA_TEMPFAIL;
- }
- } else
- error = out[0] ? out : parent_error;
-
- syserror = NULL;
- if (mda_sysexit)
- syserror = mda_sysexit_to_str(mda_sysexit);
-
- /* update queue entry */
- switch (mda_status) {
- case MDA_TEMPFAIL:
- mda_queue_tempfail(e->id, error,
- ESC_OTHER_MAIL_SYSTEM_STATUS);
- (void)snprintf(buf, sizeof buf,
- "Error (%s%s%s)",
- syserror ? syserror : "",
- syserror ? ": " : "",
- error);
- mda_log(e, "TempFail", buf);
- break;
- case MDA_PERMFAIL:
- mda_queue_permfail(e->id, error,
- ESC_OTHER_MAIL_SYSTEM_STATUS);
- (void)snprintf(buf, sizeof buf,
- "Error (%s%s%s)",
- syserror ? syserror : "",
- syserror ? ": " : "",
- error);
- mda_log(e, "PermFail", buf);
- break;
- case MDA_OK:
- mda_queue_ok(e->id);
- mda_log(e, "Ok", "Delivered");
- break;
- }
- mda_done(s);
- return;
- }
-
- errx(1, "mda_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
-}
-
-void
-mda_postfork()
-{
-}
-
-void
-mda_postprivdrop()
-{
- tree_init(&sessions);
- tree_init(&users);
- TAILQ_INIT(&runnable);
-}
-
-static void
-mda_io(struct io *io, int evt, void *arg)
-{
- struct mda_session *s = arg;
- char *ln = NULL;
- size_t sz = 0;
- ssize_t len;
-
- log_trace(TRACE_IO, "mda: %p: %s %s", s, io_strevent(evt),
- io_strio(io));
-
- switch (evt) {
- case IO_LOWAT:
-
- /* done */
- done:
- if (s->datafp == NULL) {
- log_debug("debug: mda: all data sent for session"
- " %016"PRIx64 " evpid %016"PRIx64,
- s->id, s->evp->id);
- io_free(io);
- s->io = NULL;
- return;
- }
-
- while (io_queued(s->io) < MDA_HIWAT) {
- if ((len = getline(&ln, &sz, s->datafp)) == -1)
- break;
- if (io_write(s->io, ln, len) == -1) {
- m_create(p_parent, IMSG_MDA_KILL,
- 0, 0, -1);
- m_add_id(p_parent, s->id);
- m_add_string(p_parent, "Out of memory");
- m_close(p_parent);
- io_pause(io, IO_OUT);
- free(ln);
- return;
- }
- }
-
- free(ln);
- ln = NULL;
- if (ferror(s->datafp)) {
- log_debug("debug: mda: ferror on session %016"PRIx64,
- s->id);
- m_create(p_parent, IMSG_MDA_KILL, 0, 0, -1);
- m_add_id(p_parent, s->id);
- m_add_string(p_parent, "Error reading body");
- m_close(p_parent);
- io_pause(io, IO_OUT);
- return;
- }
-
- if (feof(s->datafp)) {
- log_debug("debug: mda: end-of-file for session"
- " %016"PRIx64 " evpid %016"PRIx64,
- s->id, s->evp->id);
- fclose(s->datafp);
- s->datafp = NULL;
- if (io_queued(s->io) == 0)
- goto done;
- }
- return;
-
- case IO_TIMEOUT:
- log_debug("debug: mda: timeout on session %016"PRIx64, s->id);
- io_pause(io, IO_OUT);
- return;
-
- case IO_ERROR:
- log_debug("debug: mda: io error on session %016"PRIx64": %s",
- s->id, io_error(io));
- io_pause(io, IO_OUT);
- return;
-
- case IO_DISCONNECTED:
- log_debug("debug: mda: io disconnected on session %016"PRIx64,
- s->id);
- io_pause(io, IO_OUT);
- return;
-
- default:
- log_debug("debug: mda: unexpected event on session %016"PRIx64,
- s->id);
- io_pause(io, IO_OUT);
- return;
- }
-}
-
-static int
-mda_check_loop(FILE *fp, struct mda_envelope *e)
-{
- char *buf = NULL;
- size_t sz = 0;
- ssize_t len;
- int ret = 0;
-
- while ((len = getline(&buf, &sz, fp)) != -1) {
- if (buf[len - 1] == '\n')
- buf[len - 1] = '\0';
-
- if (strchr(buf, ':') == NULL && !isspace((unsigned char)*buf))
- break;
-
- if (strncasecmp("Delivered-To: ", buf, 14) == 0) {
- if (strcasecmp(buf + 14, e->dest) == 0) {
- ret = 1;
- break;
- }
- }
- }
-
- free(buf);
- fseek(fp, SEEK_SET, 0);
- return (ret);
-}
-
-static int
-mda_getlastline(int fd, char *dst, size_t dstsz)
-{
- FILE *fp;
- char *ln = NULL;
- size_t sz = 0;
- ssize_t len;
- int out = 0;
-
- if (lseek(fd, 0, SEEK_SET) < 0) {
- log_warn("warn: mda: lseek");
- close(fd);
- return (-1);
- }
- fp = fdopen(fd, "r");
- if (fp == NULL) {
- log_warn("warn: mda: fdopen");
- close(fd);
- return (-1);
- }
- while ((len = getline(&ln, &sz, fp)) != -1) {
- if (ln[len - 1] == '\n')
- ln[len - 1] = '\0';
- out = 1;
- }
- fclose(fp);
-
- if (out) {
- (void)strlcpy(dst, "\"", dstsz);
- (void)strnvis(dst + 1, ln, dstsz - 2, VIS_SAFE | VIS_CSTYLE | VIS_NL);
- (void)strlcat(dst, "\"", dstsz);
- }
-
- free(ln);
- return (0);
-}
-
-static void
-mda_fail(struct mda_user *user, int permfail, const char *error,
- enum enhanced_status_code code)
-{
- struct mda_envelope *e;
-
- while ((e = TAILQ_FIRST(&user->envelopes))) {
- TAILQ_REMOVE(&user->envelopes, e, entry);
- if (permfail) {
- mda_log(e, "PermFail", error);
- mda_queue_permfail(e->id, error, code);
- }
- else {
- mda_log(e, "TempFail", error);
- mda_queue_tempfail(e->id, error, code);
- }
- mda_envelope_free(e);
- }
-
- mda_user_free(user);
-}
-
-static void
-mda_drain(void)
-{
- struct mda_user *u;
-
- while ((u = (TAILQ_FIRST(&runnable)))) {
-
- TAILQ_REMOVE(&runnable, u, entry_runnable);
-
- if (u->evpcount == 0 && u->running == 0) {
- log_debug("debug: mda: all done for user \"%s\"",
- mda_user_to_text(u));
- mda_user_free(u);
- continue;
- }
-
- if (u->evpcount == 0) {
- log_debug("debug: mda: no more envelope for \"%s\"",
- mda_user_to_text(u));
- u->flags &= ~USER_RUNNABLE;
- continue;
- }
-
- if (u->running >= env->sc_mda_max_user_session) {
- log_debug("debug: mda: "
- "maximum number of session reached for user \"%s\"",
- mda_user_to_text(u));
- u->flags &= ~USER_RUNNABLE;
- continue;
- }
-
- if (tree_count(&sessions) >= env->sc_mda_max_session) {
- log_debug("debug: mda: "
- "maximum number of session reached");
- TAILQ_INSERT_HEAD(&runnable, u, entry_runnable);
- return;
- }
-
- mda_session(u);
-
- if (u->evpcount == env->sc_mda_task_lowat) {
- if (u->flags & USER_ONHOLD) {
- log_debug("debug: mda: down to lowat for user "
- "\"%s\": releasing",
- mda_user_to_text(u));
- u->flags &= ~USER_ONHOLD;
- }
- if (u->flags & USER_HOLDQ) {
- m_create(p_queue, IMSG_MDA_HOLDQ_RELEASE,
- 0, 0, -1);
- m_add_id(p_queue, u->id);
- m_add_int(p_queue, env->sc_mda_task_release);
- m_close(p_queue);
- }
- }
-
- /* re-add the user at the tail of the queue */
- TAILQ_INSERT_TAIL(&runnable, u, entry_runnable);
- }
-}
-
-static void
-mda_done(struct mda_session *s)
-{
- log_debug("debug: mda: session %016" PRIx64 " done", s->id);
-
- tree_xpop(&sessions, s->id);
-
- mda_envelope_free(s->evp);
-
- s->user->running--;
- if (!(s->user->flags & USER_RUNNABLE)) {
- log_debug("debug: mda: user \"%s\" becomes runnable",
- s->user->name);
- TAILQ_INSERT_TAIL(&runnable, s->user, entry_runnable);
- s->user->flags |= USER_RUNNABLE;
- }
-
- if (s->datafp)
- fclose(s->datafp);
- if (s->io)
- io_free(s->io);
-
- free(s);
-
- stat_decrement("mda.running", 1);
-
- mda_drain();
-}
-
-static void
-mda_log(const struct mda_envelope *evp, const char *prefix, const char *status)
-{
- char rcpt[LINE_MAX];
-
- rcpt[0] = '\0';
- if (evp->rcpt)
- (void)snprintf(rcpt, sizeof rcpt, "rcpt=<%s> ", evp->rcpt);
-
- log_info("%016"PRIx64" mda delivery evpid=%016" PRIx64 " from=<%s> to=<%s> "
- "%suser=%s delay=%s result=%s stat=%s",
- evp->session_id,
- evp->id,
- evp->sender ? evp->sender : "",
- evp->dest,
- rcpt,
- evp->user,
- duration_to_text(time(NULL) - evp->creation),
- prefix,
- status);
-}
-
-static void
-mda_queue_ok(uint64_t evpid)
-{
- m_create(p_queue, IMSG_MDA_DELIVERY_OK, 0, 0, -1);
- m_add_evpid(p_queue, evpid);
- m_close(p_queue);
-}
-
-static void
-mda_queue_tempfail(uint64_t evpid, const char *reason,
- enum enhanced_status_code code)
-{
- m_create(p_queue, IMSG_MDA_DELIVERY_TEMPFAIL, 0, 0, -1);
- m_add_evpid(p_queue, evpid);
- m_add_string(p_queue, reason);
- m_add_int(p_queue, (int)code);
- m_close(p_queue);
-}
-
-static void
-mda_queue_permfail(uint64_t evpid, const char *reason,
- enum enhanced_status_code code)
-{
- m_create(p_queue, IMSG_MDA_DELIVERY_PERMFAIL, 0, 0, -1);
- m_add_evpid(p_queue, evpid);
- m_add_string(p_queue, reason);
- m_add_int(p_queue, (int)code);
- m_close(p_queue);
-}
-
-static void
-mda_queue_loop(uint64_t evpid)
-{
- m_create(p_queue, IMSG_MDA_DELIVERY_LOOP, 0, 0, -1);
- m_add_evpid(p_queue, evpid);
- m_close(p_queue);
-}
-
-static struct mda_user *
-mda_user(const struct envelope *evp)
-{
- struct dispatcher *dsp;
- struct mda_user *u;
- void *i;
-
- i = NULL;
- dsp = dict_xget(env->sc_dispatchers, evp->dispatcher);
- while (tree_iter(&users, &i, NULL, (void**)(&u))) {
- if (!strcmp(evp->mda_user, u->name) &&
- !strcmp(dsp->u.local.table_userbase, u->usertable))
- return (u);
- }
-
- u = xcalloc(1, sizeof *u);
- u->id = generate_uid();
- TAILQ_INIT(&u->envelopes);
- (void)strlcpy(u->name, evp->mda_user, sizeof(u->name));
- (void)strlcpy(u->usertable, dsp->u.local.table_userbase,
- sizeof(u->usertable));
-
- tree_xset(&users, u->id, u);
-
- m_create(p_lka, IMSG_MDA_LOOKUP_USERINFO, 0, 0, -1);
- m_add_id(p_lka, u->id);
- m_add_string(p_lka, dsp->u.local.table_userbase);
- m_add_string(p_lka, evp->mda_user);
- m_close(p_lka);
- u->flags |= USER_WAITINFO;
-
- stat_increment("mda.user", 1);
-
- if (dsp->u.local.user)
- log_debug("mda: new user %016" PRIx64
- " for \"%s\" delivering as \"%s\"",
- u->id, mda_user_to_text(u), dsp->u.local.user);
- else
- log_debug("mda: new user %016" PRIx64
- " for \"%s\"", u->id, mda_user_to_text(u));
-
- return (u);
-}
-
-static void
-mda_user_free(struct mda_user *u)
-{
- tree_xpop(&users, u->id);
-
- if (u->flags & USER_HOLDQ) {
- m_create(p_queue, IMSG_MDA_HOLDQ_RELEASE, 0, 0, -1);
- m_add_id(p_queue, u->id);
- m_add_int(p_queue, 0);
- m_close(p_queue);
- }
-
- free(u);
- stat_decrement("mda.user", 1);
-}
-
-static const char *
-mda_user_to_text(const struct mda_user *u)
-{
- static char buf[1024];
-
- (void)snprintf(buf, sizeof(buf), "%s:%s", u->usertable, u->name);
-
- return (buf);
-}
-
-static struct mda_envelope *
-mda_envelope(uint64_t session_id, const struct envelope *evp)
-{
- struct mda_envelope *e;
- char buf[LINE_MAX];
-
- e = xcalloc(1, sizeof *e);
- e->session_id = session_id;
- e->id = evp->id;
- e->creation = evp->creation;
- buf[0] = '\0';
- if (evp->sender.user[0] && evp->sender.domain[0])
- (void)snprintf(buf, sizeof buf, "%s@%s",
- evp->sender.user, evp->sender.domain);
- e->sender = xstrdup(buf);
- (void)snprintf(buf, sizeof buf, "%s@%s", evp->dest.user,
- evp->dest.domain);
- e->dest = xstrdup(buf);
- (void)snprintf(buf, sizeof buf, "%s@%s", evp->rcpt.user,
- evp->rcpt.domain);
- e->rcpt = xstrdup(buf);
- e->user = evp->mda_user[0] ?
- xstrdup(evp->mda_user) : xstrdup(evp->dest.user);
- e->dispatcher = xstrdup(evp->dispatcher);
- if (evp->mda_exec[0])
- e->mda_exec = xstrdup(evp->mda_exec);
- if (evp->mda_subaddress[0])
- e->mda_subaddress = xstrdup(evp->mda_subaddress);
- stat_increment("mda.envelope", 1);
- return (e);
-}
-
-static void
-mda_envelope_free(struct mda_envelope *e)
-{
- free(e->sender);
- free(e->dest);
- free(e->rcpt);
- free(e->user);
- free(e->mda_exec);
- free(e);
-
- stat_decrement("mda.envelope", 1);
-}
-
-static struct mda_session *
-mda_session(struct mda_user * u)
-{
- struct mda_session *s;
-
- s = xcalloc(1, sizeof *s);
- s->id = generate_uid();
- s->user = u;
- s->io = io_new();
- io_set_callback(s->io, mda_io, s);
-
- tree_xset(&sessions, s->id, s);
-
- s->evp = TAILQ_FIRST(&u->envelopes);
- TAILQ_REMOVE(&u->envelopes, s->evp, entry);
- u->evpcount--;
- u->running++;
-
- stat_decrement("mda.pending", 1);
- stat_increment("mda.running", 1);
-
- log_debug("debug: mda: new session %016" PRIx64
- " for user \"%s\" evpid %016" PRIx64, s->id,
- mda_user_to_text(u), s->evp->id);
-
- m_create(p_queue, IMSG_MDA_OPEN_MESSAGE, 0, 0, -1);
- m_add_id(p_queue, s->id);
- m_add_msgid(p_queue, evpid_to_msgid(s->evp->id));
- m_close(p_queue);
-
- return (s);
-}
-
-static const char *
-mda_sysexit_to_str(int sysexit)
-{
- switch (sysexit) {
- case EX_USAGE:
- return "command line usage error";
- case EX_DATAERR:
- return "data format error";
- case EX_NOINPUT:
- return "cannot open input";
- case EX_NOUSER:
- return "user unknown";
- case EX_NOHOST:
- return "host name unknown";
- case EX_UNAVAILABLE:
- return "service unavailable";
- case EX_SOFTWARE:
- return "internal software error";
- case EX_OSERR:
- return "system resource problem";
- case EX_OSFILE:
- return "critical OS file missing";
- case EX_CANTCREAT:
- return "can't create user output file";
- case EX_IOERR:
- return "input/output error";
- case EX_TEMPFAIL:
- return "temporary failure";
- case EX_PROTOCOL:
- return "remote error in protocol";
- case EX_NOPERM:
- return "permission denied";
- case EX_CONFIG:
- return "local configuration error";
- default:
- break;
- }
- return NULL;
-}
-
diff --git a/smtpd/mda_mbox.c b/smtpd/mda_mbox.c
deleted file mode 100644
index 8918e3ee..00000000
--- a/smtpd/mda_mbox.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/* $OpenBSD: mda_mbox.c,v 1.2 2020/02/03 15:41:22 gilles Exp $ */
-
-/*
- * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <paths.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sysexits.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-
-
-void
-mda_mbox(struct deliver *deliver)
-{
- int ret;
- char sender[LINE_MAX];
- char *envp[] = {
- "HOME=/",
- "PATH=" _PATH_DEFPATH,
- "LOGNAME=root",
- "USER=root",
- NULL,
- };
-
- if (deliver->sender.user[0] == '\0' &&
- deliver->sender.domain[0] == '\0')
- ret = snprintf(sender, sizeof sender, "MAILER-DAEMON");
- else
- ret = snprintf(sender, sizeof sender, "%s@%s",
- deliver->sender.user, deliver->sender.domain);
- if (ret < 0 || (size_t)ret >= sizeof sender)
- errx(EX_TEMPFAIL, "sender address too long");
-
- execle(PATH_MAILLOCAL, PATH_MAILLOCAL, "-f",
- sender, deliver->userinfo.username, (char *)NULL, envp);
- perror("execl");
- _exit(EX_TEMPFAIL);
-}
-
-void
-mda_mbox_init(struct deliver *deliver)
-{
- int fd;
- int ret;
- char buffer[LINE_MAX];
-
- ret = snprintf(buffer, sizeof buffer, "%s/%s",
- _PATH_MAILDIR, deliver->userinfo.username);
- if (ret < 0 || (size_t)ret >= sizeof buffer)
- errx(EX_TEMPFAIL, "mailbox pathname too long");
-
- if ((fd = open(buffer, O_CREAT|O_EXCL, 0)) == -1) {
- if (errno == EEXIST)
- return;
- err(EX_TEMPFAIL, "open");
- }
-
- if (fchown(fd, deliver->userinfo.uid, deliver->userinfo.gid) == -1)
- err(EX_TEMPFAIL, "fchown");
-
- if (fchmod(fd, S_IRUSR|S_IWUSR) == -1)
- err(EX_TEMPFAIL, "fchown");
-}
diff --git a/smtpd/mda_unpriv.c b/smtpd/mda_unpriv.c
deleted file mode 100644
index 2143b9a0..00000000
--- a/smtpd/mda_unpriv.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/* $OpenBSD: mda_unpriv.c,v 1.6 2020/02/02 22:13:48 gilles Exp $ */
-
-/*
- * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <paths.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-
-
-void
-mda_unpriv(struct dispatcher *dsp, struct deliver *deliver,
- const char *pw_name, const char *pw_dir)
-{
- int idx;
- char *mda_environ[11];
- char mda_exec[LINE_MAX];
- char mda_wrapper[LINE_MAX];
- const char *mda_command;
- const char *mda_command_wrap;
-
- if (deliver->mda_exec[0])
- mda_command = deliver->mda_exec;
- else
- mda_command = dsp->u.local.command;
-
- if (strlcpy(mda_exec, mda_command, sizeof (mda_exec))
- >= sizeof (mda_exec))
- errx(1, "mda command line too long");
-
- if (mda_expand_format(mda_exec, sizeof mda_exec, deliver,
- &deliver->userinfo, NULL) == -1)
- errx(1, "mda command line could not be expanded");
-
- mda_command = mda_exec;
-
- /* setup environment similar to other MTA */
- idx = 0;
- xasprintf(&mda_environ[idx++], "PATH=%s", _PATH_DEFPATH);
- xasprintf(&mda_environ[idx++], "DOMAIN=%s", deliver->rcpt.domain);
- xasprintf(&mda_environ[idx++], "HOME=%s", pw_dir);
- xasprintf(&mda_environ[idx++], "RECIPIENT=%s@%s", deliver->dest.user, deliver->dest.domain);
- xasprintf(&mda_environ[idx++], "SHELL=/bin/sh");
- xasprintf(&mda_environ[idx++], "LOCAL=%s", deliver->rcpt.user);
- xasprintf(&mda_environ[idx++], "LOGNAME=%s", pw_name);
- xasprintf(&mda_environ[idx++], "USER=%s", pw_name);
-
- if (deliver->sender.user[0])
- xasprintf(&mda_environ[idx++], "SENDER=%s@%s",
- deliver->sender.user, deliver->sender.domain);
- else
- xasprintf(&mda_environ[idx++], "SENDER=");
-
- if (deliver->mda_subaddress[0])
- xasprintf(&mda_environ[idx++], "EXTENSION=%s", deliver->mda_subaddress);
-
- mda_environ[idx++] = (char *)NULL;
-
- if (dsp->u.local.mda_wrapper) {
- mda_command_wrap = dict_get(env->sc_mda_wrappers,
- dsp->u.local.mda_wrapper);
- if (mda_command_wrap == NULL)
- errx(1, "could not find wrapper %s",
- dsp->u.local.mda_wrapper);
-
- if (strlcpy(mda_wrapper, mda_command_wrap, sizeof (mda_wrapper))
- >= sizeof (mda_wrapper))
- errx(1, "mda command line too long");
-
- if (mda_expand_format(mda_wrapper, sizeof mda_wrapper, deliver,
- &deliver->userinfo, mda_command) == -1)
- errx(1, "mda command line could not be expanded");
- mda_command = mda_wrapper;
- }
- execle("/bin/sh", "/bin/sh", "-c", mda_command, (char *)NULL,
- mda_environ);
-
- perror("execle");
- _exit(1);
-}
-
diff --git a/smtpd/mda_variables.c b/smtpd/mda_variables.c
deleted file mode 100644
index b672e492..00000000
--- a/smtpd/mda_variables.c
+++ /dev/null
@@ -1,374 +0,0 @@
-/* $OpenBSD: mda_variables.c,v 1.6 2019/09/19 07:35:36 gilles Exp $ */
-
-/*
- * Copyright (c) 2011-2017 Gilles Chehade <gilles@poolp.org>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <netinet/in.h>
-
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define EXPAND_DEPTH 10
-
-ssize_t mda_expand_format(char *, size_t, const struct deliver *,
- const struct userinfo *, const char *);
-static ssize_t mda_expand_token(char *, size_t, const char *,
- const struct deliver *, const struct userinfo *, const char *);
-static int mod_lowercase(char *, size_t);
-static int mod_uppercase(char *, size_t);
-static int mod_strip(char *, size_t);
-
-static struct modifiers {
- char *name;
- int (*f)(char *buf, size_t len);
-} token_modifiers[] = {
- { "lowercase", mod_lowercase },
- { "uppercase", mod_uppercase },
- { "strip", mod_strip },
- { "raw", NULL }, /* special case, must stay last */
-};
-
-#define MAXTOKENLEN 128
-
-static ssize_t
-mda_expand_token(char *dest, size_t len, const char *token,
- const struct deliver *dlv, const struct userinfo *ui, const char *mda_command)
-{
- char rtoken[MAXTOKENLEN];
- char tmp[EXPAND_BUFFER];
- const char *string;
- char *lbracket, *rbracket, *content, *sep, *mods;
- ssize_t i;
- ssize_t begoff, endoff;
- const char *errstr = NULL;
- int replace = 1;
- int raw = 0;
-
- begoff = 0;
- endoff = EXPAND_BUFFER;
- mods = NULL;
-
- if (strlcpy(rtoken, token, sizeof rtoken) >= sizeof rtoken)
- return -1;
-
- /* token[x[:y]] -> extracts optional x and y, converts into offsets */
- if ((lbracket = strchr(rtoken, '[')) &&
- (rbracket = strchr(rtoken, ']'))) {
- /* ] before [ ... or empty */
- if (rbracket < lbracket || rbracket - lbracket <= 1)
- return -1;
-
- *lbracket = *rbracket = '\0';
- content = lbracket + 1;
-
- if ((sep = strchr(content, ':')) == NULL)
- endoff = begoff = strtonum(content, -EXPAND_BUFFER,
- EXPAND_BUFFER, &errstr);
- else {
- *sep = '\0';
- if (content != sep)
- begoff = strtonum(content, -EXPAND_BUFFER,
- EXPAND_BUFFER, &errstr);
- if (*(++sep)) {
- if (errstr == NULL)
- endoff = strtonum(sep, -EXPAND_BUFFER,
- EXPAND_BUFFER, &errstr);
- }
- }
- if (errstr)
- return -1;
-
- /* token:mod_1,mod_2,mod_n -> extract modifiers */
- mods = strchr(rbracket + 1, ':');
- } else {
- if ((mods = strchr(rtoken, ':')) != NULL)
- *mods++ = '\0';
- }
-
- /* token -> expanded token */
- if (!strcasecmp("sender", rtoken)) {
- if (snprintf(tmp, sizeof tmp, "%s@%s",
- dlv->sender.user, dlv->sender.domain) >= (int)sizeof tmp)
- return -1;
- if (strcmp(tmp, "@") == 0)
- (void)strlcpy(tmp, "", sizeof tmp);
- string = tmp;
- }
- else if (!strcasecmp("rcpt", rtoken)) {
- if (snprintf(tmp, sizeof tmp, "%s@%s",
- dlv->rcpt.user, dlv->rcpt.domain) >= (int)sizeof tmp)
- return -1;
- if (strcmp(tmp, "@") == 0)
- (void)strlcpy(tmp, "", sizeof tmp);
- string = tmp;
- }
- else if (!strcasecmp("dest", rtoken)) {
- if (snprintf(tmp, sizeof tmp, "%s@%s",
- dlv->dest.user, dlv->dest.domain) >= (int)sizeof tmp)
- return -1;
- if (strcmp(tmp, "@") == 0)
- (void)strlcpy(tmp, "", sizeof tmp);
- string = tmp;
- }
- else if (!strcasecmp("sender.user", rtoken))
- string = dlv->sender.user;
- else if (!strcasecmp("sender.domain", rtoken))
- string = dlv->sender.domain;
- else if (!strcasecmp("user.username", rtoken))
- string = ui->username;
- else if (!strcasecmp("user.directory", rtoken)) {
- string = ui->directory;
- replace = 0;
- }
- else if (!strcasecmp("rcpt.user", rtoken))
- string = dlv->rcpt.user;
- else if (!strcasecmp("rcpt.domain", rtoken))
- string = dlv->rcpt.domain;
- else if (!strcasecmp("dest.user", rtoken))
- string = dlv->dest.user;
- else if (!strcasecmp("dest.domain", rtoken))
- string = dlv->dest.domain;
- else if (!strcasecmp("mda", rtoken)) {
- string = mda_command;
- replace = 0;
- }
- else if (!strcasecmp("mbox.from", rtoken)) {
- if (snprintf(tmp, sizeof tmp, "%s@%s",
- dlv->sender.user, dlv->sender.domain) >= (int)sizeof tmp)
- return -1;
- if (strcmp(tmp, "@") == 0)
- (void)strlcpy(tmp, "MAILER-DAEMON", sizeof tmp);
- string = tmp;
- }
- else
- return -1;
-
- if (string != tmp) {
- if (strlcpy(tmp, string, sizeof tmp) >= sizeof tmp)
- return -1;
- string = tmp;
- }
-
- /* apply modifiers */
- if (mods != NULL) {
- do {
- if ((sep = strchr(mods, '|')) != NULL)
- *sep++ = '\0';
- for (i = 0; (size_t)i < nitems(token_modifiers); ++i) {
- if (!strcasecmp(token_modifiers[i].name, mods)) {
- if (token_modifiers[i].f == NULL) {
- raw = 1;
- break;
- }
- if (!token_modifiers[i].f(tmp, sizeof tmp))
- return -1; /* modifier error */
- break;
- }
- }
- if ((size_t)i == nitems(token_modifiers))
- return -1; /* modifier not found */
- } while ((mods = sep) != NULL);
- }
-
- if (!raw && replace)
- for (i = 0; (size_t)i < strlen(tmp); ++i)
- if (strchr(MAILADDR_ESCAPE, tmp[i]))
- tmp[i] = ':';
-
- /* expanded string is empty */
- i = strlen(string);
- if (i == 0)
- return 0;
-
- /* begin offset beyond end of string */
- if (begoff >= i)
- return -1;
-
- /* end offset beyond end of string, make it end of string */
- if (endoff >= i)
- endoff = i - 1;
-
- /* negative begin offset, make it relative to end of string */
- if (begoff < 0)
- begoff += i;
- /* negative end offset, make it relative to end of string,
- * note that end offset is inclusive.
- */
- if (endoff < 0)
- endoff += i - 1;
-
- /* check that final offsets are valid */
- if (begoff < 0 || endoff < 0 || endoff < begoff)
- return -1;
- endoff += 1; /* end offset is inclusive */
-
- /* check that substring does not exceed destination buffer length */
- i = endoff - begoff;
- if ((size_t)i + 1 >= len)
- return -1;
-
- string += begoff;
- for (; i; i--) {
- *dest = *string;
- dest++;
- string++;
- }
-
- return endoff - begoff;
-}
-
-
-ssize_t
-mda_expand_format(char *buf, size_t len, const struct deliver *dlv,
- const struct userinfo *ui, const char *mda_command)
-{
- char tmpbuf[EXPAND_BUFFER], *ptmp, *pbuf, *ebuf;
- char exptok[EXPAND_BUFFER];
- ssize_t exptoklen;
- char token[MAXTOKENLEN];
- size_t ret, tmpret;
-
- if (len < sizeof tmpbuf) {
- log_warnx("mda_expand_format: tmp buffer < rule buffer");
- return -1;
- }
-
- memset(tmpbuf, 0, sizeof tmpbuf);
- pbuf = buf;
- ptmp = tmpbuf;
- ret = tmpret = 0;
-
- /* special case: ~/ only allowed expanded at the beginning */
- if (strncmp(pbuf, "~/", 2) == 0) {
- tmpret = snprintf(ptmp, sizeof tmpbuf, "%s/", ui->directory);
- if (tmpret >= sizeof tmpbuf) {
- log_warnx("warn: user directory for %s too large",
- ui->directory);
- return 0;
- }
- ret += tmpret;
- ptmp += tmpret;
- pbuf += 2;
- }
-
-
- /* expansion loop */
- for (; *pbuf && ret < sizeof tmpbuf; ret += tmpret) {
- if (*pbuf == '%' && *(pbuf + 1) == '%') {
- *ptmp++ = *pbuf++;
- pbuf += 1;
- tmpret = 1;
- continue;
- }
-
- if (*pbuf != '%' || *(pbuf + 1) != '{') {
- *ptmp++ = *pbuf++;
- tmpret = 1;
- continue;
- }
-
- /* %{...} otherwise fail */
- if (*(pbuf+1) != '{' || (ebuf = strchr(pbuf+1, '}')) == NULL)
- return 0;
-
- /* extract token from %{token} */
- if ((size_t)(ebuf - pbuf) - 1 >= sizeof token)
- return 0;
-
- memcpy(token, pbuf+2, ebuf-pbuf-1);
- if (strchr(token, '}') == NULL)
- return 0;
- *strchr(token, '}') = '\0';
-
- exptoklen = mda_expand_token(exptok, sizeof exptok, token, dlv,
- ui, mda_command);
- if (exptoklen == -1)
- return -1;
-
- /* writing expanded token at ptmp will overflow tmpbuf */
- if (sizeof (tmpbuf) - (ptmp - tmpbuf) <= (size_t)exptoklen)
- return -1;
-
- memcpy(ptmp, exptok, exptoklen);
- pbuf = ebuf + 1;
- ptmp += exptoklen;
- tmpret = exptoklen;
- }
- if (ret >= sizeof tmpbuf)
- return -1;
-
- if ((ret = strlcpy(buf, tmpbuf, len)) >= len)
- return -1;
-
- return ret;
-}
-
-static int
-mod_lowercase(char *buf, size_t len)
-{
- char tmp[EXPAND_BUFFER];
-
- if (!lowercase(tmp, buf, sizeof tmp))
- return 0;
- if (strlcpy(buf, tmp, len) >= len)
- return 0;
- return 1;
-}
-
-static int
-mod_uppercase(char *buf, size_t len)
-{
- char tmp[EXPAND_BUFFER];
-
- if (!uppercase(tmp, buf, sizeof tmp))
- return 0;
- if (strlcpy(buf, tmp, len) >= len)
- return 0;
- return 1;
-}
-
-static int
-mod_strip(char *buf, size_t len)
-{
- char *tag, *at;
- unsigned int i;
-
- /* gilles+hackers -> gilles */
- if ((tag = strchr(buf, *env->sc_subaddressing_delim)) != NULL) {
- /* gilles+hackers@poolp.org -> gilles@poolp.org */
- if ((at = strchr(tag, '@')) != NULL) {
- for (i = 0; i <= strlen(at); ++i)
- tag[i] = at[i];
- } else
- *tag = '\0';
- }
- return 1;
-}
diff --git a/smtpd/mproc.c b/smtpd/mproc.c
deleted file mode 100644
index bde229e1..00000000
--- a/smtpd/mproc.c
+++ /dev/null
@@ -1,676 +0,0 @@
-/* $OpenBSD: mproc.c,v 1.36 2020/03/17 09:01:53 tobhe Exp $ */
-
-/*
- * Copyright (c) 2012 Eric Faurot <eric@faurot.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/tree.h>
-#include <sys/queue.h>
-#include <sys/uio.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static void mproc_dispatch(int, short, void *);
-
-static ssize_t imsg_read_nofd(struct imsgbuf *);
-
-int
-mproc_fork(struct mproc *p, const char *path, char *argv[])
-{
- int sp[2];
-
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1)
- return (-1);
-
- io_set_nonblocking(sp[0]);
- io_set_nonblocking(sp[1]);
-
- if ((p->pid = fork()) == -1)
- goto err;
-
- if (p->pid == 0) {
- /* child process */
- dup2(sp[0], STDIN_FILENO);
- closefrom(STDERR_FILENO + 1);
- execv(path, argv);
- err(1, "execv: %s", path);
- }
-
- /* parent process */
- close(sp[0]);
- mproc_init(p, sp[1]);
- return (0);
-
-err:
- log_warn("warn: Failed to start process %s, instance of %s", argv[0], path);
- close(sp[0]);
- close(sp[1]);
- return (-1);
-}
-
-void
-mproc_init(struct mproc *p, int fd)
-{
- imsg_init(&p->imsgbuf, fd);
-}
-
-void
-mproc_clear(struct mproc *p)
-{
- log_debug("debug: clearing p=%s, fd=%d, pid=%d", p->name, p->imsgbuf.fd, p->pid);
-
- event_del(&p->ev);
- close(p->imsgbuf.fd);
- imsg_clear(&p->imsgbuf);
-}
-
-void
-mproc_enable(struct mproc *p)
-{
- if (p->enable == 0) {
- log_trace(TRACE_MPROC, "mproc: %s -> %s: enabled",
- proc_name(smtpd_process),
- proc_name(p->proc));
- p->enable = 1;
- }
- mproc_event_add(p);
-}
-
-void
-mproc_disable(struct mproc *p)
-{
- if (p->enable == 1) {
- log_trace(TRACE_MPROC, "mproc: %s -> %s: disabled",
- proc_name(smtpd_process),
- proc_name(p->proc));
- p->enable = 0;
- }
- mproc_event_add(p);
-}
-
-void
-mproc_event_add(struct mproc *p)
-{
- short events;
-
- if (p->enable)
- events = EV_READ;
- else
- events = 0;
-
- if (p->imsgbuf.w.queued)
- events |= EV_WRITE;
-
- if (p->events)
- event_del(&p->ev);
-
- p->events = events;
- if (events) {
- event_set(&p->ev, p->imsgbuf.fd, events, mproc_dispatch, p);
- event_add(&p->ev, NULL);
- }
-}
-
-static void
-mproc_dispatch(int fd, short event, void *arg)
-{
- struct mproc *p = arg;
- struct imsg imsg;
- ssize_t n;
-
- p->events = 0;
-
- if (event & EV_READ) {
-
- if (p->proc == PROC_CLIENT)
- n = imsg_read_nofd(&p->imsgbuf);
- else
- n = imsg_read(&p->imsgbuf);
-
- switch (n) {
- case -1:
- if (errno == EAGAIN)
- break;
- log_warn("warn: %s -> %s: imsg_read",
- proc_name(smtpd_process), p->name);
- fatal("exiting");
- /* NOTREACHED */
- case 0:
- /* this pipe is dead, so remove the event handler */
- log_debug("debug: %s -> %s: pipe closed",
- proc_name(smtpd_process), p->name);
- p->handler(p, NULL);
- return;
- default:
- break;
- }
- }
-
- if (event & EV_WRITE) {
- n = msgbuf_write(&p->imsgbuf.w);
- if (n == 0 || (n == -1 && errno != EAGAIN)) {
- /* this pipe is dead, so remove the event handler */
- log_debug("debug: %s -> %s: pipe closed",
- proc_name(smtpd_process), p->name);
- p->handler(p, NULL);
- return;
- }
- }
-
- for (;;) {
- if ((n = imsg_get(&p->imsgbuf, &imsg)) == -1) {
-
- if (smtpd_process == PROC_CONTROL &&
- p->proc == PROC_CLIENT) {
- log_warnx("warn: client sent invalid imsg "
- "over control socket");
- p->handler(p, NULL);
- return;
- }
- log_warn("fatal: %s: error in imsg_get for %s",
- proc_name(smtpd_process), p->name);
- fatalx(NULL);
- }
- if (n == 0)
- break;
-
- p->handler(p, &imsg);
-
- imsg_free(&imsg);
- }
-
- mproc_event_add(p);
-}
-
-/* This should go into libutil */
-static ssize_t
-imsg_read_nofd(struct imsgbuf *ibuf)
-{
- ssize_t n;
- char *buf;
- size_t len;
-
- buf = ibuf->r.buf + ibuf->r.wpos;
- len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
-
- while ((n = recv(ibuf->fd, buf, len, 0)) == -1) {
- if (errno != EINTR)
- return (n);
- }
-
- ibuf->r.wpos += n;
- return (n);
-}
-
-void
-m_forward(struct mproc *p, struct imsg *imsg)
-{
- imsg_compose(&p->imsgbuf, imsg->hdr.type, imsg->hdr.peerid,
- imsg->hdr.pid, imsg->fd, imsg->data,
- imsg->hdr.len - sizeof(imsg->hdr));
-
- if (imsg->hdr.type != IMSG_STAT_DECREMENT &&
- imsg->hdr.type != IMSG_STAT_INCREMENT)
- log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (forward)",
- proc_name(smtpd_process),
- proc_name(p->proc),
- imsg->hdr.len - sizeof(imsg->hdr),
- imsg_to_str(imsg->hdr.type));
-
- mproc_event_add(p);
-}
-
-void
-m_compose(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd,
- void *data, size_t len)
-{
- imsg_compose(&p->imsgbuf, type, peerid, pid, fd, data, len);
-
- if (type != IMSG_STAT_DECREMENT &&
- type != IMSG_STAT_INCREMENT)
- log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s",
- proc_name(smtpd_process),
- proc_name(p->proc),
- len,
- imsg_to_str(type));
-
- mproc_event_add(p);
-}
-
-void
-m_composev(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid,
- int fd, const struct iovec *iov, int n)
-{
- size_t len;
- int i;
-
- imsg_composev(&p->imsgbuf, type, peerid, pid, fd, iov, n);
-
- len = 0;
- for (i = 0; i < n; i++)
- len += iov[i].iov_len;
-
- if (type != IMSG_STAT_DECREMENT &&
- type != IMSG_STAT_INCREMENT)
- log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s",
- proc_name(smtpd_process),
- proc_name(p->proc),
- len,
- imsg_to_str(type));
-
- mproc_event_add(p);
-}
-
-void
-m_create(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd)
-{
- p->m_pos = 0;
- p->m_type = type;
- p->m_peerid = peerid;
- p->m_pid = pid;
- p->m_fd = fd;
-}
-
-void
-m_add(struct mproc *p, const void *data, size_t len)
-{
- size_t alloc;
- void *tmp;
-
- if (p->m_pos + len + IMSG_HEADER_SIZE > MAX_IMSGSIZE) {
- log_warnx("warn: message too large");
- fatal(NULL);
- }
-
- alloc = p->m_alloc ? p->m_alloc : 128;
- while (p->m_pos + len > alloc)
- alloc *= 2;
- if (alloc != p->m_alloc) {
- log_trace(TRACE_MPROC, "mproc: %s -> %s: realloc %zu -> %zu",
- proc_name(smtpd_process),
- proc_name(p->proc),
- p->m_alloc,
- alloc);
-
- tmp = recallocarray(p->m_buf, p->m_alloc, alloc, 1);
- if (tmp == NULL)
- fatal("realloc");
- p->m_alloc = alloc;
- p->m_buf = tmp;
- }
-
- memmove(p->m_buf + p->m_pos, data, len);
- p->m_pos += len;
-}
-
-void
-m_close(struct mproc *p)
-{
- if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd,
- p->m_buf, p->m_pos) == -1)
- fatal("imsg_compose");
-
- log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s",
- proc_name(smtpd_process),
- proc_name(p->proc),
- p->m_pos,
- imsg_to_str(p->m_type));
-
- mproc_event_add(p);
-}
-
-void
-m_flush(struct mproc *p)
-{
- if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd,
- p->m_buf, p->m_pos) == -1)
- fatal("imsg_compose");
-
- log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (flush)",
- proc_name(smtpd_process),
- proc_name(p->proc),
- p->m_pos,
- imsg_to_str(p->m_type));
-
- p->m_pos = 0;
-
- if (imsg_flush(&p->imsgbuf) == -1)
- fatal("imsg_flush");
-}
-
-static struct imsg * current;
-
-static void
-m_error(const char *error)
-{
- char buf[512];
-
- (void)snprintf(buf, sizeof buf, "%s: %s: %s",
- proc_name(smtpd_process),
- imsg_to_str(current->hdr.type),
- error);
- fatalx("%s", buf);
-}
-
-void
-m_msg(struct msg *m, struct imsg *imsg)
-{
- current = imsg;
- m->pos = imsg->data;
- m->end = m->pos + (imsg->hdr.len - sizeof(imsg->hdr));
-}
-
-void
-m_end(struct msg *m)
-{
- if (m->pos != m->end)
- m_error("not at msg end");
-}
-
-int
-m_is_eom(struct msg *m)
-{
- return (m->pos == m->end);
-}
-
-static inline void
-m_get(struct msg *m, void *dst, size_t sz)
-{
- if (sz > MAX_IMSGSIZE ||
- m->end - m->pos < (ssize_t)sz)
- fatalx("msg too short");
-
- memmove(dst, m->pos, sz);
- m->pos += sz;
-}
-
-void
-m_add_int(struct mproc *m, int v)
-{
- m_add(m, &v, sizeof(v));
-};
-
-void
-m_add_u32(struct mproc *m, uint32_t u32)
-{
- m_add(m, &u32, sizeof(u32));
-};
-
-void
-m_add_size(struct mproc *m, size_t sz)
-{
- m_add(m, &sz, sizeof(sz));
-};
-
-void
-m_add_time(struct mproc *m, time_t v)
-{
- m_add(m, &v, sizeof(v));
-};
-
-void
-m_add_timeval(struct mproc *m, struct timeval *tv)
-{
- m_add(m, tv, sizeof(*tv));
-}
-
-
-void
-m_add_string(struct mproc *m, const char *v)
-{
- if (v) {
- m_add(m, "s", 1);
- m_add(m, v, strlen(v) + 1);
- }
- else
- m_add(m, "\0", 1);
-};
-
-void
-m_add_data(struct mproc *m, const void *v, size_t len)
-{
- m_add_size(m, len);
- m_add(m, v, len);
-};
-
-void
-m_add_id(struct mproc *m, uint64_t v)
-{
- m_add(m, &v, sizeof(v));
-}
-
-void
-m_add_evpid(struct mproc *m, uint64_t v)
-{
- m_add(m, &v, sizeof(v));
-}
-
-void
-m_add_msgid(struct mproc *m, uint32_t v)
-{
- m_add(m, &v, sizeof(v));
-}
-
-void
-m_add_sockaddr(struct mproc *m, const struct sockaddr *sa)
-{
- m_add_size(m, SA_LEN(sa));
- m_add(m, sa, SA_LEN(sa));
-}
-
-void
-m_add_mailaddr(struct mproc *m, const struct mailaddr *maddr)
-{
- m_add(m, maddr, sizeof(*maddr));
-}
-
-void
-m_add_envelope(struct mproc *m, const struct envelope *evp)
-{
- char buf[sizeof(*evp)];
-
- envelope_dump_buffer(evp, buf, sizeof(buf));
- m_add_evpid(m, evp->id);
- m_add_string(m, buf);
-}
-
-void
-m_add_params(struct mproc *m, struct dict *d)
-{
- const char *key;
- char *value;
- void *iter;
-
- if (d == NULL) {
- m_add_size(m, 0);
- return;
- }
- m_add_size(m, dict_count(d));
- iter = NULL;
- while (dict_iter(d, &iter, &key, (void **)&value)) {
- m_add_string(m, key);
- m_add_string(m, value);
- }
-}
-
-void
-m_get_int(struct msg *m, int *i)
-{
- m_get(m, i, sizeof(*i));
-}
-
-void
-m_get_u32(struct msg *m, uint32_t *u32)
-{
- m_get(m, u32, sizeof(*u32));
-}
-
-void
-m_get_size(struct msg *m, size_t *sz)
-{
- m_get(m, sz, sizeof(*sz));
-}
-
-void
-m_get_time(struct msg *m, time_t *t)
-{
- m_get(m, t, sizeof(*t));
-}
-
-void
-m_get_timeval(struct msg *m, struct timeval *tv)
-{
- m_get(m, tv, sizeof(*tv));
-}
-
-void
-m_get_string(struct msg *m, const char **s)
-{
- uint8_t *end;
- char c;
-
- if (m->pos >= m->end)
- m_error("msg too short");
-
- c = *m->pos++;
- if (c == '\0') {
- *s = NULL;
- return;
- }
-
- if (m->pos >= m->end)
- m_error("msg too short");
- end = memchr(m->pos, 0, m->end - m->pos);
- if (end == NULL)
- m_error("unterminated string");
-
- *s = m->pos;
- m->pos = end + 1;
-}
-
-void
-m_get_data(struct msg *m, const void **data, size_t *sz)
-{
- m_get_size(m, sz);
-
- if (*sz == 0) {
- *data = NULL;
- return;
- }
-
- if (m->pos + *sz > m->end)
- m_error("msg too short");
-
- *data = m->pos;
- m->pos += *sz;
-}
-
-void
-m_get_evpid(struct msg *m, uint64_t *evpid)
-{
- m_get(m, evpid, sizeof(*evpid));
-}
-
-void
-m_get_msgid(struct msg *m, uint32_t *msgid)
-{
- m_get(m, msgid, sizeof(*msgid));
-}
-
-void
-m_get_id(struct msg *m, uint64_t *id)
-{
- m_get(m, id, sizeof(*id));
-}
-
-void
-m_get_sockaddr(struct msg *m, struct sockaddr *sa)
-{
- size_t len;
-
- m_get_size(m, &len);
- m_get(m, sa, len);
-}
-
-void
-m_get_mailaddr(struct msg *m, struct mailaddr *maddr)
-{
- m_get(m, maddr, sizeof(*maddr));
-}
-
-void
-m_get_envelope(struct msg *m, struct envelope *evp)
-{
- uint64_t evpid;
- const char *buf;
-
- m_get_evpid(m, &evpid);
- m_get_string(m, &buf);
- if (buf == NULL)
- fatalx("empty envelope buffer");
-
- if (!envelope_load_buffer(evp, buf, strlen(buf)))
- fatalx("failed to retrieve envelope");
- evp->id = evpid;
-}
-
-void
-m_get_params(struct msg *m, struct dict *d)
-{
- size_t c;
- const char *key;
- const char *value;
- char *tmp;
-
- dict_init(d);
-
- m_get_size(m, &c);
-
- for (; c; c--) {
- m_get_string(m, &key);
- m_get_string(m, &value);
- if ((tmp = strdup(value)) == NULL)
- fatal("m_get_params");
- dict_set(d, key, tmp);
- }
-}
-
-void
-m_clear_params(struct dict *d)
-{
- char *value;
-
- while (dict_poproot(d, (void **)&value))
- free(value);
-}
diff --git a/smtpd/mta.c b/smtpd/mta.c
deleted file mode 100644
index 922170ae..00000000
--- a/smtpd/mta.c
+++ /dev/null
@@ -1,2647 +0,0 @@
-/* $OpenBSD: mta.c,v 1.234 2019/12/21 10:34:07 gilles Exp $ */
-
-/*
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <netdb.h>
-#include <grp.h> /* needed for setgroups */
-#include <limits.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define MAXERROR_PER_ROUTE 4
-
-#define DELAY_CHECK_SOURCE 1
-#define DELAY_CHECK_SOURCE_SLOW 10
-#define DELAY_CHECK_SOURCE_FAST 0
-#define DELAY_CHECK_LIMIT 5
-
-#define DELAY_QUADRATIC 1
-#define DELAY_ROUTE_BASE 15
-#define DELAY_ROUTE_MAX 3600
-
-#define RELAY_ONHOLD 0x01
-#define RELAY_HOLDQ 0x02
-
-static void mta_handle_envelope(struct envelope *, const char *);
-static void mta_query_smarthost(struct envelope *);
-static void mta_on_smarthost(struct envelope *, const char *);
-static void mta_query_mx(struct mta_relay *);
-static void mta_query_secret(struct mta_relay *);
-static void mta_query_preference(struct mta_relay *);
-static void mta_query_source(struct mta_relay *);
-static void mta_on_mx(void *, void *, void *);
-static void mta_on_secret(struct mta_relay *, const char *);
-static void mta_on_preference(struct mta_relay *, int);
-static void mta_on_source(struct mta_relay *, struct mta_source *);
-static void mta_on_timeout(struct runq *, void *);
-static void mta_connect(struct mta_connector *);
-static void mta_route_enable(struct mta_route *);
-static void mta_route_disable(struct mta_route *, int, int);
-static void mta_drain(struct mta_relay *);
-static void mta_delivery_flush_event(int, short, void *);
-static void mta_flush(struct mta_relay *, int, const char *);
-static struct mta_route *mta_find_route(struct mta_connector *, time_t, int*,
- time_t*, struct mta_mx **);
-static void mta_log(const struct mta_envelope *, const char *, const char *,
- const char *, const char *);
-
-SPLAY_HEAD(mta_relay_tree, mta_relay);
-static struct mta_relay *mta_relay(struct envelope *, struct relayhost *);
-static void mta_relay_ref(struct mta_relay *);
-static void mta_relay_unref(struct mta_relay *);
-static void mta_relay_show(struct mta_relay *, struct mproc *, uint32_t, time_t);
-static int mta_relay_cmp(const struct mta_relay *, const struct mta_relay *);
-SPLAY_PROTOTYPE(mta_relay_tree, mta_relay, entry, mta_relay_cmp);
-
-SPLAY_HEAD(mta_host_tree, mta_host);
-static struct mta_host *mta_host(const struct sockaddr *);
-static void mta_host_ref(struct mta_host *);
-static void mta_host_unref(struct mta_host *);
-static int mta_host_cmp(const struct mta_host *, const struct mta_host *);
-SPLAY_PROTOTYPE(mta_host_tree, mta_host, entry, mta_host_cmp);
-
-SPLAY_HEAD(mta_domain_tree, mta_domain);
-static struct mta_domain *mta_domain(char *, int);
-#if 0
-static void mta_domain_ref(struct mta_domain *);
-#endif
-static void mta_domain_unref(struct mta_domain *);
-static int mta_domain_cmp(const struct mta_domain *, const struct mta_domain *);
-SPLAY_PROTOTYPE(mta_domain_tree, mta_domain, entry, mta_domain_cmp);
-
-SPLAY_HEAD(mta_source_tree, mta_source);
-static struct mta_source *mta_source(const struct sockaddr *);
-static void mta_source_ref(struct mta_source *);
-static void mta_source_unref(struct mta_source *);
-static const char *mta_source_to_text(struct mta_source *);
-static int mta_source_cmp(const struct mta_source *, const struct mta_source *);
-SPLAY_PROTOTYPE(mta_source_tree, mta_source, entry, mta_source_cmp);
-
-static struct mta_connector *mta_connector(struct mta_relay *,
- struct mta_source *);
-static void mta_connector_free(struct mta_connector *);
-static const char *mta_connector_to_text(struct mta_connector *);
-
-SPLAY_HEAD(mta_route_tree, mta_route);
-static struct mta_route *mta_route(struct mta_source *, struct mta_host *);
-static void mta_route_ref(struct mta_route *);
-static void mta_route_unref(struct mta_route *);
-static const char *mta_route_to_text(struct mta_route *);
-static int mta_route_cmp(const struct mta_route *, const struct mta_route *);
-SPLAY_PROTOTYPE(mta_route_tree, mta_route, entry, mta_route_cmp);
-
-struct mta_block {
- SPLAY_ENTRY(mta_block) entry;
- struct mta_source *source;
- char *domain;
-};
-
-SPLAY_HEAD(mta_block_tree, mta_block);
-void mta_block(struct mta_source *, char *);
-void mta_unblock(struct mta_source *, char *);
-int mta_is_blocked(struct mta_source *, char *);
-static int mta_block_cmp(const struct mta_block *, const struct mta_block *);
-SPLAY_PROTOTYPE(mta_block_tree, mta_block, entry, mta_block_cmp);
-
-static struct mta_relay_tree relays;
-static struct mta_domain_tree domains;
-static struct mta_host_tree hosts;
-static struct mta_source_tree sources;
-static struct mta_route_tree routes;
-static struct mta_block_tree blocks;
-
-static struct tree wait_mx;
-static struct tree wait_preference;
-static struct tree wait_secret;
-static struct tree wait_smarthost;
-static struct tree wait_source;
-static struct tree flush_evp;
-static struct event ev_flush_evp;
-
-static struct runq *runq_relay;
-static struct runq *runq_connector;
-static struct runq *runq_route;
-static struct runq *runq_hoststat;
-
-static time_t max_seen_conndelay_route;
-static time_t max_seen_discdelay_route;
-
-#define HOSTSTAT_EXPIRE_DELAY (4 * 3600)
-struct hoststat {
- char name[HOST_NAME_MAX+1];
- time_t tm;
- char error[LINE_MAX];
- struct tree deferred;
-};
-static struct dict hoststat;
-
-void mta_hoststat_update(const char *, const char *);
-void mta_hoststat_cache(const char *, uint64_t);
-void mta_hoststat_uncache(const char *, uint64_t);
-void mta_hoststat_reschedule(const char *);
-static void mta_hoststat_remove_entry(struct hoststat *);
-
-void
-mta_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct mta_relay *relay;
- struct mta_domain *domain;
- struct mta_host *host;
- struct mta_route *route;
- struct mta_block *block;
- struct mta_mx *mx, *imx;
- struct mta_source *source;
- struct hoststat *hs;
- struct sockaddr_storage ss;
- struct envelope evp, *e;
- struct msg m;
- const char *secret;
- const char *hostname;
- const char *dom;
- const char *smarthost;
- uint64_t reqid;
- time_t t;
- char buf[LINE_MAX];
- int dnserror, preference, v, status;
- void *iter;
- uint64_t u64;
-
- switch (imsg->hdr.type) {
- case IMSG_QUEUE_TRANSFER:
- m_msg(&m, imsg);
- m_get_envelope(&m, &evp);
- m_end(&m);
- mta_handle_envelope(&evp, NULL);
- return;
-
- case IMSG_MTA_OPEN_MESSAGE:
- mta_session_imsg(p, imsg);
- return;
-
- case IMSG_MTA_LOOKUP_CREDENTIALS:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &secret);
- m_end(&m);
- relay = tree_xpop(&wait_secret, reqid);
- mta_on_secret(relay, secret[0] ? secret : NULL);
- return;
-
- case IMSG_MTA_LOOKUP_SOURCE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &status);
- if (status == LKA_OK)
- m_get_sockaddr(&m, (struct sockaddr*)&ss);
- m_end(&m);
-
- relay = tree_xpop(&wait_source, reqid);
- mta_on_source(relay, (status == LKA_OK) ?
- mta_source((struct sockaddr *)&ss) : NULL);
- return;
-
- case IMSG_MTA_LOOKUP_SMARTHOST:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &status);
- smarthost = NULL;
- if (status == LKA_OK)
- m_get_string(&m, &smarthost);
- m_end(&m);
-
- e = tree_xpop(&wait_smarthost, reqid);
- mta_on_smarthost(e, smarthost);
- return;
-
- case IMSG_MTA_LOOKUP_HELO:
- mta_session_imsg(p, imsg);
- return;
-
- case IMSG_MTA_DNS_HOST:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &hostname);
- m_get_sockaddr(&m, (struct sockaddr*)&ss);
- m_get_int(&m, &preference);
- m_end(&m);
- domain = tree_xget(&wait_mx, reqid);
- mx = xcalloc(1, sizeof *mx);
- mx->mxname = xstrdup(hostname);
- mx->host = mta_host((struct sockaddr*)&ss);
- mx->preference = preference;
- TAILQ_FOREACH(imx, &domain->mxs, entry) {
- if (imx->preference > mx->preference) {
- TAILQ_INSERT_BEFORE(imx, mx, entry);
- return;
- }
- }
- TAILQ_INSERT_TAIL(&domain->mxs, mx, entry);
- return;
-
- case IMSG_MTA_DNS_HOST_END:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &dnserror);
- m_end(&m);
- domain = tree_xpop(&wait_mx, reqid);
- domain->mxstatus = dnserror;
- if (domain->mxstatus == DNS_OK) {
- log_debug("debug: MXs for domain %s:",
- domain->name);
- TAILQ_FOREACH(mx, &domain->mxs, entry)
- log_debug(" %s preference %d",
- sa_to_text(mx->host->sa),
- mx->preference);
- }
- else {
- log_debug("debug: Failed MX query for %s:",
- domain->name);
- }
- domain->lastmxquery = time(NULL);
- waitq_run(&domain->mxs, domain);
- return;
-
- case IMSG_MTA_DNS_MX_PREFERENCE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &dnserror);
- if (dnserror == 0)
- m_get_int(&m, &preference);
- m_end(&m);
-
- relay = tree_xpop(&wait_preference, reqid);
- if (dnserror) {
- log_warnx("warn: Couldn't find backup "
- "preference for %s: error %d",
- mta_relay_to_text(relay), dnserror);
- preference = INT_MAX;
- }
- mta_on_preference(relay, preference);
- return;
-
- case IMSG_CTL_RESUME_ROUTE:
- u64 = *((uint64_t *)imsg->data);
- if (u64)
- log_debug("resuming route: %llu",
- (unsigned long long)u64);
- else
- log_debug("resuming all routes");
- SPLAY_FOREACH(route, mta_route_tree, &routes) {
- if (u64 && route->id != u64)
- continue;
-
- if (route->flags & ROUTE_DISABLED) {
- log_info("smtp-out: Enabling route %s per admin request",
- mta_route_to_text(route));
- if (!runq_cancel(runq_route, route)) {
- log_warnx("warn: route not on runq");
- fatalx("exiting");
- }
- route->flags &= ~ROUTE_DISABLED;
- route->flags |= ROUTE_NEW;
- route->nerror = 0;
- route->penalty = 0;
- mta_route_unref(route); /* from mta_route_disable */
- }
-
- if (u64)
- break;
- }
- return;
-
- case IMSG_CTL_MTA_SHOW_HOSTS:
- t = time(NULL);
- SPLAY_FOREACH(host, mta_host_tree, &hosts) {
- (void)snprintf(buf, sizeof(buf),
- "%s %s refcount=%d nconn=%zu lastconn=%s",
- sockaddr_to_text(host->sa),
- host->ptrname,
- host->refcount,
- host->nconn,
- host->lastconn ? duration_to_text(t - host->lastconn) : "-");
- m_compose(p, IMSG_CTL_MTA_SHOW_HOSTS,
- imsg->hdr.peerid, 0, -1,
- buf, strlen(buf) + 1);
- }
- m_compose(p, IMSG_CTL_MTA_SHOW_HOSTS, imsg->hdr.peerid,
- 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_MTA_SHOW_RELAYS:
- t = time(NULL);
- SPLAY_FOREACH(relay, mta_relay_tree, &relays)
- mta_relay_show(relay, p, imsg->hdr.peerid, t);
- m_compose(p, IMSG_CTL_MTA_SHOW_RELAYS, imsg->hdr.peerid,
- 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_MTA_SHOW_ROUTES:
- SPLAY_FOREACH(route, mta_route_tree, &routes) {
- v = runq_pending(runq_route, route, &t);
- (void)snprintf(buf, sizeof(buf),
- "%llu. %s %c%c%c%c nconn=%zu nerror=%d penalty=%d timeout=%s",
- (unsigned long long)route->id,
- mta_route_to_text(route),
- route->flags & ROUTE_NEW ? 'N' : '-',
- route->flags & ROUTE_DISABLED ? 'D' : '-',
- route->flags & ROUTE_RUNQ ? 'Q' : '-',
- route->flags & ROUTE_KEEPALIVE ? 'K' : '-',
- route->nconn,
- route->nerror,
- route->penalty,
- v ? duration_to_text(t - time(NULL)) : "-");
- m_compose(p, IMSG_CTL_MTA_SHOW_ROUTES,
- imsg->hdr.peerid, 0, -1,
- buf, strlen(buf) + 1);
- }
- m_compose(p, IMSG_CTL_MTA_SHOW_ROUTES, imsg->hdr.peerid,
- 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_MTA_SHOW_HOSTSTATS:
- iter = NULL;
- while (dict_iter(&hoststat, &iter, &hostname,
- (void **)&hs)) {
- (void)snprintf(buf, sizeof(buf),
- "%s|%llu|%s",
- hostname, (unsigned long long) hs->tm,
- hs->error);
- m_compose(p, IMSG_CTL_MTA_SHOW_HOSTSTATS,
- imsg->hdr.peerid, 0, -1,
- buf, strlen(buf) + 1);
- }
- m_compose(p, IMSG_CTL_MTA_SHOW_HOSTSTATS,
- imsg->hdr.peerid,
- 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_MTA_BLOCK:
- m_msg(&m, imsg);
- m_get_sockaddr(&m, (struct sockaddr*)&ss);
- m_get_string(&m, &dom);
- m_end(&m);
- source = mta_source((struct sockaddr*)&ss);
- if (*dom != '\0') {
- if (!(strlcpy(buf, dom, sizeof(buf))
- >= sizeof(buf)))
- mta_block(source, buf);
- }
- else
- mta_block(source, NULL);
- mta_source_unref(source);
- m_compose(p, IMSG_CTL_OK, imsg->hdr.peerid, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_MTA_UNBLOCK:
- m_msg(&m, imsg);
- m_get_sockaddr(&m, (struct sockaddr*)&ss);
- m_get_string(&m, &dom);
- m_end(&m);
- source = mta_source((struct sockaddr*)&ss);
- if (*dom != '\0') {
- if (!(strlcpy(buf, dom, sizeof(buf))
- >= sizeof(buf)))
- mta_unblock(source, buf);
- }
- else
- mta_unblock(source, NULL);
- mta_source_unref(source);
- m_compose(p, IMSG_CTL_OK, imsg->hdr.peerid, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_MTA_SHOW_BLOCK:
- SPLAY_FOREACH(block, mta_block_tree, &blocks) {
- (void)snprintf(buf, sizeof(buf), "%s -> %s",
- mta_source_to_text(block->source),
- block->domain ? block->domain : "*");
- m_compose(p, IMSG_CTL_MTA_SHOW_BLOCK,
- imsg->hdr.peerid, 0, -1, buf, strlen(buf) + 1);
- }
- m_compose(p, IMSG_CTL_MTA_SHOW_BLOCK, imsg->hdr.peerid,
- 0, -1, NULL, 0);
- return;
- }
-
- errx(1, "mta_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
-}
-
-void
-mta_postfork(void)
-{
-}
-
-void
-mta_postprivdrop(void)
-{
- SPLAY_INIT(&relays);
- SPLAY_INIT(&domains);
- SPLAY_INIT(&hosts);
- SPLAY_INIT(&sources);
- SPLAY_INIT(&routes);
- SPLAY_INIT(&blocks);
-
- tree_init(&wait_secret);
- tree_init(&wait_smarthost);
- tree_init(&wait_mx);
- tree_init(&wait_preference);
- tree_init(&wait_source);
- tree_init(&flush_evp);
- dict_init(&hoststat);
-
- evtimer_set(&ev_flush_evp, mta_delivery_flush_event, NULL);
-
- runq_init(&runq_relay, mta_on_timeout);
- runq_init(&runq_connector, mta_on_timeout);
- runq_init(&runq_route, mta_on_timeout);
- runq_init(&runq_hoststat, mta_on_timeout);
-}
-
-
-/*
- * Local error on the given source.
- */
-void
-mta_source_error(struct mta_relay *relay, struct mta_route *route, const char *e)
-{
- struct mta_connector *c;
-
- /*
- * Remember the source as broken for this connector.
- */
- c = mta_connector(relay, route->src);
- if (!(c->flags & CONNECTOR_ERROR_SOURCE))
- log_info("smtp-out: Error on %s: %s",
- mta_route_to_text(route), e);
- c->flags |= CONNECTOR_ERROR_SOURCE;
-}
-
-void
-mta_route_error(struct mta_relay *relay, struct mta_route *route)
-{
-#if 0
- route->nerror += 1;
-
- if (route->nerror > MAXERROR_PER_ROUTE) {
- log_info("smtp-out: Too many errors on %s: "
- "disabling for a while", mta_route_to_text(route));
- mta_route_disable(route, 2, ROUTE_DISABLED_SMTP);
- }
-#endif
-}
-
-void
-mta_route_ok(struct mta_relay *relay, struct mta_route *route)
-{
- struct mta_connector *c;
-
- if (!(route->flags & ROUTE_NEW))
- return;
-
- log_debug("debug: mta-routing: route %s is now valid.",
- mta_route_to_text(route));
-
- route->nerror = 0;
- route->flags &= ~ROUTE_NEW;
-
- c = mta_connector(relay, route->src);
- mta_connect(c);
-}
-
-void
-mta_route_down(struct mta_relay *relay, struct mta_route *route)
-{
-#if 0
- mta_route_disable(route, 2, ROUTE_DISABLED_SMTP);
-#endif
-}
-
-void
-mta_route_collect(struct mta_relay *relay, struct mta_route *route)
-{
- struct mta_connector *c;
-
- log_debug("debug: mta_route_collect(%s)",
- mta_route_to_text(route));
-
- relay->nconn -= 1;
- relay->domain->nconn -= 1;
- route->nconn -= 1;
- route->src->nconn -= 1;
- route->dst->nconn -= 1;
- route->lastdisc = time(NULL);
-
- /* First connection failed */
- if (route->flags & ROUTE_NEW)
- mta_route_disable(route, 1, ROUTE_DISABLED_NET);
-
- c = mta_connector(relay, route->src);
- c->nconn -= 1;
- mta_connect(c);
- mta_route_unref(route); /* from mta_find_route() */
- mta_relay_unref(relay); /* from mta_connect() */
-}
-
-struct mta_task *
-mta_route_next_task(struct mta_relay *relay, struct mta_route *route)
-{
- struct mta_task *task;
-
- if ((task = TAILQ_FIRST(&relay->tasks))) {
- TAILQ_REMOVE(&relay->tasks, task, entry);
- relay->ntask -= 1;
- task->relay = NULL;
-
- /* When the number of tasks is down to lowat, query some evp */
- if (relay->ntask == (size_t)relay->limits->task_lowat) {
- if (relay->state & RELAY_ONHOLD) {
- log_info("smtp-out: back to lowat on %s: releasing",
- mta_relay_to_text(relay));
- relay->state &= ~RELAY_ONHOLD;
- }
- if (relay->state & RELAY_HOLDQ) {
- m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1);
- m_add_id(p_queue, relay->id);
- m_add_int(p_queue, relay->limits->task_release);
- m_close(p_queue);
- }
- }
- else if (relay->ntask == 0 && relay->state & RELAY_HOLDQ) {
- m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1);
- m_add_id(p_queue, relay->id);
- m_add_int(p_queue, 0);
- m_close(p_queue);
- }
- }
-
- return (task);
-}
-
-static void
-mta_handle_envelope(struct envelope *evp, const char *smarthost)
-{
- struct mta_relay *relay;
- struct mta_task *task;
- struct mta_envelope *e;
- struct dispatcher *dispatcher;
- struct mailaddr maddr;
- struct relayhost relayh;
- char buf[LINE_MAX];
-
- dispatcher = dict_xget(env->sc_dispatchers, evp->dispatcher);
- if (dispatcher->u.remote.smarthost && smarthost == NULL) {
- mta_query_smarthost(evp);
- return;
- }
-
- memset(&relayh, 0, sizeof(relayh));
- relayh.tls = RELAY_TLS_OPPORTUNISTIC;
- if (smarthost && !text_to_relayhost(&relayh, smarthost)) {
- log_warnx("warn: Failed to parse smarthost %s", smarthost);
- m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1);
- m_add_evpid(p_queue, evp->id);
- m_add_string(p_queue, "Cannot parse smarthost");
- m_add_int(p_queue, ESC_OTHER_STATUS);
- m_close(p_queue);
- return;
- }
-
- if (relayh.flags & RELAY_AUTH && dispatcher->u.remote.auth == NULL) {
- log_warnx("warn: No auth table on action \"%s\" for relay %s",
- evp->dispatcher, smarthost);
- m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1);
- m_add_evpid(p_queue, evp->id);
- m_add_string(p_queue, "No auth table for relaying");
- m_add_int(p_queue, ESC_OTHER_STATUS);
- m_close(p_queue);
- return;
- }
-
- if (dispatcher->u.remote.tls_required) {
- /* Reject relay if smtp+notls:// is requested */
- if (relayh.tls == RELAY_TLS_NO) {
- log_warnx("warn: TLS required for action \"%s\"",
- evp->dispatcher);
- m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1);
- m_add_evpid(p_queue, evp->id);
- m_add_string(p_queue, "TLS required for relaying");
- m_add_int(p_queue, ESC_OTHER_STATUS);
- m_close(p_queue);
- return;
- }
- /* Update smtp:// to smtp+tls:// */
- if (relayh.tls == RELAY_TLS_OPPORTUNISTIC)
- relayh.tls = RELAY_TLS_STARTTLS;
- }
-
- relay = mta_relay(evp, &relayh);
- /* ignore if we don't know the limits yet */
- if (relay->limits &&
- relay->ntask >= (size_t)relay->limits->task_hiwat) {
- if (!(relay->state & RELAY_ONHOLD)) {
- log_info("smtp-out: hiwat reached on %s: holding envelopes",
- mta_relay_to_text(relay));
- relay->state |= RELAY_ONHOLD;
- }
- }
-
- /*
- * If the relay has too many pending tasks, tell the
- * scheduler to hold it until further notice
- */
- if (relay->state & RELAY_ONHOLD) {
- relay->state |= RELAY_HOLDQ;
- m_create(p_queue, IMSG_MTA_DELIVERY_HOLD, 0, 0, -1);
- m_add_evpid(p_queue, evp->id);
- m_add_id(p_queue, relay->id);
- m_close(p_queue);
- mta_relay_unref(relay); /* from here */
- return;
- }
-
- task = NULL;
- TAILQ_FOREACH(task, &relay->tasks, entry)
- if (task->msgid == evpid_to_msgid(evp->id))
- break;
-
- if (task == NULL) {
- task = xmalloc(sizeof *task);
- TAILQ_INIT(&task->envelopes);
- task->relay = relay;
- relay->ntask += 1;
- TAILQ_INSERT_TAIL(&relay->tasks, task, entry);
- task->msgid = evpid_to_msgid(evp->id);
- if (evp->sender.user[0] || evp->sender.domain[0])
- (void)snprintf(buf, sizeof buf, "%s@%s",
- evp->sender.user, evp->sender.domain);
- else
- buf[0] = '\0';
-
- if (dispatcher->u.remote.mail_from && evp->sender.user[0]) {
- memset(&maddr, 0, sizeof (maddr));
- if (text_to_mailaddr(&maddr,
- dispatcher->u.remote.mail_from)) {
- (void)snprintf(buf, sizeof buf, "%s@%s",
- maddr.user[0] ? maddr.user : evp->sender.user,
- maddr.domain[0] ? maddr.domain : evp->sender.domain);
- }
- }
-
- task->sender = xstrdup(buf);
- stat_increment("mta.task", 1);
- }
-
- e = xcalloc(1, sizeof *e);
- e->id = evp->id;
- e->creation = evp->creation;
- e->smtpname = xstrdup(evp->smtpname);
- (void)snprintf(buf, sizeof buf, "%s@%s",
- evp->dest.user, evp->dest.domain);
- e->dest = xstrdup(buf);
- (void)snprintf(buf, sizeof buf, "%s@%s",
- evp->rcpt.user, evp->rcpt.domain);
- if (strcmp(buf, e->dest))
- e->rcpt = xstrdup(buf);
- e->task = task;
- if (evp->dsn_orcpt.user[0] && evp->dsn_orcpt.domain[0]) {
- (void)snprintf(buf, sizeof buf, "%s@%s",
- evp->dsn_orcpt.user, evp->dsn_orcpt.domain);
- e->dsn_orcpt = xstrdup(buf);
- }
- (void)strlcpy(e->dsn_envid, evp->dsn_envid,
- sizeof e->dsn_envid);
- e->dsn_notify = evp->dsn_notify;
- e->dsn_ret = evp->dsn_ret;
-
- TAILQ_INSERT_TAIL(&task->envelopes, e, entry);
- log_debug("debug: mta: received evp:%016" PRIx64
- " for <%s>", e->id, e->dest);
-
- stat_increment("mta.envelope", 1);
-
- mta_drain(relay);
- mta_relay_unref(relay); /* from here */
-}
-
-static void
-mta_delivery_flush_event(int fd, short event, void *arg)
-{
- struct mta_envelope *e;
- struct timeval tv;
-
- if (tree_poproot(&flush_evp, NULL, (void**)(&e))) {
-
- if (e->delivery == IMSG_MTA_DELIVERY_OK) {
- m_create(p_queue, IMSG_MTA_DELIVERY_OK, 0, 0, -1);
- m_add_evpid(p_queue, e->id);
- m_add_int(p_queue, e->ext);
- m_close(p_queue);
- } else if (e->delivery == IMSG_MTA_DELIVERY_TEMPFAIL) {
- m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1);
- m_add_evpid(p_queue, e->id);
- m_add_string(p_queue, e->status);
- m_add_int(p_queue, ESC_OTHER_STATUS);
- m_close(p_queue);
- }
- else if (e->delivery == IMSG_MTA_DELIVERY_PERMFAIL) {
- m_create(p_queue, IMSG_MTA_DELIVERY_PERMFAIL, 0, 0, -1);
- m_add_evpid(p_queue, e->id);
- m_add_string(p_queue, e->status);
- m_add_int(p_queue, ESC_OTHER_STATUS);
- m_close(p_queue);
- }
- else if (e->delivery == IMSG_MTA_DELIVERY_LOOP) {
- m_create(p_queue, IMSG_MTA_DELIVERY_LOOP, 0, 0, -1);
- m_add_evpid(p_queue, e->id);
- m_close(p_queue);
- }
- else {
- log_warnx("warn: bad delivery type %d for %016" PRIx64,
- e->delivery, e->id);
- fatalx("aborting");
- }
-
- log_debug("debug: mta: flush for %016"PRIx64" (-> %s)", e->id, e->dest);
-
- free(e->smtpname);
- free(e->dest);
- free(e->rcpt);
- free(e->dsn_orcpt);
- free(e);
-
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- evtimer_add(&ev_flush_evp, &tv);
- }
-}
-
-void
-mta_delivery_log(struct mta_envelope *e, const char *source, const char *relay,
- int delivery, const char *status)
-{
- if (delivery == IMSG_MTA_DELIVERY_OK)
- mta_log(e, "Ok", source, relay, status);
- else if (delivery == IMSG_MTA_DELIVERY_TEMPFAIL)
- mta_log(e, "TempFail", source, relay, status);
- else if (delivery == IMSG_MTA_DELIVERY_PERMFAIL)
- mta_log(e, "PermFail", source, relay, status);
- else if (delivery == IMSG_MTA_DELIVERY_LOOP)
- mta_log(e, "PermFail", source, relay, "Loop detected");
- else {
- log_warnx("warn: bad delivery type %d for %016" PRIx64,
- delivery, e->id);
- fatalx("aborting");
- }
-
- e->delivery = delivery;
- if (status)
- (void)strlcpy(e->status, status, sizeof(e->status));
-}
-
-void
-mta_delivery_notify(struct mta_envelope *e)
-{
- struct timeval tv;
-
- tree_xset(&flush_evp, e->id, e);
- if (tree_count(&flush_evp) == 1) {
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- evtimer_add(&ev_flush_evp, &tv);
- }
-}
-
-static void
-mta_query_mx(struct mta_relay *relay)
-{
- uint64_t id;
-
- if (relay->status & RELAY_WAIT_MX)
- return;
-
- log_debug("debug: mta: querying MX for %s...",
- mta_relay_to_text(relay));
-
- if (waitq_wait(&relay->domain->mxs, mta_on_mx, relay)) {
- id = generate_uid();
- tree_xset(&wait_mx, id, relay->domain);
- if (relay->domain->as_host)
- m_create(p_lka, IMSG_MTA_DNS_HOST, 0, 0, -1);
- else
- m_create(p_lka, IMSG_MTA_DNS_MX, 0, 0, -1);
- m_add_id(p_lka, id);
- m_add_string(p_lka, relay->domain->name);
- m_close(p_lka);
- }
- relay->status |= RELAY_WAIT_MX;
- mta_relay_ref(relay);
-}
-
-static void
-mta_query_limits(struct mta_relay *relay)
-{
- if (relay->status & RELAY_WAIT_LIMITS)
- return;
-
- relay->limits = dict_get(env->sc_limits_dict, relay->domain->name);
- if (relay->limits == NULL)
- relay->limits = dict_get(env->sc_limits_dict, "default");
-
- if (max_seen_conndelay_route < relay->limits->conndelay_route)
- max_seen_conndelay_route = relay->limits->conndelay_route;
- if (max_seen_discdelay_route < relay->limits->discdelay_route)
- max_seen_discdelay_route = relay->limits->discdelay_route;
-}
-
-static void
-mta_query_secret(struct mta_relay *relay)
-{
- if (relay->status & RELAY_WAIT_SECRET)
- return;
-
- log_debug("debug: mta: querying secret for %s...",
- mta_relay_to_text(relay));
-
- tree_xset(&wait_secret, relay->id, relay);
- relay->status |= RELAY_WAIT_SECRET;
-
- m_create(p_lka, IMSG_MTA_LOOKUP_CREDENTIALS, 0, 0, -1);
- m_add_id(p_lka, relay->id);
- m_add_string(p_lka, relay->authtable);
- m_add_string(p_lka, relay->authlabel);
- m_close(p_lka);
-
- mta_relay_ref(relay);
-}
-
-static void
-mta_query_smarthost(struct envelope *evp0)
-{
- struct dispatcher *dispatcher;
- struct envelope *evp;
-
- evp = malloc(sizeof(*evp));
- memmove(evp, evp0, sizeof(*evp));
-
- dispatcher = dict_xget(env->sc_dispatchers, evp->dispatcher);
-
- log_debug("debug: mta: querying smarthost for %s:%s...",
- evp->dispatcher, dispatcher->u.remote.smarthost);
-
- tree_xset(&wait_smarthost, evp->id, evp);
-
- m_create(p_lka, IMSG_MTA_LOOKUP_SMARTHOST, 0, 0, -1);
- m_add_id(p_lka, evp->id);
- if (dispatcher->u.remote.smarthost_domain)
- m_add_string(p_lka, evp->dest.domain);
- else
- m_add_string(p_lka, NULL);
- m_add_string(p_lka, dispatcher->u.remote.smarthost);
- m_close(p_lka);
-
- log_debug("debug: mta: querying smarthost");
-}
-
-static void
-mta_query_preference(struct mta_relay *relay)
-{
- if (relay->status & RELAY_WAIT_PREFERENCE)
- return;
-
- log_debug("debug: mta: querying preference for %s...",
- mta_relay_to_text(relay));
-
- tree_xset(&wait_preference, relay->id, relay);
- relay->status |= RELAY_WAIT_PREFERENCE;
-
- m_create(p_lka, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1);
- m_add_id(p_lka, relay->id);
- m_add_string(p_lka, relay->domain->name);
- m_add_string(p_lka, relay->backupname);
- m_close(p_lka);
-
- mta_relay_ref(relay);
-}
-
-static void
-mta_query_source(struct mta_relay *relay)
-{
- log_debug("debug: mta: querying source for %s...",
- mta_relay_to_text(relay));
-
- relay->sourceloop += 1;
-
- if (relay->sourcetable == NULL) {
- /*
- * This is a recursive call, but it only happens once, since
- * another source will not be queried immediately.
- */
- mta_relay_ref(relay);
- mta_on_source(relay, mta_source(NULL));
- return;
- }
-
- m_create(p_lka, IMSG_MTA_LOOKUP_SOURCE, 0, 0, -1);
- m_add_id(p_lka, relay->id);
- m_add_string(p_lka, relay->sourcetable);
- m_close(p_lka);
-
- tree_xset(&wait_source, relay->id, relay);
- relay->status |= RELAY_WAIT_SOURCE;
- mta_relay_ref(relay);
-}
-
-static void
-mta_on_mx(void *tag, void *arg, void *data)
-{
- struct mta_domain *domain = data;
- struct mta_relay *relay = arg;
-
- log_debug("debug: mta: ... got mx (%p, %s, %s)",
- tag, domain->name, mta_relay_to_text(relay));
-
- switch (domain->mxstatus) {
- case DNS_OK:
- break;
- case DNS_RETRY:
- relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL;
- relay->failstr = "Temporary failure in MX lookup";
- break;
- case DNS_EINVAL:
- relay->fail = IMSG_MTA_DELIVERY_PERMFAIL;
- relay->failstr = "Invalid domain name";
- break;
- case DNS_ENONAME:
- relay->fail = IMSG_MTA_DELIVERY_PERMFAIL;
- relay->failstr = "Domain does not exist";
- break;
- case DNS_ENOTFOUND:
- relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL;
- if (relay->domain->as_host)
- relay->failstr = "Host not found";
- else
- relay->failstr = "No MX found for domain";
- break;
- default:
- fatalx("bad DNS lookup error code");
- break;
- }
-
- if (domain->mxstatus)
- log_info("smtp-out: Failed to resolve MX for %s: %s",
- mta_relay_to_text(relay), relay->failstr);
-
- relay->status &= ~RELAY_WAIT_MX;
- mta_drain(relay);
- mta_relay_unref(relay); /* from mta_drain() */
-}
-
-static void
-mta_on_secret(struct mta_relay *relay, const char *secret)
-{
- log_debug("debug: mta: ... got secret for %s: %s",
- mta_relay_to_text(relay), secret);
-
- if (secret)
- relay->secret = strdup(secret);
-
- if (relay->secret == NULL) {
- log_warnx("warn: Failed to retrieve secret "
- "for %s", mta_relay_to_text(relay));
- relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL;
- relay->failstr = "Could not retrieve credentials";
- }
-
- relay->status &= ~RELAY_WAIT_SECRET;
- mta_drain(relay);
- mta_relay_unref(relay); /* from mta_query_secret() */
-}
-
-static void
-mta_on_smarthost(struct envelope *evp, const char *smarthost)
-{
- if (smarthost == NULL) {
- log_warnx("warn: Failed to retrieve smarthost "
- "for envelope %"PRIx64, evp->id);
- m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1);
- m_add_evpid(p_queue, evp->id);
- m_add_string(p_queue, "Cannot retrieve smarthost");
- m_add_int(p_queue, ESC_OTHER_STATUS);
- m_close(p_queue);
- return;
- }
-
- log_debug("debug: mta: ... got smarthost for %016"PRIx64": %s",
- evp->id, smarthost);
- mta_handle_envelope(evp, smarthost);
- free(evp);
-}
-
-static void
-mta_on_preference(struct mta_relay *relay, int preference)
-{
- log_debug("debug: mta: ... got preference for %s: %d",
- mta_relay_to_text(relay), preference);
-
- relay->backuppref = preference;
-
- relay->status &= ~RELAY_WAIT_PREFERENCE;
- mta_drain(relay);
- mta_relay_unref(relay); /* from mta_query_preference() */
-}
-
-static void
-mta_on_source(struct mta_relay *relay, struct mta_source *source)
-{
- struct mta_connector *c;
- void *iter;
- int delay, errmask;
-
- log_debug("debug: mta: ... got source for %s: %s",
- mta_relay_to_text(relay), source ? mta_source_to_text(source) : "NULL");
-
- relay->lastsource = time(NULL);
- delay = DELAY_CHECK_SOURCE_SLOW;
-
- if (source) {
- c = mta_connector(relay, source);
- if (c->flags & CONNECTOR_NEW) {
- c->flags &= ~CONNECTOR_NEW;
- delay = DELAY_CHECK_SOURCE;
- }
- mta_connect(c);
- if ((c->flags & CONNECTOR_ERROR) == 0)
- relay->sourceloop = 0;
- else
- delay = DELAY_CHECK_SOURCE_FAST;
- mta_source_unref(source); /* from constructor */
- }
- else {
- log_warnx("warn: Failed to get source address for %s",
- mta_relay_to_text(relay));
- }
-
- if (tree_count(&relay->connectors) == 0) {
- relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL;
- relay->failstr = "Could not retrieve source address";
- }
- if (tree_count(&relay->connectors) < relay->sourceloop) {
- relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL;
- relay->failstr = "No valid route to remote MX";
-
- errmask = 0;
- iter = NULL;
- while (tree_iter(&relay->connectors, &iter, NULL, (void **)&c))
- errmask |= c->flags;
-
- if (errmask & CONNECTOR_ERROR_ROUTE_SMTP)
- relay->failstr = "Destination seem to reject all mails";
- else if (errmask & CONNECTOR_ERROR_ROUTE_NET)
- relay->failstr = "Network error on destination MXs";
- else if (errmask & CONNECTOR_ERROR_MX)
- relay->failstr = "No MX found for destination";
- else if (errmask & CONNECTOR_ERROR_FAMILY)
- relay->failstr = "Address family mismatch on destination MXs";
- else if (errmask & CONNECTOR_ERROR_BLOCKED)
- relay->failstr = "All routes to destination blocked";
- else
- relay->failstr = "No valid route to destination";
- }
-
- relay->nextsource = relay->lastsource + delay;
- relay->status &= ~RELAY_WAIT_SOURCE;
- mta_drain(relay);
- mta_relay_unref(relay); /* from mta_query_source() */
-}
-
-static void
-mta_connect(struct mta_connector *c)
-{
- struct mta_route *route;
- struct mta_mx *mx;
- struct mta_limits *l = c->relay->limits;
- int limits;
- time_t nextconn, now;
-
- /* toggle the block flag */
- if (mta_is_blocked(c->source, c->relay->domain->name))
- c->flags |= CONNECTOR_ERROR_BLOCKED;
- else
- c->flags &= ~CONNECTOR_ERROR_BLOCKED;
-
- again:
-
- log_debug("debug: mta: connecting with %s", mta_connector_to_text(c));
-
- /* Do not connect if this connector has an error. */
- if (c->flags & CONNECTOR_ERROR) {
- log_debug("debug: mta: connector error");
- return;
- }
-
- if (c->flags & CONNECTOR_WAIT) {
- log_debug("debug: mta: cancelling connector timeout");
- runq_cancel(runq_connector, c);
- c->flags &= ~CONNECTOR_WAIT;
- }
-
- /* No job. */
- if (c->relay->ntask == 0) {
- log_debug("debug: mta: no task for connector");
- return;
- }
-
- /* Do not create more connections than necessary */
- if ((c->relay->nconn_ready >= c->relay->ntask) ||
- (c->relay->nconn > 2 && c->relay->nconn >= c->relay->ntask / 2)) {
- log_debug("debug: mta: enough connections already");
- return;
- }
-
- limits = 0;
- nextconn = now = time(NULL);
-
- if (c->relay->domain->lastconn + l->conndelay_domain > nextconn) {
- log_debug("debug: mta: cannot use domain %s before %llus",
- c->relay->domain->name,
- (unsigned long long) c->relay->domain->lastconn + l->conndelay_domain - now);
- nextconn = c->relay->domain->lastconn + l->conndelay_domain;
- }
- if (c->relay->domain->nconn >= l->maxconn_per_domain) {
- log_debug("debug: mta: hit domain limit");
- limits |= CONNECTOR_LIMIT_DOMAIN;
- }
-
- if (c->source->lastconn + l->conndelay_source > nextconn) {
- log_debug("debug: mta: cannot use source %s before %llus",
- mta_source_to_text(c->source),
- (unsigned long long) c->source->lastconn + l->conndelay_source - now);
- nextconn = c->source->lastconn + l->conndelay_source;
- }
- if (c->source->nconn >= l->maxconn_per_source) {
- log_debug("debug: mta: hit source limit");
- limits |= CONNECTOR_LIMIT_SOURCE;
- }
-
- if (c->lastconn + l->conndelay_connector > nextconn) {
- log_debug("debug: mta: cannot use %s before %llus",
- mta_connector_to_text(c),
- (unsigned long long) c->lastconn + l->conndelay_connector - now);
- nextconn = c->lastconn + l->conndelay_connector;
- }
- if (c->nconn >= l->maxconn_per_connector) {
- log_debug("debug: mta: hit connector limit");
- limits |= CONNECTOR_LIMIT_CONN;
- }
-
- if (c->relay->lastconn + l->conndelay_relay > nextconn) {
- log_debug("debug: mta: cannot use %s before %llus",
- mta_relay_to_text(c->relay),
- (unsigned long long) c->relay->lastconn + l->conndelay_relay - now);
- nextconn = c->relay->lastconn + l->conndelay_relay;
- }
- if (c->relay->nconn >= l->maxconn_per_relay) {
- log_debug("debug: mta: hit relay limit");
- limits |= CONNECTOR_LIMIT_RELAY;
- }
-
- /* We can connect now, find a route */
- if (!limits && nextconn <= now)
- route = mta_find_route(c, now, &limits, &nextconn, &mx);
- else
- route = NULL;
-
- /* No route */
- if (route == NULL) {
-
- if (c->flags & CONNECTOR_ERROR) {
- /* XXX we might want to clear this flag later */
- log_debug("debug: mta-routing: no route available for %s: errors on connector",
- mta_connector_to_text(c));
- return;
- }
- else if (limits) {
- log_debug("debug: mta-routing: no route available for %s: limits reached",
- mta_connector_to_text(c));
- nextconn = now + DELAY_CHECK_LIMIT;
- }
- else {
- log_debug("debug: mta-routing: no route available for %s: must wait a bit",
- mta_connector_to_text(c));
- }
- log_debug("debug: mta: retrying to connect on %s in %llus...",
- mta_connector_to_text(c),
- (unsigned long long) nextconn - time(NULL));
- c->flags |= CONNECTOR_WAIT;
- runq_schedule_at(runq_connector, nextconn, c);
- return;
- }
-
- log_debug("debug: mta-routing: spawning new connection on %s",
- mta_route_to_text(route));
-
- c->nconn += 1;
- c->lastconn = time(NULL);
-
- c->relay->nconn += 1;
- c->relay->lastconn = c->lastconn;
- c->relay->domain->nconn += 1;
- c->relay->domain->lastconn = c->lastconn;
- route->nconn += 1;
- route->lastconn = c->lastconn;
- route->src->nconn += 1;
- route->src->lastconn = c->lastconn;
- route->dst->nconn += 1;
- route->dst->lastconn = c->lastconn;
-
- mta_session(c->relay, route, mx->mxname); /* this never fails synchronously */
- mta_relay_ref(c->relay);
-
- goto again;
-}
-
-static void
-mta_on_timeout(struct runq *runq, void *arg)
-{
- struct mta_connector *connector = arg;
- struct mta_relay *relay = arg;
- struct mta_route *route = arg;
- struct hoststat *hs = arg;
-
- if (runq == runq_relay) {
- log_debug("debug: mta: ... timeout for %s",
- mta_relay_to_text(relay));
- relay->status &= ~RELAY_WAIT_CONNECTOR;
- mta_drain(relay);
- mta_relay_unref(relay); /* from mta_drain() */
- }
- else if (runq == runq_connector) {
- log_debug("debug: mta: ... timeout for %s",
- mta_connector_to_text(connector));
- connector->flags &= ~CONNECTOR_WAIT;
- mta_connect(connector);
- }
- else if (runq == runq_route) {
- route->flags &= ~ROUTE_RUNQ;
- mta_route_enable(route);
- mta_route_unref(route);
- }
- else if (runq == runq_hoststat) {
- log_debug("debug: mta: ... timeout for hoststat %s",
- hs->name);
- mta_hoststat_remove_entry(hs);
- free(hs);
- }
-}
-
-static void
-mta_route_disable(struct mta_route *route, int penalty, int reason)
-{
- unsigned long long delay;
-
- route->penalty += penalty;
- route->lastpenalty = time(NULL);
- delay = (unsigned long long)DELAY_ROUTE_BASE * route->penalty * route->penalty;
- if (delay > DELAY_ROUTE_MAX)
- delay = DELAY_ROUTE_MAX;
-#if 0
- delay = 60;
-#endif
-
- log_info("smtp-out: Disabling route %s for %llus",
- mta_route_to_text(route), delay);
-
- if (route->flags & ROUTE_DISABLED)
- runq_cancel(runq_route, route);
- else
- mta_route_ref(route);
-
- route->flags |= reason & ROUTE_DISABLED;
- runq_schedule(runq_route, delay, route);
-}
-
-static void
-mta_route_enable(struct mta_route *route)
-{
- if (route->flags & ROUTE_DISABLED) {
- log_info("smtp-out: Enabling route %s",
- mta_route_to_text(route));
- route->flags &= ~ROUTE_DISABLED;
- route->flags |= ROUTE_NEW;
- route->nerror = 0;
- }
-
- if (route->penalty) {
-#if DELAY_QUADRATIC
- route->penalty -= 1;
- route->lastpenalty = time(NULL);
-#else
- route->penalty = 0;
-#endif
- }
-}
-
-static void
-mta_drain(struct mta_relay *r)
-{
- char buf[64];
-
- log_debug("debug: mta: draining %s "
- "refcount=%d, ntask=%zu, nconnector=%zu, nconn=%zu",
- mta_relay_to_text(r),
- r->refcount, r->ntask, tree_count(&r->connectors), r->nconn);
-
- /*
- * All done.
- */
- if (r->ntask == 0) {
- log_debug("debug: mta: all done for %s", mta_relay_to_text(r));
- return;
- }
-
- /*
- * If we know that this relay is failing flush the tasks.
- */
- if (r->fail) {
- mta_flush(r, r->fail, r->failstr);
- return;
- }
-
- /* Query secret if needed. */
- if (r->flags & RELAY_AUTH && r->secret == NULL)
- mta_query_secret(r);
-
- /* Query our preference if needed. */
- if (r->backupname && r->backuppref == -1)
- mta_query_preference(r);
-
- /* Query the domain MXs if needed. */
- if (r->domain->lastmxquery == 0)
- mta_query_mx(r);
-
- /* Query the limits if needed. */
- if (r->limits == NULL)
- mta_query_limits(r);
-
- /* Wait until we are ready to proceed. */
- if (r->status & RELAY_WAITMASK) {
- buf[0] = '\0';
- if (r->status & RELAY_WAIT_MX)
- (void)strlcat(buf, " MX", sizeof buf);
- if (r->status & RELAY_WAIT_PREFERENCE)
- (void)strlcat(buf, " preference", sizeof buf);
- if (r->status & RELAY_WAIT_SECRET)
- (void)strlcat(buf, " secret", sizeof buf);
- if (r->status & RELAY_WAIT_SOURCE)
- (void)strlcat(buf, " source", sizeof buf);
- if (r->status & RELAY_WAIT_CONNECTOR)
- (void)strlcat(buf, " connector", sizeof buf);
- log_debug("debug: mta: %s waiting for%s",
- mta_relay_to_text(r), buf);
- return;
- }
-
- /*
- * We have pending task, and it's maybe time too try a new source.
- */
- if (r->nextsource <= time(NULL))
- mta_query_source(r);
- else {
- log_debug("debug: mta: scheduling relay %s in %llus...",
- mta_relay_to_text(r),
- (unsigned long long) r->nextsource - time(NULL));
- runq_schedule_at(runq_relay, r->nextsource, r);
- r->status |= RELAY_WAIT_CONNECTOR;
- mta_relay_ref(r);
- }
-}
-
-static void
-mta_flush(struct mta_relay *relay, int fail, const char *error)
-{
- struct mta_envelope *e;
- struct mta_task *task;
- const char *domain;
- void *iter;
- struct mta_connector *c;
- size_t n, r;
-
- log_debug("debug: mta_flush(%s, %d, \"%s\")",
- mta_relay_to_text(relay), fail, error);
-
- if (fail != IMSG_MTA_DELIVERY_TEMPFAIL && fail != IMSG_MTA_DELIVERY_PERMFAIL)
- errx(1, "unexpected delivery status %d", fail);
-
- n = 0;
- while ((task = TAILQ_FIRST(&relay->tasks))) {
- TAILQ_REMOVE(&relay->tasks, task, entry);
- while ((e = TAILQ_FIRST(&task->envelopes))) {
- TAILQ_REMOVE(&task->envelopes, e, entry);
-
- /*
- * host was suspended, cache envelope id in hoststat tree
- * so that it can be retried when a delivery succeeds for
- * that domain.
- */
- domain = strchr(e->dest, '@');
- if (fail == IMSG_MTA_DELIVERY_TEMPFAIL && domain) {
- r = 0;
- iter = NULL;
- while (tree_iter(&relay->connectors, &iter,
- NULL, (void **)&c)) {
- if (c->flags & CONNECTOR_ERROR_ROUTE)
- r++;
- }
- if (tree_count(&relay->connectors) == r)
- mta_hoststat_cache(domain+1, e->id);
- }
-
- mta_delivery_log(e, NULL, relay->domain->name, fail, error);
- mta_delivery_notify(e);
-
- n++;
- }
- free(task->sender);
- free(task);
- }
-
- stat_decrement("mta.task", relay->ntask);
- stat_decrement("mta.envelope", n);
- relay->ntask = 0;
-
- /* release all waiting envelopes for the relay */
- if (relay->state & RELAY_HOLDQ) {
- m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1);
- m_add_id(p_queue, relay->id);
- m_add_int(p_queue, -1);
- m_close(p_queue);
- }
-}
-
-/*
- * Find a route to use for this connector
- */
-static struct mta_route *
-mta_find_route(struct mta_connector *c, time_t now, int *limits,
- time_t *nextconn, struct mta_mx **pmx)
-{
- struct mta_route *route, *best;
- struct mta_limits *l = c->relay->limits;
- struct mta_mx *mx;
- int level, limit_host, limit_route;
- int family_mismatch, seen, suspended_route;
- time_t tm;
-
- log_debug("debug: mta-routing: searching new route for %s...",
- mta_connector_to_text(c));
-
- tm = 0;
- limit_host = 0;
- limit_route = 0;
- suspended_route = 0;
- family_mismatch = 0;
- level = -1;
- best = NULL;
- seen = 0;
-
- TAILQ_FOREACH(mx, &c->relay->domain->mxs, entry) {
- /*
- * New preference level
- */
- if (mx->preference > level) {
-#ifndef IGNORE_MX_PREFERENCE
- /*
- * Use the current best MX if found.
- */
- if (best)
- break;
-
- /*
- * No candidate found. There are valid MXs at this
- * preference level but they reached their limit, or
- * we can't connect yet.
- */
- if (limit_host || limit_route || tm)
- break;
-
- /*
- * If we are a backup MX, do not relay to MXs with
- * a greater preference value.
- */
- if (c->relay->backuppref >= 0 &&
- mx->preference >= c->relay->backuppref)
- break;
-
- /*
- * Start looking at MXs on this preference level.
- */
-#endif
- level = mx->preference;
- }
-
- if (mx->host->flags & HOST_IGNORE)
- continue;
-
- /* Found a possibly valid mx */
- seen++;
-
- if ((c->source->sa &&
- c->source->sa->sa_family != mx->host->sa->sa_family) ||
- (l->family && l->family != mx->host->sa->sa_family)) {
- log_debug("debug: mta-routing: skipping host %s: AF mismatch",
- mta_host_to_text(mx->host));
- family_mismatch = 1;
- continue;
- }
-
- if (mx->host->nconn >= l->maxconn_per_host) {
- log_debug("debug: mta-routing: skipping host %s: too many connections",
- mta_host_to_text(mx->host));
- limit_host = 1;
- continue;
- }
-
- if (mx->host->lastconn + l->conndelay_host > now) {
- log_debug("debug: mta-routing: skipping host %s: cannot use before %llus",
- mta_host_to_text(mx->host),
- (unsigned long long) mx->host->lastconn + l->conndelay_host - now);
- if (tm == 0 || mx->host->lastconn + l->conndelay_host < tm)
- tm = mx->host->lastconn + l->conndelay_host;
- continue;
- }
-
- route = mta_route(c->source, mx->host);
-
- if (route->flags & ROUTE_DISABLED) {
- log_debug("debug: mta-routing: skipping route %s: suspend",
- mta_route_to_text(route));
- suspended_route |= route->flags & ROUTE_DISABLED;
- mta_route_unref(route); /* from here */
- continue;
- }
-
- if (route->nconn && (route->flags & ROUTE_NEW)) {
- log_debug("debug: mta-routing: skipping route %s: not validated yet",
- mta_route_to_text(route));
- limit_route = 1;
- mta_route_unref(route); /* from here */
- continue;
- }
-
- if (route->nconn >= l->maxconn_per_route) {
- log_debug("debug: mta-routing: skipping route %s: too many connections",
- mta_route_to_text(route));
- limit_route = 1;
- mta_route_unref(route); /* from here */
- continue;
- }
-
- if (route->lastconn + l->conndelay_route > now) {
- log_debug("debug: mta-routing: skipping route %s: cannot use before %llus (delay after connect)",
- mta_route_to_text(route),
- (unsigned long long) route->lastconn + l->conndelay_route - now);
- if (tm == 0 || route->lastconn + l->conndelay_route < tm)
- tm = route->lastconn + l->conndelay_route;
- mta_route_unref(route); /* from here */
- continue;
- }
-
- if (route->lastdisc + l->discdelay_route > now) {
- log_debug("debug: mta-routing: skipping route %s: cannot use before %llus (delay after disconnect)",
- mta_route_to_text(route),
- (unsigned long long) route->lastdisc + l->discdelay_route - now);
- if (tm == 0 || route->lastdisc + l->discdelay_route < tm)
- tm = route->lastdisc + l->discdelay_route;
- mta_route_unref(route); /* from here */
- continue;
- }
-
- /* Use the route with the lowest number of connections. */
- if (best && route->nconn >= best->nconn) {
- log_debug("debug: mta-routing: skipping route %s: current one is better",
- mta_route_to_text(route));
- mta_route_unref(route); /* from here */
- continue;
- }
-
- if (best)
- mta_route_unref(best); /* from here */
- best = route;
- *pmx = mx;
- log_debug("debug: mta-routing: selecting candidate route %s",
- mta_route_to_text(route));
- }
-
- if (best)
- return (best);
-
- /* Order is important */
- if (seen == 0) {
- log_info("smtp-out: No MX found for %s",
- mta_connector_to_text(c));
- c->flags |= CONNECTOR_ERROR_MX;
- }
- else if (limit_route) {
- log_debug("debug: mta: hit route limit");
- *limits |= CONNECTOR_LIMIT_ROUTE;
- }
- else if (limit_host) {
- log_debug("debug: mta: hit host limit");
- *limits |= CONNECTOR_LIMIT_HOST;
- }
- else if (tm) {
- if (tm > *nextconn)
- *nextconn = tm;
- }
- else if (family_mismatch) {
- log_info("smtp-out: Address family mismatch on %s",
- mta_connector_to_text(c));
- c->flags |= CONNECTOR_ERROR_FAMILY;
- }
- else if (suspended_route) {
- log_info("smtp-out: No valid route for %s",
- mta_connector_to_text(c));
- if (suspended_route & ROUTE_DISABLED_NET)
- c->flags |= CONNECTOR_ERROR_ROUTE_NET;
- if (suspended_route & ROUTE_DISABLED_SMTP)
- c->flags |= CONNECTOR_ERROR_ROUTE_SMTP;
- }
-
- return (NULL);
-}
-
-static void
-mta_log(const struct mta_envelope *evp, const char *prefix, const char *source,
- const char *relay, const char *status)
-{
- log_info("%016"PRIx64" mta delivery evpid=%016"PRIx64" "
- "from=<%s> to=<%s> rcpt=<%s> source=\"%s\" "
- "relay=\"%s\" delay=%s result=\"%s\" stat=\"%s\"",
- evp->session,
- evp->id,
- evp->task->sender,
- evp->dest,
- evp->rcpt ? evp->rcpt : "-",
- source ? source : "-",
- relay,
- duration_to_text(time(NULL) - evp->creation),
- prefix,
- status);
-}
-
-static struct mta_relay *
-mta_relay(struct envelope *e, struct relayhost *relayh)
-{
- struct dispatcher *dispatcher;
- struct mta_relay key, *r;
-
- dispatcher = dict_xget(env->sc_dispatchers, e->dispatcher);
-
- memset(&key, 0, sizeof key);
-
- key.pki_name = dispatcher->u.remote.pki;
- key.ca_name = dispatcher->u.remote.ca;
- key.authtable = dispatcher->u.remote.auth;
- key.sourcetable = dispatcher->u.remote.source;
- key.helotable = dispatcher->u.remote.helo_source;
- key.heloname = dispatcher->u.remote.helo;
- key.srs = dispatcher->u.remote.srs;
-
- if (relayh->hostname[0]) {
- key.domain = mta_domain(relayh->hostname, 1);
- }
- else {
- key.domain = mta_domain(e->dest.domain, 0);
- if (dispatcher->u.remote.backup) {
- key.backupname = dispatcher->u.remote.backupmx;
- if (key.backupname == NULL)
- key.backupname = e->smtpname;
- }
- }
-
- key.tls = relayh->tls;
- key.flags |= relayh->flags;
- key.port = relayh->port;
- key.authlabel = relayh->authlabel;
- if (!key.authlabel[0])
- key.authlabel = NULL;
-
- if ((key.tls == RELAY_TLS_STARTTLS || key.tls == RELAY_TLS_SMTPS) &&
- dispatcher->u.remote.tls_noverify == 0)
- key.flags |= RELAY_TLS_VERIFY;
-
- if ((r = SPLAY_FIND(mta_relay_tree, &relays, &key)) == NULL) {
- r = xcalloc(1, sizeof *r);
- TAILQ_INIT(&r->tasks);
- r->id = generate_uid();
- r->dispatcher = dispatcher;
- r->tls = key.tls;
- r->flags = key.flags;
- r->domain = key.domain;
- r->backupname = key.backupname ?
- xstrdup(key.backupname) : NULL;
- r->backuppref = -1;
- r->port = key.port;
- r->pki_name = key.pki_name ? xstrdup(key.pki_name) : NULL;
- r->ca_name = key.ca_name ? xstrdup(key.ca_name) : NULL;
- if (key.authtable)
- r->authtable = xstrdup(key.authtable);
- if (key.authlabel)
- r->authlabel = xstrdup(key.authlabel);
- if (key.sourcetable)
- r->sourcetable = xstrdup(key.sourcetable);
- if (key.helotable)
- r->helotable = xstrdup(key.helotable);
- if (key.heloname)
- r->heloname = xstrdup(key.heloname);
- r->srs = key.srs;
- SPLAY_INSERT(mta_relay_tree, &relays, r);
- stat_increment("mta.relay", 1);
- } else {
- mta_domain_unref(key.domain); /* from here */
- }
-
- r->refcount++;
- return (r);
-}
-
-static void
-mta_relay_ref(struct mta_relay *r)
-{
- r->refcount++;
-}
-
-static void
-mta_relay_unref(struct mta_relay *relay)
-{
- struct mta_connector *c;
-
- if (--relay->refcount)
- return;
-
- /* Make sure they are no envelopes held for this relay */
- if (relay->state & RELAY_HOLDQ) {
- m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1);
- m_add_id(p_queue, relay->id);
- m_add_int(p_queue, 0);
- m_close(p_queue);
- }
-
- log_debug("debug: mta: freeing %s", mta_relay_to_text(relay));
- SPLAY_REMOVE(mta_relay_tree, &relays, relay);
-
- while ((tree_poproot(&relay->connectors, NULL, (void**)&c)))
- mta_connector_free(c);
-
- free(relay->authlabel);
- free(relay->authtable);
- free(relay->backupname);
- free(relay->pki_name);
- free(relay->ca_name);
- free(relay->helotable);
- free(relay->heloname);
- free(relay->secret);
- free(relay->sourcetable);
-
- mta_domain_unref(relay->domain); /* from constructor */
- free(relay);
- stat_decrement("mta.relay", 1);
-}
-
-const char *
-mta_relay_to_text(struct mta_relay *relay)
-{
- static char buf[1024];
- char tmp[32];
- const char *sep = ",";
-
- (void)snprintf(buf, sizeof buf, "[relay:%s", relay->domain->name);
-
- if (relay->port) {
- (void)strlcat(buf, sep, sizeof buf);
- (void)snprintf(tmp, sizeof tmp, "port=%d", (int)relay->port);
- (void)strlcat(buf, tmp, sizeof buf);
- }
-
- (void)strlcat(buf, sep, sizeof buf);
- switch(relay->tls) {
- case RELAY_TLS_OPPORTUNISTIC:
- (void)strlcat(buf, "smtp", sizeof buf);
- break;
- case RELAY_TLS_STARTTLS:
- (void)strlcat(buf, "smtp+tls", sizeof buf);
- break;
- case RELAY_TLS_SMTPS:
- (void)strlcat(buf, "smtps", sizeof buf);
- break;
- case RELAY_TLS_NO:
- if (relay->flags & RELAY_LMTP)
- (void)strlcat(buf, "lmtp", sizeof buf);
- else
- (void)strlcat(buf, "smtp+notls", sizeof buf);
- break;
- default:
- (void)strlcat(buf, "???", sizeof buf);
- }
-
- if (relay->flags & RELAY_AUTH) {
- (void)strlcat(buf, sep, sizeof buf);
- (void)strlcat(buf, "auth=", sizeof buf);
- (void)strlcat(buf, relay->authtable, sizeof buf);
- (void)strlcat(buf, ":", sizeof buf);
- (void)strlcat(buf, relay->authlabel, sizeof buf);
- }
-
- if (relay->pki_name) {
- (void)strlcat(buf, sep, sizeof buf);
- (void)strlcat(buf, "pki_name=", sizeof buf);
- (void)strlcat(buf, relay->pki_name, sizeof buf);
- }
-
- if (relay->domain->as_host) {
- (void)strlcat(buf, sep, sizeof buf);
- (void)strlcat(buf, "mx", sizeof buf);
- }
-
- if (relay->backupname) {
- (void)strlcat(buf, sep, sizeof buf);
- (void)strlcat(buf, "backup=", sizeof buf);
- (void)strlcat(buf, relay->backupname, sizeof buf);
- }
-
- if (relay->sourcetable) {
- (void)strlcat(buf, sep, sizeof buf);
- (void)strlcat(buf, "sourcetable=", sizeof buf);
- (void)strlcat(buf, relay->sourcetable, sizeof buf);
- }
-
- if (relay->helotable) {
- (void)strlcat(buf, sep, sizeof buf);
- (void)strlcat(buf, "helotable=", sizeof buf);
- (void)strlcat(buf, relay->helotable, sizeof buf);
- }
-
- if (relay->heloname) {
- (void)strlcat(buf, sep, sizeof buf);
- (void)strlcat(buf, "heloname=", sizeof buf);
- (void)strlcat(buf, relay->heloname, sizeof buf);
- }
-
- (void)strlcat(buf, "]", sizeof buf);
-
- return (buf);
-}
-
-static void
-mta_relay_show(struct mta_relay *r, struct mproc *p, uint32_t id, time_t t)
-{
- struct mta_connector *c;
- void *iter;
- char buf[1024], flags[1024], dur[64];
- time_t to;
-
- flags[0] = '\0';
-
-#define SHOWSTATUS(f, n) do { \
- if (r->status & (f)) { \
- if (flags[0]) \
- (void)strlcat(flags, ",", sizeof(flags)); \
- (void)strlcat(flags, (n), sizeof(flags)); \
- } \
- } while(0)
-
- SHOWSTATUS(RELAY_WAIT_MX, "MX");
- SHOWSTATUS(RELAY_WAIT_PREFERENCE, "preference");
- SHOWSTATUS(RELAY_WAIT_SECRET, "secret");
- SHOWSTATUS(RELAY_WAIT_LIMITS, "limits");
- SHOWSTATUS(RELAY_WAIT_SOURCE, "source");
- SHOWSTATUS(RELAY_WAIT_CONNECTOR, "connector");
-#undef SHOWSTATUS
-
- if (runq_pending(runq_relay, r, &to))
- (void)snprintf(dur, sizeof(dur), "%s", duration_to_text(to - t));
- else
- (void)strlcpy(dur, "-", sizeof(dur));
-
- (void)snprintf(buf, sizeof(buf), "%s refcount=%d ntask=%zu nconn=%zu lastconn=%s timeout=%s wait=%s%s",
- mta_relay_to_text(r),
- r->refcount,
- r->ntask,
- r->nconn,
- r->lastconn ? duration_to_text(t - r->lastconn) : "-",
- dur,
- flags,
- (r->state & RELAY_ONHOLD) ? "ONHOLD" : "");
- m_compose(p, IMSG_CTL_MTA_SHOW_RELAYS, id, 0, -1, buf, strlen(buf) + 1);
-
- iter = NULL;
- while (tree_iter(&r->connectors, &iter, NULL, (void **)&c)) {
-
- if (runq_pending(runq_connector, c, &to))
- (void)snprintf(dur, sizeof(dur), "%s", duration_to_text(to - t));
- else
- (void)strlcpy(dur, "-", sizeof(dur));
-
- flags[0] = '\0';
-
-#define SHOWFLAG(f, n) do { \
- if (c->flags & (f)) { \
- if (flags[0]) \
- (void)strlcat(flags, ",", sizeof(flags)); \
- (void)strlcat(flags, (n), sizeof(flags)); \
- } \
- } while(0)
-
- SHOWFLAG(CONNECTOR_NEW, "NEW");
- SHOWFLAG(CONNECTOR_WAIT, "WAIT");
-
- SHOWFLAG(CONNECTOR_ERROR_FAMILY, "ERROR_FAMILY");
- SHOWFLAG(CONNECTOR_ERROR_SOURCE, "ERROR_SOURCE");
- SHOWFLAG(CONNECTOR_ERROR_MX, "ERROR_MX");
- SHOWFLAG(CONNECTOR_ERROR_ROUTE_NET, "ERROR_ROUTE_NET");
- SHOWFLAG(CONNECTOR_ERROR_ROUTE_SMTP, "ERROR_ROUTE_SMTP");
- SHOWFLAG(CONNECTOR_ERROR_BLOCKED, "ERROR_BLOCKED");
-
- SHOWFLAG(CONNECTOR_LIMIT_HOST, "LIMIT_HOST");
- SHOWFLAG(CONNECTOR_LIMIT_ROUTE, "LIMIT_ROUTE");
- SHOWFLAG(CONNECTOR_LIMIT_SOURCE, "LIMIT_SOURCE");
- SHOWFLAG(CONNECTOR_LIMIT_RELAY, "LIMIT_RELAY");
- SHOWFLAG(CONNECTOR_LIMIT_CONN, "LIMIT_CONN");
- SHOWFLAG(CONNECTOR_LIMIT_DOMAIN, "LIMIT_DOMAIN");
-#undef SHOWFLAG
-
- (void)snprintf(buf, sizeof(buf),
- " connector %s refcount=%d nconn=%zu lastconn=%s timeout=%s flags=%s",
- mta_source_to_text(c->source),
- c->refcount,
- c->nconn,
- c->lastconn ? duration_to_text(t - c->lastconn) : "-",
- dur,
- flags);
- m_compose(p, IMSG_CTL_MTA_SHOW_RELAYS, id, 0, -1, buf,
- strlen(buf) + 1);
-
-
- }
-}
-
-static int
-mta_relay_cmp(const struct mta_relay *a, const struct mta_relay *b)
-{
- int r;
-
- if (a->domain < b->domain)
- return (-1);
- if (a->domain > b->domain)
- return (1);
-
- if (a->tls < b->tls)
- return (-1);
- if (a->tls > b->tls)
- return (1);
-
- if (a->flags < b->flags)
- return (-1);
- if (a->flags > b->flags)
- return (1);
-
- if (a->port < b->port)
- return (-1);
- if (a->port > b->port)
- return (1);
-
- if (a->authtable == NULL && b->authtable)
- return (-1);
- if (a->authtable && b->authtable == NULL)
- return (1);
- if (a->authtable && ((r = strcmp(a->authtable, b->authtable))))
- return (r);
- if (a->authlabel == NULL && b->authlabel)
- return (-1);
- if (a->authlabel && b->authlabel == NULL)
- return (1);
- if (a->authlabel && ((r = strcmp(a->authlabel, b->authlabel))))
- return (r);
- if (a->sourcetable == NULL && b->sourcetable)
- return (-1);
- if (a->sourcetable && b->sourcetable == NULL)
- return (1);
- if (a->sourcetable && ((r = strcmp(a->sourcetable, b->sourcetable))))
- return (r);
- if (a->helotable == NULL && b->helotable)
- return (-1);
- if (a->helotable && b->helotable == NULL)
- return (1);
- if (a->helotable && ((r = strcmp(a->helotable, b->helotable))))
- return (r);
- if (a->heloname == NULL && b->heloname)
- return (-1);
- if (a->heloname && b->heloname == NULL)
- return (1);
- if (a->heloname && ((r = strcmp(a->heloname, b->heloname))))
- return (r);
-
- if (a->pki_name == NULL && b->pki_name)
- return (-1);
- if (a->pki_name && b->pki_name == NULL)
- return (1);
- if (a->pki_name && ((r = strcmp(a->pki_name, b->pki_name))))
- return (r);
-
- if (a->ca_name == NULL && b->ca_name)
- return (-1);
- if (a->ca_name && b->ca_name == NULL)
- return (1);
- if (a->ca_name && ((r = strcmp(a->ca_name, b->ca_name))))
- return (r);
-
- if (a->backupname == NULL && b->backupname)
- return (-1);
- if (a->backupname && b->backupname == NULL)
- return (1);
- if (a->backupname && ((r = strcmp(a->backupname, b->backupname))))
- return (r);
-
- if (a->srs < b->srs)
- return (-1);
- if (a->srs > b->srs)
- return (1);
-
- return (0);
-}
-
-SPLAY_GENERATE(mta_relay_tree, mta_relay, entry, mta_relay_cmp);
-
-static struct mta_host *
-mta_host(const struct sockaddr *sa)
-{
- struct mta_host key, *h;
- struct sockaddr_storage ss;
-
- memmove(&ss, sa, SA_LEN(sa));
- key.sa = (struct sockaddr*)&ss;
- h = SPLAY_FIND(mta_host_tree, &hosts, &key);
-
- if (h == NULL) {
- h = xcalloc(1, sizeof(*h));
- h->sa = xmemdup(sa, SA_LEN(sa));
- SPLAY_INSERT(mta_host_tree, &hosts, h);
- stat_increment("mta.host", 1);
- }
-
- h->refcount++;
- return (h);
-}
-
-static void
-mta_host_ref(struct mta_host *h)
-{
- h->refcount++;
-}
-
-static void
-mta_host_unref(struct mta_host *h)
-{
- if (--h->refcount)
- return;
-
- SPLAY_REMOVE(mta_host_tree, &hosts, h);
- free(h->sa);
- free(h->ptrname);
- free(h);
- stat_decrement("mta.host", 1);
-}
-
-const char *
-mta_host_to_text(struct mta_host *h)
-{
- static char buf[1024];
-
- if (h->ptrname)
- (void)snprintf(buf, sizeof buf, "%s (%s)",
- sa_to_text(h->sa), h->ptrname);
- else
- (void)snprintf(buf, sizeof buf, "%s", sa_to_text(h->sa));
-
- return (buf);
-}
-
-static int
-mta_host_cmp(const struct mta_host *a, const struct mta_host *b)
-{
- if (SA_LEN(a->sa) < SA_LEN(b->sa))
- return (-1);
- if (SA_LEN(a->sa) > SA_LEN(b->sa))
- return (1);
- return (memcmp(a->sa, b->sa, SA_LEN(a->sa)));
-}
-
-SPLAY_GENERATE(mta_host_tree, mta_host, entry, mta_host_cmp);
-
-static struct mta_domain *
-mta_domain(char *name, int as_host)
-{
- struct mta_domain key, *d;
-
- key.name = name;
- key.as_host = as_host;
- d = SPLAY_FIND(mta_domain_tree, &domains, &key);
-
- if (d == NULL) {
- d = xcalloc(1, sizeof(*d));
- d->name = xstrdup(name);
- d->as_host = as_host;
- TAILQ_INIT(&d->mxs);
- SPLAY_INSERT(mta_domain_tree, &domains, d);
- stat_increment("mta.domain", 1);
- }
-
- d->refcount++;
- return (d);
-}
-
-#if 0
-static void
-mta_domain_ref(struct mta_domain *d)
-{
- d->refcount++;
-}
-#endif
-
-static void
-mta_domain_unref(struct mta_domain *d)
-{
- struct mta_mx *mx;
-
- if (--d->refcount)
- return;
-
- while ((mx = TAILQ_FIRST(&d->mxs))) {
- TAILQ_REMOVE(&d->mxs, mx, entry);
- mta_host_unref(mx->host); /* from IMSG_DNS_HOST */
- free(mx->mxname);
- free(mx);
- }
-
- SPLAY_REMOVE(mta_domain_tree, &domains, d);
- free(d->name);
- free(d);
- stat_decrement("mta.domain", 1);
-}
-
-static int
-mta_domain_cmp(const struct mta_domain *a, const struct mta_domain *b)
-{
- if (a->as_host < b->as_host)
- return (-1);
- if (a->as_host > b->as_host)
- return (1);
- return (strcasecmp(a->name, b->name));
-}
-
-SPLAY_GENERATE(mta_domain_tree, mta_domain, entry, mta_domain_cmp);
-
-static struct mta_source *
-mta_source(const struct sockaddr *sa)
-{
- struct mta_source key, *s;
- struct sockaddr_storage ss;
-
- if (sa) {
- memmove(&ss, sa, SA_LEN(sa));
- key.sa = (struct sockaddr*)&ss;
- } else
- key.sa = NULL;
- s = SPLAY_FIND(mta_source_tree, &sources, &key);
-
- if (s == NULL) {
- s = xcalloc(1, sizeof(*s));
- if (sa)
- s->sa = xmemdup(sa, SA_LEN(sa));
- SPLAY_INSERT(mta_source_tree, &sources, s);
- stat_increment("mta.source", 1);
- }
-
- s->refcount++;
- return (s);
-}
-
-static void
-mta_source_ref(struct mta_source *s)
-{
- s->refcount++;
-}
-
-static void
-mta_source_unref(struct mta_source *s)
-{
- if (--s->refcount)
- return;
-
- SPLAY_REMOVE(mta_source_tree, &sources, s);
- free(s->sa);
- free(s);
- stat_decrement("mta.source", 1);
-}
-
-static const char *
-mta_source_to_text(struct mta_source *s)
-{
- static char buf[1024];
-
- if (s->sa == NULL)
- return "[]";
- (void)snprintf(buf, sizeof buf, "%s", sa_to_text(s->sa));
- return (buf);
-}
-
-static int
-mta_source_cmp(const struct mta_source *a, const struct mta_source *b)
-{
- if (a->sa == NULL)
- return ((b->sa == NULL) ? 0 : -1);
- if (b->sa == NULL)
- return (1);
- if (SA_LEN(a->sa) < SA_LEN(b->sa))
- return (-1);
- if (SA_LEN(a->sa) > SA_LEN(b->sa))
- return (1);
- return (memcmp(a->sa, b->sa, SA_LEN(a->sa)));
-}
-
-SPLAY_GENERATE(mta_source_tree, mta_source, entry, mta_source_cmp);
-
-static struct mta_connector *
-mta_connector(struct mta_relay *relay, struct mta_source *source)
-{
- struct mta_connector *c;
-
- c = tree_get(&relay->connectors, (uintptr_t)(source));
- if (c == NULL) {
- c = xcalloc(1, sizeof(*c));
- c->relay = relay;
- c->source = source;
- c->flags |= CONNECTOR_NEW;
- mta_source_ref(source);
- tree_xset(&relay->connectors, (uintptr_t)(source), c);
- stat_increment("mta.connector", 1);
- log_debug("debug: mta: new %s", mta_connector_to_text(c));
- }
-
- return (c);
-}
-
-static void
-mta_connector_free(struct mta_connector *c)
-{
- log_debug("debug: mta: freeing %s",
- mta_connector_to_text(c));
-
- if (c->flags & CONNECTOR_WAIT) {
- log_debug("debug: mta: cancelling timeout for %s",
- mta_connector_to_text(c));
- runq_cancel(runq_connector, c);
- }
- mta_source_unref(c->source); /* from constructor */
- free(c);
-
- stat_decrement("mta.connector", 1);
-}
-
-static const char *
-mta_connector_to_text(struct mta_connector *c)
-{
- static char buf[1024];
-
- (void)snprintf(buf, sizeof buf, "[connector:%s->%s,0x%x]",
- mta_source_to_text(c->source),
- mta_relay_to_text(c->relay),
- c->flags);
- return (buf);
-}
-
-static struct mta_route *
-mta_route(struct mta_source *src, struct mta_host *dst)
-{
- struct mta_route key, *r;
- static uint64_t rid = 0;
-
- key.src = src;
- key.dst = dst;
- r = SPLAY_FIND(mta_route_tree, &routes, &key);
-
- if (r == NULL) {
- r = xcalloc(1, sizeof(*r));
- r->src = src;
- r->dst = dst;
- r->flags |= ROUTE_NEW;
- r->id = ++rid;
- SPLAY_INSERT(mta_route_tree, &routes, r);
- mta_source_ref(src);
- mta_host_ref(dst);
- stat_increment("mta.route", 1);
- }
- else if (r->flags & ROUTE_RUNQ) {
- log_debug("debug: mta: mta_route_ref(): cancelling runq for route %s",
- mta_route_to_text(r));
- r->flags &= ~(ROUTE_RUNQ | ROUTE_KEEPALIVE);
- runq_cancel(runq_route, r);
- r->refcount--; /* from mta_route_unref() */
- }
-
- r->refcount++;
- return (r);
-}
-
-static void
-mta_route_ref(struct mta_route *r)
-{
- r->refcount++;
-}
-
-static void
-mta_route_unref(struct mta_route *r)
-{
- time_t sched, now;
- int delay;
-
- if (--r->refcount)
- return;
-
- /*
- * Nothing references this route, but we might want to keep it alive
- * for a while.
- */
- now = time(NULL);
- sched = 0;
-
- if (r->penalty) {
-#if DELAY_QUADRATIC
- delay = DELAY_ROUTE_BASE * r->penalty * r->penalty;
-#else
- delay = 15 * 60;
-#endif
- if (delay > DELAY_ROUTE_MAX)
- delay = DELAY_ROUTE_MAX;
- sched = r->lastpenalty + delay;
- log_debug("debug: mta: mta_route_unref(): keeping route %s alive for %llus (penalty %d)",
- mta_route_to_text(r), (unsigned long long) sched - now, r->penalty);
- } else if (!(r->flags & ROUTE_KEEPALIVE)) {
- if (r->lastconn + max_seen_conndelay_route > now)
- sched = r->lastconn + max_seen_conndelay_route;
- if (r->lastdisc + max_seen_discdelay_route > now &&
- r->lastdisc + max_seen_discdelay_route < sched)
- sched = r->lastdisc + max_seen_discdelay_route;
-
- if (sched > now)
- log_debug("debug: mta: mta_route_unref(): keeping route %s alive for %llus (imposed delay)",
- mta_route_to_text(r), (unsigned long long) sched - now);
- }
-
- if (sched > now) {
- r->flags |= ROUTE_RUNQ;
- runq_schedule_at(runq_route, sched, r);
- r->refcount++;
- return;
- }
-
- log_debug("debug: mta: mta_route_unref(): really discarding route %s",
- mta_route_to_text(r));
-
- SPLAY_REMOVE(mta_route_tree, &routes, r);
- mta_source_unref(r->src); /* from constructor */
- mta_host_unref(r->dst); /* from constructor */
- free(r);
- stat_decrement("mta.route", 1);
-}
-
-static const char *
-mta_route_to_text(struct mta_route *r)
-{
- static char buf[1024];
-
- (void)snprintf(buf, sizeof buf, "%s <-> %s",
- mta_source_to_text(r->src),
- mta_host_to_text(r->dst));
-
- return (buf);
-}
-
-static int
-mta_route_cmp(const struct mta_route *a, const struct mta_route *b)
-{
- if (a->src < b->src)
- return (-1);
- if (a->src > b->src)
- return (1);
-
- if (a->dst < b->dst)
- return (-1);
- if (a->dst > b->dst)
- return (1);
-
- return (0);
-}
-
-SPLAY_GENERATE(mta_route_tree, mta_route, entry, mta_route_cmp);
-
-void
-mta_block(struct mta_source *src, char *dom)
-{
- struct mta_block key, *b;
-
- key.source = src;
- key.domain = dom;
-
- b = SPLAY_FIND(mta_block_tree, &blocks, &key);
- if (b != NULL)
- return;
-
- b = xcalloc(1, sizeof(*b));
- if (dom)
- b->domain = xstrdup(dom);
- b->source = src;
- mta_source_ref(src);
- SPLAY_INSERT(mta_block_tree, &blocks, b);
-}
-
-void
-mta_unblock(struct mta_source *src, char *dom)
-{
- struct mta_block key, *b;
-
- key.source = src;
- key.domain = dom;
-
- b = SPLAY_FIND(mta_block_tree, &blocks, &key);
- if (b == NULL)
- return;
-
- SPLAY_REMOVE(mta_block_tree, &blocks, b);
-
- mta_source_unref(b->source);
- free(b->domain);
- free(b);
-}
-
-int
-mta_is_blocked(struct mta_source *src, char *dom)
-{
- struct mta_block key;
-
- key.source = src;
- key.domain = dom;
-
- if (SPLAY_FIND(mta_block_tree, &blocks, &key))
- return (1);
-
- return (0);
-}
-
-static
-int
-mta_block_cmp(const struct mta_block *a, const struct mta_block *b)
-{
- if (a->source < b->source)
- return (-1);
- if (a->source > b->source)
- return (1);
- if (!a->domain && b->domain)
- return (-1);
- if (a->domain && !b->domain)
- return (1);
- if (a->domain == b->domain)
- return (0);
- return (strcasecmp(a->domain, b->domain));
-}
-
-SPLAY_GENERATE(mta_block_tree, mta_block, entry, mta_block_cmp);
-
-
-
-/* hoststat errors are not critical, we do best effort */
-void
-mta_hoststat_update(const char *host, const char *error)
-{
- struct hoststat *hs = NULL;
- char buf[HOST_NAME_MAX+1];
-
- if (!lowercase(buf, host, sizeof buf))
- return;
-
- hs = dict_get(&hoststat, buf);
- if (hs == NULL) {
- if ((hs = calloc(1, sizeof *hs)) == NULL)
- return;
- tree_init(&hs->deferred);
- runq_schedule(runq_hoststat, HOSTSTAT_EXPIRE_DELAY, hs);
- }
- (void)strlcpy(hs->name, buf, sizeof hs->name);
- (void)strlcpy(hs->error, error, sizeof hs->error);
- hs->tm = time(NULL);
- dict_set(&hoststat, buf, hs);
-
- runq_cancel(runq_hoststat, hs);
- runq_schedule(runq_hoststat, HOSTSTAT_EXPIRE_DELAY, hs);
-}
-
-void
-mta_hoststat_cache(const char *host, uint64_t evpid)
-{
- struct hoststat *hs = NULL;
- char buf[HOST_NAME_MAX+1];
-
- if (!lowercase(buf, host, sizeof buf))
- return;
-
- hs = dict_get(&hoststat, buf);
- if (hs == NULL)
- return;
-
- if (tree_count(&hs->deferred) >= env->sc_mta_max_deferred)
- return;
-
- tree_set(&hs->deferred, evpid, NULL);
-}
-
-void
-mta_hoststat_uncache(const char *host, uint64_t evpid)
-{
- struct hoststat *hs = NULL;
- char buf[HOST_NAME_MAX+1];
-
- if (!lowercase(buf, host, sizeof buf))
- return;
-
- hs = dict_get(&hoststat, buf);
- if (hs == NULL)
- return;
-
- tree_pop(&hs->deferred, evpid);
-}
-
-void
-mta_hoststat_reschedule(const char *host)
-{
- struct hoststat *hs = NULL;
- char buf[HOST_NAME_MAX+1];
- uint64_t evpid;
-
- if (!lowercase(buf, host, sizeof buf))
- return;
-
- hs = dict_get(&hoststat, buf);
- if (hs == NULL)
- return;
-
- while (tree_poproot(&hs->deferred, &evpid, NULL)) {
- m_compose(p_queue, IMSG_MTA_SCHEDULE, 0, 0, -1,
- &evpid, sizeof evpid);
- }
-}
-
-static void
-mta_hoststat_remove_entry(struct hoststat *hs)
-{
- while (tree_poproot(&hs->deferred, NULL, NULL))
- ;
- dict_pop(&hoststat, hs->name);
- runq_cancel(runq_hoststat, hs);
-}
diff --git a/smtpd/mta_session.c b/smtpd/mta_session.c
deleted file mode 100644
index ad1c3c84..00000000
--- a/smtpd/mta_session.c
+++ /dev/null
@@ -1,2008 +0,0 @@
-/* $OpenBSD: mta_session.c,v 1.136 2020/05/21 15:38:05 millert Exp $ */
-
-/*
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <netdb.h>
-#include <openssl/ssl.h>
-#include <pwd.h>
-#include <resolv.h>
-#include <limits.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-#include "ssl.h"
-
-#define MAX_TRYBEFOREDISABLE 10
-
-#define MTA_HIWAT 65535
-
-enum mta_state {
- MTA_INIT,
- MTA_BANNER,
- MTA_EHLO,
- MTA_HELO,
- MTA_LHLO,
- MTA_STARTTLS,
- MTA_AUTH,
- MTA_AUTH_PLAIN,
- MTA_AUTH_LOGIN,
- MTA_AUTH_LOGIN_USER,
- MTA_AUTH_LOGIN_PASS,
- MTA_READY,
- MTA_MAIL,
- MTA_RCPT,
- MTA_DATA,
- MTA_BODY,
- MTA_EOM,
- MTA_LMTP_EOM,
- MTA_RSET,
- MTA_QUIT,
-};
-
-#define MTA_FORCE_ANYSSL 0x0001
-#define MTA_FORCE_SMTPS 0x0002
-#define MTA_FORCE_TLS 0x0004
-#define MTA_FORCE_PLAIN 0x0008
-#define MTA_WANT_SECURE 0x0010
-#define MTA_DOWNGRADE_PLAIN 0x0080
-
-#define MTA_TLS 0x0100
-#define MTA_TLS_VERIFIED 0x0200
-
-#define MTA_FREE 0x0400
-#define MTA_LMTP 0x0800
-#define MTA_WAIT 0x1000
-#define MTA_HANGON 0x2000
-#define MTA_RECONN 0x4000
-
-#define MTA_EXT_STARTTLS 0x01
-#define MTA_EXT_PIPELINING 0x02
-#define MTA_EXT_AUTH 0x04
-#define MTA_EXT_AUTH_PLAIN 0x08
-#define MTA_EXT_AUTH_LOGIN 0x10
-#define MTA_EXT_SIZE 0x20
-
-struct mta_session {
- uint64_t id;
- struct mta_relay *relay;
- struct mta_route *route;
- char *helo;
- char *mxname;
-
- char *username;
-
- int flags;
-
- int attempt;
- int use_smtps;
- int use_starttls;
- int use_smtp_tls;
- int ready;
-
- struct event ev;
- struct io *io;
- int ext;
-
- size_t ext_size;
-
- size_t msgtried;
- size_t msgcount;
- size_t rcptcount;
- int hangon;
-
- enum mta_state state;
- struct mta_task *task;
- struct mta_envelope *currevp;
- FILE *datafp;
- size_t datalen;
-
- size_t failures;
-
- char replybuf[2048];
-};
-
-static void mta_session_init(void);
-static void mta_start(int fd, short ev, void *arg);
-static void mta_io(struct io *, int, void *);
-static void mta_free(struct mta_session *);
-static void mta_getnameinfo_cb(void *, int, const char *, const char *);
-static void mta_on_ptr(void *, void *, void *);
-static void mta_on_timeout(struct runq *, void *);
-static void mta_connect(struct mta_session *);
-static void mta_enter_state(struct mta_session *, int);
-static void mta_flush_task(struct mta_session *, int, const char *, size_t, int);
-static void mta_error(struct mta_session *, const char *, ...);
-static void mta_send(struct mta_session *, char *, ...);
-static ssize_t mta_queue_data(struct mta_session *);
-static void mta_response(struct mta_session *, char *);
-static const char * mta_strstate(int);
-static void mta_cert_init(struct mta_session *);
-static void mta_cert_init_cb(void *, int, const char *, const void *, size_t);
-static void mta_cert_verify(struct mta_session *);
-static void mta_cert_verify_cb(void *, int);
-static void mta_tls_verified(struct mta_session *);
-static struct mta_session *mta_tree_pop(struct tree *, uint64_t);
-static const char * dsn_strret(enum dsn_ret);
-static const char * dsn_strnotify(uint8_t);
-
-void mta_hoststat_update(const char *, const char *);
-void mta_hoststat_reschedule(const char *);
-void mta_hoststat_cache(const char *, uint64_t);
-void mta_hoststat_uncache(const char *, uint64_t);
-
-
-static void mta_filter_begin(struct mta_session *);
-static void mta_filter_end(struct mta_session *);
-static void mta_connected(struct mta_session *);
-static void mta_disconnected(struct mta_session *);
-
-static void mta_report_link_connect(struct mta_session *, const char *, int,
- const struct sockaddr_storage *,
- const struct sockaddr_storage *);
-static void mta_report_link_greeting(struct mta_session *, const char *);
-static void mta_report_link_identify(struct mta_session *, const char *, const char *);
-static void mta_report_link_tls(struct mta_session *, const char *);
-static void mta_report_link_disconnect(struct mta_session *);
-static void mta_report_link_auth(struct mta_session *, const char *, const char *);
-static void mta_report_tx_reset(struct mta_session *, uint32_t);
-static void mta_report_tx_begin(struct mta_session *, uint32_t);
-static void mta_report_tx_mail(struct mta_session *, uint32_t, const char *, int);
-static void mta_report_tx_rcpt(struct mta_session *, uint32_t, const char *, int);
-static void mta_report_tx_envelope(struct mta_session *, uint32_t, uint64_t);
-static void mta_report_tx_data(struct mta_session *, uint32_t, int);
-static void mta_report_tx_commit(struct mta_session *, uint32_t, size_t);
-static void mta_report_tx_rollback(struct mta_session *, uint32_t);
-static void mta_report_protocol_client(struct mta_session *, const char *);
-static void mta_report_protocol_server(struct mta_session *, const char *);
-#if 0
-static void mta_report_filter_response(struct mta_session *, int, int, const char *);
-#endif
-static void mta_report_timeout(struct mta_session *);
-
-
-static struct tree wait_helo;
-static struct tree wait_ptr;
-static struct tree wait_fd;
-static struct tree wait_tls_init;
-static struct tree wait_tls_verify;
-
-static struct runq *hangon;
-
-#define SESSION_FILTERED(s) \
- ((s)->relay->dispatcher->u.remote.filtername)
-
-static void
-mta_session_init(void)
-{
- static int init = 0;
-
- if (!init) {
- tree_init(&wait_helo);
- tree_init(&wait_ptr);
- tree_init(&wait_fd);
- tree_init(&wait_tls_init);
- tree_init(&wait_tls_verify);
- runq_init(&hangon, mta_on_timeout);
- init = 1;
- }
-}
-
-void
-mta_session(struct mta_relay *relay, struct mta_route *route, const char *mxname)
-{
- struct mta_session *s;
- struct timeval tv;
-
- mta_session_init();
-
- s = xcalloc(1, sizeof *s);
- s->id = generate_uid();
- s->relay = relay;
- s->route = route;
- s->mxname = xstrdup(mxname);
-
- mta_filter_begin(s);
-
- if (relay->flags & RELAY_LMTP)
- s->flags |= MTA_LMTP;
- switch (relay->tls) {
- case RELAY_TLS_SMTPS:
- s->flags |= MTA_FORCE_SMTPS;
- s->flags |= MTA_WANT_SECURE;
- break;
- case RELAY_TLS_STARTTLS:
- s->flags |= MTA_FORCE_TLS;
- s->flags |= MTA_WANT_SECURE;
- break;
- case RELAY_TLS_OPPORTUNISTIC:
- /* do not force anything, try tls then smtp */
- break;
- case RELAY_TLS_NO:
- s->flags |= MTA_FORCE_PLAIN;
- break;
- default:
- fatalx("bad value for relay->tls: %d", relay->tls);
- }
-
- log_debug("debug: mta: %p: spawned for relay %s", s,
- mta_relay_to_text(relay));
- stat_increment("mta.session", 1);
-
- if (route->dst->ptrname || route->dst->lastptrquery) {
- /* We want to delay the connection since to always notify
- * the relay asynchronously.
- */
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- evtimer_set(&s->ev, mta_start, s);
- evtimer_add(&s->ev, &tv);
- } else if (waitq_wait(&route->dst->ptrname, mta_on_ptr, s)) {
- resolver_getnameinfo(s->route->dst->sa, 0, mta_getnameinfo_cb, s);
- }
-}
-
-void
-mta_session_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct mta_session *s;
- struct msg m;
- uint64_t reqid;
- const char *name;
- int status;
- struct stat sb;
-
- switch (imsg->hdr.type) {
-
- case IMSG_MTA_OPEN_MESSAGE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_end(&m);
-
- s = mta_tree_pop(&wait_fd, reqid);
- if (s == NULL) {
- if (imsg->fd != -1)
- close(imsg->fd);
- return;
- }
-
- if (imsg->fd == -1) {
- log_debug("debug: mta: failed to obtain msg fd");
- mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL,
- "Could not get message fd", 0, 0);
- mta_enter_state(s, MTA_READY);
- return;
- }
-
- if ((s->ext & MTA_EXT_SIZE) && s->ext_size != 0) {
- if (fstat(imsg->fd, &sb) == -1) {
- log_debug("debug: mta: failed to stat msg fd");
- mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL,
- "Could not stat message fd", 0, 0);
- mta_enter_state(s, MTA_READY);
- close(imsg->fd);
- return;
- }
- if (sb.st_size > (off_t)s->ext_size) {
- log_debug("debug: mta: message too large for peer");
- mta_flush_task(s, IMSG_MTA_DELIVERY_PERMFAIL,
- "message too large for peer", 0, 0);
- mta_enter_state(s, MTA_READY);
- close(imsg->fd);
- return;
- }
- }
-
- s->datafp = fdopen(imsg->fd, "r");
- if (s->datafp == NULL)
- fatal("mta: fdopen");
-
- mta_enter_state(s, MTA_MAIL);
- return;
-
- case IMSG_MTA_LOOKUP_HELO:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &status);
- if (status == LKA_OK)
- m_get_string(&m, &name);
- m_end(&m);
-
- s = mta_tree_pop(&wait_helo, reqid);
- if (s == NULL)
- return;
-
- if (status == LKA_OK) {
- s->helo = xstrdup(name);
- mta_connect(s);
- } else {
- mta_source_error(s->relay, s->route,
- "Failed to retrieve helo string");
- mta_free(s);
- }
- return;
-
- default:
- errx(1, "mta_session_imsg: unexpected %s imsg",
- imsg_to_str(imsg->hdr.type));
- }
-}
-
-static struct mta_session *
-mta_tree_pop(struct tree *wait, uint64_t reqid)
-{
- struct mta_session *s;
-
- s = tree_xpop(wait, reqid);
- if (s->flags & MTA_FREE) {
- log_debug("debug: mta: %p: zombie session", s);
- mta_free(s);
- return (NULL);
- }
- s->flags &= ~MTA_WAIT;
-
- return (s);
-}
-
-static void
-mta_free(struct mta_session *s)
-{
- struct mta_relay *relay;
- struct mta_route *route;
-
- log_debug("debug: mta: %p: session done", s);
-
- mta_disconnected(s);
-
- if (s->ready)
- s->relay->nconn_ready -= 1;
-
- if (s->flags & MTA_HANGON) {
- log_debug("debug: mta: %p: cancelling hangon timer", s);
- runq_cancel(hangon, s);
- }
-
- if (s->io)
- io_free(s->io);
-
- if (s->task)
- fatalx("current task should have been deleted already");
- if (s->datafp) {
- fclose(s->datafp);
- s->datalen = 0;
- }
- free(s->helo);
-
- relay = s->relay;
- route = s->route;
- free(s->username);
- free(s->mxname);
- free(s);
- stat_decrement("mta.session", 1);
- mta_route_collect(relay, route);
-}
-
-static void
-mta_getnameinfo_cb(void *arg, int gaierrno, const char *host, const char *serv)
-{
- struct mta_session *s = arg;
- struct mta_host *h;
-
- h = s->route->dst;
- h->lastptrquery = time(NULL);
- if (host)
- h->ptrname = xstrdup(host);
- waitq_run(&h->ptrname, h->ptrname);
-}
-
-static void
-mta_on_timeout(struct runq *runq, void *arg)
-{
- struct mta_session *s = arg;
-
- log_debug("mta: timeout for session hangon");
-
- s->flags &= ~MTA_HANGON;
- s->hangon++;
-
- mta_enter_state(s, MTA_READY);
-}
-
-static void
-mta_on_ptr(void *tag, void *arg, void *data)
-{
- struct mta_session *s = arg;
-
- mta_connect(s);
-}
-
-static void
-mta_start(int fd, short ev, void *arg)
-{
- struct mta_session *s = arg;
-
- mta_connect(s);
-}
-
-static void
-mta_connect(struct mta_session *s)
-{
- struct sockaddr_storage ss;
- struct sockaddr *sa;
- int portno;
- const char *schema;
-
- if (s->helo == NULL) {
- if (s->relay->helotable && s->route->src->sa) {
- m_create(p_lka, IMSG_MTA_LOOKUP_HELO, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_string(p_lka, s->relay->helotable);
- m_add_sockaddr(p_lka, s->route->src->sa);
- m_close(p_lka);
- tree_xset(&wait_helo, s->id, s);
- s->flags |= MTA_WAIT;
- return;
- }
- else if (s->relay->heloname)
- s->helo = xstrdup(s->relay->heloname);
- else
- s->helo = xstrdup(env->sc_hostname);
- }
-
- if (s->io) {
- io_free(s->io);
- s->io = NULL;
- }
-
- s->use_smtps = s->use_starttls = s->use_smtp_tls = 0;
-
- switch (s->attempt) {
- case 0:
- if (s->flags & MTA_FORCE_SMTPS)
- s->use_smtps = 1; /* smtps */
- else if (s->flags & (MTA_FORCE_TLS|MTA_FORCE_ANYSSL))
- s->use_starttls = 1; /* tls, tls+smtps */
- else if (!(s->flags & MTA_FORCE_PLAIN))
- s->use_smtp_tls = 1;
- break;
- case 1:
- if (s->flags & MTA_FORCE_ANYSSL) {
- s->use_smtps = 1; /* tls+smtps */
- break;
- }
- else if (s->flags & MTA_DOWNGRADE_PLAIN) {
- /* smtp, with tls failure */
- break;
- }
- default:
- mta_free(s);
- return;
- }
- portno = s->use_smtps ? 465 : 25;
-
- /* Override with relay-specified port */
- if (s->relay->port)
- portno = s->relay->port;
-
- memmove(&ss, s->route->dst->sa, SA_LEN(s->route->dst->sa));
- sa = (struct sockaddr *)&ss;
-
- if (sa->sa_family == AF_INET)
- ((struct sockaddr_in *)sa)->sin_port = htons(portno);
- else if (sa->sa_family == AF_INET6)
- ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno);
-
- s->attempt += 1;
- if (s->use_smtp_tls)
- schema = "smtp://";
- else if (s->use_starttls)
- schema = "smtp+tls://";
- else if (s->use_smtps)
- schema = "smtps://";
- else if (s->flags & MTA_LMTP)
- schema = "lmtp://";
- else
- schema = "smtp+notls://";
-
- log_info("%016"PRIx64" mta "
- "connecting address=%s%s:%d host=%s",
- s->id, schema, sa_to_text(s->route->dst->sa),
- portno, s->route->dst->ptrname);
-
- mta_enter_state(s, MTA_INIT);
- s->io = io_new();
- io_set_callback(s->io, mta_io, s);
- io_set_timeout(s->io, 300000);
- if (io_connect(s->io, sa, s->route->src->sa) == -1) {
- /*
- * This error is most likely a "no route",
- * so there is no need to try again.
- */
- log_debug("debug: mta: io_connect failed: %s", io_error(s->io));
- if (errno == EADDRNOTAVAIL)
- mta_source_error(s->relay, s->route, io_error(s->io));
- else
- mta_error(s, "Connection failed: %s", io_error(s->io));
- mta_free(s);
- }
-}
-
-static void
-mta_enter_state(struct mta_session *s, int newstate)
-{
- struct mta_envelope *e;
- size_t envid_sz;
- int oldstate;
- ssize_t q;
- char ibuf[LINE_MAX];
- char obuf[LINE_MAX];
- int offset;
- const char *srs_sender;
-
-again:
- oldstate = s->state;
-
- log_trace(TRACE_MTA, "mta: %p: %s -> %s", s,
- mta_strstate(oldstate),
- mta_strstate(newstate));
-
- s->state = newstate;
-
- memset(s->replybuf, 0, sizeof s->replybuf);
-
- /* don't try this at home! */
-#define mta_enter_state(_s, _st) do { newstate = _st; goto again; } while (0)
-
- switch (s->state) {
- case MTA_INIT:
- case MTA_BANNER:
- break;
-
- case MTA_EHLO:
- s->ext = 0;
- mta_send(s, "EHLO %s", s->helo);
- mta_report_link_identify(s, "EHLO", s->helo);
- break;
-
- case MTA_HELO:
- s->ext = 0;
- mta_send(s, "HELO %s", s->helo);
- mta_report_link_identify(s, "HELO", s->helo);
- break;
-
- case MTA_LHLO:
- s->ext = 0;
- mta_send(s, "LHLO %s", s->helo);
- mta_report_link_identify(s, "LHLO", s->helo);
- break;
-
- case MTA_STARTTLS:
- if (s->flags & MTA_DOWNGRADE_PLAIN)
- mta_enter_state(s, MTA_AUTH);
- if (s->flags & MTA_TLS) /* already started */
- mta_enter_state(s, MTA_AUTH);
- else if ((s->ext & MTA_EXT_STARTTLS) == 0) {
- if (s->flags & MTA_FORCE_TLS || s->flags & MTA_WANT_SECURE) {
- mta_error(s, "TLS required but not supported by remote host");
- s->flags |= MTA_RECONN;
- }
- else
- /* server doesn't support starttls, do not use it */
- mta_enter_state(s, MTA_AUTH);
- }
- else
- mta_send(s, "STARTTLS");
- break;
-
- case MTA_AUTH:
- if (s->relay->secret && s->flags & MTA_TLS) {
- if (s->ext & MTA_EXT_AUTH) {
- if (s->ext & MTA_EXT_AUTH_PLAIN) {
- mta_enter_state(s, MTA_AUTH_PLAIN);
- break;
- }
- if (s->ext & MTA_EXT_AUTH_LOGIN) {
- mta_enter_state(s, MTA_AUTH_LOGIN);
- break;
- }
- log_debug("debug: mta: %p: no supported AUTH method on session", s);
- mta_error(s, "no supported AUTH method");
- }
- else {
- log_debug("debug: mta: %p: AUTH not advertised on session", s);
- mta_error(s, "AUTH not advertised");
- }
- }
- else if (s->relay->secret) {
- log_debug("debug: mta: %p: not using AUTH on non-TLS "
- "session", s);
- mta_error(s, "Refuse to AUTH over unsecure channel");
- mta_connect(s);
- } else {
- mta_enter_state(s, MTA_READY);
- }
- break;
-
- case MTA_AUTH_PLAIN:
- memset(ibuf, 0, sizeof ibuf);
- if (base64_decode(s->relay->secret, (unsigned char *)ibuf,
- sizeof(ibuf)-1) == -1) {
- log_debug("debug: mta: %p: credentials too large on session", s);
- mta_error(s, "Credentials too large");
- break;
- }
- s->username = xstrdup(ibuf+1);
- mta_send(s, "AUTH PLAIN %s", s->relay->secret);
- break;
-
- case MTA_AUTH_LOGIN:
- mta_send(s, "AUTH LOGIN");
- break;
-
- case MTA_AUTH_LOGIN_USER:
- memset(ibuf, 0, sizeof ibuf);
- if (base64_decode(s->relay->secret, (unsigned char *)ibuf,
- sizeof(ibuf)-1) == -1) {
- log_debug("debug: mta: %p: credentials too large on session", s);
- mta_error(s, "Credentials too large");
- break;
- }
- s->username = xstrdup(ibuf+1);
-
- memset(obuf, 0, sizeof obuf);
- base64_encode((unsigned char *)ibuf + 1, strlen(ibuf + 1), obuf, sizeof obuf);
- mta_send(s, "%s", obuf);
-
- memset(ibuf, 0, sizeof ibuf);
- memset(obuf, 0, sizeof obuf);
- break;
-
- case MTA_AUTH_LOGIN_PASS:
- memset(ibuf, 0, sizeof ibuf);
- if (base64_decode(s->relay->secret, (unsigned char *)ibuf,
- sizeof(ibuf)-1) == -1) {
- log_debug("debug: mta: %p: credentials too large on session", s);
- mta_error(s, "Credentials too large");
- break;
- }
-
- offset = strlen(ibuf+1)+2;
- memset(obuf, 0, sizeof obuf);
- base64_encode((unsigned char *)ibuf + offset, strlen(ibuf + offset), obuf, sizeof obuf);
- mta_send(s, "%s", obuf);
-
- memset(ibuf, 0, sizeof ibuf);
- memset(obuf, 0, sizeof obuf);
- break;
-
- case MTA_READY:
- /* Ready to send a new mail */
- if (s->ready == 0) {
- s->ready = 1;
- s->relay->nconn_ready += 1;
- mta_route_ok(s->relay, s->route);
- }
-
- if (s->msgtried >= MAX_TRYBEFOREDISABLE) {
- log_info("%016"PRIx64" mta host-rejects-all-mails",
- s->id);
- mta_route_down(s->relay, s->route);
- mta_enter_state(s, MTA_QUIT);
- break;
- }
-
- if (s->msgcount >= s->relay->limits->max_mail_per_session) {
- log_debug("debug: mta: "
- "%p: cannot send more message to relay %s", s,
- mta_relay_to_text(s->relay));
- mta_enter_state(s, MTA_QUIT);
- break;
- }
-
- /*
- * When downgrading from opportunistic TLS, clear flag and
- * possibly reuse the same task (forbidden in other cases).
- */
- if (s->flags & MTA_DOWNGRADE_PLAIN)
- s->flags &= ~MTA_DOWNGRADE_PLAIN;
- else if (s->task)
- fatalx("task should be NULL at this point");
-
- if (s->task == NULL)
- s->task = mta_route_next_task(s->relay, s->route);
- if (s->task == NULL) {
- log_debug("debug: mta: %p: no task for relay %s",
- s, mta_relay_to_text(s->relay));
-
- if (s->relay->nconn > 1 ||
- s->hangon >= s->relay->limits->sessdelay_keepalive) {
- mta_enter_state(s, MTA_QUIT);
- break;
- }
-
- log_debug("mta: debug: last connection: hanging on for %llds",
- (long long)(s->relay->limits->sessdelay_keepalive -
- s->hangon));
- s->flags |= MTA_HANGON;
- runq_schedule(hangon, 1, s);
- break;
- }
-
- log_debug("debug: mta: %p: handling next task for relay %s", s,
- mta_relay_to_text(s->relay));
-
- stat_increment("mta.task.running", 1);
-
- m_create(p_queue, IMSG_MTA_OPEN_MESSAGE, 0, 0, -1);
- m_add_id(p_queue, s->id);
- m_add_msgid(p_queue, s->task->msgid);
- m_close(p_queue);
-
- tree_xset(&wait_fd, s->id, s);
- s->flags |= MTA_WAIT;
- break;
-
- case MTA_MAIL:
- s->currevp = TAILQ_FIRST(&s->task->envelopes);
-
- e = s->currevp;
- s->hangon = 0;
- s->msgtried++;
- envid_sz = strlen(e->dsn_envid);
-
- /* SRS-encode if requested for the relay action, AND we're not
- * bouncing, AND we have an RCPT which means we are forwarded,
- * AND the RCPT has a '@' just for sanity check (will always).
- */
- if (env->sc_srs_key != NULL &&
- s->relay->srs &&
- strchr(s->task->sender, '@') &&
- e->rcpt &&
- strchr(e->rcpt, '@')) {
- /* encode and replace task sender with new SRS-sender */
- srs_sender = srs_encode(s->task->sender,
- strchr(e->rcpt, '@') + 1);
- if (srs_sender) {
- free(s->task->sender);
- s->task->sender = xstrdup(srs_sender);
- }
- }
-
- if (s->ext & MTA_EXT_DSN) {
- mta_send(s, "MAIL FROM:<%s>%s%s%s%s",
- s->task->sender,
- e->dsn_ret ? " RET=" : "",
- e->dsn_ret ? dsn_strret(e->dsn_ret) : "",
- envid_sz ? " ENVID=" : "",
- envid_sz ? e->dsn_envid : "");
- } else
- mta_send(s, "MAIL FROM:<%s>", s->task->sender);
- break;
-
- case MTA_RCPT:
- if (s->currevp == NULL)
- s->currevp = TAILQ_FIRST(&s->task->envelopes);
-
- e = s->currevp;
- if (s->ext & MTA_EXT_DSN) {
- mta_send(s, "RCPT TO:<%s>%s%s%s%s",
- e->dest,
- e->dsn_notify ? " NOTIFY=" : "",
- e->dsn_notify ? dsn_strnotify(e->dsn_notify) : "",
- e->dsn_orcpt ? " ORCPT=rfc822;" : "",
- e->dsn_orcpt ? e->dsn_orcpt : "");
- } else
- mta_send(s, "RCPT TO:<%s>", e->dest);
-
- mta_report_tx_envelope(s, s->task->msgid, e->id);
- s->rcptcount++;
- break;
-
- case MTA_DATA:
- fseek(s->datafp, 0, SEEK_SET);
- mta_send(s, "DATA");
- break;
-
- case MTA_BODY:
- if (s->datafp == NULL) {
- log_trace(TRACE_MTA, "mta: %p: end-of-file", s);
- mta_enter_state(s, MTA_EOM);
- break;
- }
-
- if ((q = mta_queue_data(s)) == -1) {
- s->flags |= MTA_FREE;
- break;
- }
- if (q == 0) {
- mta_enter_state(s, MTA_BODY);
- break;
- }
-
- log_trace(TRACE_MTA, "mta: %p: >>> [...%zd bytes...]", s, q);
- break;
-
- case MTA_EOM:
- mta_send(s, ".");
- break;
-
- case MTA_LMTP_EOM:
- /* LMTP reports status of each delivery, so enable read */
- io_set_read(s->io);
- break;
-
- case MTA_RSET:
- if (s->datafp) {
- fclose(s->datafp);
- s->datafp = NULL;
- s->datalen = 0;
- }
- mta_send(s, "RSET");
- break;
-
- case MTA_QUIT:
- mta_send(s, "QUIT");
- break;
-
- default:
- fatalx("mta_enter_state: unknown state");
- }
-#undef mta_enter_state
-}
-
-/*
- * Handle a response to an SMTP command
- */
-static void
-mta_response(struct mta_session *s, char *line)
-{
- struct mta_envelope *e;
- struct sockaddr_storage ss;
- struct sockaddr *sa;
- const char *domain;
- char *pbuf;
- socklen_t sa_len;
- char buf[LINE_MAX];
- int delivery;
-
- switch (s->state) {
-
- case MTA_BANNER:
- if (line[0] != '2') {
- mta_error(s, "BANNER rejected: %s", line);
- s->flags |= MTA_FREE;
- return;
- }
-
- pbuf = "";
- if (strlen(line) > 4) {
- (void)strlcpy(buf, line + 4, sizeof buf);
- if ((pbuf = strchr(buf, ' ')))
- *pbuf = '\0';
- pbuf = valid_domainpart(buf) ? buf : "";
- }
- mta_report_link_greeting(s, pbuf);
-
- if (s->flags & MTA_LMTP)
- mta_enter_state(s, MTA_LHLO);
- else
- mta_enter_state(s, MTA_EHLO);
- break;
-
- case MTA_EHLO:
- if (line[0] != '2') {
- /* rejected at ehlo state */
- if ((s->relay->flags & RELAY_AUTH) ||
- (s->flags & MTA_WANT_SECURE)) {
- mta_error(s, "EHLO rejected: %s", line);
- s->flags |= MTA_FREE;
- return;
- }
- mta_enter_state(s, MTA_HELO);
- return;
- }
- if (!(s->flags & MTA_FORCE_PLAIN))
- mta_enter_state(s, MTA_STARTTLS);
- else
- mta_enter_state(s, MTA_READY);
- break;
-
- case MTA_HELO:
- if (line[0] != '2') {
- mta_error(s, "HELO rejected: %s", line);
- s->flags |= MTA_FREE;
- return;
- }
- mta_enter_state(s, MTA_READY);
- break;
-
- case MTA_LHLO:
- if (line[0] != '2') {
- mta_error(s, "LHLO rejected: %s", line);
- s->flags |= MTA_FREE;
- return;
- }
- mta_enter_state(s, MTA_READY);
- break;
-
- case MTA_STARTTLS:
- if (line[0] != '2') {
- if (!(s->flags & MTA_WANT_SECURE)) {
- mta_enter_state(s, MTA_AUTH);
- return;
- }
- /* XXX mark that the MX doesn't support STARTTLS */
- mta_error(s, "STARTTLS rejected: %s", line);
- s->flags |= MTA_FREE;
- return;
- }
-
- mta_cert_init(s);
- break;
-
- case MTA_AUTH_PLAIN:
- if (line[0] != '2') {
- mta_error(s, "AUTH rejected: %s", line);
- mta_report_link_auth(s, s->username, "fail");
- s->flags |= MTA_FREE;
- return;
- }
- mta_report_link_auth(s, s->username, "pass");
- mta_enter_state(s, MTA_READY);
- break;
-
- case MTA_AUTH_LOGIN:
- if (strncmp(line, "334 ", 4) != 0) {
- mta_error(s, "AUTH rejected: %s", line);
- mta_report_link_auth(s, s->username, "fail");
- s->flags |= MTA_FREE;
- return;
- }
- mta_enter_state(s, MTA_AUTH_LOGIN_USER);
- break;
-
- case MTA_AUTH_LOGIN_USER:
- if (strncmp(line, "334 ", 4) != 0) {
- mta_error(s, "AUTH rejected: %s", line);
- mta_report_link_auth(s, s->username, "fail");
- s->flags |= MTA_FREE;
- return;
- }
- mta_enter_state(s, MTA_AUTH_LOGIN_PASS);
- break;
-
- case MTA_AUTH_LOGIN_PASS:
- if (line[0] != '2') {
- mta_error(s, "AUTH rejected: %s", line);
- mta_report_link_auth(s, s->username, "fail");
- s->flags |= MTA_FREE;
- return;
- }
- mta_report_link_auth(s, s->username, "pass");
- mta_enter_state(s, MTA_READY);
- break;
-
- case MTA_MAIL:
- if (line[0] != '2') {
- if (line[0] == '5')
- delivery = IMSG_MTA_DELIVERY_PERMFAIL;
- else
- delivery = IMSG_MTA_DELIVERY_TEMPFAIL;
-
- mta_flush_task(s, delivery, line, 0, 0);
- mta_enter_state(s, MTA_RSET);
- return;
- }
- mta_report_tx_begin(s, s->task->msgid);
- mta_report_tx_mail(s, s->task->msgid, s->task->sender, 1);
- mta_enter_state(s, MTA_RCPT);
- break;
-
- case MTA_RCPT:
- e = s->currevp;
-
- /* remove envelope from hosttat cache if there */
- if ((domain = strchr(e->dest, '@')) != NULL) {
- domain++;
- mta_hoststat_uncache(domain, e->id);
- }
-
- s->currevp = TAILQ_NEXT(s->currevp, entry);
- if (line[0] == '2') {
- s->failures = 0;
- /*
- * this host is up, reschedule envelopes that
- * were cached for reschedule.
- */
- if (domain)
- mta_hoststat_reschedule(domain);
- }
- else {
- mta_report_tx_rollback(s, s->task->msgid);
- mta_report_tx_reset(s, s->task->msgid);
- if (line[0] == '5')
- delivery = IMSG_MTA_DELIVERY_PERMFAIL;
- else
- delivery = IMSG_MTA_DELIVERY_TEMPFAIL;
- s->failures++;
-
- /* remove failed envelope from task list */
- TAILQ_REMOVE(&s->task->envelopes, e, entry);
- stat_decrement("mta.envelope", 1);
-
- /* log right away */
- (void)snprintf(buf, sizeof(buf), "%s",
- mta_host_to_text(s->route->dst));
-
- e->session = s->id;
- /* XXX */
- /*
- * getsockname() can only fail with ENOBUFS here
- * best effort, don't log source ...
- */
- sa_len = sizeof(ss);
- sa = (struct sockaddr *)&ss;
- if (getsockname(io_fileno(s->io), sa, &sa_len) == -1)
- mta_delivery_log(e, NULL, buf, delivery, line);
- else
- mta_delivery_log(e, sa_to_text(sa),
- buf, delivery, line);
-
- if (domain)
- mta_hoststat_update(domain, e->status);
- mta_delivery_notify(e);
-
- if (s->relay->limits->max_failures_per_session &&
- s->failures == s->relay->limits->max_failures_per_session) {
- mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL,
- "Too many consecutive errors, closing connection", 0, 1);
- mta_enter_state(s, MTA_QUIT);
- break;
- }
-
- /*
- * if no more envelopes, flush failed queue
- */
- if (TAILQ_EMPTY(&s->task->envelopes)) {
- mta_flush_task(s, IMSG_MTA_DELIVERY_OK,
- "No envelope", 0, 0);
- mta_enter_state(s, MTA_RSET);
- break;
- }
- }
-
- switch (line[0]) {
- case '2':
- mta_report_tx_rcpt(s,
- s->task->msgid, e->dest, 1);
- break;
- case '4':
- mta_report_tx_rcpt(s,
- s->task->msgid, e->dest, -1);
- break;
- case '5':
- mta_report_tx_rcpt(s,
- s->task->msgid, e->dest, 0);
- break;
- }
-
- if (s->currevp == NULL)
- mta_enter_state(s, MTA_DATA);
- else
- mta_enter_state(s, MTA_RCPT);
- break;
-
- case MTA_DATA:
- if (line[0] == '2' || line[0] == '3') {
- mta_report_tx_data(s, s->task->msgid, 1);
- mta_enter_state(s, MTA_BODY);
- break;
- }
-
- if (line[0] == '5')
- delivery = IMSG_MTA_DELIVERY_PERMFAIL;
- else
- delivery = IMSG_MTA_DELIVERY_TEMPFAIL;
- mta_report_tx_data(s, s->task->msgid,
- delivery == IMSG_MTA_DELIVERY_TEMPFAIL ? -1 : 0);
- mta_report_tx_rollback(s, s->task->msgid);
- mta_report_tx_reset(s, s->task->msgid);
- mta_flush_task(s, delivery, line, 0, 0);
- mta_enter_state(s, MTA_RSET);
- break;
-
- case MTA_LMTP_EOM:
- case MTA_EOM:
- if (line[0] == '2') {
- delivery = IMSG_MTA_DELIVERY_OK;
- s->msgtried = 0;
- s->msgcount++;
- }
- else if (line[0] == '5')
- delivery = IMSG_MTA_DELIVERY_PERMFAIL;
- else
- delivery = IMSG_MTA_DELIVERY_TEMPFAIL;
- if (delivery != IMSG_MTA_DELIVERY_OK) {
- mta_report_tx_rollback(s, s->task->msgid);
- mta_report_tx_reset(s, s->task->msgid);
- }
- else {
- mta_report_tx_commit(s, s->task->msgid, s->datalen);
- mta_report_tx_reset(s, s->task->msgid);
- }
- mta_flush_task(s, delivery, line, (s->flags & MTA_LMTP) ? 1 : 0, 0);
- if (s->task) {
- s->rcptcount--;
- mta_enter_state(s, MTA_LMTP_EOM);
- } else {
- s->rcptcount = 0;
- if (s->relay->limits->sessdelay_transaction) {
- log_debug("debug: mta: waiting for %llds before next transaction",
- (long long int)s->relay->limits->sessdelay_transaction);
- s->hangon = s->relay->limits->sessdelay_transaction -1;
- s->flags |= MTA_HANGON;
- runq_schedule(hangon,
- s->relay->limits->sessdelay_transaction, s);
- }
- else
- mta_enter_state(s, MTA_READY);
- }
- break;
-
- case MTA_RSET:
- s->rcptcount = 0;
-
- if (s->task) {
- mta_report_tx_rollback(s, s->task->msgid);
- mta_report_tx_reset(s, s->task->msgid);
- }
- if (s->relay->limits->sessdelay_transaction) {
- log_debug("debug: mta: waiting for %llds after reset",
- (long long int)s->relay->limits->sessdelay_transaction);
- s->hangon = s->relay->limits->sessdelay_transaction -1;
- s->flags |= MTA_HANGON;
- runq_schedule(hangon,
- s->relay->limits->sessdelay_transaction, s);
- }
- else
- mta_enter_state(s, MTA_READY);
- break;
-
- default:
- fatalx("mta_response() bad state");
- }
-}
-
-static void
-mta_io(struct io *io, int evt, void *arg)
-{
- struct mta_session *s = arg;
- char *line, *msg, *p;
- size_t len;
- const char *error;
- int cont;
-
- log_trace(TRACE_IO, "mta: %p: %s %s", s, io_strevent(evt),
- io_strio(io));
-
- switch (evt) {
-
- case IO_CONNECTED:
- mta_connected(s);
-
- if (s->use_smtps) {
- io_set_write(io);
- mta_cert_init(s);
- }
- else {
- mta_enter_state(s, MTA_BANNER);
- io_set_read(io);
- }
- break;
-
- case IO_TLSREADY:
- log_info("%016"PRIx64" mta tls ciphers=%s",
- s->id, ssl_to_text(io_tls(s->io)));
- s->flags |= MTA_TLS;
-
- mta_report_link_tls(s,
- ssl_to_text(io_tls(s->io)));
-
- mta_cert_verify(s);
- break;
-
- case IO_DATAIN:
- nextline:
- line = io_getline(s->io, &len);
- if (line == NULL) {
- if (io_datalen(s->io) >= LINE_MAX) {
- mta_error(s, "Input too long");
- mta_free(s);
- }
- return;
- }
-
- /* Strip trailing '\r' */
- if (len && line[len - 1] == '\r')
- line[--len] = '\0';
-
- log_trace(TRACE_MTA, "mta: %p: <<< %s", s, line);
- mta_report_protocol_server(s, line);
-
- if ((error = parse_smtp_response(line, len, &msg, &cont))) {
- mta_error(s, "Bad response: %s", error);
- mta_free(s);
- return;
- }
-
- /* read extensions */
- if (s->state == MTA_EHLO) {
- if (strcmp(msg, "STARTTLS") == 0)
- s->ext |= MTA_EXT_STARTTLS;
- else if (strncmp(msg, "AUTH ", 5) == 0) {
- s->ext |= MTA_EXT_AUTH;
- if ((p = strstr(msg, " PLAIN")) &&
- (*(p+6) == '\0' || *(p+6) == ' '))
- s->ext |= MTA_EXT_AUTH_PLAIN;
- if ((p = strstr(msg, " LOGIN")) &&
- (*(p+6) == '\0' || *(p+6) == ' '))
- s->ext |= MTA_EXT_AUTH_LOGIN;
- }
- else if (strcmp(msg, "PIPELINING") == 0)
- s->ext |= MTA_EXT_PIPELINING;
- else if (strcmp(msg, "DSN") == 0)
- s->ext |= MTA_EXT_DSN;
- else if (strncmp(msg, "SIZE ", 5) == 0) {
- s->ext_size = strtonum(msg+5, 0, UINT32_MAX, &error);
- if (error == NULL)
- s->ext |= MTA_EXT_SIZE;
- }
- }
-
- /* continuation reply, we parse out the repeating statuses and ESC */
- if (cont) {
- if (s->replybuf[0] == '\0')
- (void)strlcat(s->replybuf, line, sizeof s->replybuf);
- else if (len > 4) {
- p = line + 4;
- if (isdigit((unsigned char)p[0]) && p[1] == '.' &&
- isdigit((unsigned char)p[2]) && p[3] == '.' &&
- isdigit((unsigned char)p[4]) && isspace((unsigned char)p[5]))
- p += 5;
- (void)strlcat(s->replybuf, p, sizeof s->replybuf);
- }
- goto nextline;
- }
-
- /* last line of a reply, check if we're on a continuation to parse out status and ESC.
- * if we overflow reply buffer or are not on continuation, log entire last line.
- */
- if (s->replybuf[0] == '\0')
- (void)strlcat(s->replybuf, line, sizeof s->replybuf);
- else if (len > 4) {
- p = line + 4;
- if (isdigit((unsigned char)p[0]) && p[1] == '.' &&
- isdigit((unsigned char)p[2]) && p[3] == '.' &&
- isdigit((unsigned char)p[4]) && isspace((unsigned char)p[5]))
- p += 5;
- if (strlcat(s->replybuf, p, sizeof s->replybuf) >= sizeof s->replybuf)
- (void)strlcpy(s->replybuf, line, sizeof s->replybuf);
- }
-
- if (s->state == MTA_QUIT) {
- log_info("%016"PRIx64" mta disconnected reason=quit messages=%zu",
- s->id, s->msgcount);
- mta_free(s);
- return;
- }
- io_set_write(io);
- mta_response(s, s->replybuf);
- if (s->flags & MTA_FREE) {
- mta_free(s);
- return;
- }
- if (s->flags & MTA_RECONN) {
- s->flags &= ~MTA_RECONN;
- mta_connect(s);
- return;
- }
-
- if (io_datalen(s->io)) {
- log_debug("debug: mta: remaining data in input buffer");
- mta_error(s, "Remote host sent too much data");
- if (s->flags & MTA_WAIT)
- s->flags |= MTA_FREE;
- else
- mta_free(s);
- }
- break;
-
- case IO_LOWAT:
- if (s->state == MTA_BODY) {
- mta_enter_state(s, MTA_BODY);
- if (s->flags & MTA_FREE) {
- mta_free(s);
- return;
- }
- }
-
- if (io_queued(s->io) == 0)
- io_set_read(io);
- break;
-
- case IO_TIMEOUT:
- log_debug("debug: mta: %p: connection timeout", s);
- mta_error(s, "Connection timeout");
- mta_report_timeout(s);
- if (!s->ready)
- mta_connect(s);
- else
- mta_free(s);
- break;
-
- case IO_ERROR:
- case IO_TLSERROR:
- log_debug("debug: mta: %p: IO error: %s", s, io_error(io));
-
- if (s->state == MTA_STARTTLS && s->use_smtp_tls) {
- /* error in non-strict SSL negotiation, downgrade to plain */
- log_info("smtp-out: Error on session %016"PRIx64
- ": opportunistic TLS failed, "
- "downgrading to plain", s->id);
- s->flags &= ~MTA_TLS;
- s->flags |= MTA_DOWNGRADE_PLAIN;
- mta_connect(s);
- break;
- }
-
- mta_error(s, "IO Error: %s", io_error(io));
- mta_free(s);
- break;
-
- case IO_DISCONNECTED:
- log_debug("debug: mta: %p: disconnected in state %s",
- s, mta_strstate(s->state));
- mta_error(s, "Connection closed unexpectedly");
- if (!s->ready)
- mta_connect(s);
- else
- mta_free(s);
- break;
-
- default:
- fatalx("mta_io() bad event");
- }
-}
-
-static void
-mta_send(struct mta_session *s, char *fmt, ...)
-{
- va_list ap;
- char *p;
- int len;
-
- va_start(ap, fmt);
- if ((len = vasprintf(&p, fmt, ap)) == -1)
- fatal("mta: vasprintf");
- va_end(ap);
-
- log_trace(TRACE_MTA, "mta: %p: >>> %s", s, p);
-
- if (strncasecmp(p, "AUTH PLAIN ", 11) == 0)
- mta_report_protocol_client(s, "AUTH PLAIN ********");
- else if (s->state == MTA_AUTH_LOGIN_USER || s->state == MTA_AUTH_LOGIN_PASS)
- mta_report_protocol_client(s, "********");
- else
- mta_report_protocol_client(s, p);
-
- io_xprintf(s->io, "%s\r\n", p);
-
- free(p);
-}
-
-/*
- * Queue some data into the input buffer
- */
-static ssize_t
-mta_queue_data(struct mta_session *s)
-{
- char *ln = NULL;
- size_t sz = 0, q;
- ssize_t len;
-
- q = io_queued(s->io);
-
- while (io_queued(s->io) < MTA_HIWAT) {
- if ((len = getline(&ln, &sz, s->datafp)) == -1)
- break;
- if (ln[len - 1] == '\n')
- ln[len - 1] = '\0';
- s->datalen += io_xprintf(s->io, "%s%s\r\n", *ln == '.' ? "." : "", ln);
- }
-
- free(ln);
- if (ferror(s->datafp)) {
- mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL,
- "Error reading content file", 0, 0);
- return (-1);
- }
-
- if (feof(s->datafp)) {
- fclose(s->datafp);
- s->datafp = NULL;
- }
-
- return (io_queued(s->io) - q);
-}
-
-static void
-mta_flush_task(struct mta_session *s, int delivery, const char *error, size_t count,
- int cache)
-{
- struct mta_envelope *e;
- char relay[LINE_MAX];
- size_t n;
- struct sockaddr_storage ss;
- struct sockaddr *sa;
- socklen_t sa_len;
- const char *domain;
-
- (void)snprintf(relay, sizeof relay, "%s", mta_host_to_text(s->route->dst));
- n = 0;
- while ((e = TAILQ_FIRST(&s->task->envelopes))) {
-
- if (count && n == count) {
- stat_decrement("mta.envelope", n);
- return;
- }
-
- TAILQ_REMOVE(&s->task->envelopes, e, entry);
-
- /* we're about to log, associate session to envelope */
- e->session = s->id;
- e->ext = s->ext;
-
- /* XXX */
- /*
- * getsockname() can only fail with ENOBUFS here
- * best effort, don't log source ...
- */
- sa = (struct sockaddr *)&ss;
- sa_len = sizeof(ss);
- if (getsockname(io_fileno(s->io), sa, &sa_len) == -1)
- mta_delivery_log(e, NULL, relay, delivery, error);
- else
- mta_delivery_log(e, sa_to_text(sa),
- relay, delivery, error);
-
- mta_delivery_notify(e);
-
- domain = strchr(e->dest, '@');
- if (domain) {
- domain++;
- mta_hoststat_update(domain, error);
- if (cache)
- mta_hoststat_cache(domain, e->id);
- }
-
- n++;
- }
-
- free(s->task->sender);
- free(s->task);
- s->task = NULL;
-
- if (s->datafp) {
- fclose(s->datafp);
- s->datafp = NULL;
- }
-
- stat_decrement("mta.envelope", n);
- stat_decrement("mta.task.running", 1);
- stat_decrement("mta.task", 1);
-}
-
-static void
-mta_error(struct mta_session *s, const char *fmt, ...)
-{
- va_list ap;
- char *error;
- int len;
-
- va_start(ap, fmt);
- if ((len = vasprintf(&error, fmt, ap)) == -1)
- fatal("mta: vasprintf");
- va_end(ap);
-
- if (s->msgcount)
- log_info("smtp-out: Error on session %016"PRIx64
- " after %zu message%s sent: %s", s->id, s->msgcount,
- (s->msgcount > 1) ? "s" : "", error);
- else
- log_info("%016"PRIx64" mta error reason=%s",
- s->id, error);
-
- /*
- * If not connected yet, and the error is not local, just ignore it
- * and try to reconnect.
- */
- if (s->state == MTA_INIT &&
- (errno == ETIMEDOUT || errno == ECONNREFUSED)) {
- log_debug("debug: mta: not reporting route error yet");
- free(error);
- return;
- }
-
- mta_route_error(s->relay, s->route);
-
- if (s->task)
- mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, error, 0, 0);
-
- free(error);
-}
-
-static void
-mta_cert_init(struct mta_session *s)
-{
- const char *name;
- int fallback;
-
- if (s->relay->pki_name) {
- name = s->relay->pki_name;
- fallback = 0;
- }
- else {
- name = s->helo;
- fallback = 1;
- }
-
- if (cert_init(name, fallback, mta_cert_init_cb, s)) {
- tree_xset(&wait_tls_init, s->id, s);
- s->flags |= MTA_WAIT;
- }
-}
-
-static void
-mta_cert_init_cb(void *arg, int status, const char *name, const void *cert,
- size_t cert_len)
-{
- struct mta_session *s = arg;
- void *ssl;
- char *xname = NULL, *xcert = NULL;
-
- if (s->flags & MTA_WAIT)
- mta_tree_pop(&wait_tls_init, s->id);
-
- if (status == CA_FAIL && s->relay->pki_name) {
- log_info("%016"PRIx64" mta closing reason=ca-failure", s->id);
- mta_free(s);
- return;
- }
-
- if (name)
- xname = xstrdup(name);
- if (cert)
- xcert = xmemdup(cert, cert_len);
- ssl = ssl_mta_init(xname, xcert, cert_len, env->sc_tls_ciphers);
- free(xname);
- free(xcert);
- if (ssl == NULL)
- fatal("mta: ssl_mta_init");
- io_start_tls(s->io, ssl);
-}
-
-static void
-mta_cert_verify(struct mta_session *s)
-{
- const char *name;
- int fallback;
-
- if (s->relay->ca_name) {
- name = s->relay->ca_name;
- fallback = 0;
- }
- else {
- name = s->helo;
- fallback = 1;
- }
-
- if (cert_verify(io_tls(s->io), name, fallback, mta_cert_verify_cb, s)) {
- tree_xset(&wait_tls_verify, s->id, s);
- io_pause(s->io, IO_IN);
- s->flags |= MTA_WAIT;
- }
-}
-
-static void
-mta_cert_verify_cb(void *arg, int status)
-{
- struct mta_session *s = arg;
- int match, resume = 0;
- X509 *cert;
-
- if (s->flags & MTA_WAIT) {
- mta_tree_pop(&wait_tls_verify, s->id);
- resume = 1;
- }
-
- if (status == CERT_OK) {
- cert = SSL_get_peer_certificate(io_tls(s->io));
- if (!cert)
- status = CERT_NOCERT;
- else {
- match = 0;
- (void)ssl_check_name(cert, s->mxname, &match);
- X509_free(cert);
- if (!match) {
- log_info("%016"PRIx64" mta "
- "ssl_check_name: no match for '%s' in cert",
- s->id, s->mxname);
- status = CERT_INVALID;
- }
- }
- }
-
- if (status == CERT_OK)
- s->flags |= MTA_TLS_VERIFIED;
- else if (s->relay->flags & RELAY_TLS_VERIFY) {
- errno = 0;
- mta_error(s, "SSL certificate check failed");
- mta_free(s);
- return;
- }
-
- mta_tls_verified(s);
- if (resume)
- io_resume(s->io, IO_IN);
-}
-
-static void
-mta_tls_verified(struct mta_session *s)
-{
- X509 *x;
-
- x = SSL_get_peer_certificate(io_tls(s->io));
- if (x) {
- log_info("%016"PRIx64" mta "
- "server-cert-check result=\"%s\"",
- s->id,
- (s->flags & MTA_TLS_VERIFIED) ? "success" : "failure");
- X509_free(x);
- }
-
- if (s->use_smtps) {
- mta_enter_state(s, MTA_BANNER);
- io_set_read(s->io);
- }
- else
- mta_enter_state(s, MTA_EHLO);
-}
-
-static const char *
-dsn_strret(enum dsn_ret ret)
-{
- if (ret == DSN_RETHDRS)
- return "HDRS";
- else if (ret == DSN_RETFULL)
- return "FULL";
- else {
- log_debug("mta: invalid ret %d", ret);
- return "???";
- }
-}
-
-static const char *
-dsn_strnotify(uint8_t arg)
-{
- static char buf[32];
- size_t sz;
-
- buf[0] = '\0';
- if (arg & DSN_SUCCESS)
- (void)strlcat(buf, "SUCCESS,", sizeof(buf));
-
- if (arg & DSN_FAILURE)
- (void)strlcat(buf, "FAILURE,", sizeof(buf));
-
- if (arg & DSN_DELAY)
- (void)strlcat(buf, "DELAY,", sizeof(buf));
-
- if (arg & DSN_NEVER)
- (void)strlcat(buf, "NEVER,", sizeof(buf));
-
- /* trim trailing comma */
- sz = strlen(buf);
- if (sz)
- buf[sz - 1] = '\0';
-
- return (buf);
-}
-
-#define CASE(x) case x : return #x
-
-static const char *
-mta_strstate(int state)
-{
- switch (state) {
- CASE(MTA_INIT);
- CASE(MTA_BANNER);
- CASE(MTA_EHLO);
- CASE(MTA_HELO);
- CASE(MTA_STARTTLS);
- CASE(MTA_AUTH);
- CASE(MTA_AUTH_PLAIN);
- CASE(MTA_AUTH_LOGIN);
- CASE(MTA_AUTH_LOGIN_USER);
- CASE(MTA_AUTH_LOGIN_PASS);
- CASE(MTA_READY);
- CASE(MTA_MAIL);
- CASE(MTA_RCPT);
- CASE(MTA_DATA);
- CASE(MTA_BODY);
- CASE(MTA_EOM);
- CASE(MTA_LMTP_EOM);
- CASE(MTA_RSET);
- CASE(MTA_QUIT);
- default:
- return "MTA_???";
- }
-}
-
-static void
-mta_filter_begin(struct mta_session *s)
-{
- if (!SESSION_FILTERED(s))
- return;
-
- m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_string(p_lka, s->relay->dispatcher->u.remote.filtername);
- m_close(p_lka);
-}
-
-static void
-mta_filter_end(struct mta_session *s)
-{
- if (!SESSION_FILTERED(s))
- return;
-
- m_create(p_lka, IMSG_FILTER_SMTP_END, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_close(p_lka);
-}
-
-static void
-mta_connected(struct mta_session *s)
-{
- struct sockaddr_storage sa_src;
- struct sockaddr_storage sa_dest;
- int sa_len;
-
- log_info("%016"PRIx64" mta connected", s->id);
-
- sa_len = sizeof sa_src;
- if (getsockname(io_fileno(s->io),
- (struct sockaddr *)&sa_src, &sa_len) == -1)
- bzero(&sa_src, sizeof sa_src);
- sa_len = sizeof sa_dest;
- if (getpeername(io_fileno(s->io),
- (struct sockaddr *)&sa_dest, &sa_len) == -1)
- bzero(&sa_dest, sizeof sa_dest);
-
- mta_report_link_connect(s,
- s->route->dst->ptrname, 1,
- &sa_src,
- &sa_dest);
-}
-
-static void
-mta_disconnected(struct mta_session *s)
-{
- mta_report_link_disconnect(s);
- mta_filter_end(s);
-}
-
-
-static void
-mta_report_link_connect(struct mta_session *s, const char *rdns, int fcrdns,
- const struct sockaddr_storage *ss_src,
- const struct sockaddr_storage *ss_dest)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_connect("smtp-out", s->id, rdns, fcrdns, ss_src, ss_dest);
-}
-
-static void
-mta_report_link_greeting(struct mta_session *s,
- const char *domain)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_greeting("smtp-out", s->id, domain);
-}
-
-static void
-mta_report_link_identify(struct mta_session *s, const char *method, const char *identity)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_identify("smtp-out", s->id, method, identity);
-}
-
-static void
-mta_report_link_tls(struct mta_session *s, const char *ssl)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_tls("smtp-out", s->id, ssl);
-}
-
-static void
-mta_report_link_disconnect(struct mta_session *s)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_disconnect("smtp-out", s->id);
-}
-
-static void
-mta_report_link_auth(struct mta_session *s, const char *user, const char *result)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_auth("smtp-out", s->id, user, result);
-}
-
-static void
-mta_report_tx_reset(struct mta_session *s, uint32_t msgid)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_reset("smtp-out", s->id, msgid);
-}
-
-static void
-mta_report_tx_begin(struct mta_session *s, uint32_t msgid)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_begin("smtp-out", s->id, msgid);
-}
-
-static void
-mta_report_tx_mail(struct mta_session *s, uint32_t msgid, const char *address, int ok)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_mail("smtp-out", s->id, msgid, address, ok);
-}
-
-static void
-mta_report_tx_rcpt(struct mta_session *s, uint32_t msgid, const char *address, int ok)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_rcpt("smtp-out", s->id, msgid, address, ok);
-}
-
-static void
-mta_report_tx_envelope(struct mta_session *s, uint32_t msgid, uint64_t evpid)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_envelope("smtp-out", s->id, msgid, evpid);
-}
-
-static void
-mta_report_tx_data(struct mta_session *s, uint32_t msgid, int ok)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_data("smtp-out", s->id, msgid, ok);
-}
-
-static void
-mta_report_tx_commit(struct mta_session *s, uint32_t msgid, size_t msgsz)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_commit("smtp-out", s->id, msgid, msgsz);
-}
-
-static void
-mta_report_tx_rollback(struct mta_session *s, uint32_t msgid)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_rollback("smtp-out", s->id, msgid);
-}
-
-static void
-mta_report_protocol_client(struct mta_session *s, const char *command)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_protocol_client("smtp-out", s->id, command);
-}
-
-static void
-mta_report_protocol_server(struct mta_session *s, const char *response)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_protocol_server("smtp-out", s->id, response);
-}
-
-#if 0
-static void
-mta_report_filter_response(struct mta_session *s, int phase, int response, const char *param)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_filter_response("smtp-out", s->id, phase, response, param);
-}
-#endif
-
-static void
-mta_report_timeout(struct mta_session *s)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_timeout("smtp-out", s->id);
-}
diff --git a/smtpd/newaliases.8 b/smtpd/newaliases.8
deleted file mode 100644
index b82e4515..00000000
--- a/smtpd/newaliases.8
+++ /dev/null
@@ -1,86 +0,0 @@
-.\" $OpenBSD: newaliases.8,v 1.12 2018/07/20 15:35:33 millert Exp $
-.\"
-.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@openbsd.org>
-.\" Copyright (c) 2008-2009 Gilles Chehade <gilles@poolp.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: July 20 2018 $
-.Dt NEWALIASES 8
-.Os
-.Sh NAME
-.Nm newaliases
-.Nd rebuild mail aliases
-.Sh SYNOPSIS
-.Nm newaliases
-.Op Fl f Ar file
-.Sh DESCRIPTION
-The
-.Nm
-utility makes changes to the mail aliases file visible to
-.Xr smtpd 8 .
-It should be run every time the
-.Xr aliases 5
-file is changed.
-The location of the alias file is defined in
-.Xr smtpd.conf 5 ,
-and defaults to
-.Pa /etc/mail/aliases .
-.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl f Ar file
-Use
-.Ar file
-as the configuration file,
-instead of the default
-.Pa /etc/mail/smtpd.conf .
-.El
-.Pp
-If using database (db) files,
-.Nm
-is equivalent to running
-.Xr makemap 8
-as follows:
-.Bd -literal -offset indent
-# makemap -t aliases /etc/mail/aliases
-.Ed
-.Pp
-If using plain text files,
-.Nm
-is equivalent to running
-.Xr smtpctl 8
-as follows:
-.Bd -literal -offset indent
-# smtpctl update table aliases
-.Ed
-.Sh FILES
-.Bl -tag -width "/etc/mail/aliasesXXX" -compact
-.It Pa /etc/mail/aliases
-List of local user mail aliases.
-.It Pa /etc/mail/virtual
-List of virtual host aliases.
-.El
-.Sh EXIT STATUS
-.Ex -std newaliases
-.Sh SEE ALSO
-.Xr smtpd.conf 5 ,
-.Xr makemap 8 ,
-.Xr smtpctl 8 ,
-.Xr smtpd 8
-.Sh HISTORY
-The
-.Nm
-command first appeared in
-.Ox 4.6
-as a replacement for the equivalent command shipped with sendmail.
diff --git a/smtpd/parse.y b/smtpd/parse.y
deleted file mode 100644
index db5be747..00000000
--- a/smtpd/parse.y
+++ /dev/null
@@ -1,3598 +0,0 @@
-/* $OpenBSD: parse.y,v 1.277 2020/02/24 23:54:27 millert Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
- * Copyright (c) 2001 Markus Friedl. All rights reserved.
- * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
- * Copyright (c) 2001 Theo de Raadt. All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-%{
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-
-#include <net/if.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <ifaddrs.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <resolv.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-#ifdef HAVE_UTIL_H
-#include <util.h>
-#endif
-
-#include <openssl/ssl.h>
-
-#include "smtpd.h"
-#include "ssl.h"
-#include "log.h"
-
-TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
-static struct file {
- TAILQ_ENTRY(file) entry;
- FILE *stream;
- char *name;
- size_t ungetpos;
- size_t ungetsize;
- u_char *ungetbuf;
- int eof_reached;
- int lineno;
- int errors;
-} *file, *topfile;
-struct file *pushfile(const char *, int);
-int popfile(void);
-int check_file_secrecy(int, const char *);
-int yyparse(void);
-int yylex(void);
-int kw_cmp(const void *, const void *);
-int lookup(char *);
-int igetc(void);
-int lgetc(int);
-void lungetc(int);
-int findeol(void);
-int yyerror(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)))
- __attribute__((__nonnull__ (1)));
-
-TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
-struct sym {
- TAILQ_ENTRY(sym) entry;
- int used;
- int persist;
- char *nam;
- char *val;
-};
-int symset(const char *, const char *, int);
-char *symget(const char *);
-
-struct smtpd *conf = NULL;
-static int errors = 0;
-
-struct table *table = NULL;
-struct mta_limits *limits;
-static struct pki *pki;
-static struct ca *sca;
-
-struct dispatcher *dispatcher;
-struct rule *rule;
-struct filter_proc *processor;
-struct filter_config *filter_config;
-static uint32_t last_dynchain_id = 1;
-
-enum listen_options {
- LO_FAMILY = 0x000001,
- LO_PORT = 0x000002,
- LO_SSL = 0x000004,
- LO_FILTER = 0x000008,
- LO_PKI = 0x000010,
- LO_AUTH = 0x000020,
- LO_TAG = 0x000040,
- LO_HOSTNAME = 0x000080,
- LO_HOSTNAMES = 0x000100,
- LO_MASKSOURCE = 0x000200,
- LO_NODSN = 0x000400,
- LO_SENDERS = 0x000800,
- LO_RECEIVEDAUTH = 0x001000,
- LO_MASQUERADE = 0x002000,
- LO_CA = 0x004000,
- LO_PROXY = 0x008000,
-};
-
-static struct listen_opts {
- char *ifx;
- int family;
- in_port_t port;
- uint16_t ssl;
- char *filtername;
- char *pki;
- char *ca;
- uint16_t auth;
- struct table *authtable;
- char *tag;
- char *hostname;
- struct table *hostnametable;
- struct table *sendertable;
- uint16_t flags;
-
- uint32_t options;
-} listen_opts;
-
-static void create_sock_listener(struct listen_opts *);
-static void create_if_listener(struct listen_opts *);
-static void config_listener(struct listener *, struct listen_opts *);
-static int host_v4(struct listen_opts *);
-static int host_v6(struct listen_opts *);
-static int host_dns(struct listen_opts *);
-static int interface(struct listen_opts *);
-
-int delaytonum(char *);
-int is_if_in_group(const char *, const char *);
-
-static int config_lo_mask_source(struct listen_opts *);
-
-typedef struct {
- union {
- int64_t number;
- struct table *table;
- char *string;
- struct host *host;
- struct mailaddr *maddr;
- } v;
- int lineno;
-} YYSTYPE;
-
-%}
-
-%token ACTION ALIAS ANY ARROW AUTH AUTH_OPTIONAL
-%token BACKUP BOUNCE BYPASS
-%token CA CERT CHAIN CHROOT CIPHERS COMMIT COMPRESSION CONNECT
-%token DATA DATA_LINE DHE DISCONNECT DOMAIN
-%token EHLO ENABLE ENCRYPTION ERROR EXPAND_ONLY
-%token FCRDNS FILTER FOR FORWARD_ONLY FROM
-%token GROUP
-%token HELO HELO_SRC HOST HOSTNAME HOSTNAMES
-%token INCLUDE INET4 INET6
-%token JUNK
-%token KEY
-%token LIMIT LISTEN LMTP LOCAL
-%token MAIL_FROM MAILDIR MASK_SRC MASQUERADE MATCH MAX_MESSAGE_SIZE MAX_DEFERRED MBOX MDA MTA MX
-%token NO_DSN NO_VERIFY NOOP
-%token ON
-%token PHASE PKI PORT PROC PROC_EXEC PROXY_V2
-%token QUEUE QUIT
-%token RCPT_TO RDNS RECIPIENT RECEIVEDAUTH REGEX RELAY REJECT REPORT REWRITE RSET
-%token SCHEDULER SENDER SENDERS SMTP SMTP_IN SMTP_OUT SMTPS SOCKET SRC SRS SUB_ADDR_DELIM
-%token TABLE TAG TAGGED TLS TLS_REQUIRE TTL
-%token USER USERBASE
-%token VERIFY VIRTUAL
-%token WARN_INTERVAL WRAPPER
-
-%token <v.string> STRING
-%token <v.number> NUMBER
-%type <v.table> table
-%type <v.number> size negation
-%type <v.table> tables tablenew tableref
-%%
-
-grammar : /* empty */
- | grammar '\n'
- | grammar include '\n'
- | grammar varset '\n'
- | grammar bounce '\n'
- | grammar ca '\n'
- | grammar mda '\n'
- | grammar mta '\n'
- | grammar pki '\n'
- | grammar proc '\n'
- | grammar queue '\n'
- | grammar scheduler '\n'
- | grammar smtp '\n'
- | grammar srs '\n'
- | grammar listen '\n'
- | grammar table '\n'
- | grammar dispatcher '\n'
- | grammar match '\n'
- | grammar filter '\n'
- | grammar error '\n' { file->errors++; }
- ;
-
-include : INCLUDE STRING {
- struct file *nfile;
-
- if ((nfile = pushfile($2, 0)) == NULL) {
- yyerror("failed to include file %s", $2);
- free($2);
- YYERROR;
- }
- free($2);
-
- file = nfile;
- lungetc('\n');
- }
- ;
-
-varset : STRING '=' STRING {
- char *s = $1;
- while (*s++) {
- if (isspace((unsigned char)*s)) {
- yyerror("macro name cannot contain "
- "whitespace");
- free($1);
- free($3);
- YYERROR;
- }
- }
- if (symset($1, $3, 0) == -1)
- fatal("cannot store variable");
- free($1);
- free($3);
- }
- ;
-
-comma : ','
- | nl
- | /* empty */
- ;
-
-optnl : '\n' optnl
- |
- ;
-
-nl : '\n' optnl
- ;
-
-negation : '!' { $$ = 1; }
- | /* empty */ { $$ = 0; }
- ;
-
-assign : '=' | ARROW;
-
-
-keyval : STRING assign STRING {
- table_add(table, $1, $3);
- free($1);
- free($3);
- }
- ;
-
-keyval_list : keyval
- | keyval comma keyval_list
- ;
-
-stringel : STRING {
- table_add(table, $1, NULL);
- free($1);
- }
- ;
-
-string_list : stringel
- | stringel comma string_list
- ;
-
-tableval_list : string_list { }
- | keyval_list { }
- ;
-
-bounce:
-BOUNCE WARN_INTERVAL {
- memset(conf->sc_bounce_warn, 0, sizeof conf->sc_bounce_warn);
-} bouncedelays
-;
-
-
-ca:
-CA STRING {
- char buf[HOST_NAME_MAX+1];
-
- /* if not catchall, check that it is a valid domain */
- if (strcmp($2, "*") != 0) {
- if (!res_hnok($2)) {
- yyerror("not a valid domain name: %s", $2);
- free($2);
- YYERROR;
- }
- }
- xlowercase(buf, $2, sizeof(buf));
- free($2);
- sca = dict_get(conf->sc_ca_dict, buf);
- if (sca == NULL) {
- sca = xcalloc(1, sizeof *sca);
- (void)strlcpy(sca->ca_name, buf, sizeof(sca->ca_name));
- dict_set(conf->sc_ca_dict, sca->ca_name, sca);
- }
-} ca_params
-;
-
-
-ca_params_opt:
-CERT STRING {
- sca->ca_cert_file = $2;
-}
-;
-
-ca_params:
-ca_params_opt
-;
-
-
-mda:
-MDA LIMIT limits_mda
-| MDA WRAPPER STRING STRING {
- if (dict_get(conf->sc_mda_wrappers, $3)) {
- yyerror("mda wrapper already declared with that name: %s", $3);
- YYERROR;
- }
- dict_set(conf->sc_mda_wrappers, $3, $4);
-}
-;
-
-
-mta:
-MTA MAX_DEFERRED NUMBER {
- conf->sc_mta_max_deferred = $3;
-}
-| MTA LIMIT FOR DOMAIN STRING {
- struct mta_limits *d;
-
- limits = dict_get(conf->sc_limits_dict, $5);
- if (limits == NULL) {
- limits = xcalloc(1, sizeof(*limits));
- dict_xset(conf->sc_limits_dict, $5, limits);
- d = dict_xget(conf->sc_limits_dict, "default");
- memmove(limits, d, sizeof(*limits));
- }
- free($5);
-} limits_mta
-| MTA LIMIT {
- limits = dict_get(conf->sc_limits_dict, "default");
-} limits_mta
-;
-
-
-pki:
-PKI STRING {
- char buf[HOST_NAME_MAX+1];
-
- /* if not catchall, check that it is a valid domain */
- if (strcmp($2, "*") != 0) {
- if (!res_hnok($2)) {
- yyerror("not a valid domain name: %s", $2);
- free($2);
- YYERROR;
- }
- }
- xlowercase(buf, $2, sizeof(buf));
- free($2);
- pki = dict_get(conf->sc_pki_dict, buf);
- if (pki == NULL) {
- pki = xcalloc(1, sizeof *pki);
- (void)strlcpy(pki->pki_name, buf, sizeof(pki->pki_name));
- dict_set(conf->sc_pki_dict, pki->pki_name, pki);
- }
-} pki_params
-;
-
-pki_params_opt:
-CERT STRING {
- pki->pki_cert_file = $2;
-}
-| KEY STRING {
- pki->pki_key_file = $2;
-}
-| DHE STRING {
- if (strcasecmp($2, "none") == 0)
- pki->pki_dhe = 0;
- else if (strcasecmp($2, "auto") == 0)
- pki->pki_dhe = 1;
- else if (strcasecmp($2, "legacy") == 0)
- pki->pki_dhe = 2;
- else {
- yyerror("invalid DHE keyword: %s", $2);
- free($2);
- YYERROR;
- }
- free($2);
-}
-;
-
-
-pki_params:
-pki_params_opt pki_params
-| /* empty */
-;
-
-
-proc:
-PROC STRING STRING {
- if (dict_get(conf->sc_filter_processes_dict, $2)) {
- yyerror("processor already exists with that name: %s", $2);
- free($2);
- free($3);
- YYERROR;
- }
- processor = xcalloc(1, sizeof *processor);
- processor->command = $3;
-} proc_params {
- dict_set(conf->sc_filter_processes_dict, $2, processor);
- processor = NULL;
-}
-;
-
-
-proc_params_opt:
-USER STRING {
- if (processor->user) {
- yyerror("user already specified for this processor");
- free($2);
- YYERROR;
- }
- processor->user = $2;
-}
-| GROUP STRING {
- if (processor->group) {
- yyerror("group already specified for this processor");
- free($2);
- YYERROR;
- }
- processor->group = $2;
-}
-| CHROOT STRING {
- if (processor->chroot) {
- yyerror("chroot already specified for this processor");
- free($2);
- YYERROR;
- }
- processor->chroot = $2;
-}
-;
-
-proc_params:
-proc_params_opt proc_params
-| /* empty */
-;
-
-
-queue:
-QUEUE COMPRESSION {
- conf->sc_queue_flags |= QUEUE_COMPRESSION;
-}
-| QUEUE ENCRYPTION {
- conf->sc_queue_flags |= QUEUE_ENCRYPTION;
-}
-| QUEUE ENCRYPTION STRING {
- if (strcasecmp($3, "stdin") == 0 || strcasecmp($3, "-") == 0) {
- conf->sc_queue_key = "stdin";
- free($3);
- }
- else
- conf->sc_queue_key = $3;
- conf->sc_queue_flags |= QUEUE_ENCRYPTION;
-}
-| QUEUE TTL STRING {
- conf->sc_ttl = delaytonum($3);
- if (conf->sc_ttl == -1) {
- yyerror("invalid ttl delay: %s", $3);
- free($3);
- YYERROR;
- }
- free($3);
-}
-;
-
-
-scheduler:
-SCHEDULER LIMIT limits_scheduler
-;
-
-
-smtp:
-SMTP LIMIT limits_smtp
-| SMTP CIPHERS STRING {
- conf->sc_tls_ciphers = $3;
-}
-| SMTP MAX_MESSAGE_SIZE size {
- conf->sc_maxsize = $3;
-}
-| SMTP SUB_ADDR_DELIM STRING {
- if (strlen($3) != 1) {
- yyerror("subaddressing-delimiter must be one character");
- free($3);
- YYERROR;
- }
- if (isspace((unsigned char)*$3) || !isprint((unsigned char)*$3) || *$3 == '@') {
- yyerror("sub-addr-delim uses invalid character");
- free($3);
- YYERROR;
- }
- conf->sc_subaddressing_delim = $3;
-}
-;
-
-srs:
-SRS KEY STRING {
- conf->sc_srs_key = $3;
-}
-| SRS KEY BACKUP STRING {
- conf->sc_srs_key_backup = $4;
-}
-| SRS TTL STRING {
- conf->sc_srs_ttl = delaytonum($3);
- if (conf->sc_srs_ttl == -1) {
- yyerror("ttl delay \"%s\" is invalid", $3);
- free($3);
- YYERROR;
- }
-
- conf->sc_srs_ttl /= 86400;
- if (conf->sc_srs_ttl == 0) {
- yyerror("ttl delay \"%s\" is too short", $3);
- free($3);
- YYERROR;
- }
- free($3);
-}
-;
-
-
-dispatcher_local_option:
-USER STRING {
- if (dispatcher->u.local.is_mbox) {
- yyerror("user may not be specified for this dispatcher");
- YYERROR;
- }
-
- if (dispatcher->u.local.forward_only) {
- yyerror("user may not be specified for forward-only");
- YYERROR;
- }
-
- if (dispatcher->u.local.expand_only) {
- yyerror("user may not be specified for expand-only");
- YYERROR;
- }
-
- if (dispatcher->u.local.user) {
- yyerror("user already specified for this dispatcher");
- YYERROR;
- }
-
- dispatcher->u.local.user = $2;
-}
-| ALIAS tables {
- struct table *t = $2;
-
- if (dispatcher->u.local.table_alias) {
- yyerror("alias mapping already specified for this dispatcher");
- YYERROR;
- }
-
- if (dispatcher->u.local.table_virtual) {
- yyerror("virtual mapping already specified for this dispatcher");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) {
- yyerror("table \"%s\" may not be used for alias lookups",
- t->t_name);
- YYERROR;
- }
-
- dispatcher->u.local.table_alias = strdup(t->t_name);
-}
-| VIRTUAL tables {
- struct table *t = $2;
-
- if (dispatcher->u.local.table_virtual) {
- yyerror("virtual mapping already specified for this dispatcher");
- YYERROR;
- }
-
- if (dispatcher->u.local.table_alias) {
- yyerror("alias mapping already specified for this dispatcher");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) {
- yyerror("table \"%s\" may not be used for virtual lookups",
- t->t_name);
- YYERROR;
- }
-
- dispatcher->u.local.table_virtual = strdup(t->t_name);
-}
-| USERBASE tables {
- struct table *t = $2;
-
- if (dispatcher->u.local.table_userbase) {
- yyerror("userbase mapping already specified for this dispatcher");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) {
- yyerror("table \"%s\" may not be used for userbase lookups",
- t->t_name);
- YYERROR;
- }
-
- dispatcher->u.local.table_userbase = strdup(t->t_name);
-}
-| WRAPPER STRING {
- if (! dict_get(conf->sc_mda_wrappers, $2)) {
- yyerror("no mda wrapper with that name: %s", $2);
- YYERROR;
- }
- dispatcher->u.local.mda_wrapper = $2;
-}
-;
-
-dispatcher_local_options:
-dispatcher_local_option dispatcher_local_options
-| /* empty */
-;
-
-dispatcher_local:
-MBOX {
- dispatcher->u.local.is_mbox = 1;
- asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.local -f %%{mbox.from} -- %%{user.username}");
-} dispatcher_local_options
-| MAILDIR {
- asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.maildir");
-} dispatcher_local_options
-| MAILDIR JUNK {
- asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.maildir -j");
-} dispatcher_local_options
-| MAILDIR STRING {
- if (strncmp($2, "~/", 2) == 0)
- asprintf(&dispatcher->u.local.command,
- PATH_LIBEXEC"/mail.maildir \"%%{user.directory}/%s\"", $2+2);
- else
- asprintf(&dispatcher->u.local.command,
- PATH_LIBEXEC"/mail.maildir \"%s\"", $2);
-} dispatcher_local_options
-| MAILDIR STRING JUNK {
- if (strncmp($2, "~/", 2) == 0)
- asprintf(&dispatcher->u.local.command,
- PATH_LIBEXEC"/mail.maildir -j \"%%{user.directory}/%s\"", $2+2);
- else
- asprintf(&dispatcher->u.local.command,
- PATH_LIBEXEC"/mail.maildir -j \"%s\"", $2);
-} dispatcher_local_options
-| LMTP STRING {
- asprintf(&dispatcher->u.local.command,
- PATH_LIBEXEC"/mail.lmtp -d \"%s\" -u", $2);
-} dispatcher_local_options
-| LMTP STRING RCPT_TO {
- asprintf(&dispatcher->u.local.command,
- PATH_LIBEXEC"/mail.lmtp -d \"%s\" -r", $2);
-} dispatcher_local_options
-| MDA STRING {
- asprintf(&dispatcher->u.local.command,
- PATH_LIBEXEC"/mail.mda \"%s\"", $2);
-} dispatcher_local_options
-| FORWARD_ONLY {
- dispatcher->u.local.forward_only = 1;
-} dispatcher_local_options
-| EXPAND_ONLY {
- dispatcher->u.local.expand_only = 1;
-} dispatcher_local_options
-
-;
-
-dispatcher_remote_option:
-HELO STRING {
- if (dispatcher->u.remote.helo) {
- yyerror("helo already specified for this dispatcher");
- YYERROR;
- }
-
- dispatcher->u.remote.helo = $2;
-}
-| HELO_SRC tables {
- struct table *t = $2;
-
- if (dispatcher->u.remote.helo_source) {
- yyerror("helo-source mapping already specified for this dispatcher");
- YYERROR;
- }
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) {
- yyerror("table \"%s\" may not be used for helo-source lookups",
- t->t_name);
- YYERROR;
- }
-
- dispatcher->u.remote.helo_source = strdup(t->t_name);
-}
-| PKI STRING {
- if (dispatcher->u.remote.pki) {
- yyerror("pki already specified for this dispatcher");
- YYERROR;
- }
-
- dispatcher->u.remote.pki = $2;
-}
-| CA STRING {
- if (dispatcher->u.remote.ca) {
- yyerror("ca already specified for this dispatcher");
- YYERROR;
- }
-
- dispatcher->u.remote.ca = $2;
-}
-| SRC tables {
- struct table *t = $2;
-
- if (dispatcher->u.remote.source) {
- yyerror("source mapping already specified for this dispatcher");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_SOURCE)) {
- yyerror("table \"%s\" may not be used for source lookups",
- t->t_name);
- YYERROR;
- }
-
- dispatcher->u.remote.source = strdup(t->t_name);
-}
-| MAIL_FROM STRING {
- if (dispatcher->u.remote.mail_from) {
- yyerror("mail-from already specified for this dispatcher");
- YYERROR;
- }
-
- dispatcher->u.remote.mail_from = $2;
-}
-| BACKUP MX STRING {
- if (dispatcher->u.remote.backup) {
- yyerror("backup already specified for this dispatcher");
- YYERROR;
- }
- if (dispatcher->u.remote.smarthost) {
- yyerror("backup and host are mutually exclusive");
- YYERROR;
- }
-
- dispatcher->u.remote.backup = 1;
- dispatcher->u.remote.backupmx = $3;
-}
-| BACKUP {
- if (dispatcher->u.remote.backup) {
- yyerror("backup already specified for this dispatcher");
- YYERROR;
- }
- if (dispatcher->u.remote.smarthost) {
- yyerror("backup and host are mutually exclusive");
- YYERROR;
- }
-
- dispatcher->u.remote.backup = 1;
-}
-| HOST tables {
- struct table *t = $2;
-
- if (dispatcher->u.remote.smarthost) {
- yyerror("host mapping already specified for this dispatcher");
- YYERROR;
- }
- if (dispatcher->u.remote.backup) {
- yyerror("backup and host are mutually exclusive");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_RELAYHOST)) {
- yyerror("table \"%s\" may not be used for host lookups",
- t->t_name);
- YYERROR;
- }
-
- dispatcher->u.remote.smarthost = strdup(t->t_name);
-}
-| DOMAIN tables {
- struct table *t = $2;
-
- if (dispatcher->u.remote.smarthost) {
- yyerror("host mapping already specified for this dispatcher");
- YYERROR;
- }
- if (dispatcher->u.remote.backup) {
- yyerror("backup and domain are mutually exclusive");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_RELAYHOST)) {
- yyerror("table \"%s\" may not be used for host lookups",
- t->t_name);
- YYERROR;
- }
-
- dispatcher->u.remote.smarthost = strdup(t->t_name);
- dispatcher->u.remote.smarthost_domain = 1;
-}
-| TLS {
- if (dispatcher->u.remote.tls_required == 1) {
- yyerror("tls already specified for this dispatcher");
- YYERROR;
- }
-
- dispatcher->u.remote.tls_required = 1;
-}
-| TLS NO_VERIFY {
- if (dispatcher->u.remote.tls_required == 1) {
- yyerror("tls already specified for this dispatcher");
- YYERROR;
- }
-
- dispatcher->u.remote.tls_required = 1;
- dispatcher->u.remote.tls_noverify = 1;
-}
-| AUTH tables {
- struct table *t = $2;
-
- if (dispatcher->u.remote.smarthost == NULL) {
- yyerror("auth may not be specified without host on a dispatcher");
- YYERROR;
- }
-
- if (dispatcher->u.remote.auth) {
- yyerror("auth mapping already specified for this dispatcher");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_CREDENTIALS)) {
- yyerror("table \"%s\" may not be used for auth lookups",
- t->t_name);
- YYERROR;
- }
-
- dispatcher->u.remote.auth = strdup(t->t_name);
-}
-| FILTER STRING {
- struct filter_config *fc;
-
- if (dispatcher->u.remote.filtername) {
- yyerror("filter already specified for this dispatcher");
- YYERROR;
- }
-
- if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) {
- yyerror("no filter exist with that name: %s", $2);
- free($2);
- YYERROR;
- }
- fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_OUT;
- dispatcher->u.remote.filtername = $2;
-}
-| FILTER {
- char buffer[128];
- char *filtername;
-
- if (dispatcher->u.remote.filtername) {
- yyerror("filter already specified for this dispatcher");
- YYERROR;
- }
-
- do {
- (void)snprintf(buffer, sizeof buffer, "<dynchain:%08x>", last_dynchain_id++);
- } while (dict_check(conf->sc_filters_dict, buffer));
-
- filtername = xstrdup(buffer);
- filter_config = xcalloc(1, sizeof *filter_config);
- filter_config->filter_type = FILTER_TYPE_CHAIN;
- filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_OUT;
- dict_init(&filter_config->chain_procs);
- dispatcher->u.remote.filtername = filtername;
-} '{' filter_list '}' {
- dict_set(conf->sc_filters_dict, dispatcher->u.remote.filtername, filter_config);
- filter_config = NULL;
-}
-| SRS {
- if (conf->sc_srs_key == NULL) {
- yyerror("an srs key is required for srs to be specified in an action");
- YYERROR;
- }
- if (dispatcher->u.remote.srs == 1) {
- yyerror("srs already specified for this dispatcher");
- YYERROR;
- }
-
- dispatcher->u.remote.srs = 1;
-}
-;
-
-dispatcher_remote_options:
-dispatcher_remote_option dispatcher_remote_options
-| /* empty */
-;
-
-dispatcher_remote :
-RELAY dispatcher_remote_options
-;
-
-dispatcher_type:
-dispatcher_local {
- dispatcher->type = DISPATCHER_LOCAL;
-}
-| dispatcher_remote {
- dispatcher->type = DISPATCHER_REMOTE;
-}
-;
-
-dispatcher_option:
-TTL STRING {
- if (dispatcher->ttl) {
- yyerror("ttl already specified for this dispatcher");
- YYERROR;
- }
-
- dispatcher->ttl = delaytonum($2);
- if (dispatcher->ttl == -1) {
- yyerror("ttl delay \"%s\" is invalid", $2);
- free($2);
- YYERROR;
- }
- free($2);
-}
-;
-
-dispatcher_options:
-dispatcher_option dispatcher_options
-| /* empty */
-;
-
-dispatcher:
-ACTION STRING {
- if (dict_get(conf->sc_dispatchers, $2)) {
- yyerror("dispatcher already declared with that name: %s", $2);
- YYERROR;
- }
- dispatcher = xcalloc(1, sizeof *dispatcher);
-} dispatcher_type dispatcher_options {
- if (dispatcher->type == DISPATCHER_LOCAL)
- if (dispatcher->u.local.table_userbase == NULL)
- dispatcher->u.local.table_userbase = "<getpwnam>";
- dict_set(conf->sc_dispatchers, $2, dispatcher);
- dispatcher = NULL;
-}
-;
-
-match_option:
-negation TAG tables {
- struct table *t = $3;
-
- if (rule->flag_tag) {
- yyerror("tag already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING)) {
- yyerror("table \"%s\" may not be used for tag lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_tag = $1 ? -1 : 1;
- rule->table_tag = strdup(t->t_name);
-}
-|
-negation TAG REGEX tables {
- struct table *t = $4;
-
- if (rule->flag_tag) {
- yyerror("tag already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
- yyerror("table \"%s\" may not be used for tag lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_tag = $1 ? -1 : 1;
- rule->flag_tag_regex = 1;
- rule->table_tag = strdup(t->t_name);
-}
-
-| negation HELO tables {
- struct table *t = $3;
-
- if (rule->flag_smtp_helo) {
- yyerror("helo already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
- yyerror("table \"%s\" may not be used for helo lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_smtp_helo = $1 ? -1 : 1;
- rule->table_smtp_helo = strdup(t->t_name);
-}
-| negation HELO REGEX tables {
- struct table *t = $4;
-
- if (rule->flag_smtp_helo) {
- yyerror("helo already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
- yyerror("table \"%s\" may not be used for helo lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_smtp_helo = $1 ? -1 : 1;
- rule->flag_smtp_helo_regex = 1;
- rule->table_smtp_helo = strdup(t->t_name);
-}
-| negation TLS {
- if (rule->flag_smtp_starttls) {
- yyerror("tls already specified for this rule");
- YYERROR;
- }
- rule->flag_smtp_starttls = $1 ? -1 : 1;
-}
-| negation AUTH {
- if (rule->flag_smtp_auth) {
- yyerror("auth already specified for this rule");
- YYERROR;
- }
- rule->flag_smtp_auth = $1 ? -1 : 1;
-}
-| negation AUTH tables {
- struct table *t = $3;
-
- if (rule->flag_smtp_auth) {
- yyerror("auth already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING|K_CREDENTIALS)) {
- yyerror("table \"%s\" may not be used for auth lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_smtp_auth = $1 ? -1 : 1;
- rule->table_smtp_auth = strdup(t->t_name);
-}
-| negation AUTH REGEX tables {
- struct table *t = $4;
-
- if (rule->flag_smtp_auth) {
- yyerror("auth already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
- yyerror("table \"%s\" may not be used for auth lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_smtp_auth = $1 ? -1 : 1;
- rule->flag_smtp_auth_regex = 1;
- rule->table_smtp_auth = strdup(t->t_name);
-}
-| negation MAIL_FROM tables {
- struct table *t = $3;
-
- if (rule->flag_smtp_mail_from) {
- yyerror("mail-from already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
- yyerror("table \"%s\" may not be used for mail-from lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_smtp_mail_from = $1 ? -1 : 1;
- rule->table_smtp_mail_from = strdup(t->t_name);
-}
-| negation MAIL_FROM REGEX tables {
- struct table *t = $4;
-
- if (rule->flag_smtp_mail_from) {
- yyerror("mail-from already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
- yyerror("table \"%s\" may not be used for mail-from lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_smtp_mail_from = $1 ? -1 : 1;
- rule->flag_smtp_mail_from_regex = 1;
- rule->table_smtp_mail_from = strdup(t->t_name);
-}
-| negation RCPT_TO tables {
- struct table *t = $3;
-
- if (rule->flag_smtp_rcpt_to) {
- yyerror("rcpt-to already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
- yyerror("table \"%s\" may not be used for rcpt-to lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
- rule->table_smtp_rcpt_to = strdup(t->t_name);
-}
-| negation RCPT_TO REGEX tables {
- struct table *t = $4;
-
- if (rule->flag_smtp_rcpt_to) {
- yyerror("rcpt-to already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
- yyerror("table \"%s\" may not be used for rcpt-to lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
- rule->flag_smtp_rcpt_to_regex = 1;
- rule->table_smtp_rcpt_to = strdup(t->t_name);
-}
-
-| negation FROM SOCKET {
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
- rule->flag_from = $1 ? -1 : 1;
- rule->flag_from_socket = 1;
-}
-| negation FROM LOCAL {
- struct table *t = table_find(conf, "<localhost>");
-
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
- rule->flag_from = $1 ? -1 : 1;
- rule->table_from = strdup(t->t_name);
-}
-| negation FROM ANY {
- struct table *t = table_find(conf, "<anyhost>");
-
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
- rule->flag_from = $1 ? -1 : 1;
- rule->table_from = strdup(t->t_name);
-}
-| negation FROM SRC tables {
- struct table *t = $4;
-
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) {
- yyerror("table \"%s\" may not be used for from lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_from = $1 ? -1 : 1;
- rule->table_from = strdup(t->t_name);
-}
-| negation FROM SRC REGEX tables {
- struct table *t = $5;
-
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
- yyerror("table \"%s\" may not be used for from lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_from = $1 ? -1 : 1;
- rule->flag_from_regex = 1;
- rule->table_from = strdup(t->t_name);
-}
-| negation FROM RDNS {
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
- rule->flag_from = $1 ? -1 : 1;
- rule->flag_from_rdns = 1;
-}
-| negation FROM RDNS tables {
- struct table *t = $4;
-
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
- yyerror("table \"%s\" may not be used for rdns lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_from = $1 ? -1 : 1;
- rule->flag_from_rdns = 1;
- rule->table_from = strdup(t->t_name);
-}
-| negation FROM RDNS REGEX tables {
- struct table *t = $5;
-
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
- yyerror("table \"%s\" may not be used for rdns lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_from = $1 ? -1 : 1;
- rule->flag_from_regex = 1;
- rule->flag_from_rdns = 1;
- rule->table_from = strdup(t->t_name);
-}
-
-| negation FROM AUTH {
- struct table *anyhost = table_find(conf, "<anyhost>");
-
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
-
- rule->flag_from = 1;
- rule->table_from = strdup(anyhost->t_name);
- rule->flag_smtp_auth = $1 ? -1 : 1;
-}
-| negation FROM AUTH tables {
- struct table *anyhost = table_find(conf, "<anyhost>");
- struct table *t = $4;
-
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING|K_CREDENTIALS)) {
- yyerror("table \"%s\" may not be used for from lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_from = 1;
- rule->table_from = strdup(anyhost->t_name);
- rule->flag_smtp_auth = $1 ? -1 : 1;
- rule->table_smtp_auth = strdup(t->t_name);
-}
-| negation FROM AUTH REGEX tables {
- struct table *anyhost = table_find(conf, "<anyhost>");
- struct table *t = $5;
-
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
- yyerror("table \"%s\" may not be used for from lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_from = 1;
- rule->table_from = strdup(anyhost->t_name);
- rule->flag_smtp_auth = $1 ? -1 : 1;
- rule->flag_smtp_auth_regex = 1;
- rule->table_smtp_auth = strdup(t->t_name);
-}
-
-| negation FROM MAIL_FROM tables {
- struct table *anyhost = table_find(conf, "<anyhost>");
- struct table *t = $4;
-
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
- yyerror("table \"%s\" may not be used for from lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_from = 1;
- rule->table_from = strdup(anyhost->t_name);
- rule->flag_smtp_mail_from = $1 ? -1 : 1;
- rule->table_smtp_mail_from = strdup(t->t_name);
-}
-| negation FROM MAIL_FROM REGEX tables {
- struct table *anyhost = table_find(conf, "<anyhost>");
- struct table *t = $5;
-
- if (rule->flag_from) {
- yyerror("from already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
- yyerror("table \"%s\" may not be used for from lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_from = 1;
- rule->table_from = strdup(anyhost->t_name);
- rule->flag_smtp_mail_from = $1 ? -1 : 1;
- rule->flag_smtp_mail_from_regex = 1;
- rule->table_smtp_mail_from = strdup(t->t_name);
-}
-
-| negation FOR LOCAL {
- struct table *t = table_find(conf, "<localnames>");
-
- if (rule->flag_for) {
- yyerror("for already specified for this rule");
- YYERROR;
- }
- rule->flag_for = $1 ? -1 : 1;
- rule->table_for = strdup(t->t_name);
-}
-| negation FOR ANY {
- struct table *t = table_find(conf, "<anydestination>");
-
- if (rule->flag_for) {
- yyerror("for already specified for this rule");
- YYERROR;
- }
- rule->flag_for = $1 ? -1 : 1;
- rule->table_for = strdup(t->t_name);
-}
-| negation FOR DOMAIN tables {
- struct table *t = $4;
-
- if (rule->flag_for) {
- yyerror("for already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
- yyerror("table \"%s\" may not be used for 'for' lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_for = $1 ? -1 : 1;
- rule->table_for = strdup(t->t_name);
-}
-| negation FOR DOMAIN REGEX tables {
- struct table *t = $5;
-
- if (rule->flag_for) {
- yyerror("for already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
- yyerror("table \"%s\" may not be used for 'for' lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_for = $1 ? -1 : 1;
- rule->flag_for_regex = 1;
- rule->table_for = strdup(t->t_name);
-}
-| negation FOR RCPT_TO tables {
- struct table *anyhost = table_find(conf, "<anydestination>");
- struct table *t = $4;
-
- if (rule->flag_for) {
- yyerror("for already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
- yyerror("table \"%s\" may not be used for for lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_for = 1;
- rule->table_for = strdup(anyhost->t_name);
- rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
- rule->table_smtp_rcpt_to = strdup(t->t_name);
-}
-| negation FOR RCPT_TO REGEX tables {
- struct table *anyhost = table_find(conf, "<anydestination>");
- struct table *t = $5;
-
- if (rule->flag_for) {
- yyerror("for already specified for this rule");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
- yyerror("table \"%s\" may not be used for for lookups",
- t->t_name);
- YYERROR;
- }
-
- rule->flag_for = 1;
- rule->table_for = strdup(anyhost->t_name);
- rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
- rule->flag_smtp_rcpt_to_regex = 1;
- rule->table_smtp_rcpt_to = strdup(t->t_name);
-}
-;
-
-match_options:
-match_option match_options
-| /* empty */
-;
-
-match_dispatcher:
-STRING {
- if (dict_get(conf->sc_dispatchers, $1) == NULL) {
- yyerror("no such dispatcher: %s", $1);
- YYERROR;
- }
- rule->dispatcher = $1;
-}
-;
-
-action:
-REJECT {
- rule->reject = 1;
-}
-| ACTION match_dispatcher
-;
-
-match:
-MATCH {
- rule = xcalloc(1, sizeof *rule);
-} match_options action {
- if (!rule->flag_from) {
- rule->table_from = strdup("<localhost>");
- rule->flag_from = 1;
- }
- if (!rule->flag_for) {
- rule->table_for = strdup("<localnames>");
- rule->flag_for = 1;
- }
- TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry);
- rule = NULL;
-}
-;
-
-filter_action_builtin:
-filter_action_builtin_nojunk
-| JUNK {
- filter_config->junk = 1;
-}
-| BYPASS {
- filter_config->bypass = 1;
-}
-;
-
-filter_action_builtin_nojunk:
-REJECT STRING {
- filter_config->reject = $2;
-}
-| DISCONNECT STRING {
- filter_config->disconnect = $2;
-}
-| REWRITE STRING {
- filter_config->rewrite = $2;
-}
-| REPORT STRING {
- filter_config->report = $2;
-}
-;
-
-filter_phase_check_fcrdns:
-negation FCRDNS {
- filter_config->not_fcrdns = $1 ? -1 : 1;
- filter_config->fcrdns = 1;
-}
-;
-
-filter_phase_check_rdns:
-negation RDNS {
- filter_config->not_rdns = $1 ? -1 : 1;
- filter_config->rdns = 1;
-}
-;
-
-filter_phase_check_rdns_table:
-negation RDNS tables {
- filter_config->not_rdns_table = $1 ? -1 : 1;
- filter_config->rdns_table = $3;
-}
-;
-filter_phase_check_rdns_regex:
-negation RDNS REGEX tables {
- filter_config->not_rdns_regex = $1 ? -1 : 1;
- filter_config->rdns_regex = $4;
-}
-;
-
-filter_phase_check_src_table:
-negation SRC tables {
- filter_config->not_src_table = $1 ? -1 : 1;
- filter_config->src_table = $3;
-}
-;
-filter_phase_check_src_regex:
-negation SRC REGEX tables {
- filter_config->not_src_regex = $1 ? -1 : 1;
- filter_config->src_regex = $4;
-}
-;
-
-filter_phase_check_helo_table:
-negation HELO tables {
- filter_config->not_helo_table = $1 ? -1 : 1;
- filter_config->helo_table = $3;
-}
-;
-filter_phase_check_helo_regex:
-negation HELO REGEX tables {
- filter_config->not_helo_regex = $1 ? -1 : 1;
- filter_config->helo_regex = $4;
-}
-;
-
-filter_phase_check_auth:
-negation AUTH {
- filter_config->not_auth = $1 ? -1 : 1;
- filter_config->auth = 1;
-}
-;
-filter_phase_check_auth_table:
-negation AUTH tables {
- filter_config->not_auth_table = $1 ? -1 : 1;
- filter_config->auth_table = $3;
-}
-;
-filter_phase_check_auth_regex:
-negation AUTH REGEX tables {
- filter_config->not_auth_regex = $1 ? -1 : 1;
- filter_config->auth_regex = $4;
-}
-;
-
-filter_phase_check_mail_from_table:
-negation MAIL_FROM tables {
- filter_config->not_mail_from_table = $1 ? -1 : 1;
- filter_config->mail_from_table = $3;
-}
-;
-filter_phase_check_mail_from_regex:
-negation MAIL_FROM REGEX tables {
- filter_config->not_mail_from_regex = $1 ? -1 : 1;
- filter_config->mail_from_regex = $4;
-}
-;
-
-filter_phase_check_rcpt_to_table:
-negation RCPT_TO tables {
- filter_config->not_rcpt_to_table = $1 ? -1 : 1;
- filter_config->rcpt_to_table = $3;
-}
-;
-filter_phase_check_rcpt_to_regex:
-negation RCPT_TO REGEX tables {
- filter_config->not_rcpt_to_regex = $1 ? -1 : 1;
- filter_config->rcpt_to_regex = $4;
-}
-;
-
-filter_phase_global_options:
-filter_phase_check_fcrdns |
-filter_phase_check_rdns |
-filter_phase_check_rdns_regex |
-filter_phase_check_rdns_table |
-filter_phase_check_src_regex |
-filter_phase_check_src_table;
-
-filter_phase_connect_options:
-filter_phase_global_options;
-
-filter_phase_helo_options:
-filter_phase_check_helo_table |
-filter_phase_check_helo_regex |
-filter_phase_global_options;
-
-filter_phase_auth_options:
-filter_phase_check_helo_table |
-filter_phase_check_helo_regex |
-filter_phase_check_auth |
-filter_phase_check_auth_table |
-filter_phase_check_auth_regex |
-filter_phase_global_options;
-
-filter_phase_mail_from_options:
-filter_phase_check_helo_table |
-filter_phase_check_helo_regex |
-filter_phase_check_auth |
-filter_phase_check_auth_table |
-filter_phase_check_auth_regex |
-filter_phase_check_mail_from_table |
-filter_phase_check_mail_from_regex |
-filter_phase_global_options;
-
-filter_phase_rcpt_to_options:
-filter_phase_check_helo_table |
-filter_phase_check_helo_regex |
-filter_phase_check_auth |
-filter_phase_check_auth_table |
-filter_phase_check_auth_regex |
-filter_phase_check_mail_from_table |
-filter_phase_check_mail_from_regex |
-filter_phase_check_rcpt_to_table |
-filter_phase_check_rcpt_to_regex |
-filter_phase_global_options;
-
-filter_phase_data_options:
-filter_phase_check_helo_table |
-filter_phase_check_helo_regex |
-filter_phase_check_auth |
-filter_phase_check_auth_table |
-filter_phase_check_auth_regex |
-filter_phase_check_mail_from_table |
-filter_phase_check_mail_from_regex |
-filter_phase_global_options;
-
-/*
-filter_phase_quit_options:
-filter_phase_check_helo_table |
-filter_phase_check_helo_regex |
-filter_phase_global_options;
-
-filter_phase_rset_options:
-filter_phase_check_helo_table |
-filter_phase_check_helo_regex |
-filter_phase_global_options;
-
-filter_phase_noop_options:
-filter_phase_check_helo_table |
-filter_phase_check_helo_regex |
-filter_phase_global_options;
-*/
-
-filter_phase_commit_options:
-filter_phase_check_helo_table |
-filter_phase_check_helo_regex |
-filter_phase_check_auth |
-filter_phase_check_auth_table |
-filter_phase_check_auth_regex |
-filter_phase_check_mail_from_table |
-filter_phase_check_mail_from_regex |
-filter_phase_global_options;
-
-
-filter_phase_connect:
-CONNECT {
- filter_config->phase = FILTER_CONNECT;
-} MATCH filter_phase_connect_options filter_action_builtin
-;
-
-
-filter_phase_helo:
-HELO {
- filter_config->phase = FILTER_HELO;
-} MATCH filter_phase_helo_options filter_action_builtin
-;
-
-filter_phase_ehlo:
-EHLO {
- filter_config->phase = FILTER_EHLO;
-} MATCH filter_phase_helo_options filter_action_builtin
-;
-
-filter_phase_auth:
-AUTH {
-} MATCH filter_phase_auth_options filter_action_builtin
-;
-
-filter_phase_mail_from:
-MAIL_FROM {
- filter_config->phase = FILTER_MAIL_FROM;
-} MATCH filter_phase_mail_from_options filter_action_builtin
-;
-
-filter_phase_rcpt_to:
-RCPT_TO {
- filter_config->phase = FILTER_RCPT_TO;
-} MATCH filter_phase_rcpt_to_options filter_action_builtin
-;
-
-filter_phase_data:
-DATA {
- filter_config->phase = FILTER_DATA;
-} MATCH filter_phase_data_options filter_action_builtin
-;
-
-/*
-filter_phase_data_line:
-DATA_LINE {
- filter_config->phase = FILTER_DATA_LINE;
-} MATCH filter_action_builtin
-;
-
-filter_phase_quit:
-QUIT {
- filter_config->phase = FILTER_QUIT;
-} filter_phase_quit_options filter_action_builtin
-;
-
-filter_phase_rset:
-RSET {
- filter_config->phase = FILTER_RSET;
-} MATCH filter_phase_rset_options filter_action_builtin
-;
-
-filter_phase_noop:
-NOOP {
- filter_config->phase = FILTER_NOOP;
-} MATCH filter_phase_noop_options filter_action_builtin
-;
-*/
-
-filter_phase_commit:
-COMMIT {
- filter_config->phase = FILTER_COMMIT;
-} MATCH filter_phase_commit_options filter_action_builtin_nojunk
-;
-
-
-
-filter_phase:
-filter_phase_connect
-| filter_phase_helo
-| filter_phase_ehlo
-| filter_phase_auth
-| filter_phase_mail_from
-| filter_phase_rcpt_to
-| filter_phase_data
-/*| filter_phase_data_line*/
-/*| filter_phase_quit*/
-/*| filter_phase_noop*/
-/*| filter_phase_rset*/
-| filter_phase_commit
-;
-
-
-filterel:
-STRING {
- struct filter_config *fr;
- struct filter_proc *fp;
- size_t i;
-
- if ((fr = dict_get(conf->sc_filters_dict, $1)) == NULL) {
- yyerror("no filter exist with that name: %s", $1);
- free($1);
- YYERROR;
- }
- if (fr->filter_type == FILTER_TYPE_CHAIN) {
- yyerror("no filter chain allowed within a filter chain: %s", $1);
- free($1);
- YYERROR;
- }
-
- for (i = 0; i < filter_config->chain_size; i++) {
- if (strcmp(filter_config->chain[i], $1) == 0) {
- yyerror("no filter allowed twice within a filter chain: %s", $1);
- free($1);
- YYERROR;
- }
- }
-
- if (fr->proc) {
- if ((fp = dict_get(&filter_config->chain_procs, fr->proc))) {
- yyerror("no proc allowed twice within a filter chain: %s", fr->proc);
- free($1);
- YYERROR;
- }
- dict_set(&filter_config->chain_procs, fr->proc, NULL);
- }
-
- fr->filter_subsystem |= filter_config->filter_subsystem;
- filter_config->chain_size += 1;
- filter_config->chain = reallocarray(filter_config->chain, filter_config->chain_size, sizeof(char *));
- if (filter_config->chain == NULL)
- err(1, NULL);
- filter_config->chain[filter_config->chain_size - 1] = $1;
-}
-;
-
-filter_list:
-filterel
-| filterel comma filter_list
-;
-
-filter:
-FILTER STRING PROC STRING {
- struct filter_proc *fp;
-
- if (dict_get(conf->sc_filters_dict, $2)) {
- yyerror("filter already exists with that name: %s", $2);
- free($2);
- free($4);
- YYERROR;
- }
- if ((fp = dict_get(conf->sc_filter_processes_dict, $4)) == NULL) {
- yyerror("no processor exist with that name: %s", $4);
- free($4);
- YYERROR;
- }
-
- filter_config = xcalloc(1, sizeof *filter_config);
- filter_config->filter_type = FILTER_TYPE_PROC;
- filter_config->name = $2;
- filter_config->proc = $4;
- dict_set(conf->sc_filters_dict, $2, filter_config);
- filter_config = NULL;
-}
-|
-FILTER STRING PROC_EXEC STRING {
- if (dict_get(conf->sc_filters_dict, $2)) {
- yyerror("filter already exists with that name: %s", $2);
- free($2);
- free($4);
- YYERROR;
- }
-
- processor = xcalloc(1, sizeof *processor);
- processor->command = $4;
-
- filter_config = xcalloc(1, sizeof *filter_config);
- filter_config->filter_type = FILTER_TYPE_PROC;
- filter_config->name = $2;
- filter_config->proc = xstrdup($2);
- dict_set(conf->sc_filters_dict, $2, filter_config);
-} proc_params {
- dict_set(conf->sc_filter_processes_dict, filter_config->proc, processor);
- processor = NULL;
- filter_config = NULL;
-}
-|
-FILTER STRING PHASE {
- if (dict_get(conf->sc_filters_dict, $2)) {
- yyerror("filter already exists with that name: %s", $2);
- free($2);
- YYERROR;
- }
- filter_config = xcalloc(1, sizeof *filter_config);
- filter_config->name = $2;
- filter_config->filter_type = FILTER_TYPE_BUILTIN;
- dict_set(conf->sc_filters_dict, $2, filter_config);
-} filter_phase {
- filter_config = NULL;
-}
-|
-FILTER STRING CHAIN {
- if (dict_get(conf->sc_filters_dict, $2)) {
- yyerror("filter already exists with that name: %s", $2);
- free($2);
- YYERROR;
- }
- filter_config = xcalloc(1, sizeof *filter_config);
- filter_config->filter_type = FILTER_TYPE_CHAIN;
- dict_init(&filter_config->chain_procs);
-} '{' filter_list '}' {
- dict_set(conf->sc_filters_dict, $2, filter_config);
- filter_config = NULL;
-}
-;
-
-size : NUMBER {
- if ($1 < 0) {
- yyerror("invalid size: %" PRId64, $1);
- YYERROR;
- }
- $$ = $1;
- }
- | STRING {
- long long result;
-
- if (scan_scaled($1, &result) == -1 || result < 0) {
- yyerror("invalid size: %s", $1);
- free($1);
- YYERROR;
- }
- free($1);
- $$ = result;
- }
- ;
-
-bouncedelay : STRING {
- time_t d;
- int i;
-
- d = delaytonum($1);
- if (d < 0) {
- yyerror("invalid bounce delay: %s", $1);
- free($1);
- YYERROR;
- }
- free($1);
- for (i = 0; i < MAX_BOUNCE_WARN; i++) {
- if (conf->sc_bounce_warn[i] != 0)
- continue;
- conf->sc_bounce_warn[i] = d;
- break;
- }
- }
- ;
-
-bouncedelays : bouncedelays ',' bouncedelay
- | bouncedelay
- ;
-
-opt_limit_mda : STRING NUMBER {
- if (!strcmp($1, "max-session")) {
- conf->sc_mda_max_session = $2;
- }
- else if (!strcmp($1, "max-session-per-user")) {
- conf->sc_mda_max_user_session = $2;
- }
- else if (!strcmp($1, "task-lowat")) {
- conf->sc_mda_task_lowat = $2;
- }
- else if (!strcmp($1, "task-hiwat")) {
- conf->sc_mda_task_hiwat = $2;
- }
- else if (!strcmp($1, "task-release")) {
- conf->sc_mda_task_release = $2;
- }
- else {
- yyerror("invalid scheduler limit keyword: %s", $1);
- free($1);
- YYERROR;
- }
- free($1);
- }
- ;
-
-limits_smtp : opt_limit_smtp limits_smtp
- | /* empty */
- ;
-
-opt_limit_smtp : STRING NUMBER {
- if (!strcmp($1, "max-rcpt")) {
- conf->sc_session_max_rcpt = $2;
- }
- else if (!strcmp($1, "max-mails")) {
- conf->sc_session_max_mails = $2;
- }
- else {
- yyerror("invalid session limit keyword: %s", $1);
- free($1);
- YYERROR;
- }
- free($1);
- }
- ;
-
-limits_mda : opt_limit_mda limits_mda
- | /* empty */
- ;
-
-opt_limit_mta : INET4 {
- limits->family = AF_INET;
- }
- | INET6 {
- limits->family = AF_INET6;
- }
- | STRING NUMBER {
- if (!limit_mta_set(limits, $1, $2)) {
- yyerror("invalid mta limit keyword: %s", $1);
- free($1);
- YYERROR;
- }
- free($1);
- }
- ;
-
-limits_mta : opt_limit_mta limits_mta
- | /* empty */
- ;
-
-opt_limit_scheduler : STRING NUMBER {
- if (!strcmp($1, "max-inflight")) {
- conf->sc_scheduler_max_inflight = $2;
- }
- else if (!strcmp($1, "max-evp-batch-size")) {
- conf->sc_scheduler_max_evp_batch_size = $2;
- }
- else if (!strcmp($1, "max-msg-batch-size")) {
- conf->sc_scheduler_max_msg_batch_size = $2;
- }
- else if (!strcmp($1, "max-schedule")) {
- conf->sc_scheduler_max_schedule = $2;
- }
- else {
- yyerror("invalid scheduler limit keyword: %s", $1);
- free($1);
- YYERROR;
- }
- free($1);
- }
- ;
-
-limits_scheduler: opt_limit_scheduler limits_scheduler
- | /* empty */
- ;
-
-
-opt_sock_listen : FILTER STRING {
- struct filter_config *fc;
-
- if (listen_opts.options & LO_FILTER) {
- yyerror("filter already specified");
- free($2);
- YYERROR;
- }
- if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) {
- yyerror("no filter exist with that name: %s", $2);
- free($2);
- YYERROR;
- }
- fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
- listen_opts.options |= LO_FILTER;
- listen_opts.filtername = $2;
- }
- | FILTER {
- char buffer[128];
-
- if (listen_opts.options & LO_FILTER) {
- yyerror("filter already specified");
- YYERROR;
- }
-
- do {
- (void)snprintf(buffer, sizeof buffer, "<dynchain:%08x>", last_dynchain_id++);
- } while (dict_check(conf->sc_filters_dict, buffer));
-
- listen_opts.options |= LO_FILTER;
- listen_opts.filtername = xstrdup(buffer);
- filter_config = xcalloc(1, sizeof *filter_config);
- filter_config->filter_type = FILTER_TYPE_CHAIN;
- filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
- dict_init(&filter_config->chain_procs);
- } '{' filter_list '}' {
- dict_set(conf->sc_filters_dict, listen_opts.filtername, filter_config);
- filter_config = NULL;
- }
- | MASK_SRC {
- if (config_lo_mask_source(&listen_opts)) {
- YYERROR;
- }
- }
- | TAG STRING {
- if (listen_opts.options & LO_TAG) {
- yyerror("tag already specified");
- YYERROR;
- }
- listen_opts.options |= LO_TAG;
-
- if (strlen($2) >= SMTPD_TAG_SIZE) {
- yyerror("tag name too long");
- free($2);
- YYERROR;
- }
- listen_opts.tag = $2;
- }
- ;
-
-opt_if_listen : INET4 {
- if (listen_opts.options & LO_FAMILY) {
- yyerror("address family already specified");
- YYERROR;
- }
- listen_opts.options |= LO_FAMILY;
- listen_opts.family = AF_INET;
- }
- | INET6 {
- if (listen_opts.options & LO_FAMILY) {
- yyerror("address family already specified");
- YYERROR;
- }
- listen_opts.options |= LO_FAMILY;
- listen_opts.family = AF_INET6;
- }
- | PORT STRING {
- struct servent *servent;
-
- if (listen_opts.options & LO_PORT) {
- yyerror("port already specified");
- YYERROR;
- }
- listen_opts.options |= LO_PORT;
-
- servent = getservbyname($2, "tcp");
- if (servent == NULL) {
- yyerror("invalid port: %s", $2);
- free($2);
- YYERROR;
- }
- free($2);
- listen_opts.port = ntohs(servent->s_port);
- }
- | PORT SMTP {
- struct servent *servent;
-
- if (listen_opts.options & LO_PORT) {
- yyerror("port already specified");
- YYERROR;
- }
- listen_opts.options |= LO_PORT;
-
- servent = getservbyname("smtp", "tcp");
- if (servent == NULL) {
- yyerror("invalid port: smtp");
- YYERROR;
- }
- listen_opts.port = ntohs(servent->s_port);
- }
- | PORT SMTPS {
- struct servent *servent;
-
- if (listen_opts.options & LO_PORT) {
- yyerror("port already specified");
- YYERROR;
- }
- listen_opts.options |= LO_PORT;
-
- servent = getservbyname("smtps", "tcp");
- if (servent == NULL) {
- yyerror("invalid port: smtps");
- YYERROR;
- }
- listen_opts.port = ntohs(servent->s_port);
- }
- | PORT NUMBER {
- if (listen_opts.options & LO_PORT) {
- yyerror("port already specified");
- YYERROR;
- }
- listen_opts.options |= LO_PORT;
-
- if ($2 <= 0 || $2 > (int)USHRT_MAX) {
- yyerror("invalid port: %" PRId64, $2);
- YYERROR;
- }
- listen_opts.port = $2;
- }
- | FILTER STRING {
- struct filter_config *fc;
-
- if (listen_opts.options & LO_FILTER) {
- yyerror("filter already specified");
- YYERROR;
- }
- if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) {
- yyerror("no filter exist with that name: %s", $2);
- free($2);
- YYERROR;
- }
- fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
- listen_opts.options |= LO_FILTER;
- listen_opts.filtername = $2;
- }
- | FILTER {
- char buffer[128];
-
- if (listen_opts.options & LO_FILTER) {
- yyerror("filter already specified");
- YYERROR;
- }
-
- do {
- (void)snprintf(buffer, sizeof buffer, "<dynchain:%08x>", last_dynchain_id++);
- } while (dict_check(conf->sc_filters_dict, buffer));
-
- listen_opts.options |= LO_FILTER;
- listen_opts.filtername = xstrdup(buffer);
- filter_config = xcalloc(1, sizeof *filter_config);
- filter_config->filter_type = FILTER_TYPE_CHAIN;
- filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
- dict_init(&filter_config->chain_procs);
- } '{' filter_list '}' {
- dict_set(conf->sc_filters_dict, listen_opts.filtername, filter_config);
- filter_config = NULL;
- }
- | SMTPS {
- if (listen_opts.options & LO_SSL) {
- yyerror("TLS mode already specified");
- YYERROR;
- }
- listen_opts.options |= LO_SSL;
- listen_opts.ssl = F_SMTPS;
- }
- | SMTPS VERIFY {
- if (listen_opts.options & LO_SSL) {
- yyerror("TLS mode already specified");
- YYERROR;
- }
- listen_opts.options |= LO_SSL;
- listen_opts.ssl = F_SMTPS|F_TLS_VERIFY;
- }
- | TLS {
- if (listen_opts.options & LO_SSL) {
- yyerror("TLS mode already specified");
- YYERROR;
- }
- listen_opts.options |= LO_SSL;
- listen_opts.ssl = F_STARTTLS;
- }
- | TLS_REQUIRE {
- if (listen_opts.options & LO_SSL) {
- yyerror("TLS mode already specified");
- YYERROR;
- }
- listen_opts.options |= LO_SSL;
- listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE;
- }
- | TLS_REQUIRE VERIFY {
- if (listen_opts.options & LO_SSL) {
- yyerror("TLS mode already specified");
- YYERROR;
- }
- listen_opts.options |= LO_SSL;
- listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE|F_TLS_VERIFY;
- }
- | PKI STRING {
- if (listen_opts.options & LO_PKI) {
- yyerror("pki already specified");
- YYERROR;
- }
- listen_opts.options |= LO_PKI;
- listen_opts.pki = $2;
- }
- | CA STRING {
- if (listen_opts.options & LO_CA) {
- yyerror("ca already specified");
- YYERROR;
- }
- listen_opts.options |= LO_CA;
- listen_opts.ca = $2;
- }
- | AUTH {
- if (listen_opts.options & LO_AUTH) {
- yyerror("auth already specified");
- YYERROR;
- }
- listen_opts.options |= LO_AUTH;
- listen_opts.auth = F_AUTH|F_AUTH_REQUIRE;
- }
- | AUTH_OPTIONAL {
- if (listen_opts.options & LO_AUTH) {
- yyerror("auth already specified");
- YYERROR;
- }
- listen_opts.options |= LO_AUTH;
- listen_opts.auth = F_AUTH;
- }
- | AUTH tables {
- if (listen_opts.options & LO_AUTH) {
- yyerror("auth already specified");
- YYERROR;
- }
- listen_opts.options |= LO_AUTH;
- listen_opts.authtable = $2;
- listen_opts.auth = F_AUTH|F_AUTH_REQUIRE;
- }
- | AUTH_OPTIONAL tables {
- if (listen_opts.options & LO_AUTH) {
- yyerror("auth already specified");
- YYERROR;
- }
- listen_opts.options |= LO_AUTH;
- listen_opts.authtable = $2;
- listen_opts.auth = F_AUTH;
- }
- | TAG STRING {
- if (listen_opts.options & LO_TAG) {
- yyerror("tag already specified");
- YYERROR;
- }
- listen_opts.options |= LO_TAG;
-
- if (strlen($2) >= SMTPD_TAG_SIZE) {
- yyerror("tag name too long");
- free($2);
- YYERROR;
- }
- listen_opts.tag = $2;
- }
- | HOSTNAME STRING {
- if (listen_opts.options & LO_HOSTNAME) {
- yyerror("hostname already specified");
- YYERROR;
- }
- listen_opts.options |= LO_HOSTNAME;
-
- listen_opts.hostname = $2;
- }
- | HOSTNAMES tables {
- struct table *t = $2;
-
- if (listen_opts.options & LO_HOSTNAMES) {
- yyerror("hostnames already specified");
- YYERROR;
- }
- listen_opts.options |= LO_HOSTNAMES;
-
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) {
- yyerror("invalid use of table \"%s\" as "
- "HOSTNAMES parameter", t->t_name);
- YYERROR;
- }
- listen_opts.hostnametable = t;
- }
- | MASK_SRC {
- if (config_lo_mask_source(&listen_opts)) {
- YYERROR;
- }
- }
- | RECEIVEDAUTH {
- if (listen_opts.options & LO_RECEIVEDAUTH) {
- yyerror("received-auth already specified");
- YYERROR;
- }
- listen_opts.options |= LO_RECEIVEDAUTH;
- listen_opts.flags |= F_RECEIVEDAUTH;
- }
- | NO_DSN {
- if (listen_opts.options & LO_NODSN) {
- yyerror("no-dsn already specified");
- YYERROR;
- }
- listen_opts.options |= LO_NODSN;
- listen_opts.flags &= ~F_EXT_DSN;
- }
- | PROXY_V2 {
- if (listen_opts.options & LO_PROXY) {
- yyerror("proxy-v2 already specified");
- YYERROR;
- }
- listen_opts.options |= LO_PROXY;
- listen_opts.flags |= F_PROXY;
- }
- | SENDERS tables {
- struct table *t = $2;
-
- if (listen_opts.options & LO_SENDERS) {
- yyerror("senders already specified");
- YYERROR;
- }
- listen_opts.options |= LO_SENDERS;
-
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_MAILADDRMAP)) {
- yyerror("invalid use of table \"%s\" as "
- "SENDERS parameter", t->t_name);
- YYERROR;
- }
- listen_opts.sendertable = t;
- }
- | SENDERS tables MASQUERADE {
- struct table *t = $2;
-
- if (listen_opts.options & LO_SENDERS) {
- yyerror("senders already specified");
- YYERROR;
- }
- listen_opts.options |= LO_SENDERS|LO_MASQUERADE;
-
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_MAILADDRMAP)) {
- yyerror("invalid use of table \"%s\" as "
- "SENDERS parameter", t->t_name);
- YYERROR;
- }
- listen_opts.sendertable = t;
- }
- ;
-
-listener_type : socket_listener
- | if_listener
- ;
-
-socket_listener : SOCKET sock_listen {
- if (conf->sc_sock_listener) {
- yyerror("socket listener already configured");
- YYERROR;
- }
- create_sock_listener(&listen_opts);
- }
- ;
-
-if_listener : STRING if_listen {
- listen_opts.ifx = $1;
- create_if_listener(&listen_opts);
- }
- ;
-
-sock_listen : opt_sock_listen sock_listen
- | /* empty */
- ;
-
-if_listen : opt_if_listen if_listen
- | /* empty */
- ;
-
-
-listen : LISTEN {
- memset(&listen_opts, 0, sizeof listen_opts);
- listen_opts.family = AF_UNSPEC;
- listen_opts.flags |= F_EXT_DSN;
- } ON listener_type
- ;
-
-table : TABLE STRING STRING {
- char *p, *backend, *config;
-
- p = $3;
- if (*p == '/') {
- backend = "static";
- config = $3;
- }
- else {
- backend = $3;
- config = NULL;
- for (p = $3; *p && *p != ':'; p++)
- ;
- if (*p == ':') {
- *p = '\0';
- backend = $3;
- config = p+1;
- }
- }
- if (config != NULL && *config != '/') {
- yyerror("invalid backend parameter for table: %s",
- $2);
- free($2);
- free($3);
- YYERROR;
- }
- table = table_create(conf, backend, $2, config);
- if (!table_config(table)) {
- yyerror("invalid configuration file %s for table %s",
- config, table->t_name);
- free($2);
- free($3);
- YYERROR;
- }
- table = NULL;
- free($2);
- free($3);
- }
- | TABLE STRING {
- table = table_create(conf, "static", $2, NULL);
- free($2);
- } '{' tableval_list '}' {
- table = NULL;
- }
- ;
-
-tablenew : STRING {
- struct table *t;
-
- t = table_create(conf, "static", NULL, NULL);
- table_add(t, $1, NULL);
- free($1);
- $$ = t;
- }
- | '{' {
- table = table_create(conf, "static", NULL, NULL);
- } tableval_list '}' {
- $$ = table;
- table = NULL;
- }
- ;
-
-tableref : '<' STRING '>' {
- struct table *t;
-
- if ((t = table_find(conf, $2)) == NULL) {
- yyerror("no such table: %s", $2);
- free($2);
- YYERROR;
- }
- free($2);
- $$ = t;
- }
- ;
-
-tables : tablenew { $$ = $1; }
- | tableref { $$ = $1; }
- ;
-
-
-%%
-
-struct keywords {
- const char *k_name;
- int k_val;
-};
-
-int
-yyerror(const char *fmt, ...)
-{
- va_list ap;
- char *msg;
-
- file->errors++;
- va_start(ap, fmt);
- if (vasprintf(&msg, fmt, ap) == -1)
- fatalx("yyerror vasprintf");
- va_end(ap);
- logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
- free(msg);
- return (0);
-}
-
-int
-kw_cmp(const void *k, const void *e)
-{
- return (strcmp(k, ((const struct keywords *)e)->k_name));
-}
-
-int
-lookup(char *s)
-{
- /* this has to be sorted always */
- static const struct keywords keywords[] = {
- { "action", ACTION },
- { "alias", ALIAS },
- { "any", ANY },
- { "auth", AUTH },
- { "auth-optional", AUTH_OPTIONAL },
- { "backup", BACKUP },
- { "bounce", BOUNCE },
- { "bypass", BYPASS },
- { "ca", CA },
- { "cert", CERT },
- { "chain", CHAIN },
- { "chroot", CHROOT },
- { "ciphers", CIPHERS },
- { "commit", COMMIT },
- { "compression", COMPRESSION },
- { "connect", CONNECT },
- { "data", DATA },
- { "data-line", DATA_LINE },
- { "dhe", DHE },
- { "disconnect", DISCONNECT },
- { "domain", DOMAIN },
- { "ehlo", EHLO },
- { "encryption", ENCRYPTION },
- { "expand-only", EXPAND_ONLY },
- { "fcrdns", FCRDNS },
- { "filter", FILTER },
- { "for", FOR },
- { "forward-only", FORWARD_ONLY },
- { "from", FROM },
- { "group", GROUP },
- { "helo", HELO },
- { "helo-src", HELO_SRC },
- { "host", HOST },
- { "hostname", HOSTNAME },
- { "hostnames", HOSTNAMES },
- { "include", INCLUDE },
- { "inet4", INET4 },
- { "inet6", INET6 },
- { "junk", JUNK },
- { "key", KEY },
- { "limit", LIMIT },
- { "listen", LISTEN },
- { "lmtp", LMTP },
- { "local", LOCAL },
- { "mail-from", MAIL_FROM },
- { "maildir", MAILDIR },
- { "mask-src", MASK_SRC },
- { "masquerade", MASQUERADE },
- { "match", MATCH },
- { "max-deferred", MAX_DEFERRED },
- { "max-message-size", MAX_MESSAGE_SIZE },
- { "mbox", MBOX },
- { "mda", MDA },
- { "mta", MTA },
- { "mx", MX },
- { "no-dsn", NO_DSN },
- { "no-verify", NO_VERIFY },
- { "noop", NOOP },
- { "on", ON },
- { "phase", PHASE },
- { "pki", PKI },
- { "port", PORT },
- { "proc", PROC },
- { "proc-exec", PROC_EXEC },
- { "proxy-v2", PROXY_V2 },
- { "queue", QUEUE },
- { "quit", QUIT },
- { "rcpt-to", RCPT_TO },
- { "rdns", RDNS },
- { "received-auth", RECEIVEDAUTH },
- { "recipient", RECIPIENT },
- { "regex", REGEX },
- { "reject", REJECT },
- { "relay", RELAY },
- { "report", REPORT },
- { "rewrite", REWRITE },
- { "rset", RSET },
- { "scheduler", SCHEDULER },
- { "senders", SENDERS },
- { "smtp", SMTP },
- { "smtp-in", SMTP_IN },
- { "smtp-out", SMTP_OUT },
- { "smtps", SMTPS },
- { "socket", SOCKET },
- { "src", SRC },
- { "srs", SRS },
- { "sub-addr-delim", SUB_ADDR_DELIM },
- { "table", TABLE },
- { "tag", TAG },
- { "tagged", TAGGED },
- { "tls", TLS },
- { "tls-require", TLS_REQUIRE },
- { "ttl", TTL },
- { "user", USER },
- { "userbase", USERBASE },
- { "verify", VERIFY },
- { "virtual", VIRTUAL },
- { "warn-interval", WARN_INTERVAL },
- { "wrapper", WRAPPER },
- };
- const struct keywords *p;
-
- p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
- sizeof(keywords[0]), kw_cmp);
-
- if (p)
- return (p->k_val);
- else
- return (STRING);
-}
-
-#define START_EXPAND 1
-#define DONE_EXPAND 2
-
-static int expanding;
-
-int
-igetc(void)
-{
- int c;
-
- while (1) {
- if (file->ungetpos > 0)
- c = file->ungetbuf[--file->ungetpos];
- else
- c = getc(file->stream);
-
- if (c == START_EXPAND)
- expanding = 1;
- else if (c == DONE_EXPAND)
- expanding = 0;
- else
- break;
- }
- return (c);
-}
-
-int
-lgetc(int quotec)
-{
- int c, next;
-
- if (quotec) {
- if ((c = igetc()) == EOF) {
- yyerror("reached end of file while parsing "
- "quoted string");
- if (file == topfile || popfile() == EOF)
- return (EOF);
- return (quotec);
- }
- return (c);
- }
-
- while ((c = igetc()) == '\\') {
- next = igetc();
- if (next != '\n') {
- c = next;
- break;
- }
- yylval.lineno = file->lineno;
- file->lineno++;
- }
-
- if (c == EOF) {
- /*
- * Fake EOL when hit EOF for the first time. This gets line
- * count right if last line in included file is syntactically
- * invalid and has no newline.
- */
- if (file->eof_reached == 0) {
- file->eof_reached = 1;
- return ('\n');
- }
- while (c == EOF) {
- if (file == topfile || popfile() == EOF)
- return (EOF);
- c = igetc();
- }
- }
- return (c);
-}
-
-void
-lungetc(int c)
-{
- if (c == EOF)
- return;
-
- if (file->ungetpos >= file->ungetsize) {
- void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
- if (p == NULL)
- err(1, "%s", __func__);
- file->ungetbuf = p;
- file->ungetsize *= 2;
- }
- file->ungetbuf[file->ungetpos++] = c;
-}
-
-int
-findeol(void)
-{
- int c;
-
- /* skip to either EOF or the first real EOL */
- while (1) {
- c = lgetc(0);
- if (c == '\n') {
- file->lineno++;
- break;
- }
- if (c == EOF)
- break;
- }
- return (ERROR);
-}
-
-int
-yylex(void)
-{
- unsigned char buf[8096];
- unsigned char *p, *val;
- int quotec, next, c;
- int token;
-
-top:
- p = buf;
- while ((c = lgetc(0)) == ' ' || c == '\t')
- ; /* nothing */
-
- yylval.lineno = file->lineno;
- if (c == '#')
- while ((c = lgetc(0)) != '\n' && c != EOF)
- ; /* nothing */
- if (c == '$' && !expanding) {
- while (1) {
- if ((c = lgetc(0)) == EOF)
- return (0);
-
- if (p + 1 >= buf + sizeof(buf) - 1) {
- yyerror("string too long");
- return (findeol());
- }
- if (isalnum(c) || c == '_') {
- *p++ = c;
- continue;
- }
- *p = '\0';
- lungetc(c);
- break;
- }
- val = symget(buf);
- if (val == NULL) {
- yyerror("macro '%s' not defined", buf);
- return (findeol());
- }
- p = val + strlen(val) - 1;
- lungetc(DONE_EXPAND);
- while (p >= val) {
- lungetc(*p);
- p--;
- }
- lungetc(START_EXPAND);
- goto top;
- }
-
- switch (c) {
- case '\'':
- case '"':
- quotec = c;
- while (1) {
- if ((c = lgetc(quotec)) == EOF)
- return (0);
- if (c == '\n') {
- file->lineno++;
- continue;
- } else if (c == '\\') {
- if ((next = lgetc(quotec)) == EOF)
- return (0);
- if (next == quotec || next == ' ' ||
- next == '\t')
- c = next;
- else if (next == '\n') {
- file->lineno++;
- continue;
- } else
- lungetc(next);
- } else if (c == quotec) {
- *p = '\0';
- break;
- } else if (c == '\0') {
- yyerror("syntax error");
- return (findeol());
- }
- if (p + 1 >= buf + sizeof(buf) - 1) {
- yyerror("string too long");
- return (findeol());
- }
- *p++ = c;
- }
- yylval.v.string = strdup(buf);
- if (yylval.v.string == NULL)
- err(1, "%s", __func__);
- return (STRING);
- }
-
-#define allowed_to_end_number(x) \
- (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
-
- if (c == '-' || isdigit(c)) {
- do {
- *p++ = c;
- if ((size_t)(p-buf) >= sizeof(buf)) {
- yyerror("string too long");
- return (findeol());
- }
- } while ((c = lgetc(0)) != EOF && isdigit(c));
- lungetc(c);
- if (p == buf + 1 && buf[0] == '-')
- goto nodigits;
- if (c == EOF || allowed_to_end_number(c)) {
- const char *errstr = NULL;
-
- *p = '\0';
- yylval.v.number = strtonum(buf, LLONG_MIN,
- LLONG_MAX, &errstr);
- if (errstr) {
- yyerror("\"%s\" invalid number: %s",
- buf, errstr);
- return (findeol());
- }
- return (NUMBER);
- } else {
-nodigits:
- while (p > buf + 1)
- lungetc(*--p);
- c = *--p;
- if (c == '-')
- return (c);
- }
- }
-
- if (c == '=') {
- if ((c = lgetc(0)) != EOF && c == '>')
- return (ARROW);
- lungetc(c);
- c = '=';
- }
-
-#define allowed_in_string(x) \
- (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
- x != '{' && x != '}' && x != '<' && x != '>' && \
- x != '!' && x != '=' && x != '#' && \
- x != ','))
-
- if (isalnum(c) || c == ':' || c == '_') {
- do {
- *p++ = c;
- if ((size_t)(p-buf) >= sizeof(buf)) {
- yyerror("string too long");
- return (findeol());
- }
- } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
- lungetc(c);
- *p = '\0';
- if ((token = lookup(buf)) == STRING)
- if ((yylval.v.string = strdup(buf)) == NULL)
- err(1, "%s", __func__);
- return (token);
- }
- if (c == '\n') {
- yylval.lineno = file->lineno;
- file->lineno++;
- }
- if (c == EOF)
- return (0);
- return (c);
-}
-
-int
-check_file_secrecy(int fd, const char *fname)
-{
- struct stat st;
-
- if (fstat(fd, &st)) {
- log_warn("warn: cannot stat %s", fname);
- return (-1);
- }
- if (st.st_uid != 0 && st.st_uid != getuid()) {
- log_warnx("warn: %s: owner not root or current user", fname);
- return (-1);
- }
- if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
- log_warnx("warn: %s: group/world readable/writeable", fname);
- return (-1);
- }
- return (0);
-}
-
-struct file *
-pushfile(const char *name, int secret)
-{
- struct file *nfile;
-
- if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
- log_warn("%s", __func__);
- return (NULL);
- }
- if ((nfile->name = strdup(name)) == NULL) {
- log_warn("%s", __func__);
- free(nfile);
- return (NULL);
- }
- if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
- log_warn("%s: %s", __func__, nfile->name);
- free(nfile->name);
- free(nfile);
- return (NULL);
- } else if (secret &&
- check_file_secrecy(fileno(nfile->stream), nfile->name)) {
- fclose(nfile->stream);
- free(nfile->name);
- free(nfile);
- return (NULL);
- }
- nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
- nfile->ungetsize = 16;
- nfile->ungetbuf = malloc(nfile->ungetsize);
- if (nfile->ungetbuf == NULL) {
- log_warn("%s", __func__);
- fclose(nfile->stream);
- free(nfile->name);
- free(nfile);
- return (NULL);
- }
- TAILQ_INSERT_TAIL(&files, nfile, entry);
- return (nfile);
-}
-
-int
-popfile(void)
-{
- struct file *prev;
-
- if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
- prev->errors += file->errors;
-
- TAILQ_REMOVE(&files, file, entry);
- fclose(file->stream);
- free(file->name);
- free(file->ungetbuf);
- free(file);
- file = prev;
- return (file ? 0 : EOF);
-}
-
-int
-parse_config(struct smtpd *x_conf, const char *filename, int opts)
-{
- struct sym *sym, *next;
-
- conf = x_conf;
- errors = 0;
-
- if ((file = pushfile(filename, 0)) == NULL) {
- purge_config(PURGE_EVERYTHING);
- return (-1);
- }
- topfile = file;
-
- /*
- * parse configuration
- */
- setservent(1);
- yyparse();
- errors = file->errors;
- popfile();
- endservent();
-
- /* If the socket listener was not configured, create a default one. */
- if (!conf->sc_sock_listener) {
- memset(&listen_opts, 0, sizeof listen_opts);
- create_sock_listener(&listen_opts);
- }
-
- /* Free macros and check which have not been used. */
- TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
- if ((conf->sc_opts & SMTPD_OPT_VERBOSE) && !sym->used)
- fprintf(stderr, "warning: macro '%s' not "
- "used\n", sym->nam);
- if (!sym->persist) {
- free(sym->nam);
- free(sym->val);
- TAILQ_REMOVE(&symhead, sym, entry);
- free(sym);
- }
- }
-
- if (TAILQ_EMPTY(conf->sc_rules)) {
- log_warnx("warn: no rules, nothing to do");
- errors++;
- }
-
- if (errors) {
- purge_config(PURGE_EVERYTHING);
- return (-1);
- }
-
- return (0);
-}
-
-int
-symset(const char *nam, const char *val, int persist)
-{
- struct sym *sym;
-
- TAILQ_FOREACH(sym, &symhead, entry) {
- if (strcmp(nam, sym->nam) == 0)
- break;
- }
-
- if (sym != NULL) {
- if (sym->persist == 1)
- return (0);
- else {
- free(sym->nam);
- free(sym->val);
- TAILQ_REMOVE(&symhead, sym, entry);
- free(sym);
- }
- }
- if ((sym = calloc(1, sizeof(*sym))) == NULL)
- return (-1);
-
- sym->nam = strdup(nam);
- if (sym->nam == NULL) {
- free(sym);
- return (-1);
- }
- sym->val = strdup(val);
- if (sym->val == NULL) {
- free(sym->nam);
- free(sym);
- return (-1);
- }
- sym->used = 0;
- sym->persist = persist;
- TAILQ_INSERT_TAIL(&symhead, sym, entry);
- return (0);
-}
-
-int
-cmdline_symset(char *s)
-{
- char *sym, *val;
- int ret;
-
- if ((val = strrchr(s, '=')) == NULL)
- return (-1);
- sym = strndup(s, val - s);
- if (sym == NULL)
- errx(1, "%s: strndup", __func__);
- ret = symset(sym, val + 1, 1);
- free(sym);
-
- return (ret);
-}
-
-char *
-symget(const char *nam)
-{
- struct sym *sym;
-
- TAILQ_FOREACH(sym, &symhead, entry) {
- if (strcmp(nam, sym->nam) == 0) {
- sym->used = 1;
- return (sym->val);
- }
- }
- return (NULL);
-}
-
-static void
-create_sock_listener(struct listen_opts *lo)
-{
- struct listener *l = xcalloc(1, sizeof(*l));
- lo->hostname = conf->sc_hostname;
- l->ss.ss_family = AF_LOCAL;
-#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
- l->ss.ss_len = sizeof(struct sockaddr *);
-#endif
- l->local = 1;
- conf->sc_sock_listener = l;
- config_listener(l, lo);
-}
-
-static void
-create_if_listener(struct listen_opts *lo)
-{
- uint16_t flags;
-
- if (lo->port != 0 && lo->ssl == F_SSL)
- errx(1, "invalid listen option: tls/smtps on same port");
-
- if (lo->auth != 0 && !lo->ssl)
- errx(1, "invalid listen option: auth requires tls/smtps");
-
- if (lo->pki && !lo->ssl)
- errx(1, "invalid listen option: pki requires tls/smtps");
-
- flags = lo->flags;
-
- if (lo->port) {
- lo->flags = lo->ssl|lo->auth|flags;
- lo->port = htons(lo->port);
- }
- else {
- if (lo->ssl & F_SMTPS) {
- lo->port = htons(465);
- lo->flags = F_SMTPS|lo->auth|flags;
- }
-
- if (!lo->ssl || (lo->ssl & F_STARTTLS)) {
- lo->port = htons(25);
- lo->flags = lo->auth|flags;
- if (lo->ssl & F_STARTTLS)
- lo->flags |= F_STARTTLS;
- }
- }
-
- if (interface(lo))
- return;
- if (host_v4(lo))
- return;
- if (host_v6(lo))
- return;
- if (host_dns(lo))
- return;
-
- errx(1, "invalid virtual ip or interface: %s", lo->ifx);
-}
-
-static void
-config_listener(struct listener *h, struct listen_opts *lo)
-{
- h->fd = -1;
- h->port = lo->port;
- h->flags = lo->flags;
-
- if (lo->hostname == NULL)
- lo->hostname = conf->sc_hostname;
-
- if (lo->options & LO_FILTER) {
- h->flags |= F_FILTERED;
- (void)strlcpy(h->filter_name,
- lo->filtername,
- sizeof(h->filter_name));
- }
-
- h->pki_name[0] = '\0';
-
- if (lo->authtable != NULL)
- (void)strlcpy(h->authtable, lo->authtable->t_name, sizeof(h->authtable));
- if (lo->pki != NULL) {
- if (!lowercase(h->pki_name, lo->pki, sizeof(h->pki_name))) {
- log_warnx("pki name too long: %s", lo->pki);
- fatalx(NULL);
- }
- if (dict_get(conf->sc_pki_dict, h->pki_name) == NULL) {
- log_warnx("pki name not found: %s", lo->pki);
- fatalx(NULL);
- }
- }
-
- if (lo->ca != NULL) {
- if (!lowercase(h->ca_name, lo->ca, sizeof(h->ca_name))) {
- log_warnx("ca name too long: %s", lo->ca);
- fatalx(NULL);
- }
- if (dict_get(conf->sc_ca_dict, h->ca_name) == NULL) {
- log_warnx("ca name not found: %s", lo->ca);
- fatalx(NULL);
- }
- }
- if (lo->tag != NULL)
- (void)strlcpy(h->tag, lo->tag, sizeof(h->tag));
-
- (void)strlcpy(h->hostname, lo->hostname, sizeof(h->hostname));
- if (lo->hostnametable)
- (void)strlcpy(h->hostnametable, lo->hostnametable->t_name, sizeof(h->hostnametable));
- if (lo->sendertable) {
- (void)strlcpy(h->sendertable, lo->sendertable->t_name, sizeof(h->sendertable));
- if (lo->options & LO_MASQUERADE)
- h->flags |= F_MASQUERADE;
- }
-
- if (lo->ssl & F_TLS_VERIFY)
- h->flags |= F_TLS_VERIFY;
-
- if (lo->ssl & F_STARTTLS_REQUIRE)
- h->flags |= F_STARTTLS_REQUIRE;
-
- if (h != conf->sc_sock_listener)
- TAILQ_INSERT_TAIL(conf->sc_listeners, h, entry);
-}
-
-static int
-host_v4(struct listen_opts *lo)
-{
- struct in_addr ina;
- struct sockaddr_in *sain;
- struct listener *h;
-
- if (lo->family != AF_UNSPEC && lo->family != AF_INET)
- return (0);
-
- memset(&ina, 0, sizeof(ina));
- if (inet_pton(AF_INET, lo->ifx, &ina) != 1)
- return (0);
-
- h = xcalloc(1, sizeof(*h));
- sain = (struct sockaddr_in *)&h->ss;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- sain->sin_len = sizeof(struct sockaddr_in);
-#endif
- sain->sin_family = AF_INET;
- sain->sin_addr.s_addr = ina.s_addr;
- sain->sin_port = lo->port;
-
- if (sain->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
- h->local = 1;
- config_listener(h, lo);
-
- return (1);
-}
-
-static int
-host_v6(struct listen_opts *lo)
-{
- struct in6_addr ina6;
- struct sockaddr_in6 *sin6;
- struct listener *h;
-
- if (lo->family != AF_UNSPEC && lo->family != AF_INET6)
- return (0);
-
- memset(&ina6, 0, sizeof(ina6));
- if (inet_pton(AF_INET6, lo->ifx, &ina6) != 1)
- return (0);
-
- h = xcalloc(1, sizeof(*h));
- sin6 = (struct sockaddr_in6 *)&h->ss;
-#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
- sin6->sin6_len = sizeof(struct sockaddr_in6);
-#endif
- sin6->sin6_family = AF_INET6;
- sin6->sin6_port = lo->port;
- memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6));
-
- if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
- h->local = 1;
- config_listener(h, lo);
-
- return (1);
-}
-
-static int
-host_dns(struct listen_opts *lo)
-{
- struct addrinfo hints, *res0, *res;
- int error, cnt = 0;
- struct sockaddr_in *sain;
- struct sockaddr_in6 *sin6;
- struct listener *h;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = lo->family;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_ADDRCONFIG;
- error = getaddrinfo(lo->ifx, NULL, &hints, &res0);
- if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
- return (0);
- if (error) {
- log_warnx("warn: host_dns: could not parse \"%s\": %s", lo->ifx,
- gai_strerror(error));
- return (-1);
- }
-
- for (res = res0; res; res = res->ai_next) {
- if (res->ai_family != AF_INET &&
- res->ai_family != AF_INET6)
- continue;
- h = xcalloc(1, sizeof(*h));
-
- h->ss.ss_family = res->ai_family;
- if (res->ai_family == AF_INET) {
- sain = (struct sockaddr_in *)&h->ss;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- sain->sin_len = sizeof(struct sockaddr_in);
-#endif
- sain->sin_addr.s_addr = ((struct sockaddr_in *)
- res->ai_addr)->sin_addr.s_addr;
- sain->sin_port = lo->port;
- if (sain->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
- h->local = 1;
- } else {
- sin6 = (struct sockaddr_in6 *)&h->ss;
-#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
- sin6->sin6_len = sizeof(struct sockaddr_in6);
-#endif
- memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
- res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
- sin6->sin6_port = lo->port;
- if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
- h->local = 1;
- }
-
- config_listener(h, lo);
-
- cnt++;
- }
-
- freeaddrinfo(res0);
- return (cnt);
-}
-
-static int
-interface(struct listen_opts *lo)
-{
- struct ifaddrs *ifap, *p;
- struct sockaddr_in *sain;
- struct sockaddr_in6 *sin6;
- struct listener *h;
- int ret = 0;
-
- if (getifaddrs(&ifap) == -1)
- fatal("getifaddrs");
-
- for (p = ifap; p != NULL; p = p->ifa_next) {
- if (p->ifa_addr == NULL)
- continue;
- if (strcmp(p->ifa_name, lo->ifx) != 0 &&
- !is_if_in_group(p->ifa_name, lo->ifx))
- continue;
- if (lo->family != AF_UNSPEC && lo->family != p->ifa_addr->sa_family)
- continue;
-
- h = xcalloc(1, sizeof(*h));
-
- switch (p->ifa_addr->sa_family) {
- case AF_INET:
- sain = (struct sockaddr_in *)&h->ss;
- *sain = *(struct sockaddr_in *)p->ifa_addr;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- sain->sin_len = sizeof(struct sockaddr_in);
-#endif
- sain->sin_port = lo->port;
- if (sain->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
- h->local = 1;
- break;
-
- case AF_INET6:
- sin6 = (struct sockaddr_in6 *)&h->ss;
- *sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
-#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
- sin6->sin6_len = sizeof(struct sockaddr_in6);
-#endif
- sin6->sin6_port = lo->port;
- if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
- h->local = 1;
- break;
-
- default:
- free(h);
- continue;
- }
-
- config_listener(h, lo);
- ret = 1;
- }
-
- freeifaddrs(ifap);
-
- return ret;
-}
-
-int
-delaytonum(char *str)
-{
- unsigned int factor;
- size_t len;
- const char *errstr = NULL;
- int delay;
-
- /* we need at least 1 digit and 1 unit */
- len = strlen(str);
- if (len < 2)
- goto bad;
-
- switch(str[len - 1]) {
-
- case 's':
- factor = 1;
- break;
-
- case 'm':
- factor = 60;
- break;
-
- case 'h':
- factor = 60 * 60;
- break;
-
- case 'd':
- factor = 24 * 60 * 60;
- break;
-
- default:
- goto bad;
- }
-
- str[len - 1] = '\0';
- delay = strtonum(str, 1, INT_MAX / factor, &errstr);
- if (errstr)
- goto bad;
-
- return (delay * factor);
-
-bad:
- return (-1);
-}
-
-int
-is_if_in_group(const char *ifname, const char *groupname)
-{
-#ifdef HAVE_STRUCT_IFGROUPREQ
- unsigned int len;
- struct ifgroupreq ifgr;
- struct ifg_req *ifg;
- int s;
- int ret = 0;
-
- if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- err(1, "socket");
-
- memset(&ifgr, 0, sizeof(ifgr));
- if (strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ) >= IFNAMSIZ)
- errx(1, "interface name too large");
-
- if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) {
- if (errno == EINVAL || errno == ENOTTY)
- goto end;
- err(1, "SIOCGIFGROUP");
- }
-
- len = ifgr.ifgr_len;
- ifgr.ifgr_groups = xcalloc(len/sizeof(struct ifg_req),
- sizeof(struct ifg_req));
- if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1)
- err(1, "SIOCGIFGROUP");
-
- ifg = ifgr.ifgr_groups;
- for (; ifg && len >= sizeof(struct ifg_req); ifg++) {
- len -= sizeof(struct ifg_req);
- if (strcmp(ifg->ifgrq_group, groupname) == 0) {
- ret = 1;
- break;
- }
- }
- free(ifgr.ifgr_groups);
-
-end:
- close(s);
- return ret;
-#else
- return (0);
-#endif
-}
-
-static int
-config_lo_mask_source(struct listen_opts *lo) {
- if (lo->options & LO_MASKSOURCE) {
- yyerror("mask-source already specified");
- return -1;
- }
- lo->options |= LO_MASKSOURCE;
- lo->flags |= F_MASK_SOURCE;
-
- return 0;
-}
-
diff --git a/smtpd/parser.c b/smtpd/parser.c
deleted file mode 100644
index 4c2321ec..00000000
--- a/smtpd/parser.c
+++ /dev/null
@@ -1,341 +0,0 @@
-/* $OpenBSD: parser.c,v 1.42 2020/01/06 11:02:38 gilles Exp $ */
-
-/*
- * Copyright (c) 2013 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/socket.h>
-
-#include <netinet/in.h>
-#include <net/if.h>
-#include <arpa/inet.h>
-
-#include <err.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "parser.h"
-
-uint64_t text_to_evpid(const char *);
-uint32_t text_to_msgid(const char *);
-
-struct node {
- int type;
- const char *token;
- struct node *parent;
- TAILQ_ENTRY(node) entry;
- TAILQ_HEAD(, node) children;
- int (*cmd)(int, struct parameter*);
-};
-
-static struct node *root;
-
-static int text_to_sockaddr(struct sockaddr *, int, const char *);
-
-#define ARGVMAX 64
-
-int
-cmd_install(const char *pattern, int (*cmd)(int, struct parameter*))
-{
- struct node *node, *tmp;
- char *s, *str, *argv[ARGVMAX], **ap;
- int i, n;
-
- /* Tokenize */
- str = s = strdup(pattern);
- if (str == NULL)
- err(1, "strdup");
- n = 0;
- for (ap = argv; n < ARGVMAX && (*ap = strsep(&str, " \t")) != NULL;) {
- if (**ap != '\0') {
- ap++;
- n++;
- }
- }
- *ap = NULL;
-
- if (root == NULL) {
- root = calloc(1, sizeof (*root));
- TAILQ_INIT(&root->children);
- }
- node = root;
-
- for (i = 0; i < n; i++) {
- TAILQ_FOREACH(tmp, &node->children, entry) {
- if (!strcmp(tmp->token, argv[i])) {
- node = tmp;
- break;
- }
- }
- if (tmp == NULL) {
- tmp = calloc(1, sizeof (*tmp));
- TAILQ_INIT(&tmp->children);
- if (!strcmp(argv[i], "<str>"))
- tmp->type = P_STR;
- else if (!strcmp(argv[i], "<int>"))
- tmp->type = P_INT;
- else if (!strcmp(argv[i], "<msgid>"))
- tmp->type = P_MSGID;
- else if (!strcmp(argv[i], "<evpid>"))
- tmp->type = P_EVPID;
- else if (!strcmp(argv[i], "<routeid>"))
- tmp->type = P_ROUTEID;
- else if (!strcmp(argv[i], "<addr>"))
- tmp->type = P_ADDR;
- else
- tmp->type = P_TOKEN;
- tmp->token = strdup(argv[i]);
- tmp->parent = node;
- TAILQ_INSERT_TAIL(&node->children, tmp, entry);
- node = tmp;
- }
- }
-
- if (node->cmd)
- errx(1, "duplicate pattern: %s", pattern);
- node->cmd = cmd;
-
- free(s);
- return (n);
-}
-
-static int
-cmd_check(const char *str, struct node *node, struct parameter *res)
-{
- const char *e;
-
- switch (node->type) {
- case P_TOKEN:
- if (!strcmp(str, node->token))
- return (1);
- return (0);
-
- case P_STR:
- res->u.u_str = str;
- return (1);
-
- case P_INT:
- res->u.u_int = strtonum(str, INT_MIN, INT_MAX, &e);
- if (e)
- return (0);
- return (1);
-
- case P_MSGID:
- if (strlen(str) != 8)
- return (0);
- res->u.u_msgid = text_to_msgid(str);
- if (res->u.u_msgid == 0)
- return (0);
- return (1);
-
- case P_EVPID:
- if (strlen(str) != 16)
- return (0);
- res->u.u_evpid = text_to_evpid(str);
- if (res->u.u_evpid == 0)
- return (0);
- return (1);
-
- case P_ROUTEID:
- res->u.u_routeid = strtonum(str, 1, LLONG_MAX, &e);
- if (e)
- return (0);
- return (1);
-
- case P_ADDR:
- if (text_to_sockaddr((struct sockaddr *)&res->u.u_ss, PF_UNSPEC, str) == 0)
- return (1);
- return (0);
-
- default:
- errx(1, "bad token type: %d", node->type);
- return (0);
- }
-}
-
-int
-cmd_run(int argc, char **argv)
-{
- struct parameter param[ARGVMAX];
- struct node *node, *tmp, *stack[ARGVMAX], *best;
- int i, j, np;
-
- node = root;
- np = 0;
-
- for (i = 0; i < argc; i++) {
- TAILQ_FOREACH(tmp, &node->children, entry) {
- if (cmd_check(argv[i], tmp, &param[np])) {
- stack[i] = tmp;
- node = tmp;
- param[np].type = node->type;
- if (node->type != P_TOKEN)
- np++;
- break;
- }
- }
- if (tmp == NULL) {
- best = NULL;
- TAILQ_FOREACH(tmp, &node->children, entry) {
- if (tmp->type != P_TOKEN)
- continue;
- if (strstr(tmp->token, argv[i]) != tmp->token)
- continue;
- if (best)
- goto fail;
- best = tmp;
- }
- if (best == NULL)
- goto fail;
- stack[i] = best;
- node = best;
- param[np].type = node->type;
- if (node->type != P_TOKEN)
- np++;
- }
- }
-
- if (node->cmd == NULL)
- goto fail;
-
- return (node->cmd(np, np ? param : NULL));
-
-fail:
- if (TAILQ_FIRST(&node->children) == NULL) {
- fprintf(stderr, "invalid command\n");
- return (-1);
- }
-
- fprintf(stderr, "possibilities are:\n");
- TAILQ_FOREACH(tmp, &node->children, entry) {
- for (j = 0; j < i; j++)
- fprintf(stderr, "%s%s", j?" ":"", stack[j]->token);
- fprintf(stderr, "%s%s\n", i?" ":"", tmp->token);
- }
-
- return (-1);
-}
-
-int
-cmd_show_params(int argc, struct parameter *argv)
-{
- int i;
-
- for (i = 0; i < argc; i++) {
- switch(argv[i].type) {
- case P_STR:
- printf(" str:\"%s\"", argv[i].u.u_str);
- break;
- case P_INT:
- printf(" int:%d", argv[i].u.u_int);
- break;
- case P_MSGID:
- printf(" msgid:%08"PRIx32, argv[i].u.u_msgid);
- break;
- case P_EVPID:
- printf(" evpid:%016"PRIx64, argv[i].u.u_evpid);
- break;
- case P_ROUTEID:
- printf(" routeid:%016"PRIx64, argv[i].u.u_routeid);
- break;
- default:
- printf(" ???:%d", argv[i].type);
- }
- }
- printf ("\n");
- return (1);
-}
-
-static int
-text_to_sockaddr(struct sockaddr *sa, int family, const char *str)
-{
- struct in_addr ina;
- struct in6_addr in6a;
- struct sockaddr_in *in;
- struct sockaddr_in6 *in6;
- char *cp, *str2;
- const char *errstr;
-
- switch (family) {
- case PF_UNSPEC:
- if (text_to_sockaddr(sa, PF_INET, str) == 0)
- return (0);
- return text_to_sockaddr(sa, PF_INET6, str);
-
- case PF_INET:
- if (inet_pton(PF_INET, str, &ina) != 1)
- return (-1);
-
- in = (struct sockaddr_in *)sa;
- memset(in, 0, sizeof *in);
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- in->sin_len = sizeof(struct sockaddr_in);
-#endif
- in->sin_family = PF_INET;
- in->sin_addr.s_addr = ina.s_addr;
- return (0);
-
- case PF_INET6:
- cp = strchr(str, SCOPE_DELIMITER);
- if (cp) {
- str2 = strdup(str);
- if (str2 == NULL)
- return (-1);
- str2[cp - str] = '\0';
- if (inet_pton(PF_INET6, str2, &in6a) != 1) {
- free(str2);
- return (-1);
- }
- cp++;
- free(str2);
- } else if (inet_pton(PF_INET6, str, &in6a) != 1)
- return (-1);
-
- in6 = (struct sockaddr_in6 *)sa;
- memset(in6, 0, sizeof *in6);
-#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
- in6->sin6_len = sizeof(struct sockaddr_in6);
-#endif
- in6->sin6_family = PF_INET6;
- in6->sin6_addr = in6a;
-
- if (cp == NULL)
- return (0);
-
- if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
- IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
- IN6_IS_ADDR_MC_NODELOCAL(&in6a))
- if ((in6->sin6_scope_id = if_nametoindex(cp)))
- return (0);
-
- in6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
- if (errstr)
- return (-1);
- return (0);
-
- default:
- break;
- }
-
- return (-1);
-}
diff --git a/smtpd/parser.h b/smtpd/parser.h
deleted file mode 100644
index f0114e9e..00000000
--- a/smtpd/parser.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* $OpenBSD: parser.h,v 1.29 2014/02/04 15:22:39 eric Exp $ */
-
-/*
- * Copyright (c) 2013 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 {
- P_TOKEN,
- P_STR,
- P_INT,
- P_MSGID,
- P_EVPID,
- P_ROUTEID,
- P_ADDR,
-};
-
-struct parameter {
- int type;
- union {
- const char *u_str;
- int u_int;
- uint32_t u_msgid;
- uint64_t u_evpid;
- uint64_t u_routeid;
- struct sockaddr_storage u_ss;
- } u;
-};
-
-int cmd_install(const char *, int (*)(int, struct parameter *));
-int cmd_run(int, char **);
-int cmd_show_params(int argc, struct parameter *argv);
diff --git a/smtpd/pony.c b/smtpd/pony.c
deleted file mode 100644
index 1865b339..00000000
--- a/smtpd/pony.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/* $OpenBSD: pony.c,v 1.27 2019/06/13 11:45:35 eric Exp $ */
-
-/*
- * Copyright (c) 2014 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <limits.h>
-#include <grp.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-void mda_imsg(struct mproc *, struct imsg *);
-void mta_imsg(struct mproc *, struct imsg *);
-void smtp_imsg(struct mproc *, struct imsg *);
-
-static void pony_shutdown(void);
-
-void
-pony_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct msg m;
- int v;
-
- if (imsg == NULL)
- pony_shutdown();
-
- switch (imsg->hdr.type) {
-
- case IMSG_GETADDRINFO:
- case IMSG_GETADDRINFO_END:
- case IMSG_GETNAMEINFO:
- case IMSG_RES_QUERY:
- resolver_dispatch_result(p, imsg);
- return;
-
- case IMSG_CERT_INIT:
- case IMSG_CERT_VERIFY:
- cert_dispatch_result(p, imsg);
- return;
-
- case IMSG_CONF_START:
- return;
- case IMSG_CONF_END:
- smtp_configure();
- return;
- case IMSG_CTL_VERBOSE:
- m_msg(&m, imsg);
- m_get_int(&m, &v);
- m_end(&m);
- log_trace_verbose(v);
- return;
- case IMSG_CTL_PROFILE:
- m_msg(&m, imsg);
- m_get_int(&m, &v);
- m_end(&m);
- profiling = v;
- return;
-
- /* smtp imsg */
- case IMSG_SMTP_CHECK_SENDER:
- case IMSG_SMTP_EXPAND_RCPT:
- case IMSG_SMTP_LOOKUP_HELO:
- case IMSG_SMTP_AUTHENTICATE:
- case IMSG_SMTP_MESSAGE_COMMIT:
- case IMSG_SMTP_MESSAGE_CREATE:
- case IMSG_SMTP_MESSAGE_OPEN:
- case IMSG_FILTER_SMTP_PROTOCOL:
- case IMSG_FILTER_SMTP_DATA_BEGIN:
- case IMSG_QUEUE_ENVELOPE_SUBMIT:
- case IMSG_QUEUE_ENVELOPE_COMMIT:
- case IMSG_QUEUE_SMTP_SESSION:
- case IMSG_CTL_SMTP_SESSION:
- case IMSG_CTL_PAUSE_SMTP:
- case IMSG_CTL_RESUME_SMTP:
- smtp_imsg(p, imsg);
- return;
-
- /* mta imsg */
- case IMSG_QUEUE_TRANSFER:
- case IMSG_MTA_OPEN_MESSAGE:
- case IMSG_MTA_LOOKUP_CREDENTIALS:
- case IMSG_MTA_LOOKUP_SMARTHOST:
- case IMSG_MTA_LOOKUP_SOURCE:
- case IMSG_MTA_LOOKUP_HELO:
- case IMSG_MTA_DNS_HOST:
- case IMSG_MTA_DNS_HOST_END:
- case IMSG_MTA_DNS_MX_PREFERENCE:
- case IMSG_CTL_RESUME_ROUTE:
- case IMSG_CTL_MTA_SHOW_HOSTS:
- case IMSG_CTL_MTA_SHOW_RELAYS:
- case IMSG_CTL_MTA_SHOW_ROUTES:
- case IMSG_CTL_MTA_SHOW_HOSTSTATS:
- case IMSG_CTL_MTA_BLOCK:
- case IMSG_CTL_MTA_UNBLOCK:
- case IMSG_CTL_MTA_SHOW_BLOCK:
- mta_imsg(p, imsg);
- return;
-
- /* mda imsg */
- case IMSG_MDA_LOOKUP_USERINFO:
- case IMSG_QUEUE_DELIVER:
- case IMSG_MDA_OPEN_MESSAGE:
- case IMSG_MDA_FORK:
- case IMSG_MDA_DONE:
- mda_imsg(p, imsg);
- return;
- default:
- break;
- }
-
- errx(1, "session_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
-}
-
-static void
-pony_shutdown(void)
-{
- log_debug("debug: pony agent exiting");
- _exit(0);
-}
-
-int
-pony(void)
-{
- struct passwd *pw;
-
- mda_postfork();
- mta_postfork();
- smtp_postfork();
-
- /* do not purge listeners and pki, they are purged
- * in smtp_configure()
- */
- purge_config(PURGE_TABLES|PURGE_RULES);
-
- if ((pw = getpwnam(SMTPD_USER)) == NULL)
- fatalx("unknown user " SMTPD_USER);
-
- if (chroot(PATH_CHROOT) == -1)
- fatal("pony: chroot");
- if (chdir("/") == -1)
- fatal("pony: chdir(\"/\")");
-
- config_process(PROC_PONY);
-
- if (setgroups(1, &pw->pw_gid) ||
- setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
- setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
- fatal("pony: cannot drop privileges");
-
- imsg_callback = pony_imsg;
- event_init();
-
- mda_postprivdrop();
- mta_postprivdrop();
- smtp_postprivdrop();
-
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, SIG_IGN);
- signal(SIGPIPE, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-
- config_peer(PROC_PARENT);
- config_peer(PROC_QUEUE);
- config_peer(PROC_LKA);
- config_peer(PROC_CONTROL);
- config_peer(PROC_CA);
-
- ca_engine_init();
-
-#if HAVE_PLEDGE
- if (pledge("stdio inet unix recvfd sendfd", NULL) == -1)
- err(1, "pledge");
-#endif
-
- event_dispatch();
- fatalx("exited event loop");
-
- return (0);
-}
diff --git a/smtpd/proxy.c b/smtpd/proxy.c
deleted file mode 100644
index fdaf4f27..00000000
--- a/smtpd/proxy.c
+++ /dev/null
@@ -1,387 +0,0 @@
-/*
- * Copyright (c) 2017 Antoine Kaufmann <toni@famkaufmann.info>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/tree.h>
-#include <sys/un.h>
-
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "smtpd.h"
-
-
-/*
- * The PROXYv2 protocol is described here:
- * http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
- */
-
-#define PROXY_CLOCAL 0x0
-#define PROXY_CPROXY 0x1
-#define PROXY_AF_UNSPEC 0x0
-#define PROXY_AF_INET 0x1
-#define PROXY_AF_INET6 0x2
-#define PROXY_AF_UNIX 0x3
-#define PROXY_TF_UNSPEC 0x0
-#define PROXY_TF_STREAM 0x1
-#define PROXY_TF_DGRAM 0x2
-
-#define PROXY_SESSION_TIMEOUT 300
-
-static const uint8_t pv2_signature[] = {
- 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D,
- 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A
-};
-
-struct proxy_hdr_v2 {
- uint8_t sig[12];
- uint8_t ver_cmd;
- uint8_t fam;
- uint16_t len;
-} __attribute__((packed));
-
-
-struct proxy_addr_ipv4 {
- uint32_t src_addr;
- uint32_t dst_addr;
- uint16_t src_port;
- uint16_t dst_port;
-} __attribute__((packed));
-
-struct proxy_addr_ipv6 {
- uint8_t src_addr[16];
- uint8_t dst_addr[16];
- uint16_t src_port;
- uint16_t dst_port;
-} __attribute__((packed));
-
-struct proxy_addr_unix {
- uint8_t src_addr[108];
- uint8_t dst_addr[108];
-} __attribute__((packed));
-
-union proxy_addr {
- struct proxy_addr_ipv4 ipv4;
- struct proxy_addr_ipv6 ipv6;
- struct proxy_addr_unix un;
-} __attribute__((packed));
-
-struct proxy_session {
- struct listener *l;
- struct io *io;
-
- uint64_t id;
- int fd;
- uint16_t header_len;
- uint16_t header_total;
- uint16_t addr_len;
- uint16_t addr_total;
-
- struct sockaddr_storage ss;
- struct proxy_hdr_v2 hdr;
- union proxy_addr addr;
-
- void (*cb_accepted)(struct listener *, int,
- const struct sockaddr_storage *, struct io *);
- void (*cb_dropped)(struct listener *, int,
- const struct sockaddr_storage *);
-};
-
-static void proxy_io(struct io *, int, void *);
-static void proxy_error(struct proxy_session *, const char *, const char *);
-static int proxy_header_validate(struct proxy_session *);
-static int proxy_translate_ss(struct proxy_session *);
-
-int
-proxy_session(struct listener *listener, int sock,
- const struct sockaddr_storage *ss,
- void (*accepted)(struct listener *, int,
- const struct sockaddr_storage *, struct io *),
- void (*dropped)(struct listener *, int,
- const struct sockaddr_storage *));
-
-int
-proxy_session(struct listener *listener, int sock,
- const struct sockaddr_storage *ss,
- void (*accepted)(struct listener *, int,
- const struct sockaddr_storage *, struct io *),
- void (*dropped)(struct listener *, int,
- const struct sockaddr_storage *))
-{
- struct proxy_session *s;
-
- if ((s = calloc(1, sizeof(*s))) == NULL)
- return (-1);
-
- if ((s->io = io_new()) == NULL) {
- free(s);
- return (-1);
- }
-
- s->id = generate_uid();
- s->l = listener;
- s->fd = sock;
- s->header_len = 0;
- s->addr_len = 0;
- s->ss = *ss;
- s->cb_accepted = accepted;
- s->cb_dropped = dropped;
-
- io_set_callback(s->io, proxy_io, s);
- io_set_fd(s->io, sock);
- io_set_timeout(s->io, PROXY_SESSION_TIMEOUT * 1000);
- io_set_read(s->io);
-
- log_info("%016"PRIx64" smtp event=proxy address=%s",
- s->id, ss_to_text(&s->ss));
-
- return 0;
-}
-
-static void
-proxy_io(struct io *io, int evt, void *arg)
-{
- struct proxy_session *s = arg;
- struct proxy_hdr_v2 *h = &s->hdr;
- uint8_t *buf;
- size_t len, off;
-
- switch (evt) {
-
- case IO_DATAIN:
- buf = io_data(io);
- len = io_datalen(io);
-
- if (s->header_len < sizeof(s->hdr)) {
- /* header is incomplete */
- off = sizeof(s->hdr) - s->header_len;
- off = (len < off ? len : off);
- memcpy((uint8_t *) &s->hdr + s->header_len, buf, off);
-
- s->header_len += off;
- buf += off;
- len -= off;
- io_drop(s->io, off);
-
- if (s->header_len < sizeof(s->hdr)) {
- /* header is still not complete */
- return;
- }
-
- if (proxy_header_validate(s) != 0)
- return;
- }
-
- if (s->addr_len < s->addr_total) {
- /* address is incomplete */
- off = s->addr_total - s->addr_len;
- off = (len < off ? len : off);
- memcpy((uint8_t *) &s->addr + s->addr_len, buf, off);
-
- s->header_len += off;
- s->addr_len += off;
- buf += off;
- len -= off;
- io_drop(s->io, off);
-
- if (s->addr_len < s->addr_total) {
- /* address is still not complete */
- return;
- }
- }
-
- if (s->header_len < s->header_total) {
- /* additional parameters not complete */
- /* these are ignored for now, but we still need to drop
- * the bytes from the buffer */
- off = s->header_total - s->header_len;
- off = (len < off ? len : off);
-
- s->header_len += off;
- io_drop(s->io, off);
-
- if (s->header_len < s->header_total)
- /* not complete yet */
- return;
- }
-
- switch(h->ver_cmd & 0xF) {
- case PROXY_CLOCAL:
- /* local address, no need to modify ss */
- break;
-
- case PROXY_CPROXY:
- if (proxy_translate_ss(s) != 0)
- return;
- break;
-
- default:
- proxy_error(s, "protocol error", "unknown command");
- return;
- }
-
- s->cb_accepted(s->l, s->fd, &s->ss, s->io);
- /* we passed off s->io, so it does not need to be freed here */
- free(s);
- break;
-
- case IO_TIMEOUT:
- proxy_error(s, "timeout", NULL);
- break;
-
- case IO_DISCONNECTED:
- proxy_error(s, "disconnected", NULL);
- break;
-
- case IO_ERROR:
- proxy_error(s, "IO error", io_error(io));
- break;
-
- default:
- fatalx("proxy_io()");
- }
-
-}
-
-static void
-proxy_error(struct proxy_session *s, const char *reason, const char *extra)
-{
- if (extra)
- log_info("proxy %p event=closed address=%s reason=\"%s (%s)\"",
- s, ss_to_text(&s->ss), reason, extra);
- else
- log_info("proxy %p event=closed address=%s reason=\"%s\"",
- s, ss_to_text(&s->ss), reason);
-
- s->cb_dropped(s->l, s->fd, &s->ss);
- io_free(s->io);
- free(s);
-}
-
-static int
-proxy_header_validate(struct proxy_session *s)
-{
- struct proxy_hdr_v2 *h = &s->hdr;
-
- if (memcmp(h->sig, pv2_signature,
- sizeof(pv2_signature)) != 0) {
- proxy_error(s, "protocol error", "invalid signature");
- return (-1);
- }
-
- if ((h->ver_cmd >> 4) != 2) {
- proxy_error(s, "protocol error", "invalid version");
- return (-1);
- }
-
- switch (h->fam) {
- case (PROXY_AF_UNSPEC << 4 | PROXY_TF_UNSPEC):
- s->addr_total = 0;
- break;
-
- case (PROXY_AF_INET << 4 | PROXY_TF_STREAM):
- s->addr_total = sizeof(s->addr.ipv4);
- break;
-
- case (PROXY_AF_INET6 << 4 | PROXY_TF_STREAM):
- s->addr_total = sizeof(s->addr.ipv6);
- break;
-
- case (PROXY_AF_UNIX << 4 | PROXY_TF_STREAM):
- s->addr_total = sizeof(s->addr.un);
- break;
-
- default:
- proxy_error(s, "protocol error", "unsupported address family");
- return (-1);
- }
-
- s->header_total = ntohs(h->len);
- if (s->header_total > UINT16_MAX - sizeof(struct proxy_hdr_v2)) {
- proxy_error(s, "protocol error", "header too long");
- return (-1);
- }
- s->header_total += sizeof(struct proxy_hdr_v2);
-
- if (s->header_total < sizeof(struct proxy_hdr_v2) + s->addr_total) {
- proxy_error(s, "protocol error", "address info too short");
- return (-1);
- }
-
- return 0;
-}
-
-static int
-proxy_translate_ss(struct proxy_session *s)
-{
- struct sockaddr_in *sin = (struct sockaddr_in *) &s->ss;
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &s->ss;
- struct sockaddr_un *sun = (struct sockaddr_un *) &s->ss;
- size_t sun_len;
-
- switch (s->hdr.fam) {
- case (PROXY_AF_UNSPEC << 4 | PROXY_TF_UNSPEC):
- /* unspec: only supported for local */
- proxy_error(s, "address translation", "UNSPEC family not "
- "supported for PROXYing");
- return (-1);
-
- case (PROXY_AF_INET << 4 | PROXY_TF_STREAM):
- memset(&s->ss, 0, sizeof(s->ss));
- sin->sin_family = AF_INET;
- sin->sin_port = s->addr.ipv4.src_port;
- sin->sin_addr.s_addr = s->addr.ipv4.src_addr;
- break;
-
- case (PROXY_AF_INET6 << 4 | PROXY_TF_STREAM):
- memset(&s->ss, 0, sizeof(s->ss));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_port = s->addr.ipv6.src_port;
- memcpy(sin6->sin6_addr.s6_addr, s->addr.ipv6.src_addr,
- sizeof(s->addr.ipv6.src_addr));
- break;
-
- case (PROXY_AF_UNIX << 4 | PROXY_TF_STREAM):
- memset(&s->ss, 0, sizeof(s->ss));
- sun_len = strnlen(s->addr.un.src_addr,
- sizeof(s->addr.un.src_addr));
- if (sun_len > sizeof(sun->sun_path)) {
- proxy_error(s, "address translation", "Unix socket path"
- " longer than supported");
- return (-1);
- }
- sun->sun_family = AF_UNIX;
- memcpy(sun->sun_path, s->addr.un.src_addr, sun_len);
- break;
-
- default:
- fatalx("proxy_translate_ss()");
- }
-
- return 0;
-}
diff --git a/smtpd/queue.c b/smtpd/queue.c
deleted file mode 100644
index 434e3647..00000000
--- a/smtpd/queue.c
+++ /dev/null
@@ -1,750 +0,0 @@
-/* $OpenBSD: queue.c,v 1.190 2020/04/22 11:35:34 eric Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <err.h>
-#include <event.h>
-#include <fcntl.h>
-#include <grp.h> /* needed for setgroups */
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static void queue_imsg(struct mproc *, struct imsg *);
-static void queue_timeout(int, short, void *);
-static void queue_bounce(struct envelope *, struct delivery_bounce *);
-static void queue_shutdown(void);
-static void queue_log(const struct envelope *, const char *, const char *);
-static void queue_msgid_walk(int, short, void *);
-
-
-static void
-queue_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct delivery_bounce bounce;
- struct msg_walkinfo *wi;
- struct timeval tv;
- struct bounce_req_msg *req_bounce;
- struct envelope evp;
- struct msg m;
- const char *reason;
- uint64_t reqid, evpid, holdq;
- uint32_t msgid;
- time_t nexttry;
- size_t n_evp;
- int fd, mta_ext, ret, v, flags, code;
-
- if (imsg == NULL)
- queue_shutdown();
-
- memset(&bounce, 0, sizeof(struct delivery_bounce));
-
- switch (imsg->hdr.type) {
- case IMSG_SMTP_MESSAGE_CREATE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_end(&m);
-
- ret = queue_message_create(&msgid);
-
- m_create(p, IMSG_SMTP_MESSAGE_CREATE, 0, 0, -1);
- m_add_id(p, reqid);
- if (ret == 0)
- m_add_int(p, 0);
- else {
- m_add_int(p, 1);
- m_add_msgid(p, msgid);
- }
- m_close(p);
- return;
-
- case IMSG_SMTP_MESSAGE_ROLLBACK:
- m_msg(&m, imsg);
- m_get_msgid(&m, &msgid);
- m_end(&m);
-
- queue_message_delete(msgid);
-
- m_create(p_scheduler, IMSG_QUEUE_MESSAGE_ROLLBACK,
- 0, 0, -1);
- m_add_msgid(p_scheduler, msgid);
- m_close(p_scheduler);
- return;
-
- case IMSG_SMTP_MESSAGE_COMMIT:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_msgid(&m, &msgid);
- m_end(&m);
-
- ret = queue_message_commit(msgid);
-
- m_create(p, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1);
- m_add_id(p, reqid);
- m_add_int(p, (ret == 0) ? 0 : 1);
- m_close(p);
-
- if (ret) {
- m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT,
- 0, 0, -1);
- m_add_msgid(p_scheduler, msgid);
- m_close(p_scheduler);
- }
- return;
-
- case IMSG_SMTP_MESSAGE_OPEN:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_msgid(&m, &msgid);
- m_end(&m);
-
- fd = queue_message_fd_rw(msgid);
-
- m_create(p, IMSG_SMTP_MESSAGE_OPEN, 0, 0, fd);
- m_add_id(p, reqid);
- m_add_int(p, (fd == -1) ? 0 : 1);
- m_close(p);
- return;
-
- case IMSG_QUEUE_SMTP_SESSION:
- bounce_fd(imsg->fd);
- return;
-
- case IMSG_LKA_ENVELOPE_SUBMIT:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_envelope(&m, &evp);
- m_end(&m);
-
- if (evp.id == 0)
- log_warnx("warn: imsg_queue_submit_envelope: evpid=0");
- if (evpid_to_msgid(evp.id) == 0)
- log_warnx("warn: imsg_queue_submit_envelope: msgid=0, "
- "evpid=%016"PRIx64, evp.id);
- ret = queue_envelope_create(&evp);
- m_create(p_pony, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1);
- m_add_id(p_pony, reqid);
- if (ret == 0)
- m_add_int(p_pony, 0);
- else {
- m_add_int(p_pony, 1);
- m_add_evpid(p_pony, evp.id);
- }
- m_close(p_pony);
- if (ret) {
- m_create(p_scheduler,
- IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1);
- m_add_envelope(p_scheduler, &evp);
- m_close(p_scheduler);
- }
- return;
-
- case IMSG_LKA_ENVELOPE_COMMIT:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_end(&m);
- m_create(p_pony, IMSG_QUEUE_ENVELOPE_COMMIT, 0, 0, -1);
- m_add_id(p_pony, reqid);
- m_add_int(p_pony, 1);
- m_close(p_pony);
- return;
-
- case IMSG_SCHED_ENVELOPE_REMOVE:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_end(&m);
-
- m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_ACK, 0, 0, -1);
- m_add_evpid(p_scheduler, evpid);
- m_close(p_scheduler);
-
- /* already removed by scheduler */
- if (queue_envelope_load(evpid, &evp) == 0)
- return;
-
- queue_log(&evp, "Remove", "Removed by administrator");
- queue_envelope_delete(evpid);
- return;
-
- case IMSG_SCHED_ENVELOPE_EXPIRE:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_end(&m);
-
- m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_ACK, 0, 0, -1);
- m_add_evpid(p_scheduler, evpid);
- m_close(p_scheduler);
-
- /* already removed by scheduler*/
- if (queue_envelope_load(evpid, &evp) == 0)
- return;
-
- bounce.type = B_FAILED;
- envelope_set_errormsg(&evp, "Envelope expired");
- envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL);
- envelope_set_esc_code(&evp, ESC_DELIVERY_TIME_EXPIRED);
- queue_bounce(&evp, &bounce);
- queue_log(&evp, "Expire", "Envelope expired");
- queue_envelope_delete(evpid);
- return;
-
- case IMSG_SCHED_ENVELOPE_BOUNCE:
- CHECK_IMSG_DATA_SIZE(imsg, sizeof *req_bounce);
- req_bounce = imsg->data;
- evpid = req_bounce->evpid;
-
- if (queue_envelope_load(evpid, &evp) == 0) {
- log_warnx("queue: bounce: failed to load envelope");
- m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1);
- m_add_evpid(p_scheduler, evpid);
- m_add_u32(p_scheduler, 0); /* not in-flight */
- m_close(p_scheduler);
- return;
- }
- queue_bounce(&evp, &req_bounce->bounce);
- evp.lastbounce = req_bounce->timestamp;
- if (!queue_envelope_update(&evp))
- log_warnx("warn: could not update envelope %016"PRIx64, evpid);
- return;
-
- case IMSG_SCHED_ENVELOPE_DELIVER:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_end(&m);
- if (queue_envelope_load(evpid, &evp) == 0) {
- log_warnx("queue: deliver: failed to load envelope");
- m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1);
- m_add_evpid(p_scheduler, evpid);
- m_add_u32(p_scheduler, 1); /* in-flight */
- m_close(p_scheduler);
- return;
- }
- evp.lasttry = time(NULL);
- m_create(p_pony, IMSG_QUEUE_DELIVER, 0, 0, -1);
- m_add_envelope(p_pony, &evp);
- m_close(p_pony);
- return;
-
- case IMSG_SCHED_ENVELOPE_INJECT:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_end(&m);
- bounce_add(evpid);
- return;
-
- case IMSG_SCHED_ENVELOPE_TRANSFER:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_end(&m);
- if (queue_envelope_load(evpid, &evp) == 0) {
- log_warnx("queue: failed to load envelope");
- m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1);
- m_add_evpid(p_scheduler, evpid);
- m_add_u32(p_scheduler, 1); /* in-flight */
- m_close(p_scheduler);
- return;
- }
- evp.lasttry = time(NULL);
- m_create(p_pony, IMSG_QUEUE_TRANSFER, 0, 0, -1);
- m_add_envelope(p_pony, &evp);
- m_close(p_pony);
- return;
-
- case IMSG_CTL_LIST_ENVELOPES:
- if (imsg->hdr.len == sizeof imsg->hdr) {
- m_forward(p_control, imsg);
- return;
- }
-
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_get_int(&m, &flags);
- m_get_time(&m, &nexttry);
- m_end(&m);
-
- if (queue_envelope_load(evpid, &evp) == 0)
- return; /* Envelope is gone, drop it */
-
- /*
- * XXX consistency: The envelope might already be on
- * its way back to the scheduler. We need to detect
- * this properly and report that state.
- */
- if (flags & EF_INFLIGHT) {
- /*
- * Not exactly correct but pretty close: The
- * value is not recorded on the envelope unless
- * a tempfail occurs.
- */
- evp.lasttry = nexttry;
- }
-
- m_create(p_control, IMSG_CTL_LIST_ENVELOPES,
- imsg->hdr.peerid, 0, -1);
- m_add_int(p_control, flags);
- m_add_time(p_control, nexttry);
- m_add_envelope(p_control, &evp);
- m_close(p_control);
- return;
-
- case IMSG_MDA_OPEN_MESSAGE:
- case IMSG_MTA_OPEN_MESSAGE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_msgid(&m, &msgid);
- m_end(&m);
- fd = queue_message_fd_r(msgid);
- m_create(p, imsg->hdr.type, 0, 0, fd);
- m_add_id(p, reqid);
- m_close(p);
- return;
-
- case IMSG_MDA_DELIVERY_OK:
- case IMSG_MTA_DELIVERY_OK:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- if (imsg->hdr.type == IMSG_MTA_DELIVERY_OK)
- m_get_int(&m, &mta_ext);
- m_end(&m);
- if (queue_envelope_load(evpid, &evp) == 0) {
- log_warn("queue: dsn: failed to load envelope");
- return;
- }
- if (evp.dsn_notify & DSN_SUCCESS) {
- bounce.type = B_DELIVERED;
- bounce.dsn_ret = evp.dsn_ret;
- envelope_set_esc_class(&evp, ESC_STATUS_OK);
- if (imsg->hdr.type == IMSG_MDA_DELIVERY_OK)
- queue_bounce(&evp, &bounce);
- else if (imsg->hdr.type == IMSG_MTA_DELIVERY_OK &&
- (mta_ext & MTA_EXT_DSN) == 0) {
- bounce.mta_without_dsn = 1;
- queue_bounce(&evp, &bounce);
- }
- }
- queue_envelope_delete(evpid);
- m_create(p_scheduler, IMSG_QUEUE_DELIVERY_OK, 0, 0, -1);
- m_add_evpid(p_scheduler, evpid);
- m_close(p_scheduler);
- return;
-
- case IMSG_MDA_DELIVERY_TEMPFAIL:
- case IMSG_MTA_DELIVERY_TEMPFAIL:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_get_string(&m, &reason);
- m_get_int(&m, &code);
- m_end(&m);
- if (queue_envelope_load(evpid, &evp) == 0) {
- log_warnx("queue: tempfail: failed to load envelope");
- m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1);
- m_add_evpid(p_scheduler, evpid);
- m_add_u32(p_scheduler, 1); /* in-flight */
- m_close(p_scheduler);
- return;
- }
- envelope_set_errormsg(&evp, "%s", reason);
- envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL);
- envelope_set_esc_code(&evp, code);
- evp.retry++;
- if (!queue_envelope_update(&evp))
- log_warnx("warn: could not update envelope %016"PRIx64, evpid);
- m_create(p_scheduler, IMSG_QUEUE_DELIVERY_TEMPFAIL, 0, 0, -1);
- m_add_envelope(p_scheduler, &evp);
- m_close(p_scheduler);
- return;
-
- case IMSG_MDA_DELIVERY_PERMFAIL:
- case IMSG_MTA_DELIVERY_PERMFAIL:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_get_string(&m, &reason);
- m_get_int(&m, &code);
- m_end(&m);
- if (queue_envelope_load(evpid, &evp) == 0) {
- log_warnx("queue: permfail: failed to load envelope");
- m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1);
- m_add_evpid(p_scheduler, evpid);
- m_add_u32(p_scheduler, 1); /* in-flight */
- m_close(p_scheduler);
- return;
- }
- bounce.type = B_FAILED;
- envelope_set_errormsg(&evp, "%s", reason);
- envelope_set_esc_class(&evp, ESC_STATUS_PERMFAIL);
- envelope_set_esc_code(&evp, code);
- queue_bounce(&evp, &bounce);
- queue_envelope_delete(evpid);
- m_create(p_scheduler, IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1);
- m_add_evpid(p_scheduler, evpid);
- m_close(p_scheduler);
- return;
-
- case IMSG_MDA_DELIVERY_LOOP:
- case IMSG_MTA_DELIVERY_LOOP:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_end(&m);
- if (queue_envelope_load(evpid, &evp) == 0) {
- log_warnx("queue: loop: failed to load envelope");
- m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1);
- m_add_evpid(p_scheduler, evpid);
- m_add_u32(p_scheduler, 1); /* in-flight */
- m_close(p_scheduler);
- return;
- }
- envelope_set_errormsg(&evp, "%s", "Loop detected");
- envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL);
- envelope_set_esc_code(&evp, ESC_ROUTING_LOOP_DETECTED);
- bounce.type = B_FAILED;
- queue_bounce(&evp, &bounce);
- queue_envelope_delete(evp.id);
- m_create(p_scheduler, IMSG_QUEUE_DELIVERY_LOOP, 0, 0, -1);
- m_add_evpid(p_scheduler, evp.id);
- m_close(p_scheduler);
- return;
-
- case IMSG_MTA_DELIVERY_HOLD:
- case IMSG_MDA_DELIVERY_HOLD:
- imsg->hdr.type = IMSG_QUEUE_HOLDQ_HOLD;
- m_forward(p_scheduler, imsg);
- return;
-
- case IMSG_MTA_SCHEDULE:
- imsg->hdr.type = IMSG_QUEUE_ENVELOPE_SCHEDULE;
- m_forward(p_scheduler, imsg);
- return;
-
- case IMSG_MTA_HOLDQ_RELEASE:
- case IMSG_MDA_HOLDQ_RELEASE:
- m_msg(&m, imsg);
- m_get_id(&m, &holdq);
- m_get_int(&m, &v);
- m_end(&m);
- m_create(p_scheduler, IMSG_QUEUE_HOLDQ_RELEASE, 0, 0, -1);
- if (imsg->hdr.type == IMSG_MTA_HOLDQ_RELEASE)
- m_add_int(p_scheduler, D_MTA);
- else
- m_add_int(p_scheduler, D_MDA);
- m_add_id(p_scheduler, holdq);
- m_add_int(p_scheduler, v);
- m_close(p_scheduler);
- return;
-
- case IMSG_CTL_PAUSE_MDA:
- case IMSG_CTL_PAUSE_MTA:
- case IMSG_CTL_RESUME_MDA:
- case IMSG_CTL_RESUME_MTA:
- m_forward(p_scheduler, imsg);
- return;
-
- case IMSG_CTL_VERBOSE:
- m_msg(&m, imsg);
- m_get_int(&m, &v);
- m_end(&m);
- log_trace_verbose(v);
- return;
-
- case IMSG_CTL_PROFILE:
- m_msg(&m, imsg);
- m_get_int(&m, &v);
- m_end(&m);
- profiling = v;
- return;
-
- case IMSG_CTL_DISCOVER_EVPID:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_end(&m);
- if (queue_envelope_load(evpid, &evp) == 0) {
- log_warnx("queue: discover: failed to load "
- "envelope %016" PRIx64, evpid);
- n_evp = 0;
- m_compose(p_control, imsg->hdr.type,
- imsg->hdr.peerid, 0, -1,
- &n_evp, sizeof n_evp);
- return;
- }
-
- m_create(p_scheduler, IMSG_QUEUE_DISCOVER_EVPID,
- 0, 0, -1);
- m_add_envelope(p_scheduler, &evp);
- m_close(p_scheduler);
-
- m_create(p_scheduler, IMSG_QUEUE_DISCOVER_MSGID,
- 0, 0, -1);
- m_add_msgid(p_scheduler, evpid_to_msgid(evpid));
- m_close(p_scheduler);
- n_evp = 1;
- m_compose(p_control, imsg->hdr.type, imsg->hdr.peerid,
- 0, -1, &n_evp, sizeof n_evp);
- return;
-
- case IMSG_CTL_DISCOVER_MSGID:
- m_msg(&m, imsg);
- m_get_msgid(&m, &msgid);
- m_end(&m);
- /* handle concurrent walk requests */
- wi = xcalloc(1, sizeof *wi);
- wi->msgid = msgid;
- wi->peerid = imsg->hdr.peerid;
- evtimer_set(&wi->ev, queue_msgid_walk, wi);
- tv.tv_sec = 0;
- tv.tv_usec = 10;
- evtimer_add(&wi->ev, &tv);
- return;
- }
-
- errx(1, "queue_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
-}
-
-static void
-queue_msgid_walk(int fd, short event, void *arg)
-{
- struct envelope evp;
- struct timeval tv;
- struct msg_walkinfo *wi = arg;
- int r;
-
- r = queue_message_walk(&evp, wi->msgid, &wi->done, &wi->data);
- if (r == -1) {
- if (wi->n_evp) {
- m_create(p_scheduler, IMSG_QUEUE_DISCOVER_MSGID,
- 0, 0, -1);
- m_add_msgid(p_scheduler, wi->msgid);
- m_close(p_scheduler);
- }
-
- m_compose(p_control, IMSG_CTL_DISCOVER_MSGID, wi->peerid, 0, -1,
- &wi->n_evp, sizeof wi->n_evp);
- evtimer_del(&wi->ev);
- free(wi);
- return;
- }
-
- if (r) {
- m_create(p_scheduler, IMSG_QUEUE_DISCOVER_EVPID, 0, 0, -1);
- m_add_envelope(p_scheduler, &evp);
- m_close(p_scheduler);
- wi->n_evp += 1;
- }
-
- tv.tv_sec = 0;
- tv.tv_usec = 10;
- evtimer_set(&wi->ev, queue_msgid_walk, wi);
- evtimer_add(&wi->ev, &tv);
-}
-
-static void
-queue_bounce(struct envelope *e, struct delivery_bounce *d)
-{
- struct envelope b;
-
- b = *e;
- b.type = D_BOUNCE;
- b.agent.bounce = *d;
- b.retry = 0;
- b.lasttry = 0;
- b.creation = time(NULL);
- b.ttl = 3600 * 24 * 7;
-
- if (e->dsn_notify & DSN_NEVER)
- return;
-
- if (b.id == 0)
- log_warnx("warn: queue_bounce: evpid=0");
- if (evpid_to_msgid(b.id) == 0)
- log_warnx("warn: queue_bounce: msgid=0, evpid=%016"PRIx64,
- b.id);
- if (e->type == D_BOUNCE) {
- log_warnx("warn: queue: double bounce!");
- } else if (e->sender.user[0] == '\0') {
- log_warnx("warn: queue: no return path!");
- } else if (!queue_envelope_create(&b)) {
- log_warnx("warn: queue: cannot bounce!");
- } else {
- log_debug("debug: queue: bouncing evp:%016" PRIx64
- " as evp:%016" PRIx64, e->id, b.id);
-
- m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1);
- m_add_envelope(p_scheduler, &b);
- m_close(p_scheduler);
-
- m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT, 0, 0, -1);
- m_add_msgid(p_scheduler, evpid_to_msgid(b.id));
- m_close(p_scheduler);
-
- stat_increment("queue.bounce", 1);
- }
-}
-
-static void
-queue_shutdown(void)
-{
- log_debug("debug: queue agent exiting");
- queue_close();
- _exit(0);
-}
-
-int
-queue(void)
-{
- struct passwd *pw;
- struct timeval tv;
- struct event ev_qload;
-
- purge_config(PURGE_EVERYTHING & ~PURGE_DISPATCHERS);
-
- if ((pw = getpwnam(SMTPD_QUEUE_USER)) == NULL)
- if ((pw = getpwnam(SMTPD_USER)) == NULL)
- fatalx("unknown user " SMTPD_USER);
-
- env->sc_queue_flags |= QUEUE_EVPCACHE;
- env->sc_queue_evpcache_size = 1024;
-
- if (chroot(PATH_SPOOL) == -1)
- fatal("queue: chroot");
- if (chdir("/") == -1)
- fatal("queue: chdir(\"/\")");
-
- config_process(PROC_QUEUE);
-
- if (env->sc_queue_flags & QUEUE_COMPRESSION)
- log_info("queue: queue compression enabled");
-
- if (env->sc_queue_key) {
- if (!crypto_setup(env->sc_queue_key, strlen(env->sc_queue_key)))
- fatalx("crypto_setup: invalid key for queue encryption");
- log_info("queue: queue encryption enabled");
- }
-
- if (setgroups(1, &pw->pw_gid) ||
- setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
- setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
- fatal("queue: cannot drop privileges");
-
- imsg_callback = queue_imsg;
- event_init();
-
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, SIG_IGN);
- signal(SIGPIPE, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-
- config_peer(PROC_PARENT);
- config_peer(PROC_CONTROL);
- config_peer(PROC_LKA);
- config_peer(PROC_SCHEDULER);
- config_peer(PROC_PONY);
-
- /* setup queue loading task */
- evtimer_set(&ev_qload, queue_timeout, &ev_qload);
- tv.tv_sec = 0;
- tv.tv_usec = 10;
- evtimer_add(&ev_qload, &tv);
-
-#if HAVE_PLEDGE
- if (pledge("stdio rpath wpath cpath flock recvfd sendfd", NULL) == -1)
- err(1, "pledge");
-#endif
-
- event_dispatch();
- fatalx("exited event loop");
-
- return (0);
-}
-
-static void
-queue_timeout(int fd, short event, void *p)
-{
- static uint32_t msgid = 0;
- struct envelope evp;
- struct event *ev = p;
- struct timeval tv;
- int r;
-
- r = queue_envelope_walk(&evp);
- if (r == -1) {
- if (msgid) {
- m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT,
- 0, 0, -1);
- m_add_msgid(p_scheduler, msgid);
- m_close(p_scheduler);
- }
- log_debug("debug: queue: done loading queue into scheduler");
- return;
- }
-
- if (r) {
- if (msgid && evpid_to_msgid(evp.id) != msgid) {
- m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT,
- 0, 0, -1);
- m_add_msgid(p_scheduler, msgid);
- m_close(p_scheduler);
- }
- msgid = evpid_to_msgid(evp.id);
- m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1);
- m_add_envelope(p_scheduler, &evp);
- m_close(p_scheduler);
- }
-
- tv.tv_sec = 0;
- tv.tv_usec = 10;
- evtimer_add(ev, &tv);
-}
-
-static void
-queue_log(const struct envelope *e, const char *prefix, const char *status)
-{
- char rcpt[LINE_MAX];
-
- (void)strlcpy(rcpt, "-", sizeof rcpt);
- if (strcmp(e->rcpt.user, e->dest.user) ||
- strcmp(e->rcpt.domain, e->dest.domain))
- (void)snprintf(rcpt, sizeof rcpt, "%s@%s",
- e->rcpt.user, e->rcpt.domain);
-
- log_info("%s: %s for %016" PRIx64 ": from=<%s@%s>, to=<%s@%s>, "
- "rcpt=<%s>, delay=%s, stat=%s",
- e->type == D_MDA ? "delivery" : "relay",
- prefix,
- e->id, e->sender.user, e->sender.domain,
- e->dest.user, e->dest.domain,
- rcpt,
- duration_to_text(time(NULL) - e->creation),
- status);
-}
diff --git a/smtpd/queue_backend.c b/smtpd/queue_backend.c
deleted file mode 100644
index fa945f47..00000000
--- a/smtpd/queue_backend.c
+++ /dev/null
@@ -1,806 +0,0 @@
-/* $OpenBSD: queue_backend.c,v 1.66 2020/04/22 11:35:34 eric Exp $ */
-
-/*
- * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <imsg.h>
-#include <limits.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static const char* envelope_validate(struct envelope *);
-
-extern struct queue_backend queue_backend_fs;
-extern struct queue_backend queue_backend_null;
-extern struct queue_backend queue_backend_proc;
-extern struct queue_backend queue_backend_ram;
-
-static void queue_envelope_cache_add(struct envelope *);
-static void queue_envelope_cache_update(struct envelope *);
-static void queue_envelope_cache_del(uint64_t evpid);
-
-TAILQ_HEAD(evplst, envelope);
-
-static struct tree evpcache_tree;
-static struct evplst evpcache_list;
-static struct queue_backend *backend;
-
-static int (*handler_close)(void);
-static int (*handler_message_create)(uint32_t *);
-static int (*handler_message_commit)(uint32_t, const char*);
-static int (*handler_message_delete)(uint32_t);
-static int (*handler_message_fd_r)(uint32_t);
-static int (*handler_envelope_create)(uint32_t, const char *, size_t, uint64_t *);
-static int (*handler_envelope_delete)(uint64_t);
-static int (*handler_envelope_update)(uint64_t, const char *, size_t);
-static int (*handler_envelope_load)(uint64_t, char *, size_t);
-static int (*handler_envelope_walk)(uint64_t *, char *, size_t);
-static int (*handler_message_walk)(uint64_t *, char *, size_t,
- uint32_t, int *, void **);
-
-#ifdef QUEUE_PROFILING
-
-static struct {
- struct timespec t0;
- const char *name;
-} profile;
-
-static inline void profile_enter(const char *name)
-{
- if ((profiling & PROFILE_QUEUE) == 0)
- return;
-
- profile.name = name;
- clock_gettime(CLOCK_MONOTONIC, &profile.t0);
-}
-
-static inline void profile_leave(void)
-{
- struct timespec t1, dt;
-
- if ((profiling & PROFILE_QUEUE) == 0)
- return;
-
- clock_gettime(CLOCK_MONOTONIC, &t1);
- timespecsub(&t1, &profile.t0, &dt);
- log_debug("profile-queue: %s %lld.%09ld", profile.name,
- (long long)dt.tv_sec, dt.tv_nsec);
-}
-#else
-#define profile_enter(x) do {} while (0)
-#define profile_leave() do {} while (0)
-#endif
-
-static int
-queue_message_path(uint32_t msgid, char *buf, size_t len)
-{
- return bsnprintf(buf, len, "%s/%08"PRIx32, PATH_TEMPORARY, msgid);
-}
-
-int
-queue_init(const char *name, int server)
-{
- struct passwd *pwq;
- struct group *gr;
- int r;
-
- pwq = getpwnam(SMTPD_QUEUE_USER);
- if (pwq == NULL)
- errx(1, "unknown user %s", SMTPD_QUEUE_USER);
-
- gr = getgrnam(SMTPD_QUEUE_GROUP);
- if (gr == NULL)
- errx(1, "unknown group %s", SMTPD_QUEUE_GROUP);
-
- tree_init(&evpcache_tree);
- TAILQ_INIT(&evpcache_list);
-
- if (!strcmp(name, "fs"))
- backend = &queue_backend_fs;
- else if (!strcmp(name, "null"))
- backend = &queue_backend_null;
- else if (!strcmp(name, "ram"))
- backend = &queue_backend_ram;
- else
- backend = &queue_backend_proc;
-
- if (server) {
- if (ckdir(PATH_SPOOL, 0711, 0, 0, 1) == 0)
- errx(1, "error in spool directory setup");
- if (ckdir(PATH_SPOOL PATH_OFFLINE, 0770, 0, gr->gr_gid, 1) == 0)
- errx(1, "error in offline directory setup");
- if (ckdir(PATH_SPOOL PATH_PURGE, 0700, pwq->pw_uid, 0, 1) == 0)
- errx(1, "error in purge directory setup");
-
- mvpurge(PATH_SPOOL PATH_TEMPORARY, PATH_SPOOL PATH_PURGE);
-
- if (ckdir(PATH_SPOOL PATH_TEMPORARY, 0700, pwq->pw_uid, 0, 1) == 0)
- errx(1, "error in purge directory setup");
- }
-
- r = backend->init(pwq, server, name);
-
- log_trace(TRACE_QUEUE, "queue-backend: queue_init(%d) -> %d", server, r);
-
- return (r);
-}
-
-int
-queue_close(void)
-{
- if (handler_close)
- return (handler_close());
-
- return (1);
-}
-
-int
-queue_message_create(uint32_t *msgid)
-{
- int r;
-
- profile_enter("queue_message_create");
- r = handler_message_create(msgid);
- profile_leave();
-
- log_trace(TRACE_QUEUE,
- "queue-backend: queue_message_create() -> %d (%08"PRIx32")",
- r, *msgid);
-
- return (r);
-}
-
-int
-queue_message_delete(uint32_t msgid)
-{
- char msgpath[PATH_MAX];
- uint64_t evpid;
- void *iter;
- int r;
-
- profile_enter("queue_message_delete");
- r = handler_message_delete(msgid);
- profile_leave();
-
- /* in case the message is incoming */
- queue_message_path(msgid, msgpath, sizeof(msgpath));
- unlink(msgpath);
-
- /* remove remaining envelopes from the cache if any (on rollback) */
- evpid = msgid_to_evpid(msgid);
- for (;;) {
- iter = NULL;
- if (!tree_iterfrom(&evpcache_tree, &iter, evpid, &evpid, NULL))
- break;
- if (evpid_to_msgid(evpid) != msgid)
- break;
- queue_envelope_cache_del(evpid);
- }
-
- log_trace(TRACE_QUEUE,
- "queue-backend: queue_message_delete(%08"PRIx32") -> %d", msgid, r);
-
- return (r);
-}
-
-int
-queue_message_commit(uint32_t msgid)
-{
- int r;
- char msgpath[PATH_MAX];
- char tmppath[PATH_MAX];
- FILE *ifp = NULL;
- FILE *ofp = NULL;
-
- profile_enter("queue_message_commit");
-
- queue_message_path(msgid, msgpath, sizeof(msgpath));
-
- if (env->sc_queue_flags & QUEUE_COMPRESSION) {
- bsnprintf(tmppath, sizeof tmppath, "%s.comp", msgpath);
- ifp = fopen(msgpath, "r");
- ofp = fopen(tmppath, "w+");
- if (ifp == NULL || ofp == NULL)
- goto err;
- if (!compress_file(ifp, ofp))
- goto err;
- fclose(ifp);
- fclose(ofp);
- ifp = NULL;
- ofp = NULL;
-
- if (rename(tmppath, msgpath) == -1) {
- if (errno == ENOSPC)
- return (0);
- unlink(tmppath);
- log_warn("rename");
- return (0);
- }
- }
-
- if (env->sc_queue_flags & QUEUE_ENCRYPTION) {
- bsnprintf(tmppath, sizeof tmppath, "%s.enc", msgpath);
- ifp = fopen(msgpath, "r");
- ofp = fopen(tmppath, "w+");
- if (ifp == NULL || ofp == NULL)
- goto err;
- if (!crypto_encrypt_file(ifp, ofp))
- goto err;
- fclose(ifp);
- fclose(ofp);
- ifp = NULL;
- ofp = NULL;
-
- if (rename(tmppath, msgpath) == -1) {
- if (errno == ENOSPC)
- return (0);
- unlink(tmppath);
- log_warn("rename");
- return (0);
- }
- }
-
- r = handler_message_commit(msgid, msgpath);
- profile_leave();
-
- /* in case it's not done by the backend */
- unlink(msgpath);
-
- log_trace(TRACE_QUEUE,
- "queue-backend: queue_message_commit(%08"PRIx32") -> %d",
- msgid, r);
-
- return (r);
-
-err:
- if (ifp)
- fclose(ifp);
- if (ofp)
- fclose(ofp);
- return 0;
-}
-
-int
-queue_message_fd_r(uint32_t msgid)
-{
- int fdin = -1, fdout = -1, fd = -1;
- FILE *ifp = NULL;
- FILE *ofp = NULL;
-
- profile_enter("queue_message_fd_r");
- fdin = handler_message_fd_r(msgid);
- profile_leave();
-
- log_trace(TRACE_QUEUE,
- "queue-backend: queue_message_fd_r(%08"PRIx32") -> %d", msgid, fdin);
-
- if (fdin == -1)
- return (-1);
-
- if (env->sc_queue_flags & QUEUE_ENCRYPTION) {
- if ((fdout = mktmpfile()) == -1)
- goto err;
- if ((fd = dup(fdout)) == -1)
- goto err;
- if ((ifp = fdopen(fdin, "r")) == NULL)
- goto err;
- fdin = fd;
- fd = -1;
- if ((ofp = fdopen(fdout, "w+")) == NULL)
- goto err;
-
- if (!crypto_decrypt_file(ifp, ofp))
- goto err;
-
- fclose(ifp);
- ifp = NULL;
- fclose(ofp);
- ofp = NULL;
- lseek(fdin, SEEK_SET, 0);
- }
-
- if (env->sc_queue_flags & QUEUE_COMPRESSION) {
- if ((fdout = mktmpfile()) == -1)
- goto err;
- if ((fd = dup(fdout)) == -1)
- goto err;
- if ((ifp = fdopen(fdin, "r")) == NULL)
- goto err;
- fdin = fd;
- fd = -1;
- if ((ofp = fdopen(fdout, "w+")) == NULL)
- goto err;
-
- if (!uncompress_file(ifp, ofp))
- goto err;
-
- fclose(ifp);
- ifp = NULL;
- fclose(ofp);
- ofp = NULL;
- lseek(fdin, SEEK_SET, 0);
- }
-
- return (fdin);
-
-err:
- if (fd != -1)
- close(fd);
- if (fdin != -1)
- close(fdin);
- if (fdout != -1)
- close(fdout);
- if (ifp)
- fclose(ifp);
- if (ofp)
- fclose(ofp);
- return -1;
-}
-
-int
-queue_message_fd_rw(uint32_t msgid)
-{
- char buf[PATH_MAX];
-
- queue_message_path(msgid, buf, sizeof(buf));
-
- return open(buf, O_RDWR | O_CREAT | O_EXCL, 0600);
-}
-
-static int
-queue_envelope_dump_buffer(struct envelope *ep, char *evpbuf, size_t evpbufsize)
-{
- char *evp;
- size_t evplen;
- size_t complen;
- char compbuf[sizeof(struct envelope)];
- size_t enclen;
- char encbuf[sizeof(struct envelope)];
-
- evp = evpbuf;
- evplen = envelope_dump_buffer(ep, evpbuf, evpbufsize);
- if (evplen == 0)
- return (0);
-
- if (env->sc_queue_flags & QUEUE_COMPRESSION) {
- complen = compress_chunk(evp, evplen, compbuf, sizeof compbuf);
- if (complen == 0)
- return (0);
- evp = compbuf;
- evplen = complen;
- }
-
- if (env->sc_queue_flags & QUEUE_ENCRYPTION) {
- enclen = crypto_encrypt_buffer(evp, evplen, encbuf, sizeof encbuf);
- if (enclen == 0)
- return (0);
- evp = encbuf;
- evplen = enclen;
- }
-
- memmove(evpbuf, evp, evplen);
-
- return (evplen);
-}
-
-static int
-queue_envelope_load_buffer(struct envelope *ep, char *evpbuf, size_t evpbufsize)
-{
- char *evp;
- size_t evplen;
- char compbuf[sizeof(struct envelope)];
- size_t complen;
- char encbuf[sizeof(struct envelope)];
- size_t enclen;
-
- evp = evpbuf;
- evplen = evpbufsize;
-
- if (env->sc_queue_flags & QUEUE_ENCRYPTION) {
- enclen = crypto_decrypt_buffer(evp, evplen, encbuf, sizeof encbuf);
- if (enclen == 0)
- return (0);
- evp = encbuf;
- evplen = enclen;
- }
-
- if (env->sc_queue_flags & QUEUE_COMPRESSION) {
- complen = uncompress_chunk(evp, evplen, compbuf, sizeof compbuf);
- if (complen == 0)
- return (0);
- evp = compbuf;
- evplen = complen;
- }
-
- return (envelope_load_buffer(ep, evp, evplen));
-}
-
-static void
-queue_envelope_cache_add(struct envelope *e)
-{
- struct envelope *cached;
-
- while (tree_count(&evpcache_tree) >= env->sc_queue_evpcache_size)
- queue_envelope_cache_del(TAILQ_LAST(&evpcache_list, evplst)->id);
-
- cached = xcalloc(1, sizeof *cached);
- *cached = *e;
- TAILQ_INSERT_HEAD(&evpcache_list, cached, entry);
- tree_xset(&evpcache_tree, e->id, cached);
- stat_increment("queue.evpcache.size", 1);
-}
-
-static void
-queue_envelope_cache_update(struct envelope *e)
-{
- struct envelope *cached;
-
- if ((cached = tree_get(&evpcache_tree, e->id)) == NULL) {
- queue_envelope_cache_add(e);
- stat_increment("queue.evpcache.update.missed", 1);
- } else {
- TAILQ_REMOVE(&evpcache_list, cached, entry);
- *cached = *e;
- TAILQ_INSERT_HEAD(&evpcache_list, cached, entry);
- stat_increment("queue.evpcache.update.hit", 1);
- }
-}
-
-static void
-queue_envelope_cache_del(uint64_t evpid)
-{
- struct envelope *cached;
-
- if ((cached = tree_pop(&evpcache_tree, evpid)) == NULL)
- return;
-
- TAILQ_REMOVE(&evpcache_list, cached, entry);
- free(cached);
- stat_decrement("queue.evpcache.size", 1);
-}
-
-int
-queue_envelope_create(struct envelope *ep)
-{
- int r;
- char evpbuf[sizeof(struct envelope)];
- size_t evplen;
- uint64_t evpid;
- uint32_t msgid;
-
- ep->creation = time(NULL);
- evplen = queue_envelope_dump_buffer(ep, evpbuf, sizeof evpbuf);
- if (evplen == 0)
- return (0);
-
- evpid = ep->id;
- msgid = evpid_to_msgid(evpid);
-
- profile_enter("queue_envelope_create");
- r = handler_envelope_create(msgid, evpbuf, evplen, &ep->id);
- profile_leave();
-
- log_trace(TRACE_QUEUE,
- "queue-backend: queue_envelope_create(%016"PRIx64", %zu) -> %d (%016"PRIx64")",
- evpid, evplen, r, ep->id);
-
- if (!r) {
- ep->creation = 0;
- ep->id = 0;
- }
-
- if (r && env->sc_queue_flags & QUEUE_EVPCACHE)
- queue_envelope_cache_add(ep);
-
- return (r);
-}
-
-int
-queue_envelope_delete(uint64_t evpid)
-{
- int r;
-
- if (env->sc_queue_flags & QUEUE_EVPCACHE)
- queue_envelope_cache_del(evpid);
-
- profile_enter("queue_envelope_delete");
- r = handler_envelope_delete(evpid);
- profile_leave();
-
- log_trace(TRACE_QUEUE,
- "queue-backend: queue_envelope_delete(%016"PRIx64") -> %d",
- evpid, r);
-
- return (r);
-}
-
-int
-queue_envelope_load(uint64_t evpid, struct envelope *ep)
-{
- const char *e;
- char evpbuf[sizeof(struct envelope)];
- size_t evplen;
- struct envelope *cached;
-
- if ((env->sc_queue_flags & QUEUE_EVPCACHE) &&
- (cached = tree_get(&evpcache_tree, evpid))) {
- *ep = *cached;
- stat_increment("queue.evpcache.load.hit", 1);
- return (1);
- }
-
- ep->id = evpid;
- profile_enter("queue_envelope_load");
- evplen = handler_envelope_load(ep->id, evpbuf, sizeof evpbuf);
- profile_leave();
-
- log_trace(TRACE_QUEUE,
- "queue-backend: queue_envelope_load(%016"PRIx64") -> %zu",
- evpid, evplen);
-
- if (evplen == 0)
- return (0);
-
- if (queue_envelope_load_buffer(ep, evpbuf, evplen)) {
- if ((e = envelope_validate(ep)) == NULL) {
- ep->id = evpid;
- if (env->sc_queue_flags & QUEUE_EVPCACHE) {
- queue_envelope_cache_add(ep);
- stat_increment("queue.evpcache.load.missed", 1);
- }
- return (1);
- }
- log_warnx("warn: invalid envelope %016" PRIx64 ": %s",
- evpid, e);
- }
- return (0);
-}
-
-int
-queue_envelope_update(struct envelope *ep)
-{
- char evpbuf[sizeof(struct envelope)];
- size_t evplen;
- int r;
-
- evplen = queue_envelope_dump_buffer(ep, evpbuf, sizeof evpbuf);
- if (evplen == 0)
- return (0);
-
- profile_enter("queue_envelope_update");
- r = handler_envelope_update(ep->id, evpbuf, evplen);
- profile_leave();
-
- if (r && env->sc_queue_flags & QUEUE_EVPCACHE)
- queue_envelope_cache_update(ep);
-
- log_trace(TRACE_QUEUE,
- "queue-backend: queue_envelope_update(%016"PRIx64") -> %d",
- ep->id, r);
-
- return (r);
-}
-
-int
-queue_message_walk(struct envelope *ep, uint32_t msgid, int *done, void **data)
-{
- char evpbuf[sizeof(struct envelope)];
- uint64_t evpid;
- int r;
- const char *e;
-
- profile_enter("queue_message_walk");
- r = handler_message_walk(&evpid, evpbuf, sizeof evpbuf,
- msgid, done, data);
- profile_leave();
-
- log_trace(TRACE_QUEUE,
- "queue-backend: queue_message_walk() -> %d (%016"PRIx64")",
- r, evpid);
-
- if (r == -1)
- return (r);
-
- if (r && queue_envelope_load_buffer(ep, evpbuf, (size_t)r)) {
- if ((e = envelope_validate(ep)) == NULL) {
- ep->id = evpid;
- /*
- * do not cache the envelope here, while discovering
- * envelopes one could re-run discover on already
- * scheduled envelopes which leads to triggering of
- * strict checks in caching. Envelopes could anyway
- * be loaded from backend if it isn't cached.
- */
- return (1);
- }
- log_warnx("warn: invalid envelope %016" PRIx64 ": %s",
- evpid, e);
- }
- return (0);
-}
-
-int
-queue_envelope_walk(struct envelope *ep)
-{
- const char *e;
- uint64_t evpid;
- char evpbuf[sizeof(struct envelope)];
- int r;
-
- profile_enter("queue_envelope_walk");
- r = handler_envelope_walk(&evpid, evpbuf, sizeof evpbuf);
- profile_leave();
-
- log_trace(TRACE_QUEUE,
- "queue-backend: queue_envelope_walk() -> %d (%016"PRIx64")",
- r, evpid);
-
- if (r == -1)
- return (r);
-
- if (r && queue_envelope_load_buffer(ep, evpbuf, (size_t)r)) {
- if ((e = envelope_validate(ep)) == NULL) {
- ep->id = evpid;
- if (env->sc_queue_flags & QUEUE_EVPCACHE)
- queue_envelope_cache_add(ep);
- return (1);
- }
- log_warnx("warn: invalid envelope %016" PRIx64 ": %s",
- evpid, e);
- }
- return (0);
-}
-
-uint32_t
-queue_generate_msgid(void)
-{
- uint32_t msgid;
-
- while ((msgid = arc4random()) == 0)
- ;
-
- return msgid;
-}
-
-uint64_t
-queue_generate_evpid(uint32_t msgid)
-{
- uint32_t rnd;
- uint64_t evpid;
-
- while ((rnd = arc4random()) == 0)
- ;
-
- evpid = msgid;
- evpid <<= 32;
- evpid |= rnd;
-
- return evpid;
-}
-
-static const char*
-envelope_validate(struct envelope *ep)
-{
- if (ep->version != SMTPD_ENVELOPE_VERSION)
- return "version mismatch";
-
- if (memchr(ep->helo, '\0', sizeof(ep->helo)) == NULL)
- return "invalid helo";
- if (ep->helo[0] == '\0')
- return "empty helo";
-
- if (memchr(ep->hostname, '\0', sizeof(ep->hostname)) == NULL)
- return "invalid hostname";
- if (ep->hostname[0] == '\0')
- return "empty hostname";
-
- if (memchr(ep->errorline, '\0', sizeof(ep->errorline)) == NULL)
- return "invalid error line";
-
- if (dict_get(env->sc_dispatchers, ep->dispatcher) == NULL)
- return "unknown dispatcher";
-
- return NULL;
-}
-
-void
-queue_api_on_close(int(*cb)(void))
-{
- handler_close = cb;
-}
-
-void
-queue_api_on_message_create(int(*cb)(uint32_t *))
-{
- handler_message_create = cb;
-}
-
-void
-queue_api_on_message_commit(int(*cb)(uint32_t, const char *))
-{
- handler_message_commit = cb;
-}
-
-void
-queue_api_on_message_delete(int(*cb)(uint32_t))
-{
- handler_message_delete = cb;
-}
-
-void
-queue_api_on_message_fd_r(int(*cb)(uint32_t))
-{
- handler_message_fd_r = cb;
-}
-
-void
-queue_api_on_envelope_create(int(*cb)(uint32_t, const char *, size_t, uint64_t *))
-{
- handler_envelope_create = cb;
-}
-
-void
-queue_api_on_envelope_delete(int(*cb)(uint64_t))
-{
- handler_envelope_delete = cb;
-}
-
-void
-queue_api_on_envelope_update(int(*cb)(uint64_t, const char *, size_t))
-{
- handler_envelope_update = cb;
-}
-
-void
-queue_api_on_envelope_load(int(*cb)(uint64_t, char *, size_t))
-{
- handler_envelope_load = cb;
-}
-
-void
-queue_api_on_envelope_walk(int(*cb)(uint64_t *, char *, size_t))
-{
- handler_envelope_walk = cb;
-}
-
-void
-queue_api_on_message_walk(int(*cb)(uint64_t *, char *, size_t,
- uint32_t, int *, void **))
-{
- handler_message_walk = cb;
-}
diff --git a/smtpd/queue_fs.c b/smtpd/queue_fs.c
deleted file mode 100644
index 097ba1e2..00000000
--- a/smtpd/queue_fs.c
+++ /dev/null
@@ -1,695 +0,0 @@
-/* $OpenBSD: queue_fs.c,v 1.20 2020/02/25 17:03:13 millert Exp $ */
-
-/*
- * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#if HAVE_SYS_MOUNT_H
-#include <sys/mount.h>
-#endif
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#ifdef HAVE_SYS_STATFS_H
-#include <sys/statfs.h>
-#endif
-
-#include <ctype.h>
-#include <dirent.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <fts.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define PATH_QUEUE "/queue"
-#define PATH_INCOMING "/incoming"
-#define PATH_EVPTMP PATH_INCOMING "/envelope.tmp"
-#define PATH_MESSAGE "/message"
-
-/* percentage of remaining space / inodes required to accept new messages */
-#define MINSPACE 5
-#define MININODES 5
-
-struct qwalk {
- FTS *fts;
- int depth;
-};
-
-static int fsqueue_check_space(void);
-static void fsqueue_envelope_path(uint64_t, char *, size_t);
-static void fsqueue_envelope_incoming_path(uint64_t, char *, size_t);
-static int fsqueue_envelope_dump(char *, const char *, size_t, int, int);
-static void fsqueue_message_path(uint32_t, char *, size_t);
-static void fsqueue_message_incoming_path(uint32_t, char *, size_t);
-static void *fsqueue_qwalk_new(void);
-static int fsqueue_qwalk(void *, uint64_t *);
-static void fsqueue_qwalk_close(void *);
-
-struct tree evpcount;
-static struct timespec startup;
-
-#define REF (int*)0xf00
-
-static int
-queue_fs_message_create(uint32_t *msgid)
-{
- char rootdir[PATH_MAX];
- struct stat sb;
-
- if (!fsqueue_check_space())
- return 0;
-
-again:
- *msgid = queue_generate_msgid();
-
- /* prevent possible collision later when moving to Q_QUEUE */
- fsqueue_message_path(*msgid, rootdir, sizeof(rootdir));
- if (stat(rootdir, &sb) != -1)
- goto again;
-
- /* we hit an unexpected error, temporarily fail */
- if (errno != ENOENT) {
- *msgid = 0;
- return 0;
- }
-
- fsqueue_message_incoming_path(*msgid, rootdir, sizeof(rootdir));
- if (mkdir(rootdir, 0700) == -1) {
- if (errno == EEXIST)
- goto again;
-
- if (errno == ENOSPC) {
- *msgid = 0;
- return 0;
- }
-
- log_warn("warn: queue-fs: mkdir");
- *msgid = 0;
- return 0;
- }
-
- return (1);
-}
-
-static int
-queue_fs_message_commit(uint32_t msgid, const char *path)
-{
- char incomingdir[PATH_MAX];
- char queuedir[PATH_MAX];
- char msgdir[PATH_MAX];
- char msgpath[PATH_MAX];
-
- /* before-first, move the message content in the incoming directory */
- fsqueue_message_incoming_path(msgid, msgpath, sizeof(msgpath));
- if (strlcat(msgpath, PATH_MESSAGE, sizeof(msgpath))
- >= sizeof(msgpath))
- return (0);
- if (rename(path, msgpath) == -1)
- return (0);
-
- fsqueue_message_incoming_path(msgid, incomingdir, sizeof(incomingdir));
- fsqueue_message_path(msgid, msgdir, sizeof(msgdir));
- if (strlcpy(queuedir, msgdir, sizeof(queuedir))
- >= sizeof(queuedir))
- return (0);
-
- /* first attempt to rename */
- if (rename(incomingdir, msgdir) == 0)
- return 1;
- if (errno == ENOSPC)
- return 0;
- if (errno != ENOENT) {
- log_warn("warn: queue-fs: rename");
- return 0;
- }
-
- /* create the bucket */
- *strrchr(queuedir, '/') = '\0';
- if (mkdir(queuedir, 0700) == -1) {
- if (errno == ENOSPC)
- return 0;
- if (errno != EEXIST) {
- log_warn("warn: queue-fs: mkdir");
- return 0;
- }
- }
-
- /* rename */
- if (rename(incomingdir, msgdir) == -1) {
- if (errno == ENOSPC)
- return 0;
- log_warn("warn: queue-fs: rename");
- return 0;
- }
-
- return 1;
-}
-
-static int
-queue_fs_message_fd_r(uint32_t msgid)
-{
- int fd;
- char path[PATH_MAX];
-
- fsqueue_message_path(msgid, path, sizeof(path));
- if (strlcat(path, PATH_MESSAGE, sizeof(path))
- >= sizeof(path))
- return -1;
-
- if ((fd = open(path, O_RDONLY)) == -1) {
- log_warn("warn: queue-fs: open");
- return -1;
- }
-
- return fd;
-}
-
-static int
-queue_fs_message_delete(uint32_t msgid)
-{
- char path[PATH_MAX];
- struct stat sb;
-
- fsqueue_message_incoming_path(msgid, path, sizeof(path));
- if (stat(path, &sb) == -1)
- fsqueue_message_path(msgid, path, sizeof(path));
-
- if (rmtree(path, 0) == -1)
- log_warn("warn: queue-fs: rmtree");
-
- tree_pop(&evpcount, msgid);
-
- return 1;
-}
-
-static int
-queue_fs_envelope_create(uint32_t msgid, const char *buf, size_t len,
- uint64_t *evpid)
-{
- char path[PATH_MAX];
- int queued = 0, i, r = 0, *n;
- struct stat sb;
-
- if (msgid == 0) {
- log_warnx("warn: queue-fs: msgid=0, evpid=%016"PRIx64, *evpid);
- goto done;
- }
-
- fsqueue_message_incoming_path(msgid, path, sizeof(path));
- if (stat(path, &sb) == -1)
- queued = 1;
-
- for (i = 0; i < 20; i ++) {
- *evpid = queue_generate_evpid(msgid);
- if (queued)
- fsqueue_envelope_path(*evpid, path, sizeof(path));
- else
- fsqueue_envelope_incoming_path(*evpid, path,
- sizeof(path));
-
- if ((r = fsqueue_envelope_dump(path, buf, len, 0, 0)) != 0)
- goto done;
- }
- r = 0;
- log_warnx("warn: queue-fs: could not allocate evpid");
-
-done:
- if (r) {
- n = tree_pop(&evpcount, msgid);
- if (n == NULL)
- n = REF;
- n += 1;
- tree_xset(&evpcount, msgid, n);
- }
- return (r);
-}
-
-static int
-queue_fs_envelope_load(uint64_t evpid, char *buf, size_t len)
-{
- char pathname[PATH_MAX];
- FILE *fp;
- size_t r;
-
- fsqueue_envelope_path(evpid, pathname, sizeof(pathname));
-
- fp = fopen(pathname, "r");
- if (fp == NULL) {
- if (errno != ENOENT && errno != ENFILE)
- log_warn("warn: queue-fs: fopen");
- return 0;
- }
-
- r = fread(buf, 1, len, fp);
- if (r) {
- if (r == len) {
- log_warn("warn: queue-fs: too large");
- r = 0;
- }
- else
- buf[r] = '\0';
- }
- fclose(fp);
-
- return (r);
-}
-
-static int
-queue_fs_envelope_update(uint64_t evpid, const char *buf, size_t len)
-{
- char dest[PATH_MAX];
-
- fsqueue_envelope_path(evpid, dest, sizeof(dest));
-
- return (fsqueue_envelope_dump(dest, buf, len, 1, 1));
-}
-
-static int
-queue_fs_envelope_delete(uint64_t evpid)
-{
- char pathname[PATH_MAX];
- uint32_t msgid;
- int *n;
-
- fsqueue_envelope_path(evpid, pathname, sizeof(pathname));
- if (unlink(pathname) == -1)
- if (errno != ENOENT)
- return 0;
-
- msgid = evpid_to_msgid(evpid);
- n = tree_pop(&evpcount, msgid);
- n -= 1;
-
- if (n - REF == 0)
- queue_fs_message_delete(msgid);
- else
- tree_xset(&evpcount, msgid, n);
-
- return (1);
-}
-
-static int
-queue_fs_message_walk(uint64_t *evpid, char *buf, size_t len,
- uint32_t msgid, int *done, void **data)
-{
- struct dirent *dp;
- DIR *dir = *data;
- char path[PATH_MAX];
- char msgid_str[9];
- char *tmp;
- int r, *n;
-
- if (*done)
- return (-1);
-
- if (!bsnprintf(path, sizeof path, "%s/%02x/%08x",
- PATH_QUEUE, (msgid & 0xff000000) >> 24, msgid))
- fatalx("queue_fs_message_walk: path does not fit buffer");
-
- if (dir == NULL) {
- if ((dir = opendir(path)) == NULL) {
- log_warn("warn: queue_fs: opendir: %s", path);
- *done = 1;
- return (-1);
- }
-
- *data = dir;
- }
-
- (void)snprintf(msgid_str, sizeof msgid_str, "%08" PRIx32, msgid);
- while ((dp = readdir(dir)) != NULL) {
-#if defined(HAVE_STRUCT_DIR_D_TYPE)
- if (dp->d_type != DT_REG)
- continue;
-#endif
-
- /* ignore files other than envelopes */
- if (strlen(dp->d_name) != 16 ||
- strncmp(dp->d_name, msgid_str, 8))
- continue;
-
- tmp = NULL;
- *evpid = strtoull(dp->d_name, &tmp, 16);
- if (tmp && *tmp != '\0') {
- log_debug("debug: fsqueue: bogus file %s", dp->d_name);
- continue;
- }
-
- memset(buf, 0, len);
- r = queue_fs_envelope_load(*evpid, buf, len);
- if (r) {
- n = tree_pop(&evpcount, msgid);
- if (n == NULL)
- n = REF;
-
- n += 1;
- tree_xset(&evpcount, msgid, n);
- }
-
- return (r);
- }
-
- (void)closedir(dir);
- *done = 1;
- return (-1);
-}
-
-static int
-queue_fs_envelope_walk(uint64_t *evpid, char *buf, size_t len)
-{
- static int done = 0;
- static void *hdl = NULL;
- int r, *n;
- uint32_t msgid;
-
- if (done)
- return (-1);
-
- if (hdl == NULL)
- hdl = fsqueue_qwalk_new();
-
- if (fsqueue_qwalk(hdl, evpid)) {
- memset(buf, 0, len);
- r = queue_fs_envelope_load(*evpid, buf, len);
- if (r) {
- msgid = evpid_to_msgid(*evpid);
- n = tree_pop(&evpcount, msgid);
- if (n == NULL)
- n = REF;
- n += 1;
- tree_xset(&evpcount, msgid, n);
- }
- return (r);
- }
-
- fsqueue_qwalk_close(hdl);
- done = 1;
- return (-1);
-}
-
-static int
-fsqueue_check_space(void)
-{
-#ifdef __OpenBSD__
- struct statfs buf;
- uint64_t used;
- uint64_t total;
-
- if (statfs(PATH_QUEUE, &buf) == -1) {
- log_warn("warn: queue-fs: statfs");
- return 0;
- }
-
- /*
- * f_bfree and f_ffree is not set on all filesystems.
- * They could be signed or unsigned integers.
- * Some systems will set them to 0, others will set them to -1.
- */
- if (buf.f_bfree == 0 || buf.f_ffree == 0 ||
- (int64_t)buf.f_bfree == -1 || (int64_t)buf.f_ffree == -1)
- return 1;
-
- used = buf.f_blocks - buf.f_bfree;
- total = buf.f_bavail + used;
- if (total != 0)
- used = (float)used / (float)total * 100;
- else
- used = 100;
- if (100 - used < MINSPACE) {
- log_warnx("warn: not enough disk space: %llu%% left",
- (unsigned long long) 100 - used);
- log_warnx("warn: temporarily rejecting messages");
- return 0;
- }
-
- used = buf.f_files - buf.f_ffree;
- total = buf.f_favail + used;
- if (total != 0)
- used = (float)used / (float)total * 100;
- else
- used = 100;
- if (100 - used < MININODES) {
- log_warnx("warn: not enough inodes: %llu%% left",
- (unsigned long long) 100 - used);
- log_warnx("warn: temporarily rejecting messages");
- return 0;
- }
-#endif
- return 1;
-}
-
-static void
-fsqueue_envelope_path(uint64_t evpid, char *buf, size_t len)
-{
- if (!bsnprintf(buf, len, "%s/%02x/%08x/%016" PRIx64,
- PATH_QUEUE,
- (evpid_to_msgid(evpid) & 0xff000000) >> 24,
- evpid_to_msgid(evpid),
- evpid))
- fatalx("fsqueue_envelope_path: path does not fit buffer");
-}
-
-static void
-fsqueue_envelope_incoming_path(uint64_t evpid, char *buf, size_t len)
-{
- if (!bsnprintf(buf, len, "%s/%08x/%016" PRIx64,
- PATH_INCOMING,
- evpid_to_msgid(evpid),
- evpid))
- fatalx("fsqueue_envelope_incoming_path: path does not fit buffer");
-}
-
-static int
-fsqueue_envelope_dump(char *dest, const char *evpbuf, size_t evplen,
- int do_atomic, int do_sync)
-{
- const char *path = do_atomic ? PATH_EVPTMP : dest;
- FILE *fp = NULL;
- int fd;
- size_t w;
-
- if ((fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0600)) == -1) {
- log_warn("warn: queue-fs: open");
- goto tempfail;
- }
-
- if ((fp = fdopen(fd, "w")) == NULL) {
- log_warn("warn: queue-fs: fdopen");
- goto tempfail;
- }
-
- w = fwrite(evpbuf, 1, evplen, fp);
- if (w < evplen) {
- log_warn("warn: queue-fs: short write");
- goto tempfail;
- }
- if (fflush(fp)) {
- log_warn("warn: queue-fs: fflush");
- goto tempfail;
- }
- if (do_sync && fsync(fileno(fp))) {
- log_warn("warn: queue-fs: fsync");
- goto tempfail;
- }
- if (fclose(fp) != 0) {
- log_warn("warn: queue-fs: fclose");
- fp = NULL;
- goto tempfail;
- }
- fp = NULL;
- fd = -1;
-
- if (do_atomic && rename(path, dest) == -1) {
- log_warn("warn: queue-fs: rename");
- goto tempfail;
- }
- return (1);
-
-tempfail:
- if (fp)
- fclose(fp);
- else if (fd != -1)
- close(fd);
- if (unlink(path) == -1)
- log_warn("warn: queue-fs: unlink");
- return (0);
-}
-
-static void
-fsqueue_message_path(uint32_t msgid, char *buf, size_t len)
-{
- if (!bsnprintf(buf, len, "%s/%02x/%08x",
- PATH_QUEUE,
- (msgid & 0xff000000) >> 24,
- msgid))
- fatalx("fsqueue_message_path: path does not fit buffer");
-}
-
-static void
-fsqueue_message_incoming_path(uint32_t msgid, char *buf, size_t len)
-{
- if (!bsnprintf(buf, len, "%s/%08x",
- PATH_INCOMING,
- msgid))
- fatalx("fsqueue_message_incoming_path: path does not fit buffer");
-}
-
-static void *
-fsqueue_qwalk_new(void)
-{
- char path[PATH_MAX];
- char * const path_argv[] = { path, NULL };
- struct qwalk *q;
-
- q = xcalloc(1, sizeof(*q));
- (void)strlcpy(path, PATH_QUEUE, sizeof(path));
- q->fts = fts_open(path_argv,
- FTS_PHYSICAL | FTS_NOCHDIR, NULL);
-
- if (q->fts == NULL)
- err(1, "fsqueue_qwalk_new: fts_open: %s", path);
-
- return (q);
-}
-
-static void
-fsqueue_qwalk_close(void *hdl)
-{
- struct qwalk *q = hdl;
-
- fts_close(q->fts);
-
- free(q);
-}
-
-static int
-fsqueue_qwalk(void *hdl, uint64_t *evpid)
-{
- struct qwalk *q = hdl;
- FTSENT *e;
- char *tmp;
-
- while ((e = fts_read(q->fts)) != NULL) {
- switch (e->fts_info) {
- case FTS_D:
- q->depth += 1;
- if (q->depth == 2 && e->fts_namelen != 2) {
- log_debug("debug: fsqueue: bogus directory %s",
- e->fts_path);
- fts_set(q->fts, e, FTS_SKIP);
- break;
- }
- if (q->depth == 3 && e->fts_namelen != 8) {
- log_debug("debug: fsqueue: bogus directory %s",
- e->fts_path);
- fts_set(q->fts, e, FTS_SKIP);
- break;
- }
- break;
-
- case FTS_DP:
- case FTS_DNR:
- q->depth -= 1;
- break;
-
- case FTS_F:
- if (q->depth != 3)
- break;
- if (e->fts_namelen != 16)
- break;
-#if HAVE_STRUCT_STAT_ST_MTIM
- if (timespeccmp(&e->fts_statp->st_mtim, &startup, >))
-#endif
-#if HAVE_STRUCT_STAT_ST_MTIMSPEC
- if (timespeccmp(&e->fts_statp->st_mtimspec, &startup, >))
-#endif
- break;
- tmp = NULL;
- *evpid = strtoull(e->fts_name, &tmp, 16);
- if (tmp && *tmp != '\0') {
- log_debug("debug: fsqueue: bogus file %s",
- e->fts_path);
- break;
- }
- return (1);
- default:
- break;
- }
- }
-
- return (0);
-}
-
-static int
-queue_fs_init(struct passwd *pw, int server, const char *conf)
-{
- unsigned int n;
- char *paths[] = { PATH_QUEUE, PATH_INCOMING };
- char path[PATH_MAX];
- int ret;
-
- /* remove incoming/ if it exists */
- if (server)
- mvpurge(PATH_SPOOL PATH_INCOMING, PATH_SPOOL PATH_PURGE);
-
- fsqueue_envelope_path(0, path, sizeof(path));
-
- ret = 1;
- for (n = 0; n < nitems(paths); n++) {
- (void)strlcpy(path, PATH_SPOOL, sizeof(path));
- if (strlcat(path, paths[n], sizeof(path)) >= sizeof(path))
- errx(1, "path too long %s%s", PATH_SPOOL, paths[n]);
- if (ckdir(path, 0700, pw->pw_uid, 0, server) == 0)
- ret = 0;
- }
-
- if (clock_gettime(CLOCK_REALTIME, &startup))
- err(1, "clock_gettime");
-
- tree_init(&evpcount);
-
- queue_api_on_message_create(queue_fs_message_create);
- queue_api_on_message_commit(queue_fs_message_commit);
- queue_api_on_message_delete(queue_fs_message_delete);
- queue_api_on_message_fd_r(queue_fs_message_fd_r);
- queue_api_on_envelope_create(queue_fs_envelope_create);
- queue_api_on_envelope_delete(queue_fs_envelope_delete);
- queue_api_on_envelope_update(queue_fs_envelope_update);
- queue_api_on_envelope_load(queue_fs_envelope_load);
- queue_api_on_envelope_walk(queue_fs_envelope_walk);
- queue_api_on_message_walk(queue_fs_message_walk);
-
- return (ret);
-}
-
-struct queue_backend queue_backend_fs = {
- queue_fs_init,
-};
diff --git a/smtpd/queue_null.c b/smtpd/queue_null.c
deleted file mode 100644
index 1e608be8..00000000
--- a/smtpd/queue_null.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/* $OpenBSD: queue_null.c,v 1.8 2018/12/30 23:09:58 guenther 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static int
-queue_null_message_create(uint32_t *msgid)
-{
- *msgid = queue_generate_msgid();
- return (1);
-}
-
-static int
-queue_null_message_commit(uint32_t msgid, const char *path)
-{
- return (1);
-}
-
-static int
-queue_null_message_delete(uint32_t msgid)
-{
- return (1);
-}
-
-static int
-queue_null_message_fd_r(uint32_t msgid)
-{
- return (-1);
-}
-
-static int
-queue_null_envelope_create(uint32_t msgid, const char *buf, size_t len,
- uint64_t *evpid)
-{
- *evpid = queue_generate_evpid(msgid);
- return (1);
-}
-
-static int
-queue_null_envelope_delete(uint64_t evpid)
-{
- return (1);
-}
-
-static int
-queue_null_envelope_update(uint64_t evpid, const char *buf, size_t len)
-{
- return (1);
-}
-
-static int
-queue_null_envelope_load(uint64_t evpid, char *buf, size_t len)
-{
- return (0);
-}
-
-static int
-queue_null_envelope_walk(uint64_t *evpid, char *buf, size_t len)
-{
- return (-1);
-}
-
-static int
-queue_null_init(struct passwd *pw, int server, const char *conf)
-{
- queue_api_on_message_create(queue_null_message_create);
- queue_api_on_message_commit(queue_null_message_commit);
- queue_api_on_message_delete(queue_null_message_delete);
- queue_api_on_message_fd_r(queue_null_message_fd_r);
- queue_api_on_envelope_create(queue_null_envelope_create);
- queue_api_on_envelope_delete(queue_null_envelope_delete);
- queue_api_on_envelope_update(queue_null_envelope_update);
- queue_api_on_envelope_load(queue_null_envelope_load);
- queue_api_on_envelope_walk(queue_null_envelope_walk);
-
- return (1);
-}
-
-struct queue_backend queue_backend_null = {
- queue_null_init,
-};
diff --git a/smtpd/queue_proc.c b/smtpd/queue_proc.c
deleted file mode 100644
index d6e0f409..00000000
--- a/smtpd/queue_proc.c
+++ /dev/null
@@ -1,337 +0,0 @@
-/* $OpenBSD: queue_proc.c,v 1.8 2018/12/30 23:09:58 guenther Exp $ */
-
-/*
- * Copyright (c) 2013 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static struct imsgbuf ibuf;
-static struct imsg imsg;
-static size_t rlen;
-static char *rdata;
-
-static void
-queue_proc_call(void)
-{
- ssize_t n;
-
- if (imsg_flush(&ibuf) == -1) {
- log_warn("warn: queue-proc: imsg_flush");
- fatalx("queue-proc: exiting");
- }
-
- while (1) {
- if ((n = imsg_get(&ibuf, &imsg)) == -1) {
- log_warn("warn: queue-proc: imsg_get");
- break;
- }
- if (n) {
- rlen = imsg.hdr.len - IMSG_HEADER_SIZE;
- rdata = imsg.data;
-
- if (imsg.hdr.type != PROC_QUEUE_OK) {
- log_warnx("warn: queue-proc: bad response");
- break;
- }
- return;
- }
-
- if ((n = imsg_read(&ibuf)) == -1 && errno != EAGAIN) {
- log_warn("warn: queue-proc: imsg_read");
- break;
- }
-
- if (n == 0) {
- log_warnx("warn: queue-proc: pipe closed");
- break;
- }
- }
-
- fatalx("queue-proc: exiting");
-}
-
-static void
-queue_proc_read(void *dst, size_t len)
-{
- if (len > rlen) {
- log_warnx("warn: queue-proc: bad msg len");
- fatalx("queue-proc: exiting");
- }
-
- memmove(dst, rdata, len);
- rlen -= len;
- rdata += len;
-}
-
-static void
-queue_proc_end(void)
-{
- if (rlen) {
- log_warnx("warn: queue-proc: bogus data");
- fatalx("queue-proc: exiting");
- }
- imsg_free(&imsg);
-}
-
-/*
- * API
- */
-
-static int
-queue_proc_close(void)
-{
- int r;
-
- imsg_compose(&ibuf, PROC_QUEUE_CLOSE, 0, 0, -1, NULL, 0);
-
- queue_proc_call();
- queue_proc_read(&r, sizeof(r));
- queue_proc_end();
-
- return (r);
-}
-
-static int
-queue_proc_message_create(uint32_t *msgid)
-{
- int r;
-
- imsg_compose(&ibuf, PROC_QUEUE_MESSAGE_CREATE, 0, 0, -1, NULL, 0);
-
- queue_proc_call();
- queue_proc_read(&r, sizeof(r));
- if (r == 1)
- queue_proc_read(msgid, sizeof(*msgid));
- queue_proc_end();
-
- return (r);
-}
-
-static int
-queue_proc_message_commit(uint32_t msgid, const char *path)
-{
- int r, fd;
-
- fd = open(path, O_RDONLY);
- if (fd == -1) {
- log_warn("queue-proc: open: %s", path);
- return (0);
- }
-
- imsg_compose(&ibuf, PROC_QUEUE_MESSAGE_COMMIT, 0, 0, fd, &msgid,
- sizeof(msgid));
-
- queue_proc_call();
- queue_proc_read(&r, sizeof(r));
- queue_proc_end();
-
- return (r);
-}
-
-static int
-queue_proc_message_delete(uint32_t msgid)
-{
- int r;
-
- imsg_compose(&ibuf, PROC_QUEUE_MESSAGE_DELETE, 0, 0, -1, &msgid,
- sizeof(msgid));
-
- queue_proc_call();
- queue_proc_read(&r, sizeof(r));
- queue_proc_end();
-
- return (r);
-}
-
-static int
-queue_proc_message_fd_r(uint32_t msgid)
-{
- imsg_compose(&ibuf, PROC_QUEUE_MESSAGE_FD_R, 0, 0, -1, &msgid,
- sizeof(msgid));
-
- queue_proc_call();
- queue_proc_end();
-
- return (imsg.fd);
-}
-
-static int
-queue_proc_envelope_create(uint32_t msgid, const char *buf, size_t len,
- uint64_t *evpid)
-{
- struct ibuf *b;
- int r;
-
- msgid = evpid_to_msgid(*evpid);
- b = imsg_create(&ibuf, PROC_QUEUE_ENVELOPE_CREATE, 0, 0,
- sizeof(msgid) + len);
- if (imsg_add(b, &msgid, sizeof(msgid)) == -1 ||
- imsg_add(b, buf, len) == -1)
- return (0);
- imsg_close(&ibuf, b);
-
- queue_proc_call();
- queue_proc_read(&r, sizeof(r));
- if (r == 1)
- queue_proc_read(evpid, sizeof(*evpid));
- queue_proc_end();
-
- return (r);
-}
-
-static int
-queue_proc_envelope_delete(uint64_t evpid)
-{
- int r;
-
- imsg_compose(&ibuf, PROC_QUEUE_ENVELOPE_DELETE, 0, 0, -1, &evpid,
- sizeof(evpid));
-
- queue_proc_call();
- queue_proc_read(&r, sizeof(r));
- queue_proc_end();
-
- return (r);
-}
-
-static int
-queue_proc_envelope_update(uint64_t evpid, const char *buf, size_t len)
-{
- struct ibuf *b;
- int r;
-
- b = imsg_create(&ibuf, PROC_QUEUE_ENVELOPE_UPDATE, 0, 0,
- len + sizeof(evpid));
- if (imsg_add(b, &evpid, sizeof(evpid)) == -1 ||
- imsg_add(b, buf, len) == -1)
- return (0);
- imsg_close(&ibuf, b);
-
- queue_proc_call();
- queue_proc_read(&r, sizeof(r));
- queue_proc_end();
-
- return (r);
-}
-
-static int
-queue_proc_envelope_load(uint64_t evpid, char *buf, size_t len)
-{
- int r;
-
- imsg_compose(&ibuf, PROC_QUEUE_ENVELOPE_LOAD, 0, 0, -1, &evpid,
- sizeof(evpid));
-
- queue_proc_call();
-
- if (rlen > len) {
- log_warnx("warn: queue-proc: buf too small");
- fatalx("queue-proc: exiting");
- }
-
- r = rlen;
- queue_proc_read(buf, rlen);
- queue_proc_end();
-
- return (r);
-}
-
-static int
-queue_proc_envelope_walk(uint64_t *evpid, char *buf, size_t len)
-{
- int r;
-
- imsg_compose(&ibuf, PROC_QUEUE_ENVELOPE_WALK, 0, 0, -1, NULL, 0);
-
- queue_proc_call();
- queue_proc_read(&r, sizeof(r));
-
- if (r > 0) {
- queue_proc_read(evpid, sizeof(*evpid));
- if (rlen > len) {
- log_warnx("warn: queue-proc: buf too small");
- fatalx("queue-proc: exiting");
- }
- if (r != (int)rlen) {
- log_warnx("warn: queue-proc: len mismatch");
- fatalx("queue-proc: exiting");
- }
- queue_proc_read(buf, rlen);
- }
- queue_proc_end();
-
- return (r);
-}
-
-static int
-queue_proc_init(struct passwd *pw, int server, const char *conf)
-{
- uint32_t version;
- int fd;
-
- fd = fork_proc_backend("queue", conf, "queue-proc");
- if (fd == -1)
- fatalx("queue-proc: exiting");
-
- imsg_init(&ibuf, fd);
-
- version = PROC_QUEUE_API_VERSION;
- imsg_compose(&ibuf, PROC_QUEUE_INIT, 0, 0, -1,
- &version, sizeof(version));
-
- queue_api_on_close(queue_proc_close);
- queue_api_on_message_create(queue_proc_message_create);
- queue_api_on_message_commit(queue_proc_message_commit);
- queue_api_on_message_delete(queue_proc_message_delete);
- queue_api_on_message_fd_r(queue_proc_message_fd_r);
- queue_api_on_envelope_create(queue_proc_envelope_create);
- queue_api_on_envelope_delete(queue_proc_envelope_delete);
- queue_api_on_envelope_update(queue_proc_envelope_update);
- queue_api_on_envelope_load(queue_proc_envelope_load);
- queue_api_on_envelope_walk(queue_proc_envelope_walk);
-
- queue_proc_call();
- queue_proc_end();
-
- return (1);
-}
-
-struct queue_backend queue_backend_proc = {
- queue_proc_init,
-};
diff --git a/smtpd/queue_ram.c b/smtpd/queue_ram.c
deleted file mode 100644
index 50ce17e1..00000000
--- a/smtpd/queue_ram.c
+++ /dev/null
@@ -1,336 +0,0 @@
-/* $OpenBSD: queue_ram.c,v 1.9 2018/12/30 23:09:58 guenther 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-struct qr_envelope {
- char *buf;
- size_t len;
-};
-
-struct qr_message {
- char *buf;
- size_t len;
- struct tree envelopes;
-};
-
-static struct tree messages;
-
-static struct qr_message *
-get_message(uint32_t msgid)
-{
- struct qr_message *msg;
-
- msg = tree_get(&messages, msgid);
- if (msg == NULL)
- log_warn("warn: queue-ram: message not found");
-
- return (msg);
-}
-
-static int
-queue_ram_message_create(uint32_t *msgid)
-{
- struct qr_message *msg;
-
- msg = calloc(1, sizeof(*msg));
- if (msg == NULL) {
- log_warn("warn: queue-ram: calloc");
- return (0);
- }
- tree_init(&msg->envelopes);
-
- do {
- *msgid = queue_generate_msgid();
- } while (tree_check(&messages, *msgid));
-
- tree_xset(&messages, *msgid, msg);
-
- return (1);
-}
-
-static int
-queue_ram_message_commit(uint32_t msgid, const char *path)
-{
- struct qr_message *msg;
- struct stat sb;
- size_t n;
- FILE *f;
- int ret;
-
- if ((msg = tree_get(&messages, msgid)) == NULL) {
- log_warnx("warn: queue-ram: msgid not found");
- return (0);
- }
-
- f = fopen(path, "rb");
- if (f == NULL) {
- log_warn("warn: queue-ram: fopen: %s", path);
- return (0);
- }
- if (fstat(fileno(f), &sb) == -1) {
- log_warn("warn: queue-ram: fstat");
- fclose(f);
- return (0);
- }
-
- msg->len = sb.st_size;
- msg->buf = malloc(msg->len);
- if (msg->buf == NULL) {
- log_warn("warn: queue-ram: malloc");
- fclose(f);
- return (0);
- }
-
- ret = 0;
- n = fread(msg->buf, 1, msg->len, f);
- if (ferror(f))
- log_warn("warn: queue-ram: fread");
- else if ((off_t)n != sb.st_size)
- log_warnx("warn: queue-ram: bad read");
- else {
- ret = 1;
- stat_increment("queue.ram.message.size", msg->len);
- }
- fclose(f);
-
- return (ret);
-}
-
-static int
-queue_ram_message_delete(uint32_t msgid)
-{
- struct qr_message *msg;
- struct qr_envelope *evp;
- uint64_t evpid;
-
- if ((msg = tree_pop(&messages, msgid)) == NULL) {
- log_warnx("warn: queue-ram: not found");
- return (0);
- }
- while (tree_poproot(&messages, &evpid, (void**)&evp)) {
- stat_decrement("queue.ram.envelope.size", evp->len);
- free(evp->buf);
- free(evp);
- }
- stat_decrement("queue.ram.message.size", msg->len);
- free(msg->buf);
- free(msg);
- return (0);
-}
-
-static int
-queue_ram_message_fd_r(uint32_t msgid)
-{
- struct qr_message *msg;
- size_t n;
- FILE *f;
- int fd, fd2;
-
- if ((msg = tree_get(&messages, msgid)) == NULL) {
- log_warnx("warn: queue-ram: not found");
- return (-1);
- }
-
- fd = mktmpfile();
- if (fd == -1) {
- log_warn("warn: queue-ram: mktmpfile");
- return (-1);
- }
-
- fd2 = dup(fd);
- if (fd2 == -1) {
- log_warn("warn: queue-ram: dup");
- close(fd);
- return (-1);
- }
- f = fdopen(fd2, "w");
- if (f == NULL) {
- log_warn("warn: queue-ram: fdopen");
- close(fd);
- close(fd2);
- return (-1);
- }
- n = fwrite(msg->buf, 1, msg->len, f);
- if (n != msg->len) {
- log_warn("warn: queue-ram: write");
- close(fd);
- fclose(f);
- return (-1);
- }
- fclose(f);
- lseek(fd, 0, SEEK_SET);
- return (fd);
-}
-
-static int
-queue_ram_envelope_create(uint32_t msgid, const char *buf, size_t len,
- uint64_t *evpid)
-{
- struct qr_envelope *evp;
- struct qr_message *msg;
-
- if ((msg = get_message(msgid)) == NULL)
- return (0);
-
- do {
- *evpid = queue_generate_evpid(msgid);
- } while (tree_check(&msg->envelopes, *evpid));
- evp = calloc(1, sizeof *evp);
- if (evp == NULL) {
- log_warn("warn: queue-ram: calloc");
- return (0);
- }
- evp->len = len;
- evp->buf = malloc(len);
- if (evp->buf == NULL) {
- log_warn("warn: queue-ram: malloc");
- free(evp);
- return (0);
- }
- memmove(evp->buf, buf, len);
- tree_xset(&msg->envelopes, *evpid, evp);
- stat_increment("queue.ram.envelope.size", len);
- return (1);
-}
-
-static int
-queue_ram_envelope_delete(uint64_t evpid)
-{
- struct qr_envelope *evp;
- struct qr_message *msg;
-
- if ((msg = get_message(evpid_to_msgid(evpid))) == NULL)
- return (0);
-
- if ((evp = tree_pop(&msg->envelopes, evpid)) == NULL) {
- log_warnx("warn: queue-ram: not found");
- return (0);
- }
- stat_decrement("queue.ram.envelope.size", evp->len);
- free(evp->buf);
- free(evp);
- if (tree_empty(&msg->envelopes)) {
- tree_xpop(&messages, evpid_to_msgid(evpid));
- stat_decrement("queue.ram.message.size", msg->len);
- free(msg->buf);
- free(msg);
- }
- return (1);
-}
-
-static int
-queue_ram_envelope_update(uint64_t evpid, const char *buf, size_t len)
-{
- struct qr_envelope *evp;
- struct qr_message *msg;
- void *tmp;
-
- if ((msg = get_message(evpid_to_msgid(evpid))) == NULL)
- return (0);
-
- if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) {
- log_warn("warn: queue-ram: not found");
- return (0);
- }
- tmp = malloc(len);
- if (tmp == NULL) {
- log_warn("warn: queue-ram: malloc");
- return (0);
- }
- memmove(tmp, buf, len);
- free(evp->buf);
- evp->len = len;
- evp->buf = tmp;
- stat_decrement("queue.ram.envelope.size", evp->len);
- stat_increment("queue.ram.envelope.size", len);
- return (1);
-}
-
-static int
-queue_ram_envelope_load(uint64_t evpid, char *buf, size_t len)
-{
- struct qr_envelope *evp;
- struct qr_message *msg;
-
- if ((msg = get_message(evpid_to_msgid(evpid))) == NULL)
- return (0);
-
- if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) {
- log_warn("warn: queue-ram: not found");
- return (0);
- }
- if (len < evp->len) {
- log_warnx("warn: queue-ram: buffer too small");
- return (0);
- }
- memmove(buf, evp->buf, evp->len);
- return (evp->len);
-}
-
-static int
-queue_ram_envelope_walk(uint64_t *evpid, char *buf, size_t len)
-{
- return (-1);
-}
-
-static int
-queue_ram_init(struct passwd *pw, int server, const char * conf)
-{
- tree_init(&messages);
-
- queue_api_on_message_create(queue_ram_message_create);
- queue_api_on_message_commit(queue_ram_message_commit);
- queue_api_on_message_delete(queue_ram_message_delete);
- queue_api_on_message_fd_r(queue_ram_message_fd_r);
- queue_api_on_envelope_create(queue_ram_envelope_create);
- queue_api_on_envelope_delete(queue_ram_envelope_delete);
- queue_api_on_envelope_update(queue_ram_envelope_update);
- queue_api_on_envelope_load(queue_ram_envelope_load);
- queue_api_on_envelope_walk(queue_ram_envelope_walk);
-
- return (1);
-}
-
-struct queue_backend queue_backend_ram = {
- queue_ram_init,
-};
diff --git a/smtpd/report_smtp.c b/smtpd/report_smtp.c
deleted file mode 100644
index 7802eaae..00000000
--- a/smtpd/report_smtp.c
+++ /dev/null
@@ -1,335 +0,0 @@
-/* $OpenBSD: report_smtp.c,v 1.11 2020/01/07 23:03:37 gilles Exp $ */
-
-/*
- * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-
-#include <netinet/in.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <limits.h>
-#include <inttypes.h>
-#include <openssl/ssl.h>
-#include <resolv.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
-#include <vis.h>
-#else
-#include "bsd-vis.h"
-#endif
-
-#include "smtpd.h"
-#include "log.h"
-#include "ssl.h"
-#include "rfc5322.h"
-
-void
-report_smtp_link_connect(const char *direction, uint64_t qid, const char *rdns, int fcrdns,
- const struct sockaddr_storage *ss_src,
- const struct sockaddr_storage *ss_dest)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_LINK_CONNECT, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_string(p_lka, rdns);
- m_add_int(p_lka, fcrdns);
- m_add_sockaddr(p_lka, (const struct sockaddr *)ss_src);
- m_add_sockaddr(p_lka, (const struct sockaddr *)ss_dest);
- m_close(p_lka);
-}
-
-void
-report_smtp_link_greeting(const char *direction, uint64_t qid,
- const char *domain)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_LINK_GREETING, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_string(p_lka, domain);
- m_close(p_lka);
-}
-
-void
-report_smtp_link_identify(const char *direction, uint64_t qid, const char *method, const char *identity)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_LINK_IDENTIFY, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_string(p_lka, method);
- m_add_string(p_lka, identity);
- m_close(p_lka);
-}
-
-void
-report_smtp_link_tls(const char *direction, uint64_t qid, const char *ssl)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_LINK_TLS, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_string(p_lka, ssl);
- m_close(p_lka);
-}
-
-void
-report_smtp_link_disconnect(const char *direction, uint64_t qid)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_LINK_DISCONNECT, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_close(p_lka);
-}
-
-void
-report_smtp_link_auth(const char *direction, uint64_t qid, const char *user, const char *result)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_LINK_AUTH, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_string(p_lka, user);
- m_add_string(p_lka, result);
- m_close(p_lka);
-}
-
-void
-report_smtp_tx_reset(const char *direction, uint64_t qid, uint32_t msgid)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_TX_RESET, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_u32(p_lka, msgid);
- m_close(p_lka);
-}
-
-void
-report_smtp_tx_begin(const char *direction, uint64_t qid, uint32_t msgid)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_TX_BEGIN, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_u32(p_lka, msgid);
- m_close(p_lka);
-}
-
-void
-report_smtp_tx_mail(const char *direction, uint64_t qid, uint32_t msgid, const char *address, int ok)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_TX_MAIL, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_u32(p_lka, msgid);
- m_add_string(p_lka, address);
- m_add_int(p_lka, ok);
- m_close(p_lka);
-}
-
-void
-report_smtp_tx_rcpt(const char *direction, uint64_t qid, uint32_t msgid, const char *address, int ok)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_TX_RCPT, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_u32(p_lka, msgid);
- m_add_string(p_lka, address);
- m_add_int(p_lka, ok);
- m_close(p_lka);
-}
-
-void
-report_smtp_tx_envelope(const char *direction, uint64_t qid, uint32_t msgid, uint64_t evpid)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_TX_ENVELOPE, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_u32(p_lka, msgid);
- m_add_id(p_lka, evpid);
- m_close(p_lka);
-}
-
-void
-report_smtp_tx_data(const char *direction, uint64_t qid, uint32_t msgid, int ok)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_TX_DATA, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_u32(p_lka, msgid);
- m_add_int(p_lka, ok);
- m_close(p_lka);
-}
-
-void
-report_smtp_tx_commit(const char *direction, uint64_t qid, uint32_t msgid, size_t msgsz)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_TX_COMMIT, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_u32(p_lka, msgid);
- m_add_size(p_lka, msgsz);
- m_close(p_lka);
-}
-
-void
-report_smtp_tx_rollback(const char *direction, uint64_t qid, uint32_t msgid)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_TX_ROLLBACK, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_u32(p_lka, msgid);
- m_close(p_lka);
-}
-
-void
-report_smtp_protocol_client(const char *direction, uint64_t qid, const char *command)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_PROTOCOL_CLIENT, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_string(p_lka, command);
- m_close(p_lka);
-}
-
-void
-report_smtp_protocol_server(const char *direction, uint64_t qid, const char *response)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_PROTOCOL_SERVER, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_string(p_lka, response);
- m_close(p_lka);
-}
-
-void
-report_smtp_filter_response(const char *direction, uint64_t qid, int phase, int response, const char *param)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_FILTER_RESPONSE, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_add_int(p_lka, phase);
- m_add_int(p_lka, response);
- m_add_string(p_lka, param);
- m_close(p_lka);
-}
-
-void
-report_smtp_timeout(const char *direction, uint64_t qid)
-{
- struct timeval tv;
-
- gettimeofday(&tv, NULL);
-
- m_create(p_lka, IMSG_REPORT_SMTP_TIMEOUT, 0, 0, -1);
- m_add_string(p_lka, direction);
- m_add_timeval(p_lka, &tv);
- m_add_id(p_lka, qid);
- m_close(p_lka);
-}
diff --git a/smtpd/resolver.c b/smtpd/resolver.c
deleted file mode 100644
index f0f0f8ea..00000000
--- a/smtpd/resolver.c
+++ /dev/null
@@ -1,462 +0,0 @@
-/* $OpenBSD: resolver.c,v 1.5 2019/06/13 11:45:35 eric Exp $ */
-
-/*
- * Copyright (c) 2017-2018 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/tree.h>
-#include <sys/queue.h>
-#include <netinet/in.h>
-
-#include <asr.h>
-#include <ctype.h>
-#include <errno.h>
-#include <imsg.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define p_resolver p_lka
-
-struct request {
- SPLAY_ENTRY(request) entry;
- uint32_t id;
- void (*cb_ai)(void *, int, struct addrinfo *);
- void (*cb_ni)(void *, int, const char *, const char *);
- void (*cb_res)(void *, int, int, int, const void *, int);
- void *arg;
- struct addrinfo *ai;
-};
-
-struct session {
- uint32_t reqid;
- struct mproc *proc;
- char *host;
- char *serv;
-};
-
-SPLAY_HEAD(reqtree, request);
-
-static void resolver_init(void);
-static void resolver_getaddrinfo_cb(struct asr_result *, void *);
-static void resolver_getnameinfo_cb(struct asr_result *, void *);
-static void resolver_res_query_cb(struct asr_result *, void *);
-
-static int request_cmp(struct request *, struct request *);
-SPLAY_PROTOTYPE(reqtree, request, entry, request_cmp);
-
-/* musl work-around */
-void portable_freeaddrinfo(struct addrinfo *);
-
-static struct reqtree reqs;
-
-void
-resolver_getaddrinfo(const char *hostname, const char *servname,
- const struct addrinfo *hints, void (*cb)(void *, int, struct addrinfo *),
- void *arg)
-{
- struct request *req;
-
- resolver_init();
-
- req = calloc(1, sizeof(*req));
- if (req == NULL) {
- cb(arg, EAI_MEMORY, NULL);
- return;
- }
-
- while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
- req->id = arc4random();
- req->cb_ai = cb;
- req->arg = arg;
-
- SPLAY_INSERT(reqtree, &reqs, req);
-
- m_create(p_resolver, IMSG_GETADDRINFO, req->id, 0, -1);
- m_add_int(p_resolver, hints ? hints->ai_flags : 0);
- m_add_int(p_resolver, hints ? hints->ai_family : 0);
- m_add_int(p_resolver, hints ? hints->ai_socktype : 0);
- m_add_int(p_resolver, hints ? hints->ai_protocol : 0);
- m_add_string(p_resolver, hostname);
- m_add_string(p_resolver, servname);
- m_close(p_resolver);
-}
-
-void
-resolver_getnameinfo(const struct sockaddr *sa, int flags,
- void(*cb)(void *, int, const char *, const char *), void *arg)
-{
- struct request *req;
-
- resolver_init();
-
- req = calloc(1, sizeof(*req));
- if (req == NULL) {
- cb(arg, EAI_MEMORY, NULL, NULL);
- return;
- }
-
- while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
- req->id = arc4random();
- req->cb_ni = cb;
- req->arg = arg;
-
- SPLAY_INSERT(reqtree, &reqs, req);
-
- m_create(p_resolver, IMSG_GETNAMEINFO, req->id, 0, -1);
- m_add_sockaddr(p_resolver, sa);
- m_add_int(p_resolver, flags);
- m_close(p_resolver);
-}
-
-void
-resolver_res_query(const char *dname, int class, int type,
- void (*cb)(void *, int, int, int, const void *, int), void *arg)
-{
- struct request *req;
-
- resolver_init();
-
- req = calloc(1, sizeof(*req));
- if (req == NULL) {
- cb(arg, NETDB_INTERNAL, 0, 0, NULL, 0);
- return;
- }
-
- while (req->id == 0 || SPLAY_FIND(reqtree, &reqs, req))
- req->id = arc4random();
- req->cb_res = cb;
- req->arg = arg;
-
- SPLAY_INSERT(reqtree, &reqs, req);
-
- m_create(p_resolver, IMSG_RES_QUERY, req->id, 0, -1);
- m_add_string(p_resolver, dname);
- m_add_int(p_resolver, class);
- m_add_int(p_resolver, type);
- m_close(p_resolver);
-}
-
-void
-resolver_dispatch_request(struct mproc *proc, struct imsg *imsg)
-{
- const char *hostname, *servname, *dname;
- struct session *s;
- struct asr_query *q;
- struct addrinfo hints;
- struct sockaddr_storage ss;
- struct sockaddr *sa;
- struct msg m;
- uint32_t reqid;
- int class, type, flags, save_errno;
-
- reqid = imsg->hdr.peerid;
- m_msg(&m, imsg);
-
- switch (imsg->hdr.type) {
-
- case IMSG_GETADDRINFO:
- servname = NULL;
- memset(&hints, 0 , sizeof(hints));
- m_get_int(&m, &hints.ai_flags);
- m_get_int(&m, &hints.ai_family);
- m_get_int(&m, &hints.ai_socktype);
- m_get_int(&m, &hints.ai_protocol);
- m_get_string(&m, &hostname);
- m_get_string(&m, &servname);
- m_end(&m);
-
- s = NULL;
- q = NULL;
- if ((s = calloc(1, sizeof(*s))) &&
- (q = getaddrinfo_async(hostname, servname, &hints, NULL)) &&
- (event_asr_run(q, resolver_getaddrinfo_cb, s))) {
- s->reqid = reqid;
- s->proc = proc;
- break;
- }
- save_errno = errno;
-
- if (q)
- asr_abort(q);
- if (s)
- free(s);
-
- m_create(proc, IMSG_GETADDRINFO_END, reqid, 0, -1);
- m_add_int(proc, EAI_SYSTEM);
- m_add_int(proc, save_errno);
- m_close(proc);
- break;
-
- case IMSG_GETNAMEINFO:
- sa = (struct sockaddr*)&ss;
- m_get_sockaddr(&m, sa);
- m_get_int(&m, &flags);
- m_end(&m);
-
- s = NULL;
- q = NULL;
- if ((s = calloc(1, sizeof(*s))) &&
- (s->host = malloc(NI_MAXHOST)) &&
- (s->serv = malloc(NI_MAXSERV)) &&
- (q = getnameinfo_async(sa, SA_LEN(sa), s->host, NI_MAXHOST,
- s->serv, NI_MAXSERV, flags, NULL)) &&
- (event_asr_run(q, resolver_getnameinfo_cb, s))) {
- s->reqid = reqid;
- s->proc = proc;
- break;
- }
- save_errno = errno;
-
- if (q)
- asr_abort(q);
- if (s) {
- free(s->host);
- free(s->serv);
- free(s);
- }
-
- m_create(proc, IMSG_GETNAMEINFO, reqid, 0, -1);
- m_add_int(proc, EAI_SYSTEM);
- m_add_int(proc, save_errno);
- m_add_string(proc, NULL);
- m_add_string(proc, NULL);
- m_close(proc);
- break;
-
- case IMSG_RES_QUERY:
- m_get_string(&m, &dname);
- m_get_int(&m, &class);
- m_get_int(&m, &type);
- m_end(&m);
-
- s = NULL;
- q = NULL;
- if ((s = calloc(1, sizeof(*s))) &&
- (q = res_query_async(dname, class, type, NULL)) &&
- (event_asr_run(q, resolver_res_query_cb, s))) {
- s->reqid = reqid;
- s->proc = proc;
- break;
- }
- save_errno = errno;
-
- if (q)
- asr_abort(q);
- if (s)
- free(s);
-
- m_create(proc, IMSG_RES_QUERY, reqid, 0, -1);
- m_add_int(proc, NETDB_INTERNAL);
- m_add_int(proc, save_errno);
- m_add_int(proc, 0);
- m_add_int(proc, 0);
- m_add_data(proc, NULL, 0);
- m_close(proc);
- break;
-
- default:
- fatalx("%s: %s", __func__, imsg_to_str(imsg->hdr.type));
- }
-}
-
-void
-resolver_dispatch_result(struct mproc *proc, struct imsg *imsg)
-{
- struct request key, *req;
- struct sockaddr_storage ss;
- struct addrinfo *ai;
- struct msg m;
- const char *cname, *host, *serv;
- const void *data;
- size_t datalen;
- int gai_errno, herrno, rcode, count;
-
- key.id = imsg->hdr.peerid;
- req = SPLAY_FIND(reqtree, &reqs, &key);
- if (req == NULL)
- fatalx("%s: unknown request %08x", __func__, imsg->hdr.peerid);
-
- m_msg(&m, imsg);
-
- switch (imsg->hdr.type) {
-
- case IMSG_GETADDRINFO:
- ai = calloc(1, sizeof(*ai));
- if (ai == NULL) {
- log_warn("%s: calloc", __func__);
- break;
- }
- m_get_int(&m, &ai->ai_flags);
- m_get_int(&m, &ai->ai_family);
- m_get_int(&m, &ai->ai_socktype);
- m_get_int(&m, &ai->ai_protocol);
- m_get_sockaddr(&m, (struct sockaddr *)&ss);
- m_get_string(&m, &cname);
- m_end(&m);
-
- ai->ai_addr = malloc(SS_LEN(&ss));
- if (ai->ai_addr == NULL) {
- log_warn("%s: malloc", __func__);
- free(ai);
- break;
- }
-
- memmove(ai->ai_addr, &ss, SS_LEN(&ss));
-
- if (cname) {
- ai->ai_canonname = strdup(cname);
- if (ai->ai_canonname == NULL) {
- log_warn("%s: strdup", __func__);
- free(ai->ai_addr);
- free(ai);
- break;
- }
- }
-
- ai->ai_next = req->ai;
- req->ai = ai;
- break;
-
- case IMSG_GETADDRINFO_END:
- m_get_int(&m, &gai_errno);
- m_get_int(&m, &errno);
- m_end(&m);
-
- SPLAY_REMOVE(reqtree, &reqs, req);
- req->cb_ai(req->arg, gai_errno, req->ai);
- free(req);
- break;
-
- case IMSG_GETNAMEINFO:
- m_get_int(&m, &gai_errno);
- m_get_int(&m, &errno);
- m_get_string(&m, &host);
- m_get_string(&m, &serv);
- m_end(&m);
-
- SPLAY_REMOVE(reqtree, &reqs, req);
- req->cb_ni(req->arg, gai_errno, host, serv);
- free(req);
- break;
-
- case IMSG_RES_QUERY:
- m_get_int(&m, &herrno);
- m_get_int(&m, &errno);
- m_get_int(&m, &rcode);
- m_get_int(&m, &count);
- m_get_data(&m, &data, &datalen);
- m_end(&m);
-
- SPLAY_REMOVE(reqtree, &reqs, req);
- req->cb_res(req->arg, herrno, rcode, count, data, datalen);
- free(req);
- break;
- }
-}
-
-static void
-resolver_init(void)
-{
- static int init = 0;
-
- if (init == 0) {
- SPLAY_INIT(&reqs);
- init = 1;
- }
-}
-
-static void
-resolver_getaddrinfo_cb(struct asr_result *ar, void *arg)
-{
- struct session *s = arg;
- struct addrinfo *ai;
-
- for (ai = ar->ar_addrinfo; ai; ai = ai->ai_next) {
- m_create(s->proc, IMSG_GETADDRINFO, s->reqid, 0, -1);
- m_add_int(s->proc, ai->ai_flags);
- m_add_int(s->proc, ai->ai_family);
- m_add_int(s->proc, ai->ai_socktype);
- m_add_int(s->proc, ai->ai_protocol);
- m_add_sockaddr(s->proc, ai->ai_addr);
- m_add_string(s->proc, ai->ai_canonname);
- m_close(s->proc);
- }
-
- m_create(s->proc, IMSG_GETADDRINFO_END, s->reqid, 0, -1);
- m_add_int(s->proc, ar->ar_gai_errno);
- m_add_int(s->proc, ar->ar_errno);
- m_close(s->proc);
-
- if (ar->ar_addrinfo)
- portable_freeaddrinfo(ar->ar_addrinfo);
- free(s);
-}
-
-static void
-resolver_getnameinfo_cb(struct asr_result *ar, void *arg)
-{
- struct session *s = arg;
-
- m_create(s->proc, IMSG_GETNAMEINFO, s->reqid, 0, -1);
- m_add_int(s->proc, ar->ar_gai_errno);
- m_add_int(s->proc, ar->ar_errno);
- m_add_string(s->proc, ar->ar_gai_errno ? NULL : s->host);
- m_add_string(s->proc, ar->ar_gai_errno ? NULL : s->serv);
- m_close(s->proc);
-
- free(s->host);
- free(s->serv);
- free(s);
-}
-
-static void
-resolver_res_query_cb(struct asr_result *ar, void *arg)
-{
- struct session *s = arg;
-
- m_create(s->proc, IMSG_RES_QUERY, s->reqid, 0, -1);
- m_add_int(s->proc, ar->ar_h_errno);
- m_add_int(s->proc, ar->ar_errno);
- m_add_int(s->proc, ar->ar_rcode);
- m_add_int(s->proc, ar->ar_count);
- m_add_data(s->proc, ar->ar_data, ar->ar_datalen);
- m_close(s->proc);
-
- free(ar->ar_data);
- free(s);
-}
-
-static int
-request_cmp(struct request *a, struct request *b)
-{
- if (a->id < b->id)
- return -1;
- if (a->id > b->id)
- return 1;
- return 0;
-}
-
-SPLAY_GENERATE(reqtree, request, entry, request_cmp);
diff --git a/smtpd/rfc5322.c b/smtpd/rfc5322.c
deleted file mode 100644
index 0af66772..00000000
--- a/smtpd/rfc5322.c
+++ /dev/null
@@ -1,266 +0,0 @@
-/* $OpenBSD: rfc5322.c,v 1.2 2018/10/24 18:59:29 gilles Exp $ */
-
-/*
- * Copyright (c) 2018 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 "includes.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "rfc5322.h"
-
-struct buf {
- char *buf;
- size_t bufsz;
- size_t buflen;
- size_t bufmax;
-};
-
-static int buf_alloc(struct buf *, size_t);
-static int buf_grow(struct buf *, size_t);
-static int buf_cat(struct buf *, const char *);
-
-struct rfc5322_parser {
- const char *line;
- int state; /* last parser state */
- int next; /* parser needs data */
- int unfold;
- const char *currhdr;
- struct buf hdr;
- struct buf val;
-};
-
-struct rfc5322_parser *
-rfc5322_parser_new(void)
-{
- struct rfc5322_parser *parser;
-
- parser = calloc(1, sizeof(*parser));
- if (parser == NULL)
- return NULL;
-
- rfc5322_clear(parser);
- parser->hdr.bufmax = 1024;
- parser->val.bufmax = 65536;
-
- return parser;
-}
-
-void
-rfc5322_free(struct rfc5322_parser *parser)
-{
- free(parser->hdr.buf);
- free(parser->val.buf);
- free(parser);
-}
-
-void
-rfc5322_clear(struct rfc5322_parser *parser)
-{
- parser->line = NULL;
- parser->state = RFC5322_NONE;
- parser->next = 0;
- parser->hdr.buflen = 0;
- parser->val.buflen = 0;
-}
-
-int
-rfc5322_push(struct rfc5322_parser *parser, const char *line)
-{
- if (parser->line) {
- errno = EALREADY;
- return -1;
- }
-
- parser->line = line;
- parser->next = 0;
-
- return 0;
-}
-
-int
-rfc5322_unfold_header(struct rfc5322_parser *parser)
-{
- if (parser->unfold) {
- errno = EALREADY;
- return -1;
- }
-
- if (parser->currhdr == NULL) {
- errno = EOPNOTSUPP;
- return -1;
- }
-
- if (buf_cat(&parser->val, parser->currhdr) == -1)
- return -1;
-
- parser->currhdr = NULL;
- parser->unfold = 1;
-
- return 0;
-}
-
-static int
-_rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res)
-{
- size_t len;
- const char *pos, *line;
-
- line = parser->line;
-
- switch(parser->state) {
-
- case RFC5322_HEADER_START:
- case RFC5322_HEADER_CONT:
- res->hdr = parser->hdr.buf;
-
- if (line && (line[0] == ' ' || line[0] == '\t')) {
- parser->line = NULL;
- parser->next = 1;
- if (parser->unfold) {
- if (buf_cat(&parser->val, "\n") == -1 ||
- buf_cat(&parser->val, line) == -1)
- return -1;
- }
- res->value = line;
- return RFC5322_HEADER_CONT;
- }
-
- if (parser->unfold) {
- parser->val.buflen = 0;
- parser->unfold = 0;
- res->value = parser->val.buf;
- }
- return RFC5322_HEADER_END;
-
- case RFC5322_NONE:
- case RFC5322_HEADER_END:
- if (line && (pos = strchr(line, ':'))) {
- len = pos - line;
- if (buf_grow(&parser->hdr, len + 1) == -1)
- return -1;
- (void)memcpy(parser->hdr.buf, line, len);
- parser->hdr.buf[len] = '\0';
- parser->hdr.buflen = len + 1;
- parser->line = NULL;
- parser->next = 1;
- parser->currhdr = pos + 1;
- res->hdr = parser->hdr.buf;
- res->value = pos + 1;
- return RFC5322_HEADER_START;
- }
-
- return RFC5322_END_OF_HEADERS;
-
- case RFC5322_END_OF_HEADERS:
- if (line == NULL)
- return RFC5322_END_OF_MESSAGE;
-
- if (line[0] == '\0') {
- parser->line = NULL;
- parser->next = 1;
- res->value = line;
- return RFC5322_BODY_START;
- }
-
- errno = EINVAL;
- return -1;
-
- case RFC5322_BODY_START:
- case RFC5322_BODY:
- if (line == NULL)
- return RFC5322_END_OF_MESSAGE;
-
- parser->line = NULL;
- parser->next = 1;
- res->value = line;
- return RFC5322_BODY;
-
- case RFC5322_END_OF_MESSAGE:
- errno = ENOMSG;
- return -1;
-
- default:
- errno = EINVAL;
- return -1;
- }
-}
-
-int
-rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res)
-{
- memset(res, 0, sizeof(*res));
-
- if (parser->next)
- return RFC5322_NONE;
-
- return (parser->state = _rfc5322_next(parser, res));
-}
-
-static int
-buf_alloc(struct buf *b, size_t need)
-{
- char *buf;
- size_t alloc;
-
- if (b->buf && b->bufsz >= need)
- return 0;
-
- if (need >= b->bufmax) {
- errno = ERANGE;
- return -1;
- }
-
-#define N 256
- alloc = N * (need / N) + ((need % N) ? N : 0);
-#undef N
- buf = reallocarray(b->buf, alloc, 1);
- if (buf == NULL)
- return -1;
-
- b->buf = buf;
- b->bufsz = alloc;
-
- return 0;
-}
-
-static int
-buf_grow(struct buf *b, size_t sz)
-{
- if (SIZE_T_MAX - b->buflen <= sz) {
- errno = ERANGE;
- return -1;
- }
-
- return buf_alloc(b, b->buflen + sz);
-}
-
-static int
-buf_cat(struct buf *b, const char *s)
-{
- size_t len = strlen(s);
-
- if (buf_grow(b, len + 1) == -1)
- return -1;
-
- (void)memmove(b->buf + b->buflen, s, len + 1);
- b->buflen += len;
- return 0;
-}
diff --git a/smtpd/rfc5322.h b/smtpd/rfc5322.h
deleted file mode 100644
index 0979bd4c..00000000
--- a/smtpd/rfc5322.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* $OpenBSD: rfc5322.h,v 1.1 2018/08/23 10:07:06 eric Exp $ */
-
-/*
- * Copyright (c) 2018 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 rfc5322_result {
- const char *hdr;
- const char *value;
-};
-
-#define RFC5322_ERR -1
-#define RFC5322_NONE 0
-#define RFC5322_HEADER_START 1
-#define RFC5322_HEADER_CONT 2
-#define RFC5322_HEADER_END 3
-#define RFC5322_END_OF_HEADERS 4
-#define RFC5322_BODY_START 5
-#define RFC5322_BODY 6
-#define RFC5322_END_OF_MESSAGE 7
-
-struct rfc5322_parser;
-
-struct rfc5322_parser *rfc5322_parser_new(void);
-void rfc5322_free(struct rfc5322_parser *);
-void rfc5322_clear(struct rfc5322_parser *);
-int rfc5322_push(struct rfc5322_parser *, const char *);
-int rfc5322_next(struct rfc5322_parser *, struct rfc5322_result *);
-int rfc5322_unfold_header(struct rfc5322_parser *);
diff --git a/smtpd/ruleset.c b/smtpd/ruleset.c
deleted file mode 100644
index 719a2913..00000000
--- a/smtpd/ruleset.c
+++ /dev/null
@@ -1,265 +0,0 @@
-/* $OpenBSD: ruleset.c,v 1.47 2019/11/25 14:18:33 gilles Exp $ */
-
-/*
- * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <netinet/in.h>
-
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <string.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define MATCH_RESULT(r, neg) ((r) == -1 ? -1 : ((neg) < 0 ? !(r) : (r)))
-
-static int
-ruleset_match_tag(struct rule *r, const struct envelope *evp)
-{
- int ret;
- struct table *table;
- enum table_service service = K_STRING;
-
- if (!r->flag_tag)
- return 1;
-
- if (r->flag_tag_regex)
- service = K_REGEX;
-
- table = table_find(env, r->table_tag);
- ret = table_match(table, service, evp->tag);
-
- return MATCH_RESULT(ret, r->flag_tag);
-}
-
-static int
-ruleset_match_from(struct rule *r, const struct envelope *evp)
-{
- int ret;
- int has_rdns;
- const char *key;
- struct table *table;
- enum table_service service = K_NETADDR;
-
- if (!r->flag_from)
- return 1;
-
- if (evp->flags & EF_INTERNAL) {
- /* if expanded from an empty table_from, skip rule
- * if no table
- */
- if (r->table_from == NULL)
- return 0;
- key = "local";
- }
- else if (r->flag_from_rdns) {
- has_rdns = strcmp(evp->hostname, "<unknown>") != 0;
- if (r->table_from == NULL)
- return MATCH_RESULT(has_rdns, r->flag_from);
- if (!has_rdns)
- return 0;
- key = evp->hostname;
- }
- else {
- key = ss_to_text(&evp->ss);
- if (r->flag_from_socket) {
- if (strcmp(key, "local") == 0)
- return MATCH_RESULT(1, r->flag_from);
- else
- return r->flag_from < 0 ? 1 : 0;
- }
- }
- if (r->flag_from_regex)
- service = K_REGEX;
-
- table = table_find(env, r->table_from);
- ret = table_match(table, service, key);
-
- return MATCH_RESULT(ret, r->flag_from);
-}
-
-static int
-ruleset_match_to(struct rule *r, const struct envelope *evp)
-{
- int ret;
- struct table *table;
- enum table_service service = K_DOMAIN;
-
- if (!r->flag_for)
- return 1;
-
- if (r->flag_for_regex)
- service = K_REGEX;
-
- table = table_find(env, r->table_for);
- ret = table_match(table, service, evp->dest.domain);
-
- return MATCH_RESULT(ret, r->flag_for);
-}
-
-static int
-ruleset_match_smtp_helo(struct rule *r, const struct envelope *evp)
-{
- int ret;
- struct table *table;
- enum table_service service = K_DOMAIN;
-
- if (!r->flag_smtp_helo)
- return 1;
-
- if (r->flag_smtp_helo_regex)
- service = K_REGEX;
-
- table = table_find(env, r->table_smtp_helo);
- ret = table_match(table, service, evp->helo);
-
- return MATCH_RESULT(ret, r->flag_smtp_helo);
-}
-
-static int
-ruleset_match_smtp_starttls(struct rule *r, const struct envelope *evp)
-{
- if (!r->flag_smtp_starttls)
- return 1;
-
- /* XXX - not until TLS flag is added to envelope */
- return -1;
-}
-
-static int
-ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp)
-{
- int ret;
- struct table *table;
- enum table_service service;
-
- if (!r->flag_smtp_auth)
- return 1;
-
- if (!(evp->flags & EF_AUTHENTICATED))
- ret = 0;
- else if (r->table_smtp_auth) {
-
- if (r->flag_smtp_auth_regex)
- service = K_REGEX;
- else
- service = strchr(evp->username, '@') ?
- K_MAILADDR : K_STRING;
- table = table_find(env, r->table_smtp_auth);
- ret = table_match(table, service, evp->username);
- }
- else
- ret = 1;
-
- return MATCH_RESULT(ret, r->flag_smtp_auth);
-}
-
-static int
-ruleset_match_smtp_mail_from(struct rule *r, const struct envelope *evp)
-{
- int ret;
- const char *key;
- struct table *table;
- enum table_service service = K_MAILADDR;
-
- if (!r->flag_smtp_mail_from)
- return 1;
-
- if (r->flag_smtp_mail_from_regex)
- service = K_REGEX;
-
- if ((key = mailaddr_to_text(&evp->sender)) == NULL)
- return -1;
-
- table = table_find(env, r->table_smtp_mail_from);
- ret = table_match(table, service, key);
-
- return MATCH_RESULT(ret, r->flag_smtp_mail_from);
-}
-
-static int
-ruleset_match_smtp_rcpt_to(struct rule *r, const struct envelope *evp)
-{
- int ret;
- const char *key;
- struct table *table;
- enum table_service service = K_MAILADDR;
-
- if (!r->flag_smtp_rcpt_to)
- return 1;
-
- if (r->flag_smtp_rcpt_to_regex)
- service = K_REGEX;
-
- if ((key = mailaddr_to_text(&evp->dest)) == NULL)
- return -1;
-
- table = table_find(env, r->table_smtp_rcpt_to);
- ret = table_match(table, service, key);
-
- return MATCH_RESULT(ret, r->flag_smtp_rcpt_to);
-}
-
-struct rule *
-ruleset_match(const struct envelope *evp)
-{
- struct rule *r;
- int i = 0;
-
-#define MATCH_EVAL(x) \
- switch ((x)) { \
- case -1: goto tempfail; \
- case 0: continue; \
- default: break; \
- }
- TAILQ_FOREACH(r, env->sc_rules, r_entry) {
- ++i;
- MATCH_EVAL(ruleset_match_tag(r, evp));
- MATCH_EVAL(ruleset_match_from(r, evp));
- MATCH_EVAL(ruleset_match_to(r, evp));
- MATCH_EVAL(ruleset_match_smtp_helo(r, evp));
- MATCH_EVAL(ruleset_match_smtp_auth(r, evp));
- MATCH_EVAL(ruleset_match_smtp_starttls(r, evp));
- MATCH_EVAL(ruleset_match_smtp_mail_from(r, evp));
- MATCH_EVAL(ruleset_match_smtp_rcpt_to(r, evp));
- goto matched;
- }
-#undef MATCH_EVAL
-
- errno = 0;
- log_trace(TRACE_RULES, "no rule matched");
- return (NULL);
-
-tempfail:
- errno = EAGAIN;
- log_trace(TRACE_RULES, "temporary failure in processing of a rule");
- return (NULL);
-
-matched:
- log_trace(TRACE_RULES, "rule #%d matched: %s", i, rule_to_text(r));
- return r;
-}
diff --git a/smtpd/runq.c b/smtpd/runq.c
deleted file mode 100644
index 786d36fb..00000000
--- a/smtpd/runq.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/* $OpenBSD: runq.c,v 1.3 2019/06/14 19:55:25 eric Exp $ */
-
-/*
- * Copyright (c) 2013,2019 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/uio.h>
-
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <time.h>
-
-#include "smtpd.h"
-
-struct job {
- TAILQ_ENTRY(job) entry;
- time_t when;
- void *arg;
-};
-
-struct runq {
- TAILQ_HEAD(, job) jobs;
- void (*cb)(struct runq *, void *);
- struct event ev;
-};
-
-static void runq_timeout(int, short, void *);
-
-static struct runq *active;
-
-static void
-runq_reset(struct runq *runq)
-{
- struct timeval tv;
- struct job *job;
- time_t now;
-
- job = TAILQ_FIRST(&runq->jobs);
- if (job == NULL)
- return;
-
- now = time(NULL);
- if (job->when <= now)
- tv.tv_sec = 0;
- else
- tv.tv_sec = job->when - now;
- tv.tv_usec = 0;
- evtimer_add(&runq->ev, &tv);
-}
-
-static void
-runq_timeout(int fd, short ev, void *arg)
-{
- struct runq *runq = arg;
- struct job *job;
- time_t now;
-
- active = runq;
- now = time(NULL);
-
- while((job = TAILQ_FIRST(&runq->jobs))) {
- if (job->when > now)
- break;
- TAILQ_REMOVE(&runq->jobs, job, entry);
- runq->cb(runq, job->arg);
- free(job);
- }
-
- active = NULL;
- runq_reset(runq);
-}
-
-int
-runq_init(struct runq **runqp, void (*cb)(struct runq *, void *))
-{
- struct runq *runq;
-
- runq = malloc(sizeof(*runq));
- if (runq == NULL)
- return (0);
-
- runq->cb = cb;
- TAILQ_INIT(&runq->jobs);
- evtimer_set(&runq->ev, runq_timeout, runq);
-
- *runqp = runq;
-
- return (1);
-}
-
-int
-runq_schedule(struct runq *runq, time_t delay, void *arg)
-{
- time_t t;
-
- time(&t);
- return runq_schedule_at(runq, t + delay, arg);
-}
-
-int
-runq_schedule_at(struct runq *runq, time_t when, void *arg)
-{
- struct job *job, *tmpjob;
-
- job = malloc(sizeof(*job));
- if (job == NULL)
- return (0);
-
- job->arg = arg;
- job->when = when;
-
- TAILQ_FOREACH(tmpjob, &runq->jobs, entry) {
- if (tmpjob->when > job->when) {
- TAILQ_INSERT_BEFORE(tmpjob, job, entry);
- goto done;
- }
- }
- TAILQ_INSERT_TAIL(&runq->jobs, job, entry);
-
- done:
- if (runq != active && job == TAILQ_FIRST(&runq->jobs)) {
- evtimer_del(&runq->ev);
- runq_reset(runq);
- }
- return (1);
-}
-
-int
-runq_cancel(struct runq *runq, void *arg)
-{
- struct job *job, *first;
-
- first = TAILQ_FIRST(&runq->jobs);
- TAILQ_FOREACH(job, &runq->jobs, entry) {
- if (job->arg == arg) {
- TAILQ_REMOVE(&runq->jobs, job, entry);
- free(job);
- if (runq != active && job == first) {
- evtimer_del(&runq->ev);
- runq_reset(runq);
- }
- return (1);
- }
- }
-
- return (0);
-}
-
-int
-runq_pending(struct runq *runq, void *arg, time_t *when)
-{
- struct job *job;
-
- TAILQ_FOREACH(job, &runq->jobs, entry) {
- if (job->arg == arg) {
- if (when)
- *when = job->when;
- return (1);
- }
- }
-
- return (0);
-}
diff --git a/smtpd/scheduler.c b/smtpd/scheduler.c
deleted file mode 100644
index ea70a83d..00000000
--- a/smtpd/scheduler.c
+++ /dev/null
@@ -1,618 +0,0 @@
-/* $OpenBSD: scheduler.c,v 1.60 2018/12/30 23:09:58 guenther Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2008-2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <dirent.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <grp.h> /* needed for setgroups */
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static void scheduler_imsg(struct mproc *, struct imsg *);
-static void scheduler_shutdown(void);
-static void scheduler_reset_events(void);
-static void scheduler_timeout(int, short, void *);
-
-static struct scheduler_backend *backend = NULL;
-static struct event ev;
-static size_t ninflight = 0;
-static int *types;
-static uint64_t *evpids;
-static uint32_t *msgids;
-static struct evpstate *state;
-
-extern const char *backend_scheduler;
-
-void
-scheduler_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct bounce_req_msg req;
- struct envelope evp;
- struct scheduler_info si;
- struct msg m;
- uint64_t evpid, id, holdq;
- uint32_t msgid;
- uint32_t inflight;
- size_t n, i;
- time_t timestamp;
- int v, r, type;
-
- if (imsg == NULL)
- scheduler_shutdown();
-
- switch (imsg->hdr.type) {
-
- case IMSG_QUEUE_ENVELOPE_SUBMIT:
- m_msg(&m, imsg);
- m_get_envelope(&m, &evp);
- m_end(&m);
- log_trace(TRACE_SCHEDULER,
- "scheduler: inserting evp:%016" PRIx64, evp.id);
- scheduler_info(&si, &evp);
- stat_increment("scheduler.envelope.incoming", 1);
- backend->insert(&si);
- return;
-
- case IMSG_QUEUE_MESSAGE_COMMIT:
- m_msg(&m, imsg);
- m_get_msgid(&m, &msgid);
- m_end(&m);
- log_trace(TRACE_SCHEDULER,
- "scheduler: committing msg:%08" PRIx32, msgid);
- n = backend->commit(msgid);
- stat_decrement("scheduler.envelope.incoming", n);
- stat_increment("scheduler.envelope", n);
- scheduler_reset_events();
- return;
-
- case IMSG_QUEUE_DISCOVER_EVPID:
- m_msg(&m, imsg);
- m_get_envelope(&m, &evp);
- m_end(&m);
- r = backend->query(evp.id);
- if (r) {
- log_debug("debug: scheduler: evp:%016" PRIx64
- " already scheduled", evp.id);
- return;
- }
- log_trace(TRACE_SCHEDULER,
- "scheduler: discovering evp:%016" PRIx64, evp.id);
- scheduler_info(&si, &evp);
- stat_increment("scheduler.envelope.incoming", 1);
- backend->insert(&si);
- return;
-
- case IMSG_QUEUE_DISCOVER_MSGID:
- m_msg(&m, imsg);
- m_get_msgid(&m, &msgid);
- m_end(&m);
- r = backend->query(msgid);
- if (r) {
- log_debug("debug: scheduler: msgid:%08" PRIx32
- " already scheduled", msgid);
- return;
- }
- log_trace(TRACE_SCHEDULER,
- "scheduler: committing msg:%08" PRIx32, msgid);
- n = backend->commit(msgid);
- stat_decrement("scheduler.envelope.incoming", n);
- stat_increment("scheduler.envelope", n);
- scheduler_reset_events();
- return;
-
- case IMSG_QUEUE_MESSAGE_ROLLBACK:
- m_msg(&m, imsg);
- m_get_msgid(&m, &msgid);
- m_end(&m);
- log_trace(TRACE_SCHEDULER, "scheduler: aborting msg:%08" PRIx32,
- msgid);
- n = backend->rollback(msgid);
- stat_decrement("scheduler.envelope.incoming", n);
- scheduler_reset_events();
- return;
-
- case IMSG_QUEUE_ENVELOPE_REMOVE:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_get_u32(&m, &inflight);
- m_end(&m);
- log_trace(TRACE_SCHEDULER,
- "scheduler: queue requested removal of evp:%016" PRIx64,
- evpid);
- stat_decrement("scheduler.envelope", 1);
- if (!inflight)
- backend->remove(evpid);
- else {
- backend->delete(evpid);
- ninflight -= 1;
- stat_decrement("scheduler.envelope.inflight", 1);
- }
-
- scheduler_reset_events();
- return;
-
- case IMSG_QUEUE_ENVELOPE_ACK:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_end(&m);
- log_trace(TRACE_SCHEDULER,
- "scheduler: queue ack removal of evp:%016" PRIx64,
- evpid);
- ninflight -= 1;
- stat_decrement("scheduler.envelope.inflight", 1);
- scheduler_reset_events();
- return;
-
- case IMSG_QUEUE_DELIVERY_OK:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_end(&m);
- log_trace(TRACE_SCHEDULER,
- "scheduler: deleting evp:%016" PRIx64 " (ok)", evpid);
- backend->delete(evpid);
- ninflight -= 1;
- stat_increment("scheduler.delivery.ok", 1);
- stat_decrement("scheduler.envelope.inflight", 1);
- stat_decrement("scheduler.envelope", 1);
- scheduler_reset_events();
- return;
-
- case IMSG_QUEUE_DELIVERY_TEMPFAIL:
- m_msg(&m, imsg);
- m_get_envelope(&m, &evp);
- m_end(&m);
- log_trace(TRACE_SCHEDULER,
- "scheduler: updating evp:%016" PRIx64, evp.id);
- scheduler_info(&si, &evp);
- backend->update(&si);
- ninflight -= 1;
- stat_increment("scheduler.delivery.tempfail", 1);
- stat_decrement("scheduler.envelope.inflight", 1);
-
- for (i = 0; i < MAX_BOUNCE_WARN; i++) {
- if (env->sc_bounce_warn[i] == 0)
- break;
- timestamp = si.creation + env->sc_bounce_warn[i];
- if (si.nexttry >= timestamp &&
- si.lastbounce < timestamp) {
- req.evpid = evp.id;
- req.timestamp = timestamp;
- req.bounce.type = B_DELAYED;
- req.bounce.delay = env->sc_bounce_warn[i];
- req.bounce.ttl = si.ttl;
- m_compose(p, IMSG_SCHED_ENVELOPE_BOUNCE, 0, 0, -1,
- &req, sizeof req);
- break;
- }
- }
- scheduler_reset_events();
- return;
-
- case IMSG_QUEUE_DELIVERY_PERMFAIL:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_end(&m);
- log_trace(TRACE_SCHEDULER,
- "scheduler: deleting evp:%016" PRIx64 " (fail)", evpid);
- backend->delete(evpid);
- ninflight -= 1;
- stat_increment("scheduler.delivery.permfail", 1);
- stat_decrement("scheduler.envelope.inflight", 1);
- stat_decrement("scheduler.envelope", 1);
- scheduler_reset_events();
- return;
-
- case IMSG_QUEUE_DELIVERY_LOOP:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_end(&m);
- log_trace(TRACE_SCHEDULER,
- "scheduler: deleting evp:%016" PRIx64 " (loop)", evpid);
- backend->delete(evpid);
- ninflight -= 1;
- stat_increment("scheduler.delivery.loop", 1);
- stat_decrement("scheduler.envelope.inflight", 1);
- stat_decrement("scheduler.envelope", 1);
- scheduler_reset_events();
- return;
-
- case IMSG_QUEUE_HOLDQ_HOLD:
- m_msg(&m, imsg);
- m_get_evpid(&m, &evpid);
- m_get_id(&m, &holdq);
- m_end(&m);
- log_trace(TRACE_SCHEDULER,
- "scheduler: holding evp:%016" PRIx64 " on %016" PRIx64,
- evpid, holdq);
- backend->hold(evpid, holdq);
- ninflight -= 1;
- stat_decrement("scheduler.envelope.inflight", 1);
- scheduler_reset_events();
- return;
-
- case IMSG_QUEUE_HOLDQ_RELEASE:
- m_msg(&m, imsg);
- m_get_int(&m, &type);
- m_get_id(&m, &holdq);
- m_get_int(&m, &r);
- m_end(&m);
- log_trace(TRACE_SCHEDULER,
- "scheduler: releasing %d on holdq (%d, %016" PRIx64 ")",
- r, type, holdq);
- backend->release(type, holdq, r);
- scheduler_reset_events();
- return;
-
- case IMSG_CTL_PAUSE_MDA:
- log_trace(TRACE_SCHEDULER, "scheduler: pausing mda");
- env->sc_flags |= SMTPD_MDA_PAUSED;
- return;
-
- case IMSG_CTL_RESUME_MDA:
- log_trace(TRACE_SCHEDULER, "scheduler: resuming mda");
- env->sc_flags &= ~SMTPD_MDA_PAUSED;
- scheduler_reset_events();
- return;
-
- case IMSG_CTL_PAUSE_MTA:
- log_trace(TRACE_SCHEDULER, "scheduler: pausing mta");
- env->sc_flags |= SMTPD_MTA_PAUSED;
- return;
-
- case IMSG_CTL_RESUME_MTA:
- log_trace(TRACE_SCHEDULER, "scheduler: resuming mta");
- env->sc_flags &= ~SMTPD_MTA_PAUSED;
- scheduler_reset_events();
- return;
-
- case IMSG_CTL_VERBOSE:
- m_msg(&m, imsg);
- m_get_int(&m, &v);
- m_end(&m);
- log_setverbose(v);
- return;
-
- case IMSG_CTL_PROFILE:
- m_msg(&m, imsg);
- m_get_int(&m, &v);
- m_end(&m);
- profiling = v;
- return;
-
- case IMSG_CTL_LIST_MESSAGES:
- msgid = *(uint32_t *)(imsg->data);
- n = backend->messages(msgid, msgids, env->sc_scheduler_max_msg_batch_size);
- m_compose(p, IMSG_CTL_LIST_MESSAGES, imsg->hdr.peerid, 0, -1,
- msgids, n * sizeof (*msgids));
- return;
-
- case IMSG_CTL_LIST_ENVELOPES:
- id = *(uint64_t *)(imsg->data);
- n = backend->envelopes(id, state, env->sc_scheduler_max_evp_batch_size);
- for (i = 0; i < n; i++) {
- m_create(p_queue, IMSG_CTL_LIST_ENVELOPES,
- imsg->hdr.peerid, 0, -1);
- m_add_evpid(p_queue, state[i].evpid);
- m_add_int(p_queue, state[i].flags);
- m_add_time(p_queue, state[i].time);
- m_close(p_queue);
- }
- m_compose(p_queue, IMSG_CTL_LIST_ENVELOPES,
- imsg->hdr.peerid, 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_SCHEDULE:
- id = *(uint64_t *)(imsg->data);
- if (id <= 0xffffffffL)
- log_debug("debug: scheduler: "
- "scheduling msg:%08" PRIx64, id);
- else
- log_debug("debug: scheduler: "
- "scheduling evp:%016" PRIx64, id);
- r = backend->schedule(id);
- scheduler_reset_events();
- m_compose(p, r ? IMSG_CTL_OK : IMSG_CTL_FAIL, imsg->hdr.peerid,
- 0, -1, NULL, 0);
- return;
-
- case IMSG_QUEUE_ENVELOPE_SCHEDULE:
- id = *(uint64_t *)(imsg->data);
- backend->schedule(id);
- scheduler_reset_events();
- return;
-
- case IMSG_CTL_REMOVE:
- id = *(uint64_t *)(imsg->data);
- if (id <= 0xffffffffL)
- log_debug("debug: scheduler: "
- "removing msg:%08" PRIx64, id);
- else
- log_debug("debug: scheduler: "
- "removing evp:%016" PRIx64, id);
- r = backend->remove(id);
- scheduler_reset_events();
- m_compose(p, r ? IMSG_CTL_OK : IMSG_CTL_FAIL, imsg->hdr.peerid,
- 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_PAUSE_EVP:
- id = *(uint64_t *)(imsg->data);
- if (id <= 0xffffffffL)
- log_debug("debug: scheduler: "
- "suspending msg:%08" PRIx64, id);
- else
- log_debug("debug: scheduler: "
- "suspending evp:%016" PRIx64, id);
- r = backend->suspend(id);
- scheduler_reset_events();
- m_compose(p, r ? IMSG_CTL_OK : IMSG_CTL_FAIL, imsg->hdr.peerid,
- 0, -1, NULL, 0);
- return;
-
- case IMSG_CTL_RESUME_EVP:
- id = *(uint64_t *)(imsg->data);
- if (id <= 0xffffffffL)
- log_debug("debug: scheduler: "
- "resuming msg:%08" PRIx64, id);
- else
- log_debug("debug: scheduler: "
- "resuming evp:%016" PRIx64, id);
- r = backend->resume(id);
- scheduler_reset_events();
- m_compose(p, r ? IMSG_CTL_OK : IMSG_CTL_FAIL, imsg->hdr.peerid,
- 0, -1, NULL, 0);
- return;
- }
-
- errx(1, "scheduler_imsg: unexpected %s imsg",
- imsg_to_str(imsg->hdr.type));
-}
-
-static void
-scheduler_shutdown(void)
-{
- log_debug("debug: scheduler agent exiting");
- _exit(0);
-}
-
-static void
-scheduler_reset_events(void)
-{
- struct timeval tv;
-
- evtimer_del(&ev);
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- evtimer_add(&ev, &tv);
-}
-
-int
-scheduler(void)
-{
- struct passwd *pw;
-
- backend = scheduler_backend_lookup(backend_scheduler);
- if (backend == NULL)
- errx(1, "cannot find scheduler backend \"%s\"",
- backend_scheduler);
-
- purge_config(PURGE_EVERYTHING & ~PURGE_DISPATCHERS);
-
- if ((pw = getpwnam(SMTPD_USER)) == NULL)
- fatalx("unknown user " SMTPD_USER);
-
- config_process(PROC_SCHEDULER);
-
- backend->init(backend_scheduler);
-
- if (chroot(PATH_CHROOT) == -1)
- fatal("scheduler: chroot");
- if (chdir("/") == -1)
- fatal("scheduler: chdir(\"/\")");
-
- if (setgroups(1, &pw->pw_gid) ||
- setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
- setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
- fatal("scheduler: cannot drop privileges");
-
- evpids = xcalloc(env->sc_scheduler_max_schedule, sizeof *evpids);
- types = xcalloc(env->sc_scheduler_max_schedule, sizeof *types);
- msgids = xcalloc(env->sc_scheduler_max_msg_batch_size, sizeof *msgids);
- state = xcalloc(env->sc_scheduler_max_evp_batch_size, sizeof *state);
-
- imsg_callback = scheduler_imsg;
- event_init();
-
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, SIG_IGN);
- signal(SIGPIPE, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-
- config_peer(PROC_CONTROL);
- config_peer(PROC_QUEUE);
-
- evtimer_set(&ev, scheduler_timeout, NULL);
- scheduler_reset_events();
-
-#if HAVE_PLEDGE
- if (pledge("stdio", NULL) == -1)
- err(1, "pledge");
-#endif
-
- event_dispatch();
- fatalx("exited event loop");
-
- return (0);
-}
-
-static void
-scheduler_timeout(int fd, short event, void *p)
-{
- struct timeval tv;
- size_t i;
- size_t d_inflight;
- size_t d_envelope;
- size_t d_removed;
- size_t d_expired;
- size_t d_updated;
- size_t count;
- int mask, r, delay;
-
- tv.tv_sec = 0;
- tv.tv_usec = 0;
-
- mask = SCHED_UPDATE;
-
- if (ninflight < env->sc_scheduler_max_inflight) {
- mask |= SCHED_EXPIRE | SCHED_REMOVE | SCHED_BOUNCE;
- if (!(env->sc_flags & SMTPD_MDA_PAUSED))
- mask |= SCHED_MDA;
- if (!(env->sc_flags & SMTPD_MTA_PAUSED))
- mask |= SCHED_MTA;
- }
-
- count = env->sc_scheduler_max_schedule;
-
- log_trace(TRACE_SCHEDULER, "scheduler: getting batch: mask=0x%x, count=%zu", mask, count);
-
- r = backend->batch(mask, &delay, &count, evpids, types);
-
- log_trace(TRACE_SCHEDULER, "scheduler: got r=%i, delay=%i, count=%zu", r, delay, count);
-
- if (r < 0)
- fatalx("scheduler: error in batch handler");
-
- if (r == 0) {
-
- if (delay < -1)
- fatalx("scheduler: invalid delay %d", delay);
-
- if (delay == -1) {
- log_trace(TRACE_SCHEDULER, "scheduler: sleeping");
- return;
- }
-
- tv.tv_sec = delay;
- tv.tv_usec = 0;
- log_trace(TRACE_SCHEDULER,
- "scheduler: waiting for %s", duration_to_text(tv.tv_sec));
- evtimer_add(&ev, &tv);
- return;
- }
-
- d_inflight = 0;
- d_envelope = 0;
- d_removed = 0;
- d_expired = 0;
- d_updated = 0;
-
- for (i = 0; i < count; i++) {
- switch(types[i]) {
- case SCHED_REMOVE:
- log_debug("debug: scheduler: evp:%016" PRIx64
- " removed", evpids[i]);
- m_create(p_queue, IMSG_SCHED_ENVELOPE_REMOVE, 0, 0, -1);
- m_add_evpid(p_queue, evpids[i]);
- m_close(p_queue);
- d_envelope += 1;
- d_removed += 1;
- d_inflight += 1;
- break;
-
- case SCHED_EXPIRE:
- log_debug("debug: scheduler: evp:%016" PRIx64
- " expired", evpids[i]);
- m_create(p_queue, IMSG_SCHED_ENVELOPE_EXPIRE, 0, 0, -1);
- m_add_evpid(p_queue, evpids[i]);
- m_close(p_queue);
- d_envelope += 1;
- d_expired += 1;
- d_inflight += 1;
- break;
-
- case SCHED_UPDATE:
- log_debug("debug: scheduler: evp:%016" PRIx64
- " scheduled (update)", evpids[i]);
- d_updated += 1;
- break;
-
- case SCHED_BOUNCE:
- log_debug("debug: scheduler: evp:%016" PRIx64
- " scheduled (bounce)", evpids[i]);
- m_create(p_queue, IMSG_SCHED_ENVELOPE_INJECT, 0, 0, -1);
- m_add_evpid(p_queue, evpids[i]);
- m_close(p_queue);
- d_inflight += 1;
- break;
-
- case SCHED_MDA:
- log_debug("debug: scheduler: evp:%016" PRIx64
- " scheduled (mda)", evpids[i]);
- m_create(p_queue, IMSG_SCHED_ENVELOPE_DELIVER, 0, 0, -1);
- m_add_evpid(p_queue, evpids[i]);
- m_close(p_queue);
- d_inflight += 1;
- break;
-
- case SCHED_MTA:
- log_debug("debug: scheduler: evp:%016" PRIx64
- " scheduled (mta)", evpids[i]);
- m_create(p_queue, IMSG_SCHED_ENVELOPE_TRANSFER, 0, 0, -1);
- m_add_evpid(p_queue, evpids[i]);
- m_close(p_queue);
- d_inflight += 1;
- break;
- }
- }
-
- stat_decrement("scheduler.envelope", d_envelope);
- stat_increment("scheduler.envelope.inflight", d_inflight);
- stat_increment("scheduler.envelope.expired", d_expired);
- stat_increment("scheduler.envelope.removed", d_removed);
- stat_increment("scheduler.envelope.updated", d_updated);
-
- ninflight += d_inflight;
-
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- evtimer_add(&ev, &tv);
-}
diff --git a/smtpd/scheduler_backend.c b/smtpd/scheduler_backend.c
deleted file mode 100644
index ad2b4cab..00000000
--- a/smtpd/scheduler_backend.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/* $OpenBSD: scheduler_backend.c,v 1.16 2018/05/24 11:38:24 gilles Exp $ */
-
-/*
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-extern struct scheduler_backend scheduler_backend_null;
-extern struct scheduler_backend scheduler_backend_proc;
-extern struct scheduler_backend scheduler_backend_ramqueue;
-
-struct scheduler_backend *
-scheduler_backend_lookup(const char *name)
-{
- if (!strcmp(name, "null"))
- return &scheduler_backend_null;
- if (!strcmp(name, "ramqueue"))
- return &scheduler_backend_ramqueue;
-
- return &scheduler_backend_proc;
-}
-
-void
-scheduler_info(struct scheduler_info *sched, struct envelope *evp)
-{
- struct dispatcher *disp;
-
- disp = evp->type == D_BOUNCE ?
- env->sc_dispatcher_bounce :
- dict_xget(env->sc_dispatchers, evp->dispatcher);
-
- switch (disp->type) {
- case DISPATCHER_LOCAL:
- sched->type = D_MDA;
- break;
- case DISPATCHER_REMOTE:
- sched->type = D_MTA;
- break;
- case DISPATCHER_BOUNCE:
- sched->type = D_BOUNCE;
- break;
- }
- sched->ttl = disp->ttl ? disp->ttl : env->sc_ttl;
-
- sched->evpid = evp->id;
- sched->creation = evp->creation;
- sched->retry = evp->retry;
- sched->lasttry = evp->lasttry;
- sched->lastbounce = evp->lastbounce;
- sched->nexttry = 0;
-}
diff --git a/smtpd/scheduler_null.c b/smtpd/scheduler_null.c
deleted file mode 100644
index 40db6205..00000000
--- a/smtpd/scheduler_null.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/* $OpenBSD: scheduler_null.c,v 1.9 2015/01/20 17:37:54 deraadt 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <limits.h>
-
-#include "smtpd.h"
-
-static int scheduler_null_init(const char *);
-static int scheduler_null_insert(struct scheduler_info *);
-static size_t scheduler_null_commit(uint32_t);
-static size_t scheduler_null_rollback(uint32_t);
-static int scheduler_null_update(struct scheduler_info *);
-static int scheduler_null_delete(uint64_t);
-static int scheduler_null_hold(uint64_t, uint64_t);
-static int scheduler_null_release(int, uint64_t, int);
-static int scheduler_null_batch(int, int*, size_t*, uint64_t*, int*);
-static size_t scheduler_null_messages(uint32_t, uint32_t *, size_t);
-static size_t scheduler_null_envelopes(uint64_t, struct evpstate *, size_t);
-static int scheduler_null_schedule(uint64_t);
-static int scheduler_null_remove(uint64_t);
-static int scheduler_null_suspend(uint64_t);
-static int scheduler_null_resume(uint64_t);
-
-struct scheduler_backend scheduler_backend_null = {
- scheduler_null_init,
-
- scheduler_null_insert,
- scheduler_null_commit,
- scheduler_null_rollback,
-
- scheduler_null_update,
- scheduler_null_delete,
- scheduler_null_hold,
- scheduler_null_release,
-
- scheduler_null_batch,
-
- scheduler_null_messages,
- scheduler_null_envelopes,
- scheduler_null_schedule,
- scheduler_null_remove,
- scheduler_null_suspend,
- scheduler_null_resume,
-};
-
-static int
-scheduler_null_init(const char *arg)
-{
- return (1);
-}
-
-static int
-scheduler_null_insert(struct scheduler_info *si)
-{
- return (0);
-}
-
-static size_t
-scheduler_null_commit(uint32_t msgid)
-{
- return (0);
-}
-
-static size_t
-scheduler_null_rollback(uint32_t msgid)
-{
- return (0);
-}
-
-static int
-scheduler_null_update(struct scheduler_info *si)
-{
- return (0);
-}
-
-static int
-scheduler_null_delete(uint64_t evpid)
-{
- return (0);
-}
-
-static int
-scheduler_null_hold(uint64_t evpid, uint64_t holdq)
-{
- return (0);
-}
-
-static int
-scheduler_null_release(int type, uint64_t holdq, int n)
-{
- return (0);
-}
-
-static int
-scheduler_null_batch(int typemask, int *delay, size_t *count, uint64_t *evpids, int *types)
-{
- *delay = 0;
-
- return (0);
-}
-
-static int
-scheduler_null_schedule(uint64_t evpid)
-{
- return (0);
-}
-
-static int
-scheduler_null_remove(uint64_t evpid)
-{
- return (0);
-}
-
-static int
-scheduler_null_suspend(uint64_t evpid)
-{
- return (0);
-}
-
-static int
-scheduler_null_resume(uint64_t evpid)
-{
- return (0);
-}
-
-static size_t
-scheduler_null_messages(uint32_t from, uint32_t *dst, size_t size)
-{
- return (0);
-}
-
-static size_t
-scheduler_null_envelopes(uint64_t from, struct evpstate *dst, size_t size)
-{
- return (0);
-}
diff --git a/smtpd/scheduler_proc.c b/smtpd/scheduler_proc.c
deleted file mode 100644
index 5f4e8b70..00000000
--- a/smtpd/scheduler_proc.c
+++ /dev/null
@@ -1,446 +0,0 @@
-/* $OpenBSD: scheduler_proc.c,v 1.8 2015/12/05 13:14:21 claudio Exp $ */
-
-/*
- * Copyright (c) 2013 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static struct imsgbuf ibuf;
-static struct imsg imsg;
-static size_t rlen;
-static char *rdata;
-
-static void
-scheduler_proc_call(void)
-{
- ssize_t n;
-
- if (imsg_flush(&ibuf) == -1) {
- log_warn("warn: scheduler-proc: imsg_flush");
- fatalx("scheduler-proc: exiting");
- }
-
- while (1) {
- if ((n = imsg_get(&ibuf, &imsg)) == -1) {
- log_warn("warn: scheduler-proc: imsg_get");
- break;
- }
- if (n) {
- rlen = imsg.hdr.len - IMSG_HEADER_SIZE;
- rdata = imsg.data;
-
- if (imsg.hdr.type != PROC_SCHEDULER_OK) {
- log_warnx("warn: scheduler-proc: bad response");
- break;
- }
- return;
- }
-
- if ((n = imsg_read(&ibuf)) == -1 && errno != EAGAIN) {
- log_warn("warn: scheduler-proc: imsg_read");
- break;
- }
-
- if (n == 0) {
- log_warnx("warn: scheduler-proc: pipe closed");
- break;
- }
- }
-
- fatalx("scheduler-proc: exiting");
-}
-
-static void
-scheduler_proc_read(void *dst, size_t len)
-{
- if (len > rlen) {
- log_warnx("warn: scheduler-proc: bad msg len");
- fatalx("scheduler-proc: exiting");
- }
-
- memmove(dst, rdata, len);
- rlen -= len;
- rdata += len;
-}
-
-static void
-scheduler_proc_end(void)
-{
- if (rlen) {
- log_warnx("warn: scheduler-proc: bogus data");
- fatalx("scheduler-proc: exiting");
- }
- imsg_free(&imsg);
-}
-
-/*
- * API
- */
-
-static int
-scheduler_proc_init(const char *conf)
-{
- int fd, r;
- uint32_t version;
-
- fd = fork_proc_backend("scheduler", conf, "scheduler-proc");
- if (fd == -1)
- fatalx("scheduler-proc: exiting");
-
- imsg_init(&ibuf, fd);
-
- version = PROC_SCHEDULER_API_VERSION;
- imsg_compose(&ibuf, PROC_SCHEDULER_INIT, 0, 0, -1,
- &version, sizeof(version));
- scheduler_proc_call();
- scheduler_proc_read(&r, sizeof(r));
- scheduler_proc_end();
-
- return (1);
-}
-
-static int
-scheduler_proc_insert(struct scheduler_info *si)
-{
- int r;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_INSERT");
-
- imsg_compose(&ibuf, PROC_SCHEDULER_INSERT, 0, 0, -1, si, sizeof(*si));
-
- scheduler_proc_call();
- scheduler_proc_read(&r, sizeof(r));
- scheduler_proc_end();
-
- return (r);
-}
-
-static size_t
-scheduler_proc_commit(uint32_t msgid)
-{
- size_t s;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_COMMIT");
-
- imsg_compose(&ibuf, PROC_SCHEDULER_COMMIT, 0, 0, -1,
- &msgid, sizeof(msgid));
-
- scheduler_proc_call();
- scheduler_proc_read(&s, sizeof(s));
- scheduler_proc_end();
-
- return (s);
-}
-
-static size_t
-scheduler_proc_rollback(uint32_t msgid)
-{
- size_t s;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_ROLLBACK");
-
- imsg_compose(&ibuf, PROC_SCHEDULER_ROLLBACK, 0, 0, -1,
- &msgid, sizeof(msgid));
-
- scheduler_proc_call();
- scheduler_proc_read(&s, sizeof(s));
- scheduler_proc_end();
-
- return (s);
-}
-
-static int
-scheduler_proc_update(struct scheduler_info *si)
-{
- int r;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_UPDATE");
-
- imsg_compose(&ibuf, PROC_SCHEDULER_UPDATE, 0, 0, -1, si, sizeof(*si));
-
- scheduler_proc_call();
- scheduler_proc_read(&r, sizeof(r));
- if (r == 1)
- scheduler_proc_read(si, sizeof(*si));
- scheduler_proc_end();
-
- return (r);
-}
-
-static int
-scheduler_proc_delete(uint64_t evpid)
-{
- int r;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_DELETE");
-
- imsg_compose(&ibuf, PROC_SCHEDULER_DELETE, 0, 0, -1,
- &evpid, sizeof(evpid));
-
- scheduler_proc_call();
- scheduler_proc_read(&r, sizeof(r));
- scheduler_proc_end();
-
- return (r);
-}
-
-static int
-scheduler_proc_hold(uint64_t evpid, uint64_t holdq)
-{
- struct ibuf *buf;
- int r;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_HOLD");
-
- buf = imsg_create(&ibuf, PROC_SCHEDULER_HOLD, 0, 0,
- sizeof(evpid) + sizeof(holdq));
- if (buf == NULL)
- return (-1);
- if (imsg_add(buf, &evpid, sizeof(evpid)) == -1)
- return (-1);
- if (imsg_add(buf, &holdq, sizeof(holdq)) == -1)
- return (-1);
- imsg_close(&ibuf, buf);
-
- scheduler_proc_call();
-
- scheduler_proc_read(&r, sizeof(r));
- scheduler_proc_end();
-
- return (r);
-}
-
-static int
-scheduler_proc_release(int type, uint64_t holdq, int n)
-{
- struct ibuf *buf;
- int r;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_RELEASE");
-
- buf = imsg_create(&ibuf, PROC_SCHEDULER_RELEASE, 0, 0,
- sizeof(holdq) + sizeof(n));
- if (buf == NULL)
- return (-1);
- if (imsg_add(buf, &type, sizeof(type)) == -1)
- return (-1);
- if (imsg_add(buf, &holdq, sizeof(holdq)) == -1)
- return (-1);
- if (imsg_add(buf, &n, sizeof(n)) == -1)
- return (-1);
- imsg_close(&ibuf, buf);
-
- scheduler_proc_call();
-
- scheduler_proc_read(&r, sizeof(r));
- scheduler_proc_end();
-
- return (r);
-}
-
-static int
-scheduler_proc_batch(int typemask, int *delay, size_t *count, uint64_t *evpids, int *types)
-{
- struct ibuf *buf;
- int r;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_BATCH");
-
- buf = imsg_create(&ibuf, PROC_SCHEDULER_BATCH, 0, 0,
- sizeof(typemask) + sizeof(*count));
- if (buf == NULL)
- return (-1);
- if (imsg_add(buf, &typemask, sizeof(typemask)) == -1)
- return (-1);
- if (imsg_add(buf, count, sizeof(*count)) == -1)
- return (-1);
- imsg_close(&ibuf, buf);
-
- scheduler_proc_call();
- scheduler_proc_read(&r, sizeof(r));
- scheduler_proc_read(delay, sizeof(*delay));
- scheduler_proc_read(count, sizeof(*count));
- if (r > 0) {
- scheduler_proc_read(evpids, sizeof(*evpids) * (*count));
- scheduler_proc_read(types, sizeof(*types) * (*count));
- }
- scheduler_proc_end();
-
- return (r);
-}
-
-static size_t
-scheduler_proc_messages(uint32_t from, uint32_t *dst, size_t size)
-{
- struct ibuf *buf;
- size_t s;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_MESSAGES");
-
- buf = imsg_create(&ibuf, PROC_SCHEDULER_MESSAGES, 0, 0,
- sizeof(from) + sizeof(size));
- if (buf == NULL)
- return (-1);
- if (imsg_add(buf, &from, sizeof(from)) == -1)
- return (-1);
- if (imsg_add(buf, &size, sizeof(size)) == -1)
- return (-1);
- imsg_close(&ibuf, buf);
-
- scheduler_proc_call();
-
- s = rlen / sizeof(*dst);
- scheduler_proc_read(dst, s * sizeof(*dst));
- scheduler_proc_end();
-
- return (s);
-}
-
-static size_t
-scheduler_proc_envelopes(uint64_t from, struct evpstate *dst, size_t size)
-{
- struct ibuf *buf;
- size_t s;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_ENVELOPES");
-
- buf = imsg_create(&ibuf, PROC_SCHEDULER_ENVELOPES, 0, 0,
- sizeof(from) + sizeof(size));
- if (buf == NULL)
- return (-1);
- if (imsg_add(buf, &from, sizeof(from)) == -1)
- return (-1);
- if (imsg_add(buf, &size, sizeof(size)) == -1)
- return (-1);
- imsg_close(&ibuf, buf);
-
- scheduler_proc_call();
-
- s = rlen / sizeof(*dst);
- scheduler_proc_read(dst, s * sizeof(*dst));
- scheduler_proc_end();
-
- return (s);
-}
-
-static int
-scheduler_proc_schedule(uint64_t evpid)
-{
- int r;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_SCHEDULE");
-
- imsg_compose(&ibuf, PROC_SCHEDULER_SCHEDULE, 0, 0, -1,
- &evpid, sizeof(evpid));
-
- scheduler_proc_call();
-
- scheduler_proc_read(&r, sizeof(r));
- scheduler_proc_end();
-
- return (r);
-}
-
-static int
-scheduler_proc_remove(uint64_t evpid)
-{
- int r;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_REMOVE");
-
- imsg_compose(&ibuf, PROC_SCHEDULER_REMOVE, 0, 0, -1,
- &evpid, sizeof(evpid));
-
- scheduler_proc_call();
-
- scheduler_proc_read(&r, sizeof(r));
- scheduler_proc_end();
-
- return (r);
-}
-
-static int
-scheduler_proc_suspend(uint64_t evpid)
-{
- int r;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_SUSPEND");
-
- imsg_compose(&ibuf, PROC_SCHEDULER_SUSPEND, 0, 0, -1,
- &evpid, sizeof(evpid));
-
- scheduler_proc_call();
-
- scheduler_proc_read(&r, sizeof(r));
- scheduler_proc_end();
-
- return (r);
-}
-
-static int
-scheduler_proc_resume(uint64_t evpid)
-{
- int r;
-
- log_debug("debug: scheduler-proc: PROC_SCHEDULER_RESUME");
-
- imsg_compose(&ibuf, PROC_SCHEDULER_RESUME, 0, 0, -1,
- &evpid, sizeof(evpid));
-
- scheduler_proc_call();
-
- scheduler_proc_read(&r, sizeof(r));
- scheduler_proc_end();
-
- return (r);
-}
-
-struct scheduler_backend scheduler_backend_proc = {
- scheduler_proc_init,
- scheduler_proc_insert,
- scheduler_proc_commit,
- scheduler_proc_rollback,
- scheduler_proc_update,
- scheduler_proc_delete,
- scheduler_proc_hold,
- scheduler_proc_release,
- scheduler_proc_batch,
- scheduler_proc_messages,
- scheduler_proc_envelopes,
- scheduler_proc_schedule,
- scheduler_proc_remove,
- scheduler_proc_suspend,
- scheduler_proc_resume,
-};
diff --git a/smtpd/scheduler_ramqueue.c b/smtpd/scheduler_ramqueue.c
deleted file mode 100644
index 0c04fc0b..00000000
--- a/smtpd/scheduler_ramqueue.c
+++ /dev/null
@@ -1,1204 +0,0 @@
-/* $OpenBSD: scheduler_ramqueue.c,v 1.45 2018/05/31 21:06:12 gilles Exp $ */
-
-/*
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-#include <time.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-TAILQ_HEAD(evplist, rq_envelope);
-
-struct rq_message {
- uint32_t msgid;
- struct tree envelopes;
-};
-
-struct rq_envelope {
- TAILQ_ENTRY(rq_envelope) entry;
- SPLAY_ENTRY(rq_envelope) t_entry;
-
- uint64_t evpid;
- uint64_t holdq;
- enum delivery_type type;
-
-#define RQ_EVPSTATE_PENDING 0
-#define RQ_EVPSTATE_SCHEDULED 1
-#define RQ_EVPSTATE_INFLIGHT 2
-#define RQ_EVPSTATE_HELD 3
- uint8_t state;
-
-#define RQ_ENVELOPE_EXPIRED 0x01
-#define RQ_ENVELOPE_REMOVED 0x02
-#define RQ_ENVELOPE_SUSPEND 0x04
-#define RQ_ENVELOPE_UPDATE 0x08
-#define RQ_ENVELOPE_OVERFLOW 0x10
- uint8_t flags;
-
- time_t ctime;
- time_t sched;
- time_t expire;
-
- struct rq_message *message;
-
- time_t t_inflight;
- time_t t_scheduled;
-};
-
-struct rq_holdq {
- struct evplist q;
- size_t count;
-};
-
-struct rq_queue {
- size_t evpcount;
- struct tree messages;
- SPLAY_HEAD(prioqtree, rq_envelope) q_priotree;
-
- struct evplist q_pending;
- struct evplist q_inflight;
-
- struct evplist q_mta;
- struct evplist q_mda;
- struct evplist q_bounce;
- struct evplist q_update;
- struct evplist q_expired;
- struct evplist q_removed;
-};
-
-static int rq_envelope_cmp(struct rq_envelope *, struct rq_envelope *);
-
-SPLAY_PROTOTYPE(prioqtree, rq_envelope, t_entry, rq_envelope_cmp);
-static int scheduler_ram_init(const char *);
-static int scheduler_ram_insert(struct scheduler_info *);
-static size_t scheduler_ram_commit(uint32_t);
-static size_t scheduler_ram_rollback(uint32_t);
-static int scheduler_ram_update(struct scheduler_info *);
-static int scheduler_ram_delete(uint64_t);
-static int scheduler_ram_hold(uint64_t, uint64_t);
-static int scheduler_ram_release(int, uint64_t, int);
-static int scheduler_ram_batch(int, int *, size_t *, uint64_t *, int *);
-static size_t scheduler_ram_messages(uint32_t, uint32_t *, size_t);
-static size_t scheduler_ram_envelopes(uint64_t, struct evpstate *, size_t);
-static int scheduler_ram_schedule(uint64_t);
-static int scheduler_ram_remove(uint64_t);
-static int scheduler_ram_suspend(uint64_t);
-static int scheduler_ram_resume(uint64_t);
-static int scheduler_ram_query(uint64_t);
-
-static void sorted_insert(struct rq_queue *, struct rq_envelope *);
-
-static void rq_queue_init(struct rq_queue *);
-static void rq_queue_merge(struct rq_queue *, struct rq_queue *);
-static void rq_queue_dump(struct rq_queue *, const char *);
-static void rq_queue_schedule(struct rq_queue *rq);
-static struct evplist *rq_envelope_list(struct rq_queue *, struct rq_envelope *);
-static void rq_envelope_schedule(struct rq_queue *, struct rq_envelope *);
-static int rq_envelope_remove(struct rq_queue *, struct rq_envelope *);
-static int rq_envelope_suspend(struct rq_queue *, struct rq_envelope *);
-static int rq_envelope_resume(struct rq_queue *, struct rq_envelope *);
-static void rq_envelope_delete(struct rq_queue *, struct rq_envelope *);
-static const char *rq_envelope_to_text(struct rq_envelope *);
-
-struct scheduler_backend scheduler_backend_ramqueue = {
- scheduler_ram_init,
-
- scheduler_ram_insert,
- scheduler_ram_commit,
- scheduler_ram_rollback,
-
- scheduler_ram_update,
- scheduler_ram_delete,
- scheduler_ram_hold,
- scheduler_ram_release,
-
- scheduler_ram_batch,
-
- scheduler_ram_messages,
- scheduler_ram_envelopes,
- scheduler_ram_schedule,
- scheduler_ram_remove,
- scheduler_ram_suspend,
- scheduler_ram_resume,
- scheduler_ram_query,
-};
-
-static struct rq_queue ramqueue;
-static struct tree updates;
-static struct tree holdqs[3]; /* delivery type */
-
-static time_t currtime;
-
-#define BACKOFF_TRANSFER 400
-#define BACKOFF_DELIVERY 10
-#define BACKOFF_OVERFLOW 3
-
-static time_t
-scheduler_backoff(time_t t0, time_t base, uint32_t step)
-{
- return (t0 + base * step * step);
-}
-
-static time_t
-scheduler_next(time_t t0, time_t base, uint32_t step)
-{
- time_t t;
-
- /* XXX be more efficient */
- while ((t = scheduler_backoff(t0, base, step)) <= currtime)
- step++;
-
- return (t);
-}
-
-static int
-scheduler_ram_init(const char *arg)
-{
- rq_queue_init(&ramqueue);
- tree_init(&updates);
- tree_init(&holdqs[D_MDA]);
- tree_init(&holdqs[D_MTA]);
- tree_init(&holdqs[D_BOUNCE]);
-
- return (1);
-}
-
-static int
-scheduler_ram_insert(struct scheduler_info *si)
-{
- struct rq_queue *update;
- struct rq_message *message;
- struct rq_envelope *envelope;
- uint32_t msgid;
-
- currtime = time(NULL);
-
- msgid = evpid_to_msgid(si->evpid);
-
- /* find/prepare a ramqueue update */
- if ((update = tree_get(&updates, msgid)) == NULL) {
- update = xcalloc(1, sizeof *update);
- stat_increment("scheduler.ramqueue.update", 1);
- rq_queue_init(update);
- tree_xset(&updates, msgid, update);
- }
-
- /* find/prepare the msgtree message in ramqueue update */
- if ((message = tree_get(&update->messages, msgid)) == NULL) {
- message = xcalloc(1, sizeof *message);
- message->msgid = msgid;
- tree_init(&message->envelopes);
- tree_xset(&update->messages, msgid, message);
- stat_increment("scheduler.ramqueue.message", 1);
- }
-
- /* create envelope in ramqueue message */
- envelope = xcalloc(1, sizeof *envelope);
- envelope->evpid = si->evpid;
- envelope->type = si->type;
- envelope->message = message;
- envelope->ctime = si->creation;
- envelope->expire = si->creation + si->ttl;
- envelope->sched = scheduler_backoff(si->creation,
- (si->type == D_MTA) ? BACKOFF_TRANSFER : BACKOFF_DELIVERY, si->retry);
- tree_xset(&message->envelopes, envelope->evpid, envelope);
-
- update->evpcount++;
- stat_increment("scheduler.ramqueue.envelope", 1);
-
- envelope->state = RQ_EVPSTATE_PENDING;
- TAILQ_INSERT_TAIL(&update->q_pending, envelope, entry);
-
- si->nexttry = envelope->sched;
-
- return (1);
-}
-
-static size_t
-scheduler_ram_commit(uint32_t msgid)
-{
- struct rq_queue *update;
- size_t r;
-
- currtime = time(NULL);
-
- update = tree_xpop(&updates, msgid);
- r = update->evpcount;
-
- if (tracing & TRACE_SCHEDULER)
- rq_queue_dump(update, "update to commit");
-
- rq_queue_merge(&ramqueue, update);
-
- if (tracing & TRACE_SCHEDULER)
- rq_queue_dump(&ramqueue, "resulting queue");
-
- rq_queue_schedule(&ramqueue);
-
- free(update);
- stat_decrement("scheduler.ramqueue.update", 1);
-
- return (r);
-}
-
-static size_t
-scheduler_ram_rollback(uint32_t msgid)
-{
- struct rq_queue *update;
- struct rq_envelope *evp;
- size_t r;
-
- currtime = time(NULL);
-
- if ((update = tree_pop(&updates, msgid)) == NULL)
- return (0);
- r = update->evpcount;
-
- while ((evp = TAILQ_FIRST(&update->q_pending))) {
- TAILQ_REMOVE(&update->q_pending, evp, entry);
- rq_envelope_delete(update, evp);
- }
-
- free(update);
- stat_decrement("scheduler.ramqueue.update", 1);
-
- return (r);
-}
-
-static int
-scheduler_ram_update(struct scheduler_info *si)
-{
- struct rq_message *msg;
- struct rq_envelope *evp;
- uint32_t msgid;
-
- currtime = time(NULL);
-
- msgid = evpid_to_msgid(si->evpid);
- msg = tree_xget(&ramqueue.messages, msgid);
- evp = tree_xget(&msg->envelopes, si->evpid);
-
- /* it *must* be in-flight */
- if (evp->state != RQ_EVPSTATE_INFLIGHT)
- errx(1, "evp:%016" PRIx64 " not in-flight", si->evpid);
-
- TAILQ_REMOVE(&ramqueue.q_inflight, evp, entry);
-
- /*
- * If the envelope was removed while inflight, schedule it for
- * removal immediately.
- */
- if (evp->flags & RQ_ENVELOPE_REMOVED) {
- TAILQ_INSERT_TAIL(&ramqueue.q_removed, evp, entry);
- evp->state = RQ_EVPSTATE_SCHEDULED;
- evp->t_scheduled = currtime;
- return (1);
- }
-
- evp->sched = scheduler_next(evp->ctime,
- (si->type == D_MTA) ? BACKOFF_TRANSFER : BACKOFF_DELIVERY, si->retry);
-
- evp->state = RQ_EVPSTATE_PENDING;
- if (!(evp->flags & RQ_ENVELOPE_SUSPEND))
- sorted_insert(&ramqueue, evp);
-
- si->nexttry = evp->sched;
-
- return (1);
-}
-
-static int
-scheduler_ram_delete(uint64_t evpid)
-{
- struct rq_message *msg;
- struct rq_envelope *evp;
- uint32_t msgid;
-
- currtime = time(NULL);
-
- msgid = evpid_to_msgid(evpid);
- msg = tree_xget(&ramqueue.messages, msgid);
- evp = tree_xget(&msg->envelopes, evpid);
-
- /* it *must* be in-flight */
- if (evp->state != RQ_EVPSTATE_INFLIGHT)
- errx(1, "evp:%016" PRIx64 " not in-flight", evpid);
-
- TAILQ_REMOVE(&ramqueue.q_inflight, evp, entry);
-
- rq_envelope_delete(&ramqueue, evp);
-
- return (1);
-}
-
-#define HOLDQ_MAXSIZE 1000
-
-static int
-scheduler_ram_hold(uint64_t evpid, uint64_t holdq)
-{
- struct rq_holdq *hq;
- struct rq_message *msg;
- struct rq_envelope *evp;
- uint32_t msgid;
-
- currtime = time(NULL);
-
- msgid = evpid_to_msgid(evpid);
- msg = tree_xget(&ramqueue.messages, msgid);
- evp = tree_xget(&msg->envelopes, evpid);
-
- /* it *must* be in-flight */
- if (evp->state != RQ_EVPSTATE_INFLIGHT)
- errx(1, "evp:%016" PRIx64 " not in-flight", evpid);
-
- TAILQ_REMOVE(&ramqueue.q_inflight, evp, entry);
-
- /* If the envelope is suspended, just mark it as pending */
- if (evp->flags & RQ_ENVELOPE_SUSPEND) {
- evp->state = RQ_EVPSTATE_PENDING;
- return (0);
- }
-
- hq = tree_get(&holdqs[evp->type], holdq);
- if (hq == NULL) {
- hq = xcalloc(1, sizeof(*hq));
- TAILQ_INIT(&hq->q);
- tree_xset(&holdqs[evp->type], holdq, hq);
- stat_increment("scheduler.ramqueue.holdq", 1);
- }
-
- /* If the holdq is full, just "tempfail" the envelope */
- if (hq->count >= HOLDQ_MAXSIZE) {
- evp->state = RQ_EVPSTATE_PENDING;
- evp->flags |= RQ_ENVELOPE_UPDATE;
- evp->flags |= RQ_ENVELOPE_OVERFLOW;
- sorted_insert(&ramqueue, evp);
- stat_increment("scheduler.ramqueue.hold-overflow", 1);
- return (0);
- }
-
- evp->state = RQ_EVPSTATE_HELD;
- evp->holdq = holdq;
- /* This is an optimization: upon release, the envelopes will be
- * inserted in the pending queue from the first element to the last.
- * Since elements already in the queue were received first, they
- * were scheduled first, so they will be reinserted before the
- * current element.
- */
- TAILQ_INSERT_HEAD(&hq->q, evp, entry);
- hq->count += 1;
- stat_increment("scheduler.ramqueue.hold", 1);
-
- return (1);
-}
-
-static int
-scheduler_ram_release(int type, uint64_t holdq, int n)
-{
- struct rq_holdq *hq;
- struct rq_envelope *evp;
- int i, update;
-
- currtime = time(NULL);
-
- hq = tree_get(&holdqs[type], holdq);
- if (hq == NULL)
- return (0);
-
- if (n == -1) {
- n = 0;
- update = 1;
- }
- else
- update = 0;
-
- for (i = 0; n == 0 || i < n; i++) {
- evp = TAILQ_FIRST(&hq->q);
- if (evp == NULL)
- break;
-
- TAILQ_REMOVE(&hq->q, evp, entry);
- hq->count -= 1;
- evp->holdq = 0;
-
- /* When released, all envelopes are put in the pending queue
- * and will be rescheduled immediately. As an optimization,
- * we could just schedule them directly.
- */
- evp->state = RQ_EVPSTATE_PENDING;
- if (update)
- evp->flags |= RQ_ENVELOPE_UPDATE;
- sorted_insert(&ramqueue, evp);
- }
-
- if (TAILQ_EMPTY(&hq->q)) {
- tree_xpop(&holdqs[type], holdq);
- free(hq);
- stat_decrement("scheduler.ramqueue.holdq", 1);
- }
- stat_decrement("scheduler.ramqueue.hold", i);
-
- return (i);
-}
-
-static int
-scheduler_ram_batch(int mask, int *delay, size_t *count, uint64_t *evpids, int *types)
-{
- struct rq_envelope *evp;
- size_t i, n;
- time_t t;
-
- currtime = time(NULL);
-
- rq_queue_schedule(&ramqueue);
- if (tracing & TRACE_SCHEDULER)
- rq_queue_dump(&ramqueue, "scheduler_ram_batch()");
-
- i = 0;
- n = 0;
-
- for (;;) {
-
- if (mask & SCHED_REMOVE && (evp = TAILQ_FIRST(&ramqueue.q_removed))) {
- TAILQ_REMOVE(&ramqueue.q_removed, evp, entry);
- types[i] = SCHED_REMOVE;
- evpids[i] = evp->evpid;
- rq_envelope_delete(&ramqueue, evp);
-
- if (++i == *count)
- break;
- }
-
- if (mask & SCHED_EXPIRE && (evp = TAILQ_FIRST(&ramqueue.q_expired))) {
- TAILQ_REMOVE(&ramqueue.q_expired, evp, entry);
- types[i] = SCHED_EXPIRE;
- evpids[i] = evp->evpid;
- rq_envelope_delete(&ramqueue, evp);
-
- if (++i == *count)
- break;
- }
-
- if (mask & SCHED_UPDATE && (evp = TAILQ_FIRST(&ramqueue.q_update))) {
- TAILQ_REMOVE(&ramqueue.q_update, evp, entry);
- types[i] = SCHED_UPDATE;
- evpids[i] = evp->evpid;
-
- if (evp->flags & RQ_ENVELOPE_OVERFLOW)
- t = BACKOFF_OVERFLOW;
- else if (evp->type == D_MTA)
- t = BACKOFF_TRANSFER;
- else
- t = BACKOFF_DELIVERY;
-
- evp->sched = scheduler_next(evp->ctime, t, 0);
- evp->flags &= ~(RQ_ENVELOPE_UPDATE|RQ_ENVELOPE_OVERFLOW);
- evp->state = RQ_EVPSTATE_PENDING;
- if (!(evp->flags & RQ_ENVELOPE_SUSPEND))
- sorted_insert(&ramqueue, evp);
-
- if (++i == *count)
- break;
- }
-
- if (mask & SCHED_BOUNCE && (evp = TAILQ_FIRST(&ramqueue.q_bounce))) {
- TAILQ_REMOVE(&ramqueue.q_bounce, evp, entry);
- types[i] = SCHED_BOUNCE;
- evpids[i] = evp->evpid;
-
- TAILQ_INSERT_TAIL(&ramqueue.q_inflight, evp, entry);
- evp->state = RQ_EVPSTATE_INFLIGHT;
- evp->t_inflight = currtime;
-
- if (++i == *count)
- break;
- }
-
- if (mask & SCHED_MDA && (evp = TAILQ_FIRST(&ramqueue.q_mda))) {
- TAILQ_REMOVE(&ramqueue.q_mda, evp, entry);
- types[i] = SCHED_MDA;
- evpids[i] = evp->evpid;
-
- TAILQ_INSERT_TAIL(&ramqueue.q_inflight, evp, entry);
- evp->state = RQ_EVPSTATE_INFLIGHT;
- evp->t_inflight = currtime;
-
- if (++i == *count)
- break;
- }
-
- if (mask & SCHED_MTA && (evp = TAILQ_FIRST(&ramqueue.q_mta))) {
- TAILQ_REMOVE(&ramqueue.q_mta, evp, entry);
- types[i] = SCHED_MTA;
- evpids[i] = evp->evpid;
-
- TAILQ_INSERT_TAIL(&ramqueue.q_inflight, evp, entry);
- evp->state = RQ_EVPSTATE_INFLIGHT;
- evp->t_inflight = currtime;
-
- if (++i == *count)
- break;
- }
-
- /* nothing seen this round */
- if (i == n)
- break;
-
- n = i;
- }
-
- if (i) {
- *count = i;
- return (1);
- }
-
- if ((evp = TAILQ_FIRST(&ramqueue.q_pending))) {
- if (evp->sched < evp->expire)
- t = evp->sched;
- else
- t = evp->expire;
- *delay = (t < currtime) ? 0 : (t - currtime);
- }
- else
- *delay = -1;
-
- return (0);
-}
-
-static size_t
-scheduler_ram_messages(uint32_t from, uint32_t *dst, size_t size)
-{
- uint64_t id;
- size_t n;
- void *i;
-
- for (n = 0, i = NULL; n < size; n++) {
- if (tree_iterfrom(&ramqueue.messages, &i, from, &id, NULL) == 0)
- break;
- dst[n] = id;
- }
-
- return (n);
-}
-
-static size_t
-scheduler_ram_envelopes(uint64_t from, struct evpstate *dst, size_t size)
-{
- struct rq_message *msg;
- struct rq_envelope *evp;
- void *i;
- size_t n;
-
- if ((msg = tree_get(&ramqueue.messages, evpid_to_msgid(from))) == NULL)
- return (0);
-
- for (n = 0, i = NULL; n < size; ) {
-
- if (tree_iterfrom(&msg->envelopes, &i, from, NULL,
- (void**)&evp) == 0)
- break;
-
- if (evp->flags & (RQ_ENVELOPE_REMOVED | RQ_ENVELOPE_EXPIRED))
- continue;
-
- dst[n].evpid = evp->evpid;
- dst[n].flags = 0;
- dst[n].retry = 0;
- dst[n].time = 0;
-
- if (evp->state == RQ_EVPSTATE_PENDING) {
- dst[n].time = evp->sched;
- dst[n].flags = EF_PENDING;
- }
- else if (evp->state == RQ_EVPSTATE_SCHEDULED) {
- dst[n].time = evp->t_scheduled;
- dst[n].flags = EF_PENDING;
- }
- else if (evp->state == RQ_EVPSTATE_INFLIGHT) {
- dst[n].time = evp->t_inflight;
- dst[n].flags = EF_INFLIGHT;
- }
- else if (evp->state == RQ_EVPSTATE_HELD) {
- /* same as scheduled */
- dst[n].time = evp->t_scheduled;
- dst[n].flags = EF_PENDING;
- dst[n].flags |= EF_HOLD;
- }
- if (evp->flags & RQ_ENVELOPE_SUSPEND)
- dst[n].flags |= EF_SUSPEND;
-
- n++;
- }
-
- return (n);
-}
-
-static int
-scheduler_ram_schedule(uint64_t evpid)
-{
- struct rq_message *msg;
- struct rq_envelope *evp;
- uint32_t msgid;
- void *i;
- int r;
-
- currtime = time(NULL);
-
- if (evpid > 0xffffffff) {
- msgid = evpid_to_msgid(evpid);
- if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL)
- return (0);
- if ((evp = tree_get(&msg->envelopes, evpid)) == NULL)
- return (0);
- if (evp->state == RQ_EVPSTATE_INFLIGHT)
- return (0);
- rq_envelope_schedule(&ramqueue, evp);
- return (1);
- }
- else {
- msgid = evpid;
- if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL)
- return (0);
- i = NULL;
- r = 0;
- while (tree_iter(&msg->envelopes, &i, NULL, (void*)(&evp))) {
- if (evp->state == RQ_EVPSTATE_INFLIGHT)
- continue;
- rq_envelope_schedule(&ramqueue, evp);
- r++;
- }
- return (r);
- }
-}
-
-static int
-scheduler_ram_remove(uint64_t evpid)
-{
- struct rq_message *msg;
- struct rq_envelope *evp;
- uint32_t msgid;
- void *i;
- int r;
-
- currtime = time(NULL);
-
- if (evpid > 0xffffffff) {
- msgid = evpid_to_msgid(evpid);
- if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL)
- return (0);
- if ((evp = tree_get(&msg->envelopes, evpid)) == NULL)
- return (0);
- if (rq_envelope_remove(&ramqueue, evp))
- return (1);
- return (0);
- }
- else {
- msgid = evpid;
- if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL)
- return (0);
- i = NULL;
- r = 0;
- while (tree_iter(&msg->envelopes, &i, NULL, (void*)(&evp)))
- if (rq_envelope_remove(&ramqueue, evp))
- r++;
- return (r);
- }
-}
-
-static int
-scheduler_ram_suspend(uint64_t evpid)
-{
- struct rq_message *msg;
- struct rq_envelope *evp;
- uint32_t msgid;
- void *i;
- int r;
-
- currtime = time(NULL);
-
- if (evpid > 0xffffffff) {
- msgid = evpid_to_msgid(evpid);
- if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL)
- return (0);
- if ((evp = tree_get(&msg->envelopes, evpid)) == NULL)
- return (0);
- if (rq_envelope_suspend(&ramqueue, evp))
- return (1);
- return (0);
- }
- else {
- msgid = evpid;
- if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL)
- return (0);
- i = NULL;
- r = 0;
- while (tree_iter(&msg->envelopes, &i, NULL, (void*)(&evp)))
- if (rq_envelope_suspend(&ramqueue, evp))
- r++;
- return (r);
- }
-}
-
-static int
-scheduler_ram_resume(uint64_t evpid)
-{
- struct rq_message *msg;
- struct rq_envelope *evp;
- uint32_t msgid;
- void *i;
- int r;
-
- currtime = time(NULL);
-
- if (evpid > 0xffffffff) {
- msgid = evpid_to_msgid(evpid);
- if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL)
- return (0);
- if ((evp = tree_get(&msg->envelopes, evpid)) == NULL)
- return (0);
- if (rq_envelope_resume(&ramqueue, evp))
- return (1);
- return (0);
- }
- else {
- msgid = evpid;
- if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL)
- return (0);
- i = NULL;
- r = 0;
- while (tree_iter(&msg->envelopes, &i, NULL, (void*)(&evp)))
- if (rq_envelope_resume(&ramqueue, evp))
- r++;
- return (r);
- }
-}
-
-static int
-scheduler_ram_query(uint64_t evpid)
-{
- uint32_t msgid;
-
- if (evpid > 0xffffffff)
- msgid = evpid_to_msgid(evpid);
- else
- msgid = evpid;
-
- if (tree_get(&ramqueue.messages, msgid) == NULL)
- return (0);
-
- return (1);
-}
-
-static void
-sorted_insert(struct rq_queue *rq, struct rq_envelope *evp)
-{
- struct rq_envelope *evp2;
-
- SPLAY_INSERT(prioqtree, &rq->q_priotree, evp);
- evp2 = SPLAY_NEXT(prioqtree, &rq->q_priotree, evp);
- if (evp2)
- TAILQ_INSERT_BEFORE(evp2, evp, entry);
- else
- TAILQ_INSERT_TAIL(&rq->q_pending, evp, entry);
-}
-
-static void
-rq_queue_init(struct rq_queue *rq)
-{
- memset(rq, 0, sizeof *rq);
- tree_init(&rq->messages);
- TAILQ_INIT(&rq->q_pending);
- TAILQ_INIT(&rq->q_inflight);
- TAILQ_INIT(&rq->q_mta);
- TAILQ_INIT(&rq->q_mda);
- TAILQ_INIT(&rq->q_bounce);
- TAILQ_INIT(&rq->q_update);
- TAILQ_INIT(&rq->q_expired);
- TAILQ_INIT(&rq->q_removed);
- SPLAY_INIT(&rq->q_priotree);
-}
-
-static void
-rq_queue_merge(struct rq_queue *rq, struct rq_queue *update)
-{
- struct rq_message *message, *tomessage;
- struct rq_envelope *envelope;
- uint64_t id;
- void *i;
-
- while (tree_poproot(&update->messages, &id, (void*)&message)) {
- if ((tomessage = tree_get(&rq->messages, id)) == NULL) {
- /* message does not exist. re-use structure */
- tree_xset(&rq->messages, id, message);
- continue;
- }
- /* need to re-link all envelopes before merging them */
- i = NULL;
- while ((tree_iter(&message->envelopes, &i, &id,
- (void*)&envelope)))
- envelope->message = tomessage;
- tree_merge(&tomessage->envelopes, &message->envelopes);
- free(message);
- stat_decrement("scheduler.ramqueue.message", 1);
- }
-
- /* Sorted insert in the pending queue */
- while ((envelope = TAILQ_FIRST(&update->q_pending))) {
- TAILQ_REMOVE(&update->q_pending, envelope, entry);
- sorted_insert(rq, envelope);
- }
-
- rq->evpcount += update->evpcount;
-}
-
-#define SCHEDULEMAX 1024
-
-static void
-rq_queue_schedule(struct rq_queue *rq)
-{
- struct rq_envelope *evp;
- size_t n;
-
- n = 0;
- while ((evp = TAILQ_FIRST(&rq->q_pending))) {
- if (evp->sched > currtime && evp->expire > currtime)
- break;
-
- if (n == SCHEDULEMAX)
- break;
-
- if (evp->state != RQ_EVPSTATE_PENDING)
- errx(1, "evp:%016" PRIx64 " flags=0x%x", evp->evpid,
- evp->flags);
-
- if (evp->expire <= currtime) {
- TAILQ_REMOVE(&rq->q_pending, evp, entry);
- SPLAY_REMOVE(prioqtree, &rq->q_priotree, evp);
- TAILQ_INSERT_TAIL(&rq->q_expired, evp, entry);
- evp->state = RQ_EVPSTATE_SCHEDULED;
- evp->flags |= RQ_ENVELOPE_EXPIRED;
- evp->t_scheduled = currtime;
- continue;
- }
- rq_envelope_schedule(rq, evp);
- n += 1;
- }
-}
-
-static struct evplist *
-rq_envelope_list(struct rq_queue *rq, struct rq_envelope *evp)
-{
- switch (evp->state) {
- case RQ_EVPSTATE_PENDING:
- return &rq->q_pending;
-
- case RQ_EVPSTATE_SCHEDULED:
- if (evp->flags & RQ_ENVELOPE_EXPIRED)
- return &rq->q_expired;
- if (evp->flags & RQ_ENVELOPE_REMOVED)
- return &rq->q_removed;
- if (evp->flags & RQ_ENVELOPE_UPDATE)
- return &rq->q_update;
- if (evp->type == D_MTA)
- return &rq->q_mta;
- if (evp->type == D_MDA)
- return &rq->q_mda;
- if (evp->type == D_BOUNCE)
- return &rq->q_bounce;
- errx(1, "%016" PRIx64 " bad evp type %d", evp->evpid, evp->type);
-
- case RQ_EVPSTATE_INFLIGHT:
- return &rq->q_inflight;
-
- case RQ_EVPSTATE_HELD:
- return (NULL);
- }
-
- errx(1, "%016" PRIx64 " bad state %d", evp->evpid, evp->state);
- return (NULL);
-}
-
-static void
-rq_envelope_schedule(struct rq_queue *rq, struct rq_envelope *evp)
-{
- struct rq_holdq *hq;
- struct evplist *q = NULL;
-
- switch (evp->type) {
- case D_MTA:
- q = &rq->q_mta;
- break;
- case D_MDA:
- q = &rq->q_mda;
- break;
- case D_BOUNCE:
- q = &rq->q_bounce;
- break;
- }
-
- if (evp->flags & RQ_ENVELOPE_UPDATE)
- q = &rq->q_update;
-
- if (evp->state == RQ_EVPSTATE_HELD) {
- hq = tree_xget(&holdqs[evp->type], evp->holdq);
- TAILQ_REMOVE(&hq->q, evp, entry);
- hq->count -= 1;
- if (TAILQ_EMPTY(&hq->q)) {
- tree_xpop(&holdqs[evp->type], evp->holdq);
- free(hq);
- }
- evp->holdq = 0;
- stat_decrement("scheduler.ramqueue.hold", 1);
- }
- else if (!(evp->flags & RQ_ENVELOPE_SUSPEND)) {
- TAILQ_REMOVE(&rq->q_pending, evp, entry);
- SPLAY_REMOVE(prioqtree, &rq->q_priotree, evp);
- }
-
- TAILQ_INSERT_TAIL(q, evp, entry);
- evp->state = RQ_EVPSTATE_SCHEDULED;
- evp->t_scheduled = currtime;
-}
-
-static int
-rq_envelope_remove(struct rq_queue *rq, struct rq_envelope *evp)
-{
- struct rq_holdq *hq;
- struct evplist *evl;
-
- if (evp->flags & (RQ_ENVELOPE_REMOVED | RQ_ENVELOPE_EXPIRED))
- return (0);
- /*
- * If envelope is inflight, mark it envelope for removal.
- */
- if (evp->state == RQ_EVPSTATE_INFLIGHT) {
- evp->flags |= RQ_ENVELOPE_REMOVED;
- return (1);
- }
-
- if (evp->state == RQ_EVPSTATE_HELD) {
- hq = tree_xget(&holdqs[evp->type], evp->holdq);
- TAILQ_REMOVE(&hq->q, evp, entry);
- hq->count -= 1;
- if (TAILQ_EMPTY(&hq->q)) {
- tree_xpop(&holdqs[evp->type], evp->holdq);
- free(hq);
- }
- evp->holdq = 0;
- stat_decrement("scheduler.ramqueue.hold", 1);
- }
- else if (!(evp->flags & RQ_ENVELOPE_SUSPEND)) {
- evl = rq_envelope_list(rq, evp);
- TAILQ_REMOVE(evl, evp, entry);
- if (evl == &rq->q_pending)
- SPLAY_REMOVE(prioqtree, &rq->q_priotree, evp);
- }
-
- TAILQ_INSERT_TAIL(&rq->q_removed, evp, entry);
- evp->state = RQ_EVPSTATE_SCHEDULED;
- evp->flags |= RQ_ENVELOPE_REMOVED;
- evp->t_scheduled = currtime;
-
- return (1);
-}
-
-static int
-rq_envelope_suspend(struct rq_queue *rq, struct rq_envelope *evp)
-{
- struct rq_holdq *hq;
- struct evplist *evl;
-
- if (evp->flags & RQ_ENVELOPE_SUSPEND)
- return (0);
-
- if (evp->state == RQ_EVPSTATE_HELD) {
- hq = tree_xget(&holdqs[evp->type], evp->holdq);
- TAILQ_REMOVE(&hq->q, evp, entry);
- hq->count -= 1;
- if (TAILQ_EMPTY(&hq->q)) {
- tree_xpop(&holdqs[evp->type], evp->holdq);
- free(hq);
- }
- evp->holdq = 0;
- evp->state = RQ_EVPSTATE_PENDING;
- stat_decrement("scheduler.ramqueue.hold", 1);
- }
- else if (evp->state != RQ_EVPSTATE_INFLIGHT) {
- evl = rq_envelope_list(rq, evp);
- TAILQ_REMOVE(evl, evp, entry);
- if (evl == &rq->q_pending)
- SPLAY_REMOVE(prioqtree, &rq->q_priotree, evp);
- }
-
- evp->flags |= RQ_ENVELOPE_SUSPEND;
-
- return (1);
-}
-
-static int
-rq_envelope_resume(struct rq_queue *rq, struct rq_envelope *evp)
-{
- struct evplist *evl;
-
- if (!(evp->flags & RQ_ENVELOPE_SUSPEND))
- return (0);
-
- if (evp->state != RQ_EVPSTATE_INFLIGHT) {
- evl = rq_envelope_list(rq, evp);
- if (evl == &rq->q_pending)
- sorted_insert(rq, evp);
- else
- TAILQ_INSERT_TAIL(evl, evp, entry);
- }
-
- evp->flags &= ~RQ_ENVELOPE_SUSPEND;
-
- return (1);
-}
-
-static void
-rq_envelope_delete(struct rq_queue *rq, struct rq_envelope *evp)
-{
- tree_xpop(&evp->message->envelopes, evp->evpid);
- if (tree_empty(&evp->message->envelopes)) {
- tree_xpop(&rq->messages, evp->message->msgid);
- free(evp->message);
- stat_decrement("scheduler.ramqueue.message", 1);
- }
-
- free(evp);
- rq->evpcount--;
- stat_decrement("scheduler.ramqueue.envelope", 1);
-}
-
-static const char *
-rq_envelope_to_text(struct rq_envelope *e)
-{
- static char buf[256];
- char t[64];
-
- (void)snprintf(buf, sizeof buf, "evp:%016" PRIx64 " [", e->evpid);
-
- if (e->type == D_BOUNCE)
- (void)strlcat(buf, "bounce", sizeof buf);
- else if (e->type == D_MDA)
- (void)strlcat(buf, "mda", sizeof buf);
- else if (e->type == D_MTA)
- (void)strlcat(buf, "mta", sizeof buf);
-
- (void)snprintf(t, sizeof t, ",expire=%s",
- duration_to_text(e->expire - currtime));
- (void)strlcat(buf, t, sizeof buf);
-
-
- switch (e->state) {
- case RQ_EVPSTATE_PENDING:
- (void)snprintf(t, sizeof t, ",pending=%s",
- duration_to_text(e->sched - currtime));
- (void)strlcat(buf, t, sizeof buf);
- break;
-
- case RQ_EVPSTATE_SCHEDULED:
- (void)snprintf(t, sizeof t, ",scheduled=%s",
- duration_to_text(currtime - e->t_scheduled));
- (void)strlcat(buf, t, sizeof buf);
- break;
-
- case RQ_EVPSTATE_INFLIGHT:
- (void)snprintf(t, sizeof t, ",inflight=%s",
- duration_to_text(currtime - e->t_inflight));
- (void)strlcat(buf, t, sizeof buf);
- break;
-
- case RQ_EVPSTATE_HELD:
- (void)snprintf(t, sizeof t, ",held=%s",
- duration_to_text(currtime - e->t_inflight));
- (void)strlcat(buf, t, sizeof buf);
- break;
- default:
- errx(1, "%016" PRIx64 " bad state %d", e->evpid, e->state);
- }
-
- if (e->flags & RQ_ENVELOPE_REMOVED)
- (void)strlcat(buf, ",removed", sizeof buf);
- if (e->flags & RQ_ENVELOPE_EXPIRED)
- (void)strlcat(buf, ",expired", sizeof buf);
- if (e->flags & RQ_ENVELOPE_SUSPEND)
- (void)strlcat(buf, ",suspended", sizeof buf);
-
- (void)strlcat(buf, "]", sizeof buf);
-
- return (buf);
-}
-
-static void
-rq_queue_dump(struct rq_queue *rq, const char * name)
-{
- struct rq_message *message;
- struct rq_envelope *envelope;
- void *i, *j;
- uint64_t id;
-
- log_debug("debug: /--- ramqueue: %s", name);
-
- i = NULL;
- while ((tree_iter(&rq->messages, &i, &id, (void*)&message))) {
- log_debug("debug: | msg:%08" PRIx32, message->msgid);
- j = NULL;
- while ((tree_iter(&message->envelopes, &j, &id,
- (void*)&envelope)))
- log_debug("debug: | %s",
- rq_envelope_to_text(envelope));
- }
- log_debug("debug: \\---");
-}
-
-static int
-rq_envelope_cmp(struct rq_envelope *e1, struct rq_envelope *e2)
-{
- time_t ref1, ref2;
-
- ref1 = (e1->sched < e1->expire) ? e1->sched : e1->expire;
- ref2 = (e2->sched < e2->expire) ? e2->sched : e2->expire;
- if (ref1 != ref2)
- return (ref1 < ref2) ? -1 : 1;
-
- if (e1->evpid != e2->evpid)
- return (e1->evpid < e2->evpid) ? -1 : 1;
-
- return 0;
-}
-
-SPLAY_GENERATE(prioqtree, rq_envelope, t_entry, rq_envelope_cmp);
diff --git a/smtpd/sendmail.8 b/smtpd/sendmail.8
deleted file mode 100644
index 1696a861..00000000
--- a/smtpd/sendmail.8
+++ /dev/null
@@ -1,86 +0,0 @@
-.\" $OpenBSD: sendmail.8,v 1.4 2015/10/23 15:48:16 jung Exp $
-.\"
-.\" Copyright (C) 2013 Ryan Kavanagh <rak@debian.org>
-.\" All rights reserved.
-.\"
-.\" Permission to use, copy, modify, and/or 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.
-.Dd $Mdocdate: October 23 2015 $
-.Dt SENDMAIL 8
-.Os
-.Sh NAME
-.Nm sendmail
-.Nd a mail enqueuer for
-.Xr smtpd 8
-.Sh SYNOPSIS
-.Nm
-.Op Fl tv
-.Op Fl F Ar name
-.Op Fl f Ar from
-.Ar to ...
-.Sh DESCRIPTION
-The
-.Nm
-utility is a local enqueuer for the
-.Xr smtpd 8
-daemon,
-compatible with
-.Xr mailwrapper 8 .
-The message is read on standard input (stdin) until
-.Nm
-encounters an end-of-file.
-The
-.Nm
-enqueuer is not intended to be used directly to send mail,
-but rather via a frontend known as a mail user agent.
-.Pp
-Unless the optional
-.Fl t
-flag is specified,
-one or more recipients must be specified on the command line.
-.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl F Ar name
-Set the sender's full name.
-.It Fl f Ar from
-Set the sender's address.
-.It Fl t
-Read the message's To:, Cc:, and Bcc: fields for recipients.
-The Bcc: field will be deleted before sending.
-.It Fl v
-Enable verbose output.
-.El
-.Pp
-To maintain compatibility with Sendmail, Inc.'s implementation of
-.Nm ,
-various other flags are accepted,
-but have no effect.
-.Sh EXIT STATUS
-.Ex -std
-.Sh SEE ALSO
-.Xr smtpctl 8 ,
-.Xr smtpd 8
-.Sh AUTHORS
-.Sy OpenSMTPD
-is primarily developed by Gilles Chehade,
-Eric Faurot,
-and Charles Longeau,
-with contributions from various
-.Ox
-hackers.
-It is distributed under the ISC license.
-.Pp
-This manpage was written by
-.An Ryan Kavanagh
-.Aq Mt rak@debian.org
-for the Debian project and is distributed under the ISC license.
diff --git a/smtpd/smtp.1 b/smtpd/smtp.1
deleted file mode 100644
index 3cc03844..00000000
--- a/smtpd/smtp.1
+++ /dev/null
@@ -1,96 +0,0 @@
-.\" $OpenBSD: smtp.1,v 1.7 2018/07/04 08:23:43 jmc Exp $
-.\"
-.\" Copyright (c) 2018, 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.
-.\"
-.Dd $Mdocdate: July 4 2018 $
-.Dt SMTP 1
-.Os
-.Sh NAME
-.Nm smtp
-.Nd Simple Mail Transfer Protocol client
-.Sh SYNOPSIS
-.Nm
-.Op Fl Chnv
-.Op Fl F Ar from
-.Op Fl H Ar helo
-.Op Fl s Ar server
-.Op Ar recipient ...
-.Sh DESCRIPTION
-The
-.Nm
-utility is a Simple Mail Transfer Protocol
-.Pq SMTP
-client which can be used to run an SMTP transaction against an SMTP server.
-.Pp
-By default,
-.Nm
-reads the mail content from the standard input, establishes an SMTP session,
-and runs an SMTP transaction for all the specified recipients.
-The content is sent unaltered as mail data.
-.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl C
-Do not require server certificate to be valid.
-.It Fl F Ar from
-Set the return-path (MAIL FROM) for the SMTP transaction.
-Default to the current username.
-.It Fl H Ar helo
-Define the hostname to advertise (HELO) when establishing the SMTP session.
-.It Fl h
-Display version and usage.
-.It Fl n
-Do not actually execute a transaction,
-just try to establish an SMTP session and quit.
-When this option is given, no message is read from the standard input.
-.It Fl s Ar server
-Specify the server to connect to and connection parameters.
-The format is
-.Sm off
-.Op Ar proto No :// Op Ar user : pass No @
-.Ar host Op : Ar port .
-.Sm on
-The following protocols are available:
-.Pp
-.Bl -tag -width "smtp+notls" -compact
-.It smtp
-Normal SMTP session with opportunistic STARTTLS.
-.It smtp+tls
-Normal SMTP session with mandatory STARTTLS.
-.It smtp+notls
-Plain text SMTP session without TLS.
-.It lmtp
-LMTP session with opportunistic STARTTLS.
-.It lmtp+tls
-LMTP session with mandatory STARTTLS.
-.It lmtp+notls
-Plain text LMTP session without TLS.
-.It smtps
-SMTP session with forced TLS on connection.
-.El
-.Pp
-Defaults to
-.Dq smtp://localhost:25 .
-.It Fl v
-Be more verbose.
-This option can be specified multiple times.
-.El
-.Sh SEE ALSO
-.Xr smtpd 8
-.Sh HISTORY
-The
-.Nm
-program first appeared in
-.Ox 6.4 .
diff --git a/smtpd/smtp.c b/smtpd/smtp.c
deleted file mode 100644
index 602fd0d6..00000000
--- a/smtpd/smtp.c
+++ /dev/null
@@ -1,387 +0,0 @@
-/* $OpenBSD: smtp.c,v 1.166 2019/08/10 16:07:01 gilles Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <grp.h> /* needed for setgroups */
-#include <imsg.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <signal.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <openssl/ssl.h>
-
-#include "smtpd.h"
-#include "log.h"
-#include "ssl.h"
-
-static void smtp_setup_events(void);
-static void smtp_pause(void);
-static void smtp_resume(void);
-static void smtp_accept(int, short, void *);
-static void smtp_dropped(struct listener *, int, const struct sockaddr_storage *);
-static int smtp_enqueue(void);
-static int smtp_can_accept(void);
-static void smtp_setup_listeners(void);
-static int smtp_sni_callback(SSL *, int *, void *);
-
-int
-proxy_session(struct listener *listener, int sock,
- const struct sockaddr_storage *ss,
- void (*accepted)(struct listener *, int,
- const struct sockaddr_storage *, struct io *),
- void (*dropped)(struct listener *, int,
- const struct sockaddr_storage *));
-
-static void smtp_accepted(struct listener *, int, const struct sockaddr_storage *, struct io *);
-
-
-#define SMTP_FD_RESERVE 5
-#define getdtablecount() 0
-
-static size_t sessions;
-static size_t maxsessions;
-
-void
-smtp_imsg(struct mproc *p, struct imsg *imsg)
-{
- switch (imsg->hdr.type) {
- case IMSG_SMTP_CHECK_SENDER:
- case IMSG_SMTP_EXPAND_RCPT:
- case IMSG_SMTP_LOOKUP_HELO:
- case IMSG_SMTP_AUTHENTICATE:
- case IMSG_FILTER_SMTP_PROTOCOL:
- case IMSG_FILTER_SMTP_DATA_BEGIN:
- smtp_session_imsg(p, imsg);
- return;
-
- case IMSG_SMTP_MESSAGE_COMMIT:
- case IMSG_SMTP_MESSAGE_CREATE:
- case IMSG_SMTP_MESSAGE_OPEN:
- case IMSG_QUEUE_ENVELOPE_SUBMIT:
- case IMSG_QUEUE_ENVELOPE_COMMIT:
- smtp_session_imsg(p, imsg);
- return;
-
- case IMSG_QUEUE_SMTP_SESSION:
- m_compose(p, IMSG_QUEUE_SMTP_SESSION, 0, 0, smtp_enqueue(),
- imsg->data, imsg->hdr.len - sizeof imsg->hdr);
- return;
-
- case IMSG_CTL_SMTP_SESSION:
- m_compose(p, IMSG_CTL_SMTP_SESSION, imsg->hdr.peerid, 0,
- smtp_enqueue(), NULL, 0);
- return;
-
- case IMSG_CTL_PAUSE_SMTP:
- log_debug("debug: smtp: pausing listening sockets");
- smtp_pause();
- env->sc_flags |= SMTPD_SMTP_PAUSED;
- return;
-
- case IMSG_CTL_RESUME_SMTP:
- log_debug("debug: smtp: resuming listening sockets");
- env->sc_flags &= ~SMTPD_SMTP_PAUSED;
- smtp_resume();
- return;
- }
-
- errx(1, "smtp_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
-}
-
-void
-smtp_postfork(void)
-{
- smtp_setup_listeners();
-}
-
-void
-smtp_postprivdrop(void)
-{
-}
-
-void
-smtp_configure(void)
-{
- smtp_setup_events();
-}
-
-static void
-smtp_setup_listeners(void)
-{
- struct listener *l;
- int opt;
-
- TAILQ_FOREACH(l, env->sc_listeners, entry) {
- if ((l->fd = socket(l->ss.ss_family, SOCK_STREAM, 0)) == -1) {
- if (errno == EAFNOSUPPORT) {
- log_warn("smtpd: socket");
- continue;
- }
- fatal("smtpd: socket");
- }
- opt = 1;
-#ifdef SO_REUSEADDR
- if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, &opt,
- sizeof(opt)) == -1)
- fatal("smtpd: setsockopt");
-#else
- if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEPORT, &opt,
- sizeof(opt)) < 0)
- fatal("smtpd: setsockopt");
-#endif
-#ifdef IPV6_V6ONLY
- /*
- * If using IPv6, bind only to IPv6 if possible.
- * This avoids ambiguities with IPv4-mapped IPv6 addresses.
- */
- if (l->ss.ss_family == AF_INET6)
- if (setsockopt(l->fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt,
- sizeof(opt)) < 0)
- fatal("smtpd: setsockopt");
-#endif
- if (bind(l->fd, (struct sockaddr *)&l->ss, SS_LEN(&l->ss)) == -1)
- fatal("smtpd: bind");
- }
-}
-
-static void
-smtp_setup_events(void)
-{
- struct listener *l;
- struct pki *pki;
- SSL_CTX *ssl_ctx;
- void *iter;
- const char *k;
-
- TAILQ_FOREACH(l, env->sc_listeners, entry) {
- log_debug("debug: smtp: listen on %s port %d flags 0x%01x"
- " pki \"%s\""
- " ca \"%s\"", ss_to_text(&l->ss), ntohs(l->port),
- l->flags, l->pki_name, l->ca_name);
-
- io_set_nonblocking(l->fd);
- if (listen(l->fd, SMTPD_BACKLOG) == -1)
- fatal("listen");
- event_set(&l->ev, l->fd, EV_READ|EV_PERSIST, smtp_accept, l);
-
- if (!(env->sc_flags & SMTPD_SMTP_PAUSED))
- event_add(&l->ev, NULL);
- }
-
- iter = NULL;
- while (dict_iter(env->sc_pki_dict, &iter, &k, (void **)&pki)) {
- if (!ssl_setup((SSL_CTX **)&ssl_ctx, pki, smtp_sni_callback,
- env->sc_tls_ciphers))
- fatal("smtp_setup_events: ssl_setup failure");
- dict_xset(env->sc_ssl_dict, k, ssl_ctx);
- }
-
- purge_config(PURGE_PKI_KEYS);
-
- maxsessions = (getdtablesize() - getdtablecount()) / 2 - SMTP_FD_RESERVE;
- log_debug("debug: smtp: will accept at most %zu clients", maxsessions);
-}
-
-static void
-smtp_pause(void)
-{
- struct listener *l;
-
- if (env->sc_flags & (SMTPD_SMTP_DISABLED|SMTPD_SMTP_PAUSED))
- return;
-
- TAILQ_FOREACH(l, env->sc_listeners, entry)
- event_del(&l->ev);
-}
-
-static void
-smtp_resume(void)
-{
- struct listener *l;
-
- if (env->sc_flags & (SMTPD_SMTP_DISABLED|SMTPD_SMTP_PAUSED))
- return;
-
- TAILQ_FOREACH(l, env->sc_listeners, entry)
- event_add(&l->ev, NULL);
-}
-
-static int
-smtp_enqueue(void)
-{
- struct listener *listener = env->sc_sock_listener;
- int fd[2];
-
- /*
- * Some enqueue requests buffered in IMSG may still arrive even after
- * call to smtp_pause() because enqueue listener is not a real socket
- * and thus cannot be paused properly.
- */
- if (env->sc_flags & SMTPD_SMTP_PAUSED)
- return (-1);
-
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd))
- return (-1);
-
- if ((smtp_session(listener, fd[0], &listener->ss, env->sc_hostname, NULL)) == -1) {
- close(fd[0]);
- close(fd[1]);
- return (-1);
- }
-
- sessions++;
- stat_increment("smtp.session", 1);
- stat_increment("smtp.session.local", 1);
-
- return (fd[1]);
-}
-
-static void
-smtp_accept(int fd, short event, void *p)
-{
- struct listener *listener = p;
- struct sockaddr_storage ss;
- socklen_t len;
- int sock;
-
- if (env->sc_flags & SMTPD_SMTP_PAUSED)
- fatalx("smtp_session: unexpected client");
-
- if (!smtp_can_accept()) {
- log_warnx("warn: Disabling incoming SMTP connections: "
- "Client limit reached");
- goto pause;
- }
-
- len = sizeof(ss);
- if ((sock = accept(fd, (struct sockaddr *)&ss, &len)) == -1) {
- if (errno == ENFILE || errno == EMFILE) {
- log_warn("warn: Disabling incoming SMTP connections");
- goto pause;
- }
- if (errno == EINTR || errno == EWOULDBLOCK ||
- errno == ECONNABORTED)
- return;
- fatal("smtp_accept");
- }
-
- if (listener->flags & F_PROXY) {
- io_set_nonblocking(sock);
- if (proxy_session(listener, sock, &ss,
- smtp_accepted, smtp_dropped) == -1) {
- close(sock);
- return;
- }
- return;
- }
-
- smtp_accepted(listener, sock, &ss, NULL);
- return;
-
-pause:
- smtp_pause();
- env->sc_flags |= SMTPD_SMTP_DISABLED;
- return;
-}
-
-static int
-smtp_can_accept(void)
-{
- if (sessions + 1 == maxsessions)
- return 0;
- return (getdtablesize() - getdtablecount() - SMTP_FD_RESERVE >= 2);
-}
-
-void
-smtp_collect(void)
-{
- sessions--;
- stat_decrement("smtp.session", 1);
-
- if (!smtp_can_accept())
- return;
-
- if (env->sc_flags & SMTPD_SMTP_DISABLED) {
- log_warnx("warn: smtp: "
- "fd exhaustion over, re-enabling incoming connections");
- env->sc_flags &= ~SMTPD_SMTP_DISABLED;
- smtp_resume();
- }
-}
-
-static int
-smtp_sni_callback(SSL *ssl, int *ad, void *arg)
-{
- const char *sn;
- void *ssl_ctx;
-
- sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
- if (sn == NULL)
- return SSL_TLSEXT_ERR_NOACK;
- ssl_ctx = dict_get(env->sc_ssl_dict, sn);
- if (ssl_ctx == NULL)
- return SSL_TLSEXT_ERR_NOACK;
- SSL_set_SSL_CTX(ssl, ssl_ctx);
- return SSL_TLSEXT_ERR_OK;
-}
-
-static void
-smtp_accepted(struct listener *listener, int sock, const struct sockaddr_storage *ss, struct io *io)
-{
- int ret;
-
- ret = smtp_session(listener, sock, ss, NULL, io);
- if (ret == -1) {
- log_warn("warn: Failed to create SMTP session");
- close(sock);
- return;
- }
- io_set_nonblocking(sock);
-
- sessions++;
- stat_increment("smtp.session", 1);
- if (listener->ss.ss_family == AF_LOCAL)
- stat_increment("smtp.session.local", 1);
- if (listener->ss.ss_family == AF_INET)
- stat_increment("smtp.session.inet4", 1);
- if (listener->ss.ss_family == AF_INET6)
- stat_increment("smtp.session.inet6", 1);
-}
-
-static void
-smtp_dropped(struct listener *listener, int sock, const struct sockaddr_storage *ss)
-{
- close(sock);
- sessions--;
-}
diff --git a/smtpd/smtp.h b/smtpd/smtp.h
deleted file mode 100644
index dc91d878..00000000
--- a/smtpd/smtp.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/* $OpenBSD: smtp.h,v 1.3 2019/09/02 20:05:21 eric Exp $ */
-
-/*
- * Copyright (c) 2018 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.
- */
-
-#define TLS_NO 0
-#define TLS_YES 1
-#define TLS_FORCE 2
-#define TLS_SMTPS 3
-
-#define CERT_OK 0
-#define CERT_UNKNOWN 1
-#define CERT_INVALID 2
-
-#define FAIL_INTERNAL 1 /* malloc, etc. (check errno) */
-#define FAIL_CONN 2 /* disconnect, timeout... (check errno) */
-#define FAIL_PROTO 3 /* protocol violation */
-#define FAIL_IMPL 4 /* server lacks a required feature */
-#define FAIL_RESP 5 /* rejected command */
-
-struct smtp_params {
-
- /* Client options */
- size_t linemax; /* max input line size */
- size_t ibufmax; /* max input buffer size */
- size_t obufmax; /* max output buffer size */
-
- /* Connection settings */
- const struct sockaddr *dst; /* address to connect to */
- const struct sockaddr *src; /* address to bind to */
- int timeout; /* timeout in seconds */
-
- /* TLS options */
- int tls_req; /* requested TLS mode */
- int tls_verify; /* need valid server certificate */
-
- /* SMTP options */
- int lmtp; /* use LMTP protocol */
- const char *helo; /* string to use with HELO */
- const char *auth_user; /* for AUTH */
- const char *auth_pass; /* for AUTH */
-};
-
-struct smtp_rcpt {
- const char *to;
- const char *dsn_notify;
- const char *dsn_orcpt;
- int done;
-};
-
-struct smtp_mail {
- const char *from;
- const char *dsn_ret;
- const char *dsn_envid;
- struct smtp_rcpt *rcpt;
- int rcptcount;
- FILE *fp;
-};
-
-struct smtp_status {
- struct smtp_rcpt *rcpt;
- const char *cmd;
- const char *status;
-};
-
-struct smtp_client;
-
-/* smtp_client.c */
-struct smtp_client *smtp_connect(const struct smtp_params *, void *);
-void smtp_cert_verified(struct smtp_client *, int);
-void smtp_set_tls(struct smtp_client *, void *);
-void smtp_quit(struct smtp_client *);
-void smtp_sendmail(struct smtp_client *, struct smtp_mail *);
-
-/* callbacks */
-void smtp_verify_server_cert(void *, struct smtp_client *, void *);
-void smtp_require_tls(void *, struct smtp_client *);
-void smtp_ready(void *, struct smtp_client *);
-void smtp_failed(void *, struct smtp_client *, int, const char *);
-void smtp_closed(void *, struct smtp_client *);
-void smtp_status(void *, struct smtp_client *, struct smtp_status *);
-void smtp_done(void *, struct smtp_client *, struct smtp_mail *);
diff --git a/smtpd/smtp/Makefile b/smtpd/smtp/Makefile
deleted file mode 100644
index 380e3ad6..00000000
--- a/smtpd/smtp/Makefile
+++ /dev/null
@@ -1,24 +0,0 @@
-# $OpenBSD: Makefile,v 1.1 2018/04/26 13:57:13 eric Exp $
-
-.PATH: ${.CURDIR}/..
-
-PROG= smtp
-
-BINDIR= /usr/bin
-MAN= smtp.1
-
-SRCS+= iobuf.c
-SRCS+= ioev.c
-SRCS+= log.c
-SRCS+= smtp_client.c
-SRCS+= smtpc.c
-SRCS+= ssl.c
-SRCS+= ssl_smtpd.c
-SRCS+= ssl_verify.c
-
-CPPFLAGS+= -DIO_TLS
-
-LDADD+= -levent -lutil -lssl -lcrypto -lm -lz
-DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} ${LIBM} ${LIBZ}
-
-.include <bsd.prog.mk>
diff --git a/smtpd/smtp_client.c b/smtpd/smtp_client.c
deleted file mode 100644
index 8e146e1b..00000000
--- a/smtpd/smtp_client.c
+++ /dev/null
@@ -1,923 +0,0 @@
-/* $OpenBSD: smtp_client.c,v 1.14 2020/04/24 11:34:07 eric Exp $ */
-
-/*
- * Copyright (c) 2018 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 <netinet/in.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <limits.h>
-#include <resolv.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "log.h"
-#include "ioev.h"
-#include "smtp.h"
-
-#define TRACE_SMTPCLT 2
-#define TRACE_IO 3
-
-enum {
- STATE_INIT = 0,
- STATE_BANNER,
- STATE_EHLO,
- STATE_HELO,
- STATE_LHLO,
- STATE_STARTTLS,
- STATE_AUTH,
- STATE_AUTH_PLAIN,
- STATE_AUTH_LOGIN,
- STATE_AUTH_LOGIN_USER,
- STATE_AUTH_LOGIN_PASS,
- STATE_READY,
- STATE_MAIL,
- STATE_RCPT,
- STATE_DATA,
- STATE_BODY,
- STATE_EOM,
- STATE_RSET,
- STATE_QUIT,
-
- STATE_LAST
-};
-
-#define base64_encode __b64_ntop
-#define base64_decode __b64_pton
-
-#define FLAG_TLS 0x01
-#define FLAG_TLS_VERIFIED 0x02
-
-#define SMTP_EXT_STARTTLS 0x01
-#define SMTP_EXT_PIPELINING 0x02
-#define SMTP_EXT_AUTH 0x04
-#define SMTP_EXT_AUTH_PLAIN 0x08
-#define SMTP_EXT_AUTH_LOGIN 0x10
-#define SMTP_EXT_DSN 0x20
-#define SMTP_EXT_SIZE 0x40
-
-struct smtp_client {
- void *tag;
- struct smtp_params params;
-
- int state;
- int flags;
- int ext;
- size_t ext_size;
-
- struct io *io;
- char *reply;
- size_t replysz;
-
- struct smtp_mail *mail;
- int rcptidx;
- int rcptok;
-};
-
-void log_trace_verbose(int);
-void log_trace(int, const char *, ...)
- __attribute__((format (printf, 2, 3)));
-
-static void smtp_client_io(struct io *, int, void *);
-static void smtp_client_free(struct smtp_client *);
-static void smtp_client_state(struct smtp_client *, int);
-static void smtp_client_abort(struct smtp_client *, int, const char *);
-static void smtp_client_cancel(struct smtp_client *, int, const char *);
-static void smtp_client_sendcmd(struct smtp_client *, char *, ...);
-static void smtp_client_sendbody(struct smtp_client *);
-static int smtp_client_readline(struct smtp_client *);
-static int smtp_client_replycat(struct smtp_client *, const char *);
-static void smtp_client_response(struct smtp_client *, const char *);
-static void smtp_client_mail_abort(struct smtp_client *);
-static void smtp_client_mail_status(struct smtp_client *, const char *);
-static void smtp_client_rcpt_status(struct smtp_client *, struct smtp_rcpt *, const char *);
-
-static const char *strstate[STATE_LAST] = {
- "INIT",
- "BANNER",
- "EHLO",
- "HELO",
- "LHLO",
- "STARTTLS",
- "AUTH",
- "AUTH_PLAIN",
- "AUTH_LOGIN",
- "AUTH_LOGIN_USER",
- "AUTH_LOGIN_PASS",
- "READY",
- "MAIL",
- "RCPT",
- "DATA",
- "BODY",
- "EOM",
- "RSET",
- "QUIT",
-};
-
-struct smtp_client *
-smtp_connect(const struct smtp_params *params, void *tag)
-{
- struct smtp_client *proto;
-
- proto = calloc(1, sizeof *proto);
- if (proto == NULL)
- return NULL;
-
- memmove(&proto->params, params, sizeof(*params));
- proto->tag = tag;
- proto->io = io_new();
- if (proto->io == NULL) {
- free(proto);
- return NULL;
- }
- io_set_callback(proto->io, smtp_client_io, proto);
- io_set_timeout(proto->io, proto->params.timeout);
-
- if (io_connect(proto->io, proto->params.dst, proto->params.src) == -1) {
- smtp_client_abort(proto, FAIL_CONN, io_error(proto->io));
- return NULL;
- }
-
- return proto;
-}
-
-void
-smtp_cert_verified(struct smtp_client *proto, int verified)
-{
- if (verified == CERT_OK)
- proto->flags |= FLAG_TLS_VERIFIED;
-
- else if (proto->params.tls_verify) {
- errno = EAUTH;
- smtp_client_abort(proto, FAIL_CONN,
- "Invalid server certificate");
- return;
- }
-
- io_resume(proto->io, IO_IN);
-
- if (proto->state == STATE_INIT)
- smtp_client_state(proto, STATE_BANNER);
- else {
- /* Clear extensions before re-issueing an EHLO command. */
- proto->ext = 0;
- smtp_client_state(proto, STATE_EHLO);
- }
-}
-
-void
-smtp_set_tls(struct smtp_client *proto, void *ctx)
-{
- io_start_tls(proto->io, ctx);
-}
-
-void
-smtp_quit(struct smtp_client *proto)
-{
- if (proto->state != STATE_READY)
- fatalx("connection is not ready");
-
- smtp_client_state(proto, STATE_QUIT);
-}
-
-void
-smtp_sendmail(struct smtp_client *proto, struct smtp_mail *mail)
-{
- if (proto->state != STATE_READY)
- fatalx("connection is not ready");
-
- proto->mail = mail;
- smtp_client_state(proto, STATE_MAIL);
-}
-
-static void
-smtp_client_free(struct smtp_client *proto)
-{
- if (proto->mail)
- fatalx("current task should have been deleted already");
-
- smtp_closed(proto->tag, proto);
-
- if (proto->io)
- io_free(proto->io);
-
- free(proto->reply);
- free(proto);
-}
-
-/*
- * End the session immediatly.
- */
-static void
-smtp_client_abort(struct smtp_client *proto, int err, const char *reason)
-{
- smtp_failed(proto->tag, proto, err, reason);
-
- if (proto->mail)
- smtp_client_mail_abort(proto);
-
- smtp_client_free(proto);
-}
-
-/*
- * Properly close the session.
- */
-static void
-smtp_client_cancel(struct smtp_client *proto, int err, const char *reason)
-{
- if (proto->mail)
- fatal("not supposed to have a mail");
-
- smtp_failed(proto->tag, proto, err, reason);
-
- smtp_client_state(proto, STATE_QUIT);
-}
-
-static void
-smtp_client_state(struct smtp_client *proto, int newstate)
-{
- struct smtp_rcpt *rcpt;
- char ibuf[LINE_MAX], obuf[LINE_MAX];
- size_t n;
- int oldstate;
-
- if (proto->reply)
- proto->reply[0] = '\0';
-
- again:
- oldstate = proto->state;
- proto->state = newstate;
-
- log_trace(TRACE_SMTPCLT, "%p: %s -> %s", proto,
- strstate[oldstate],
- strstate[newstate]);
-
- /* don't try this at home! */
-#define smtp_client_state(_s, _st) do { newstate = _st; goto again; } while (0)
-
- switch (proto->state) {
- case STATE_BANNER:
- io_set_read(proto->io);
- break;
-
- case STATE_EHLO:
- smtp_client_sendcmd(proto, "EHLO %s", proto->params.helo);
- break;
-
- case STATE_HELO:
- smtp_client_sendcmd(proto, "HELO %s", proto->params.helo);
- break;
-
- case STATE_LHLO:
- smtp_client_sendcmd(proto, "LHLO %s", proto->params.helo);
- break;
-
- case STATE_STARTTLS:
- if (proto->params.tls_req == TLS_NO || proto->flags & FLAG_TLS)
- smtp_client_state(proto, STATE_AUTH);
- else if (proto->ext & SMTP_EXT_STARTTLS)
- smtp_client_sendcmd(proto, "STARTTLS");
- else if (proto->params.tls_req == TLS_FORCE)
- smtp_client_cancel(proto, FAIL_IMPL,
- "TLS not supported by remote host");
- else
- smtp_client_state(proto, STATE_AUTH);
- break;
-
- case STATE_AUTH:
- if (!proto->params.auth_user)
- smtp_client_state(proto, STATE_READY);
- else if ((proto->flags & FLAG_TLS) == 0)
- smtp_client_cancel(proto, FAIL_IMPL,
- "Authentication requires TLS");
- else if ((proto->ext & SMTP_EXT_AUTH) == 0)
- smtp_client_cancel(proto, FAIL_IMPL,
- "AUTH not supported by remote host");
- else if (proto->ext & SMTP_EXT_AUTH_PLAIN)
- smtp_client_state(proto, STATE_AUTH_PLAIN);
- else if (proto->ext & SMTP_EXT_AUTH_LOGIN)
- smtp_client_state(proto, STATE_AUTH_LOGIN);
- else
- smtp_client_cancel(proto, FAIL_IMPL,
- "No supported AUTH method");
- break;
-
- case STATE_AUTH_PLAIN:
- (void)strlcpy(ibuf, "-", sizeof(ibuf));
- (void)strlcat(ibuf, proto->params.auth_user, sizeof(ibuf));
- if (strlcat(ibuf, ":", sizeof(ibuf)) >= sizeof(ibuf)) {
- errno = EMSGSIZE;
- smtp_client_cancel(proto, FAIL_INTERNAL,
- "credentials too large");
- break;
- }
- n = strlcat(ibuf, proto->params.auth_pass, sizeof(ibuf));
- if (n >= sizeof(ibuf)) {
- errno = EMSGSIZE;
- smtp_client_cancel(proto, FAIL_INTERNAL,
- "credentials too large");
- break;
- }
- *strchr(ibuf, ':') = '\0';
- ibuf[0] = '\0';
- if (base64_encode(ibuf, n, obuf, sizeof(obuf)) == -1) {
- errno = EMSGSIZE;
- smtp_client_cancel(proto, FAIL_INTERNAL,
- "credentials too large");
- break;
- }
- smtp_client_sendcmd(proto, "AUTH PLAIN %s", obuf);
- explicit_bzero(ibuf, sizeof ibuf);
- explicit_bzero(obuf, sizeof obuf);
- break;
-
- case STATE_AUTH_LOGIN:
- smtp_client_sendcmd(proto, "AUTH LOGIN");
- break;
-
- case STATE_AUTH_LOGIN_USER:
- if (base64_encode(proto->params.auth_user,
- strlen(proto->params.auth_user), obuf,
- sizeof(obuf)) == -1) {
- errno = EMSGSIZE;
- smtp_client_cancel(proto, FAIL_INTERNAL,
- "credentials too large");
- break;
- }
- smtp_client_sendcmd(proto, "%s", obuf);
- explicit_bzero(obuf, sizeof obuf);
- break;
-
- case STATE_AUTH_LOGIN_PASS:
- if (base64_encode(proto->params.auth_pass,
- strlen(proto->params.auth_pass), obuf,
- sizeof(obuf)) == -1) {
- errno = EMSGSIZE;
- smtp_client_cancel(proto, FAIL_INTERNAL,
- "credentials too large");
- break;
- }
- smtp_client_sendcmd(proto, "%s", obuf);
- explicit_bzero(obuf, sizeof obuf);
- break;
-
- case STATE_READY:
- smtp_ready(proto->tag, proto);
- break;
-
- case STATE_MAIL:
- if (proto->ext & SMTP_EXT_DSN)
- smtp_client_sendcmd(proto, "MAIL FROM:<%s>%s%s%s%s",
- proto->mail->from,
- proto->mail->dsn_ret ? " RET=" : "",
- proto->mail->dsn_ret ? proto->mail->dsn_ret : "",
- proto->mail->dsn_envid ? " ENVID=" : "",
- proto->mail->dsn_envid ? proto->mail->dsn_envid : "");
- else
- smtp_client_sendcmd(proto, "MAIL FROM:<%s>",
- proto->mail->from);
- break;
-
- case STATE_RCPT:
- if (proto->rcptidx == proto->mail->rcptcount) {
- smtp_client_state(proto, STATE_DATA);
- break;
- }
- rcpt = &proto->mail->rcpt[proto->rcptidx];
- if (proto->ext & SMTP_EXT_DSN)
- smtp_client_sendcmd(proto, "RCPT TO:<%s>%s%s%s%s",
- rcpt->to,
- rcpt->dsn_notify ? " NOTIFY=" : "",
- rcpt->dsn_notify ? rcpt->dsn_notify : "",
- rcpt->dsn_orcpt ? " ORCPT=" : "",
- rcpt->dsn_orcpt ? rcpt->dsn_orcpt : "");
- else
- smtp_client_sendcmd(proto, "RCPT TO:<%s>", rcpt->to);
- break;
-
- case STATE_DATA:
- if (proto->rcptok == 0) {
- smtp_client_mail_abort(proto);
- smtp_client_state(proto, STATE_RSET);
- }
- else
- smtp_client_sendcmd(proto, "DATA");
- break;
-
- case STATE_BODY:
- fseek(proto->mail->fp, 0, SEEK_SET);
- smtp_client_sendbody(proto);
- break;
-
- case STATE_EOM:
- smtp_client_sendcmd(proto, ".");
- break;
-
- case STATE_RSET:
- smtp_client_sendcmd(proto, "RSET");
- break;
-
- case STATE_QUIT:
- smtp_client_sendcmd(proto, "QUIT");
- break;
-
- default:
- fatalx("%s: bad state %d", __func__, proto->state);
- }
-#undef smtp_client_state
-}
-
-/*
- * Handle a response to an SMTP command
- */
-static void
-smtp_client_response(struct smtp_client *proto, const char *line)
-{
- struct smtp_rcpt *rcpt;
- int i, seen;
-
- switch (proto->state) {
- case STATE_BANNER:
- if (line[0] != '2')
- smtp_client_abort(proto, FAIL_RESP, line);
- else if (proto->params.lmtp)
- smtp_client_state(proto, STATE_LHLO);
- else
- smtp_client_state(proto, STATE_EHLO);
- break;
-
- case STATE_EHLO:
- if (line[0] != '2') {
- /*
- * Either rejected or not implemented. If we want to
- * use EHLO extensions, report an SMTP error.
- * Otherwise, fallback to using HELO.
- */
- if ((proto->params.tls_req == TLS_FORCE) ||
- (proto->params.auth_user))
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_HELO);
- break;
- }
- smtp_client_state(proto, STATE_STARTTLS);
- break;
-
- case STATE_HELO:
- if (line[0] != '2')
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_READY);
- break;
-
- case STATE_LHLO:
- if (line[0] != '2')
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_READY);
- break;
-
- case STATE_STARTTLS:
- if (line[0] != '2') {
- if ((proto->params.tls_req == TLS_FORCE) ||
- (proto->params.auth_user)) {
- smtp_client_cancel(proto, FAIL_RESP, line);
- break;
- }
- smtp_client_state(proto, STATE_AUTH);
- }
- else
- smtp_require_tls(proto->tag, proto);
- break;
-
- case STATE_AUTH_PLAIN:
- if (line[0] != '2')
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_READY);
- break;
-
- case STATE_AUTH_LOGIN:
- if (strncmp(line, "334 ", 4))
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_AUTH_LOGIN_USER);
- break;
-
- case STATE_AUTH_LOGIN_USER:
- if (strncmp(line, "334 ", 4))
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_AUTH_LOGIN_PASS);
- break;
-
- case STATE_AUTH_LOGIN_PASS:
- if (line[0] != '2')
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_READY);
- break;
-
- case STATE_MAIL:
- if (line[0] != '2') {
- smtp_client_mail_status(proto, line);
- smtp_client_state(proto, STATE_RSET);
- }
- else
- smtp_client_state(proto, STATE_RCPT);
- break;
-
- case STATE_RCPT:
- rcpt = &proto->mail->rcpt[proto->rcptidx++];
- if (line[0] != '2')
- smtp_client_rcpt_status(proto, rcpt, line);
- else {
- proto->rcptok++;
- smtp_client_state(proto, STATE_RCPT);
- }
- break;
-
- case STATE_DATA:
- if (line[0] != '2' && line[0] != '3') {
- smtp_client_mail_status(proto, line);
- smtp_client_state(proto, STATE_RSET);
- }
- else
- smtp_client_state(proto, STATE_BODY);
- break;
-
- case STATE_EOM:
- if (proto->params.lmtp) {
- /*
- * LMTP reports a status of each accepted RCPT.
- * Report status for the first pending RCPT and read
- * more lines if another rcpt needs a status.
- */
- for (i = 0, seen = 0; i < proto->mail->rcptcount; i++) {
- rcpt = &proto->mail->rcpt[i];
- if (rcpt->done)
- continue;
- if (seen) {
- io_set_read(proto->io);
- return;
- }
- smtp_client_rcpt_status(proto,
- &proto->mail->rcpt[i], line);
- seen = 1;
- }
- }
- smtp_client_mail_status(proto, line);
- smtp_client_state(proto, STATE_READY);
- break;
-
- case STATE_RSET:
- if (line[0] != '2')
- smtp_client_cancel(proto, FAIL_RESP, line);
- else
- smtp_client_state(proto, STATE_READY);
- break;
-
- case STATE_QUIT:
- smtp_client_free(proto);
- break;
-
- default:
- fatalx("%s: bad state %d", __func__, proto->state);
- }
-}
-
-static void
-smtp_client_io(struct io *io, int evt, void *arg)
-{
- struct smtp_client *proto = arg;
-
- log_trace(TRACE_IO, "%p: %s %s", proto, io_strevent(evt), io_strio(io));
-
- switch (evt) {
- case IO_CONNECTED:
- if (proto->params.tls_req == TLS_SMTPS) {
- io_set_write(io);
- smtp_require_tls(proto->tag, proto);
- }
- else
- smtp_client_state(proto, STATE_BANNER);
- break;
-
- case IO_TLSREADY:
- proto->flags |= FLAG_TLS;
- io_pause(proto->io, IO_IN);
- smtp_verify_server_cert(proto->tag, proto, io_tls(proto->io));
- break;
-
- case IO_DATAIN:
- while (smtp_client_readline(proto))
- ;
- break;
-
- case IO_LOWAT:
- if (proto->state == STATE_BODY)
- smtp_client_sendbody(proto);
- else
- io_set_read(io);
- break;
-
- case IO_TIMEOUT:
- errno = ETIMEDOUT;
- smtp_client_abort(proto, FAIL_CONN, "Connection timeout");
- break;
-
- case IO_ERROR:
- smtp_client_abort(proto, FAIL_CONN, io_error(io));
- break;
-
- case IO_TLSERROR:
- smtp_client_abort(proto, FAIL_CONN, io_error(io));
- break;
-
- case IO_DISCONNECTED:
- smtp_client_abort(proto, FAIL_CONN, io_error(io));
- break;
-
- default:
- fatalx("%s: bad event %d", __func__, evt);
- }
-}
-
-/*
- * return 1 if a new line is expected.
- */
-static int
-smtp_client_readline(struct smtp_client *proto)
-{
- const char *e;
- size_t len;
- char *line, *msg, *p;
- int cont;
-
- line = io_getline(proto->io, &len);
- if (line == NULL) {
- if (io_datalen(proto->io) >= proto->params.linemax)
- smtp_client_abort(proto, FAIL_PROTO, "Line too long");
- return 0;
- }
-
- /* Strip trailing '\r' */
- if (len && line[len - 1] == '\r')
- line[--len] = '\0';
-
- log_trace(TRACE_SMTPCLT, "%p: <<< %s", proto, line);
-
- /* Validate SMTP */
- if (len > 3) {
- msg = line + 4;
- cont = (line[3] == '-');
- } else if (len == 3) {
- msg = line + 3;
- cont = 0;
- } else {
- smtp_client_abort(proto, FAIL_PROTO, "Response too short");
- return 0;
- }
-
- /* Validate reply code. */
- if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) ||
- !isdigit((unsigned char)line[2])) {
- smtp_client_abort(proto, FAIL_PROTO, "Invalid reply code");
- return 0;
- }
-
- /* Validate reply message. */
- for (p = msg; *p; p++)
- if (!isprint((unsigned char)*p)) {
- smtp_client_abort(proto, FAIL_PROTO,
- "Non-printable characters in response");
- return 0;
- }
-
- /* Read extensions. */
- if (proto->state == STATE_EHLO) {
- if (strcmp(msg, "STARTTLS") == 0)
- proto->ext |= SMTP_EXT_STARTTLS;
- else if (strncmp(msg, "AUTH ", 5) == 0) {
- proto->ext |= SMTP_EXT_AUTH;
- if ((p = strstr(msg, " PLAIN")) &&
- (*(p+6) == '\0' || *(p+6) == ' '))
- proto->ext |= SMTP_EXT_AUTH_PLAIN;
- if ((p = strstr(msg, " LOGIN")) &&
- (*(p+6) == '\0' || *(p+6) == ' '))
- proto->ext |= SMTP_EXT_AUTH_LOGIN;
- }
- else if (strcmp(msg, "PIPELINING") == 0)
- proto->ext |= SMTP_EXT_PIPELINING;
- else if (strcmp(msg, "DSN") == 0)
- proto->ext |= SMTP_EXT_DSN;
- else if (strncmp(msg, "SIZE ", 5) == 0) {
- proto->ext_size = strtonum(msg + 5, 0, SIZE_T_MAX, &e);
- if (e == NULL)
- proto->ext |= SMTP_EXT_SIZE;
- }
- }
-
- if (smtp_client_replycat(proto, line) == -1) {
- smtp_client_abort(proto, FAIL_INTERNAL, NULL);
- return 0;
- }
-
- if (cont)
- return 1;
-
- if (io_datalen(proto->io)) {
- /*
- * There should be no pending data after a response is read,
- * except for the multiple status lines after a LMTP message.
- * It can also happen with pipelineing, but we don't do that
- * for now.
- */
- if (!(proto->params.lmtp && proto->state == STATE_EOM)) {
- smtp_client_abort(proto, FAIL_PROTO, "Trailing data");
- return 0;
- }
- }
-
- io_set_write(proto->io);
- smtp_client_response(proto, proto->reply);
- return 0;
-}
-
-/*
- * Concatenate the given response line.
- */
-static int
-smtp_client_replycat(struct smtp_client *proto, const char *line)
-{
- size_t len;
- char *tmp;
- int first;
-
- if (proto->reply && proto->reply[0]) {
- /*
- * If the line is the continuation of an multi-line response,
- * skip the status and ESC parts. First, skip the status, then
- * skip the separator amd ESC if found.
- */
- first = 0;
- line += 3;
- if (line[0]) {
- line += 1;
- if (isdigit((unsigned char)line[0]) && line[1] == '.' &&
- isdigit((unsigned char)line[2]) && line[3] == '.' &&
- isdigit((unsigned char)line[4]) &&
- isspace((unsigned char)line[5]))
- line += 5;
- }
- } else
- first = 1;
-
- if (proto->reply) {
- len = strlcat(proto->reply, line, proto->replysz);
- if (len < proto->replysz)
- return 0;
- }
- else
- len = strlen(line);
-
- if (len > proto->params.ibufmax) {
- errno = EMSGSIZE;
- return -1;
- }
-
- /* Allocate by multiples of 2^8 */
- len += (len % 256) ? (256 - (len % 256)) : 0;
-
- tmp = realloc(proto->reply, len);
- if (tmp == NULL)
- return -1;
- if (proto->reply == NULL)
- tmp[0] = '\0';
-
- proto->reply = tmp;
- proto->replysz = len;
- (void)strlcat(proto->reply, line, proto->replysz);
-
- /* Replace the separator with a space for the first line. */
- if (first && proto->reply[3])
- proto->reply[3] = ' ';
-
- return 0;
-}
-
-static void
-smtp_client_sendbody(struct smtp_client *proto)
-{
- ssize_t len;
- size_t sz = 0, total, w;
- char *ln = NULL;
- int n;
-
- total = io_queued(proto->io);
- w = 0;
-
- while (total < proto->params.obufmax) {
- if ((len = getline(&ln, &sz, proto->mail->fp)) == -1)
- break;
- if (ln[len - 1] == '\n')
- ln[len - 1] = '\0';
- n = io_printf(proto->io, "%s%s\r\n", *ln == '.'?".":"", ln);
- if (n == -1) {
- free(ln);
- smtp_client_abort(proto, FAIL_INTERNAL, NULL);
- return;
- }
- total += n;
- w += n;
- }
- free(ln);
-
- if (ferror(proto->mail->fp)) {
- smtp_client_abort(proto, FAIL_INTERNAL, "Cannot read message");
- return;
- }
-
- log_trace(TRACE_SMTPCLT, "%p: >>> [...%zd bytes...]", proto, w);
-
- if (feof(proto->mail->fp))
- smtp_client_state(proto, STATE_EOM);
-}
-
-static void
-smtp_client_sendcmd(struct smtp_client *proto, char *fmt, ...)
-{
- va_list ap;
- char *p;
- int len;
-
- va_start(ap, fmt);
- len = vasprintf(&p, fmt, ap);
- va_end(ap);
-
- if (len == -1) {
- smtp_client_abort(proto, FAIL_INTERNAL, NULL);
- return;
- }
-
- log_trace(TRACE_SMTPCLT, "mta: %p: >>> %s", proto, p);
-
- len = io_printf(proto->io, "%s\r\n", p);
- free(p);
-
- if (len == -1)
- smtp_client_abort(proto, FAIL_INTERNAL, NULL);
-}
-
-static void
-smtp_client_mail_status(struct smtp_client *proto, const char *status)
-{
- int i;
-
- for (i = 0; i < proto->mail->rcptcount; i++)
- smtp_client_rcpt_status(proto, &proto->mail->rcpt[i], status);
-
- smtp_done(proto->tag, proto, proto->mail);
- proto->mail = NULL;
-}
-
-static void
-smtp_client_mail_abort(struct smtp_client *proto)
-{
- smtp_done(proto->tag, proto, proto->mail);
- proto->mail = NULL;
-}
-
-static void
-smtp_client_rcpt_status(struct smtp_client *proto, struct smtp_rcpt *rcpt, const char *line)
-{
- struct smtp_status status;
-
- if (rcpt->done)
- return;
-
- rcpt->done = 1;
- status.rcpt = rcpt;
- status.cmd = strstate[proto->state];
- status.status = line;
- smtp_status(proto->tag, proto, &status);
-}
diff --git a/smtpd/smtp_session.c b/smtpd/smtp_session.c
deleted file mode 100644
index aefce155..00000000
--- a/smtpd/smtp_session.c
+++ /dev/null
@@ -1,3223 +0,0 @@
-/* $OpenBSD: smtp_session.c,v 1.426 2020/04/24 11:34:07 eric Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2008-2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-
-#include <netinet/in.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <limits.h>
-#include <inttypes.h>
-#include <openssl/ssl.h>
-#include <resolv.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
-#include <vis.h>
-#else
-#include "bsd-vis.h"
-#endif
-
-#include "smtpd.h"
-#include "log.h"
-#include "ssl.h"
-#include "rfc5322.h"
-
-#define SMTP_LINE_MAX 65535
-#define DATA_HIWAT 65535
-#define APPEND_DOMAIN_BUFFER_SIZE SMTP_LINE_MAX
-
-enum smtp_state {
- STATE_NEW = 0,
- STATE_CONNECTED,
- STATE_TLS,
- STATE_HELO,
- STATE_AUTH_INIT,
- STATE_AUTH_USERNAME,
- STATE_AUTH_PASSWORD,
- STATE_AUTH_FINALIZE,
- STATE_BODY,
- STATE_QUIT,
-};
-
-enum session_flags {
- SF_EHLO = 0x0001,
- SF_8BITMIME = 0x0002,
- SF_SECURE = 0x0004,
- SF_AUTHENTICATED = 0x0008,
- SF_BOUNCE = 0x0010,
- SF_VERIFIED = 0x0020,
- SF_BADINPUT = 0x0080,
-};
-
-enum {
- TX_OK = 0,
- TX_ERROR_ENVELOPE,
- TX_ERROR_SIZE,
- TX_ERROR_IO,
- TX_ERROR_LOOP,
- TX_ERROR_MALFORMED,
- TX_ERROR_RESOURCES,
- TX_ERROR_INTERNAL,
-};
-
-enum smtp_command {
- CMD_HELO = 0,
- CMD_EHLO,
- CMD_STARTTLS,
- CMD_AUTH,
- CMD_MAIL_FROM,
- CMD_RCPT_TO,
- CMD_DATA,
- CMD_RSET,
- CMD_QUIT,
- CMD_HELP,
- CMD_WIZ,
- CMD_NOOP,
- CMD_COMMIT,
-};
-
-struct smtp_rcpt {
- TAILQ_ENTRY(smtp_rcpt) entry;
- uint64_t evpid;
- struct mailaddr maddr;
- size_t destcount;
-};
-
-struct smtp_tx {
- struct smtp_session *session;
- uint32_t msgid;
-
- struct envelope evp;
- size_t rcptcount;
- size_t destcount;
- TAILQ_HEAD(, smtp_rcpt) rcpts;
-
- time_t time;
- int error;
- size_t datain;
- size_t odatalen;
- FILE *ofile;
- struct io *filter;
- struct rfc5322_parser *parser;
- int rcvcount;
- int has_date;
- int has_message_id;
-
- uint8_t junk;
-};
-
-struct smtp_session {
- uint64_t id;
- struct io *io;
- struct listener *listener;
- void *ssl_ctx;
- struct sockaddr_storage ss;
- char rdns[HOST_NAME_MAX+1];
- char smtpname[HOST_NAME_MAX+1];
- int fcrdns;
-
- int flags;
- enum smtp_state state;
-
- uint8_t banner_sent;
- char helo[LINE_MAX];
- char cmd[LINE_MAX];
- char username[SMTPD_MAXMAILADDRSIZE];
-
- size_t mailcount;
- struct event pause;
-
- struct smtp_tx *tx;
-
- enum smtp_command last_cmd;
- enum filter_phase filter_phase;
- const char *filter_param;
-
- uint8_t junk;
-};
-
-#define ADVERTISE_TLS(s) \
- ((s)->listener->flags & F_STARTTLS && !((s)->flags & SF_SECURE))
-
-#define ADVERTISE_AUTH(s) \
- ((s)->listener->flags & F_AUTH && (s)->flags & SF_SECURE && \
- !((s)->flags & SF_AUTHENTICATED))
-
-#define ADVERTISE_EXT_DSN(s) \
- ((s)->listener->flags & F_EXT_DSN)
-
-#define SESSION_FILTERED(s) \
- ((s)->listener->flags & F_FILTERED)
-
-#define SESSION_DATA_FILTERED(s) \
- ((s)->listener->flags & F_FILTERED)
-
-
-static int smtp_mailaddr(struct mailaddr *, char *, int, char **, const char *);
-static void smtp_session_init(void);
-static void smtp_lookup_servername(struct smtp_session *);
-static void smtp_getnameinfo_cb(void *, int, const char *, const char *);
-static void smtp_getaddrinfo_cb(void *, int, struct addrinfo *);
-static void smtp_connected(struct smtp_session *);
-static void smtp_send_banner(struct smtp_session *);
-static void smtp_tls_verified(struct smtp_session *);
-static void smtp_io(struct io *, int, void *);
-static void smtp_enter_state(struct smtp_session *, int);
-static void smtp_reply(struct smtp_session *, char *, ...);
-static void smtp_command(struct smtp_session *, char *);
-static void smtp_rfc4954_auth_plain(struct smtp_session *, char *);
-static void smtp_rfc4954_auth_login(struct smtp_session *, char *);
-static void smtp_free(struct smtp_session *, const char *);
-static const char *smtp_strstate(int);
-static void smtp_cert_init(struct smtp_session *);
-static void smtp_cert_init_cb(void *, int, const char *, const void *, size_t);
-static void smtp_cert_verify(struct smtp_session *);
-static void smtp_cert_verify_cb(void *, int);
-static void smtp_auth_failure_pause(struct smtp_session *);
-static void smtp_auth_failure_resume(int, short, void *);
-
-static int smtp_tx(struct smtp_session *);
-static void smtp_tx_free(struct smtp_tx *);
-static void smtp_tx_create_message(struct smtp_tx *);
-static void smtp_tx_mail_from(struct smtp_tx *, const char *);
-static void smtp_tx_rcpt_to(struct smtp_tx *, const char *);
-static void smtp_tx_open_message(struct smtp_tx *);
-static void smtp_tx_commit(struct smtp_tx *);
-static void smtp_tx_rollback(struct smtp_tx *);
-static int smtp_tx_dataline(struct smtp_tx *, const char *);
-static int smtp_tx_filtered_dataline(struct smtp_tx *, const char *);
-static void smtp_tx_eom(struct smtp_tx *);
-static void smtp_filter_fd(struct smtp_tx *, int);
-static int smtp_message_fd(struct smtp_tx *, int);
-static void smtp_message_begin(struct smtp_tx *);
-static void smtp_message_end(struct smtp_tx *);
-static int smtp_filter_printf(struct smtp_tx *, const char *, ...)
- __attribute__((__format__ (printf, 2, 3)));
-static int smtp_message_printf(struct smtp_tx *, const char *, ...)
- __attribute__((__format__ (printf, 2, 3)));
-
-static int smtp_check_rset(struct smtp_session *, const char *);
-static int smtp_check_helo(struct smtp_session *, const char *);
-static int smtp_check_ehlo(struct smtp_session *, const char *);
-static int smtp_check_auth(struct smtp_session *s, const char *);
-static int smtp_check_starttls(struct smtp_session *, const char *);
-static int smtp_check_mail_from(struct smtp_session *, const char *);
-static int smtp_check_rcpt_to(struct smtp_session *, const char *);
-static int smtp_check_data(struct smtp_session *, const char *);
-static int smtp_check_noparam(struct smtp_session *, const char *);
-
-static void smtp_filter_phase(enum filter_phase, struct smtp_session *, const char *);
-
-static void smtp_proceed_connected(struct smtp_session *);
-static void smtp_proceed_rset(struct smtp_session *, const char *);
-static void smtp_proceed_helo(struct smtp_session *, const char *);
-static void smtp_proceed_ehlo(struct smtp_session *, const char *);
-static void smtp_proceed_auth(struct smtp_session *, const char *);
-static void smtp_proceed_starttls(struct smtp_session *, const char *);
-static void smtp_proceed_mail_from(struct smtp_session *, const char *);
-static void smtp_proceed_rcpt_to(struct smtp_session *, const char *);
-static void smtp_proceed_data(struct smtp_session *, const char *);
-static void smtp_proceed_noop(struct smtp_session *, const char *);
-static void smtp_proceed_help(struct smtp_session *, const char *);
-static void smtp_proceed_wiz(struct smtp_session *, const char *);
-static void smtp_proceed_quit(struct smtp_session *, const char *);
-static void smtp_proceed_commit(struct smtp_session *, const char *);
-static void smtp_proceed_rollback(struct smtp_session *, const char *);
-
-static void smtp_filter_begin(struct smtp_session *);
-static void smtp_filter_end(struct smtp_session *);
-static void smtp_filter_data_begin(struct smtp_session *);
-static void smtp_filter_data_end(struct smtp_session *);
-
-static void smtp_report_link_connect(struct smtp_session *, const char *, int,
- const struct sockaddr_storage *,
- const struct sockaddr_storage *);
-static void smtp_report_link_greeting(struct smtp_session *, const char *);
-static void smtp_report_link_identify(struct smtp_session *, const char *, const char *);
-static void smtp_report_link_tls(struct smtp_session *, const char *);
-static void smtp_report_link_disconnect(struct smtp_session *);
-static void smtp_report_link_auth(struct smtp_session *, const char *, const char *);
-static void smtp_report_tx_reset(struct smtp_session *, uint32_t);
-static void smtp_report_tx_begin(struct smtp_session *, uint32_t);
-static void smtp_report_tx_mail(struct smtp_session *, uint32_t, const char *, int);
-static void smtp_report_tx_rcpt(struct smtp_session *, uint32_t, const char *, int);
-static void smtp_report_tx_envelope(struct smtp_session *, uint32_t, uint64_t);
-static void smtp_report_tx_data(struct smtp_session *, uint32_t, int);
-static void smtp_report_tx_commit(struct smtp_session *, uint32_t, size_t);
-static void smtp_report_tx_rollback(struct smtp_session *, uint32_t);
-static void smtp_report_protocol_client(struct smtp_session *, const char *);
-static void smtp_report_protocol_server(struct smtp_session *, const char *);
-static void smtp_report_filter_response(struct smtp_session *, int, int, const char *);
-static void smtp_report_timeout(struct smtp_session *);
-
-
-/* musl work-around */
-void portable_freeaddrinfo(struct addrinfo *);
-
-
-static struct {
- int code;
- enum filter_phase filter_phase;
- const char *cmd;
-
- int (*check)(struct smtp_session *, const char *);
- void (*proceed)(struct smtp_session *, const char *);
-} commands[] = {
- { CMD_HELO, FILTER_HELO, "HELO", smtp_check_helo, smtp_proceed_helo },
- { CMD_EHLO, FILTER_EHLO, "EHLO", smtp_check_ehlo, smtp_proceed_ehlo },
- { CMD_STARTTLS, FILTER_STARTTLS, "STARTTLS", smtp_check_starttls, smtp_proceed_starttls },
- { CMD_AUTH, FILTER_AUTH, "AUTH", smtp_check_auth, smtp_proceed_auth },
- { CMD_MAIL_FROM, FILTER_MAIL_FROM, "MAIL FROM", smtp_check_mail_from, smtp_proceed_mail_from },
- { CMD_RCPT_TO, FILTER_RCPT_TO, "RCPT TO", smtp_check_rcpt_to, smtp_proceed_rcpt_to },
- { CMD_DATA, FILTER_DATA, "DATA", smtp_check_data, smtp_proceed_data },
- { CMD_RSET, FILTER_RSET, "RSET", smtp_check_rset, smtp_proceed_rset },
- { CMD_QUIT, FILTER_QUIT, "QUIT", smtp_check_noparam, smtp_proceed_quit },
- { CMD_NOOP, FILTER_NOOP, "NOOP", smtp_check_noparam, smtp_proceed_noop },
- { CMD_HELP, FILTER_HELP, "HELP", smtp_check_noparam, smtp_proceed_help },
- { CMD_WIZ, FILTER_WIZ, "WIZ", smtp_check_noparam, smtp_proceed_wiz },
- { CMD_COMMIT, FILTER_COMMIT, ".", smtp_check_noparam, smtp_proceed_commit },
- { -1, 0, NULL, NULL },
-};
-
-static struct tree wait_lka_helo;
-static struct tree wait_lka_mail;
-static struct tree wait_lka_rcpt;
-static struct tree wait_parent_auth;
-static struct tree wait_queue_msg;
-static struct tree wait_queue_fd;
-static struct tree wait_queue_commit;
-static struct tree wait_ssl_init;
-static struct tree wait_ssl_verify;
-static struct tree wait_filters;
-static struct tree wait_filter_fd;
-
-static void
-header_append_domain_buffer(char *buffer, char *domain, size_t len)
-{
- size_t i;
- int escape, quote, comment, bracket;
- int has_domain, has_bracket, has_group;
- int pos_bracket, pos_component, pos_insert;
- char copy[APPEND_DOMAIN_BUFFER_SIZE];
-
- escape = quote = comment = bracket = 0;
- has_domain = has_bracket = has_group = 0;
- pos_bracket = pos_insert = pos_component = 0;
- for (i = 0; buffer[i]; ++i) {
- if (buffer[i] == '(' && !escape && !quote)
- comment++;
- if (buffer[i] == '"' && !escape && !comment)
- quote = !quote;
- if (buffer[i] == ')' && !escape && !quote && comment)
- comment--;
- if (buffer[i] == '\\' && !escape && !comment && !quote)
- escape = 1;
- else
- escape = 0;
- if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) {
- bracket++;
- has_bracket = 1;
- }
- if (buffer[i] == '>' && !escape && !comment && !quote && bracket) {
- bracket--;
- pos_bracket = i;
- }
- if (buffer[i] == '@' && !escape && !comment && !quote)
- has_domain = 1;
- if (buffer[i] == ':' && !escape && !comment && !quote)
- has_group = 1;
-
- /* update insert point if not in comment and not on a whitespace */
- if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i]))
- pos_component = i;
- }
-
- /* parse error, do not attempt to modify */
- if (escape || quote || comment || bracket)
- return;
-
- /* domain already present, no need to modify */
- if (has_domain)
- return;
-
- /* address is group, skip */
- if (has_group)
- return;
-
- /* there's an address between brackets, just append domain */
- if (has_bracket) {
- pos_bracket--;
- while (isspace((unsigned char)buffer[pos_bracket]))
- pos_bracket--;
- if (buffer[pos_bracket] == '<')
- return;
- pos_insert = pos_bracket + 1;
- }
- else {
- /* otherwise append address to last component */
- pos_insert = pos_component + 1;
-
- /* empty address */
- if (buffer[pos_component] == '\0' ||
- isspace((unsigned char)buffer[pos_component]))
- return;
- }
-
- if (snprintf(copy, sizeof copy, "%.*s@%s%s",
- (int)pos_insert, buffer,
- domain,
- buffer+pos_insert) >= (int)sizeof copy)
- return;
-
- memcpy(buffer, copy, len);
-}
-
-static void
-header_address_rewrite_buffer(char *buffer, const char *address, size_t len)
-{
- size_t i;
- int address_len;
- int escape, quote, comment, bracket;
- int has_bracket, has_group;
- int pos_bracket_beg, pos_bracket_end, pos_component_beg, pos_component_end;
- int insert_beg, insert_end;
- char copy[APPEND_DOMAIN_BUFFER_SIZE];
-
- escape = quote = comment = bracket = 0;
- has_bracket = has_group = 0;
- pos_bracket_beg = pos_bracket_end = pos_component_beg = pos_component_end = 0;
- for (i = 0; buffer[i]; ++i) {
- if (buffer[i] == '(' && !escape && !quote)
- comment++;
- if (buffer[i] == '"' && !escape && !comment)
- quote = !quote;
- if (buffer[i] == ')' && !escape && !quote && comment)
- comment--;
- if (buffer[i] == '\\' && !escape && !comment && !quote)
- escape = 1;
- else
- escape = 0;
- if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) {
- bracket++;
- has_bracket = 1;
- pos_bracket_beg = i+1;
- }
- if (buffer[i] == '>' && !escape && !comment && !quote && bracket) {
- bracket--;
- pos_bracket_end = i;
- }
- if (buffer[i] == ':' && !escape && !comment && !quote)
- has_group = 1;
-
- /* update insert point if not in comment and not on a whitespace */
- if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i]))
- pos_component_end = i;
- }
-
- /* parse error, do not attempt to modify */
- if (escape || quote || comment || bracket)
- return;
-
- /* address is group, skip */
- if (has_group)
- return;
-
- /* there's an address between brackets, just replace everything brackets */
- if (has_bracket) {
- insert_beg = pos_bracket_beg;
- insert_end = pos_bracket_end;
- }
- else {
- if (pos_component_end == 0)
- pos_component_beg = 0;
- else {
- for (pos_component_beg = pos_component_end; pos_component_beg >= 0; --pos_component_beg)
- if (buffer[pos_component_beg] == ')' || isspace((unsigned char)buffer[pos_component_beg]))
- break;
- pos_component_beg += 1;
- pos_component_end += 1;
- }
- insert_beg = pos_component_beg;
- insert_end = pos_component_end;
- }
-
- /* check that masquerade won' t overflow */
- address_len = strlen(address);
- if (strlen(buffer) - (insert_end - insert_beg) + address_len >= len)
- return;
-
- (void)strlcpy(copy, buffer, sizeof copy);
- (void)strlcpy(copy+insert_beg, address, sizeof (copy) - insert_beg);
- (void)strlcat(copy, buffer+insert_end, sizeof (copy));
- memcpy(buffer, copy, len);
-}
-
-static void
-header_domain_append_callback(struct smtp_tx *tx, const char *hdr,
- const char *val)
-{
- size_t i, j, linelen;
- int escape, quote, comment, skip;
- char buffer[APPEND_DOMAIN_BUFFER_SIZE];
- const char *line, *end;
-
- if (smtp_message_printf(tx, "%s:", hdr) == -1)
- return;
-
- j = 0;
- escape = quote = comment = skip = 0;
- memset(buffer, 0, sizeof buffer);
-
- for (line = val; line; line = end) {
- end = strchr(line, '\n');
- if (end) {
- linelen = end - line;
- end++;
- }
- else
- linelen = strlen(line);
-
- for (i = 0; i < linelen; ++i) {
- if (line[i] == '(' && !escape && !quote)
- comment++;
- if (line[i] == '"' && !escape && !comment)
- quote = !quote;
- if (line[i] == ')' && !escape && !quote && comment)
- comment--;
- if (line[i] == '\\' && !escape && !comment && !quote)
- escape = 1;
- else
- escape = 0;
-
- /* found a separator, buffer contains a full address */
- if (line[i] == ',' && !escape && !quote && !comment) {
- if (!skip && j + strlen(tx->session->listener->hostname) + 1 < sizeof buffer) {
- header_append_domain_buffer(buffer, tx->session->listener->hostname, sizeof buffer);
- if (tx->session->flags & SF_AUTHENTICATED &&
- tx->session->listener->sendertable[0] &&
- tx->session->listener->flags & F_MASQUERADE &&
- !(strcasecmp(hdr, "From")))
- header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender),
- sizeof buffer);
- }
- if (smtp_message_printf(tx, "%s,", buffer) == -1)
- return;
- j = 0;
- skip = 0;
- memset(buffer, 0, sizeof buffer);
- }
- else {
- if (skip) {
- if (smtp_message_printf(tx, "%c", line[i]) == -1)
- return;
- }
- else {
- buffer[j++] = line[i];
- if (j == sizeof (buffer) - 1) {
- if (smtp_message_printf(tx, "%s", buffer) == -1)
- return;
- skip = 1;
- j = 0;
- memset(buffer, 0, sizeof buffer);
- }
- }
- }
- }
- if (skip) {
- if (smtp_message_printf(tx, "\n") == -1)
- return;
- }
- else {
- buffer[j++] = '\n';
- if (j == sizeof (buffer) - 1) {
- if (smtp_message_printf(tx, "%s", buffer) == -1)
- return;
- skip = 1;
- j = 0;
- memset(buffer, 0, sizeof buffer);
- }
- }
- }
-
- /* end of header, if buffer is not empty we'll process it */
- if (buffer[0]) {
- if (j + strlen(tx->session->listener->hostname) + 1 < sizeof buffer) {
- header_append_domain_buffer(buffer, tx->session->listener->hostname, sizeof buffer);
- if (tx->session->flags & SF_AUTHENTICATED &&
- tx->session->listener->sendertable[0] &&
- tx->session->listener->flags & F_MASQUERADE &&
- !(strcasecmp(hdr, "From")))
- header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender),
- sizeof buffer);
- }
- smtp_message_printf(tx, "%s", buffer);
- }
-}
-
-static void
-smtp_session_init(void)
-{
- static int init = 0;
-
- if (!init) {
- tree_init(&wait_lka_helo);
- tree_init(&wait_lka_mail);
- tree_init(&wait_lka_rcpt);
- tree_init(&wait_parent_auth);
- tree_init(&wait_queue_msg);
- tree_init(&wait_queue_fd);
- tree_init(&wait_queue_commit);
- tree_init(&wait_ssl_init);
- tree_init(&wait_ssl_verify);
- tree_init(&wait_filters);
- tree_init(&wait_filter_fd);
- init = 1;
- }
-}
-
-int
-smtp_session(struct listener *listener, int sock,
- const struct sockaddr_storage *ss, const char *hostname, struct io *io)
-{
- struct smtp_session *s;
-
- smtp_session_init();
-
- if ((s = calloc(1, sizeof(*s))) == NULL)
- return (-1);
-
- s->id = generate_uid();
- s->listener = listener;
- memmove(&s->ss, ss, sizeof(*ss));
-
- if (io != NULL)
- s->io = io;
- else
- s->io = io_new();
-
- io_set_callback(s->io, smtp_io, s);
- io_set_fd(s->io, sock);
- io_set_timeout(s->io, SMTPD_SESSION_TIMEOUT * 1000);
- io_set_write(s->io);
- s->state = STATE_NEW;
-
- (void)strlcpy(s->smtpname, listener->hostname, sizeof(s->smtpname));
-
- log_trace(TRACE_SMTP, "smtp: %p: connected to listener %p "
- "[hostname=%s, port=%d, tag=%s]", s, listener,
- listener->hostname, ntohs(listener->port), listener->tag);
-
- /* For local enqueueing, the hostname is already set */
- if (hostname) {
- s->flags |= SF_AUTHENTICATED;
- /* A bit of a hack */
- if (!strcmp(hostname, "localhost"))
- s->flags |= SF_BOUNCE;
- (void)strlcpy(s->rdns, hostname, sizeof(s->rdns));
- s->fcrdns = 1;
- smtp_lookup_servername(s);
- } else {
- resolver_getnameinfo((struct sockaddr *)&s->ss, NI_NAMEREQD,
- smtp_getnameinfo_cb, s);
- }
-
- /* session may have been freed by now */
-
- return (0);
-}
-
-static void
-smtp_getnameinfo_cb(void *arg, int gaierrno, const char *host, const char *serv)
-{
- struct smtp_session *s = arg;
- struct addrinfo hints;
-
- if (gaierrno) {
- (void)strlcpy(s->rdns, "<unknown>", sizeof(s->rdns));
-
- if (gaierrno == EAI_NODATA || gaierrno == EAI_NONAME)
- s->fcrdns = 0;
- else {
- log_warnx("getnameinfo: %s: %s", ss_to_text(&s->ss),
- gai_strerror(gaierrno));
- s->fcrdns = -1;
- }
-
- smtp_lookup_servername(s);
- return;
- }
-
- (void)strlcpy(s->rdns, host, sizeof(s->rdns));
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = s->ss.ss_family;
- hints.ai_socktype = SOCK_STREAM;
- resolver_getaddrinfo(s->rdns, NULL, &hints, smtp_getaddrinfo_cb, s);
-}
-
-static void
-smtp_getaddrinfo_cb(void *arg, int gaierrno, struct addrinfo *ai0)
-{
- struct smtp_session *s = arg;
- struct addrinfo *ai;
- char fwd[64], rev[64];
-
- if (gaierrno) {
- if (gaierrno == EAI_NODATA || gaierrno == EAI_NONAME)
- s->fcrdns = 0;
- else {
- log_warnx("getaddrinfo: %s: %s", s->rdns,
- gai_strerror(gaierrno));
- s->fcrdns = -1;
- }
- }
- else {
- strlcpy(rev, ss_to_text(&s->ss), sizeof(rev));
- for (ai = ai0; ai; ai = ai->ai_next) {
- strlcpy(fwd, sa_to_text(ai->ai_addr), sizeof(fwd));
- if (!strcmp(fwd, rev)) {
- s->fcrdns = 1;
- break;
- }
- }
- portable_freeaddrinfo(ai0);
- }
-
- smtp_lookup_servername(s);
-}
-
-void
-smtp_session_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct smtp_session *s;
- struct smtp_rcpt *rcpt;
- char user[SMTPD_MAXMAILADDRSIZE];
- char tmp[SMTP_LINE_MAX];
- struct msg m;
- const char *line, *helo;
- uint64_t reqid, evpid;
- uint32_t msgid;
- int status, success;
- int filter_response;
- const char *filter_param;
- uint8_t i;
-
- switch (imsg->hdr.type) {
-
- case IMSG_SMTP_CHECK_SENDER:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &status);
- m_end(&m);
- s = tree_xpop(&wait_lka_mail, reqid);
- switch (status) {
- case LKA_OK:
- smtp_tx_create_message(s->tx);
- break;
-
- case LKA_PERMFAIL:
- smtp_tx_free(s->tx);
- smtp_reply(s, "%d %s", 530, "Sender rejected");
- break;
- case LKA_TEMPFAIL:
- smtp_tx_free(s->tx);
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- break;
- }
- return;
-
- case IMSG_SMTP_EXPAND_RCPT:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &status);
- m_get_string(&m, &line);
- m_end(&m);
- s = tree_xpop(&wait_lka_rcpt, reqid);
-
- tmp[0] = '\0';
- if (s->tx->evp.rcpt.user[0]) {
- (void)strlcpy(tmp, s->tx->evp.rcpt.user, sizeof tmp);
- if (s->tx->evp.rcpt.domain[0]) {
- (void)strlcat(tmp, "@", sizeof tmp);
- (void)strlcat(tmp, s->tx->evp.rcpt.domain,
- sizeof tmp);
- }
- }
-
- switch (status) {
- case LKA_OK:
- fatalx("unexpected ok");
- case LKA_PERMFAIL:
- smtp_reply(s, "%s: <%s>", line, tmp);
- break;
- case LKA_TEMPFAIL:
- smtp_reply(s, "%s: <%s>", line, tmp);
- break;
- }
- return;
-
- case IMSG_SMTP_LOOKUP_HELO:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- s = tree_xpop(&wait_lka_helo, reqid);
- m_get_int(&m, &status);
- if (status == LKA_OK) {
- m_get_string(&m, &helo);
- (void)strlcpy(s->smtpname, helo, sizeof(s->smtpname));
- }
- m_end(&m);
- smtp_connected(s);
- return;
-
- case IMSG_SMTP_MESSAGE_CREATE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- s = tree_xpop(&wait_queue_msg, reqid);
- if (success) {
- m_get_msgid(&m, &msgid);
- s->tx->msgid = msgid;
- s->tx->evp.id = msgid_to_evpid(msgid);
- s->tx->rcptcount = 0;
- smtp_reply(s, "250 %s Ok",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
- } else {
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_tx_free(s->tx);
- smtp_enter_state(s, STATE_QUIT);
- }
- m_end(&m);
- return;
-
- case IMSG_SMTP_MESSAGE_OPEN:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- m_end(&m);
-
- s = tree_xpop(&wait_queue_fd, reqid);
- if (!success || imsg->fd == -1) {
- if (imsg->fd != -1)
- close(imsg->fd);
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- return;
- }
-
- log_debug("smtp: %p: fd %d from queue", s, imsg->fd);
-
- if (smtp_message_fd(s->tx, imsg->fd)) {
- if (!SESSION_DATA_FILTERED(s))
- smtp_message_begin(s->tx);
- else
- smtp_filter_data_begin(s);
- }
- return;
-
- case IMSG_FILTER_SMTP_DATA_BEGIN:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- m_end(&m);
-
- s = tree_xpop(&wait_filter_fd, reqid);
- if (!success || imsg->fd == -1) {
- if (imsg->fd != -1)
- close(imsg->fd);
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- return;
- }
-
- log_debug("smtp: %p: fd %d from lka", s, imsg->fd);
-
- smtp_filter_fd(s->tx, imsg->fd);
- smtp_message_begin(s->tx);
- return;
-
- case IMSG_QUEUE_ENVELOPE_SUBMIT:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- s = tree_xget(&wait_lka_rcpt, reqid);
- if (success) {
- m_get_evpid(&m, &evpid);
- s->tx->evp.id = evpid;
- s->tx->destcount++;
- smtp_report_tx_envelope(s, s->tx->msgid, evpid);
- }
- else
- s->tx->error = TX_ERROR_ENVELOPE;
- m_end(&m);
- return;
-
- case IMSG_QUEUE_ENVELOPE_COMMIT:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- m_end(&m);
- if (!success)
- fatalx("commit evp failed: not supposed to happen");
- s = tree_xpop(&wait_lka_rcpt, reqid);
- if (s->tx->error) {
- /*
- * If an envelope failed, we can't cancel the last
- * RCPT only so we must cancel the whole transaction
- * and close the connection.
- */
- smtp_reply(s, "421 %s Temporary failure",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- }
- else {
- rcpt = xcalloc(1, sizeof(*rcpt));
- rcpt->evpid = s->tx->evp.id;
- rcpt->destcount = s->tx->destcount;
- rcpt->maddr = s->tx->evp.rcpt;
- TAILQ_INSERT_TAIL(&s->tx->rcpts, rcpt, entry);
-
- s->tx->destcount = 0;
- s->tx->rcptcount++;
- smtp_reply(s, "250 %s %s: Recipient ok",
- esc_code(ESC_STATUS_OK, ESC_DESTINATION_ADDRESS_VALID),
- esc_description(ESC_DESTINATION_ADDRESS_VALID));
- }
- return;
-
- case IMSG_SMTP_MESSAGE_COMMIT:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- m_end(&m);
- s = tree_xpop(&wait_queue_commit, reqid);
- if (!success) {
- smtp_reply(s, "421 %s Temporary failure",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_tx_free(s->tx);
- smtp_enter_state(s, STATE_QUIT);
- return;
- }
-
- smtp_reply(s, "250 %s %08x Message accepted for delivery",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS),
- s->tx->msgid);
- smtp_report_tx_commit(s, s->tx->msgid, s->tx->odatalen);
- smtp_report_tx_reset(s, s->tx->msgid);
-
- log_info("%016"PRIx64" smtp message "
- "msgid=%08x size=%zu nrcpt=%zu proto=%s",
- s->id,
- s->tx->msgid,
- s->tx->odatalen,
- s->tx->rcptcount,
- s->flags & SF_EHLO ? "ESMTP" : "SMTP");
- TAILQ_FOREACH(rcpt, &s->tx->rcpts, entry) {
- log_info("%016"PRIx64" smtp envelope "
- "evpid=%016"PRIx64" from=<%s%s%s> to=<%s%s%s>",
- s->id,
- rcpt->evpid,
- s->tx->evp.sender.user,
- s->tx->evp.sender.user[0] == '\0' ? "" : "@",
- s->tx->evp.sender.domain,
- rcpt->maddr.user,
- rcpt->maddr.user[0] == '\0' ? "" : "@",
- rcpt->maddr.domain);
- }
- smtp_tx_free(s->tx);
- s->mailcount++;
- smtp_enter_state(s, STATE_HELO);
- return;
-
- case IMSG_SMTP_AUTHENTICATE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- m_end(&m);
-
- s = tree_xpop(&wait_parent_auth, reqid);
- strnvis(user, s->username, sizeof user, VIS_WHITE | VIS_SAFE);
- if (success == LKA_OK) {
- log_info("%016"PRIx64" smtp "
- "authentication user=%s "
- "result=ok",
- s->id, user);
- s->flags |= SF_AUTHENTICATED;
- smtp_report_link_auth(s, user, "pass");
- smtp_reply(s, "235 %s Authentication succeeded",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
- }
- else if (success == LKA_PERMFAIL) {
- log_info("%016"PRIx64" smtp "
- "authentication user=%s "
- "result=permfail",
- s->id, user);
- smtp_report_link_auth(s, user, "fail");
- smtp_auth_failure_pause(s);
- return;
- }
- else if (success == LKA_TEMPFAIL) {
- log_info("%016"PRIx64" smtp "
- "authentication user=%s "
- "result=tempfail",
- s->id, user);
- smtp_report_link_auth(s, user, "error");
- smtp_reply(s, "421 %s Temporary failure",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- }
- else
- fatalx("bad lka response");
-
- smtp_enter_state(s, STATE_HELO);
- return;
-
- case IMSG_FILTER_SMTP_PROTOCOL:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &filter_response);
- if (filter_response != FILTER_PROCEED &&
- filter_response != FILTER_JUNK)
- m_get_string(&m, &filter_param);
- else
- filter_param = NULL;
- m_end(&m);
-
- s = tree_xpop(&wait_filters, reqid);
-
- switch (filter_response) {
- case FILTER_REJECT:
- case FILTER_DISCONNECT:
- if (!valid_smtp_response(filter_param) ||
- (filter_param[0] != '4' && filter_param[0] != '5'))
- filter_param = "421 Internal server error";
- if (!strncmp(filter_param, "421", 3))
- filter_response = FILTER_DISCONNECT;
-
- smtp_report_filter_response(s, s->filter_phase,
- filter_response, filter_param);
-
- smtp_reply(s, "%s", filter_param);
-
- if (filter_response == FILTER_DISCONNECT)
- smtp_enter_state(s, STATE_QUIT);
- else if (s->filter_phase == FILTER_COMMIT)
- smtp_proceed_rollback(s, NULL);
- break;
-
-
- case FILTER_JUNK:
- if (s->tx)
- s->tx->junk = 1;
- else
- s->junk = 1;
- /* fallthrough */
-
- case FILTER_PROCEED:
- filter_param = s->filter_param;
- /* fallthrough */
-
- case FILTER_REWRITE:
- smtp_report_filter_response(s, s->filter_phase,
- filter_response,
- filter_param == s->filter_param ? NULL : filter_param);
- if (s->filter_phase == FILTER_CONNECT) {
- smtp_proceed_connected(s);
- return;
- }
- for (i = 0; i < nitems(commands); ++i)
- if (commands[i].filter_phase == s->filter_phase) {
- if (filter_response == FILTER_REWRITE)
- if (!commands[i].check(s, filter_param))
- break;
- commands[i].proceed(s, filter_param);
- break;
- }
- break;
- }
- return;
- }
-
- log_warnx("smtp_session_imsg: unexpected %s imsg",
- imsg_to_str(imsg->hdr.type));
- fatalx(NULL);
-}
-
-static void
-smtp_tls_verified(struct smtp_session *s)
-{
- X509 *x;
-
- x = SSL_get_peer_certificate(io_tls(s->io));
- if (x) {
- log_info("%016"PRIx64" smtp "
- "client-cert-check result=\"%s\"",
- s->id,
- (s->flags & SF_VERIFIED) ? "success" : "failure");
- X509_free(x);
- }
-
- if (s->listener->flags & F_SMTPS) {
- stat_increment("smtp.smtps", 1);
- io_set_write(s->io);
- smtp_send_banner(s);
- }
- else {
- stat_increment("smtp.tls", 1);
- smtp_enter_state(s, STATE_HELO);
- }
-}
-
-static void
-smtp_io(struct io *io, int evt, void *arg)
-{
- struct smtp_session *s = arg;
- char *line;
- size_t len;
- int eom;
-
- log_trace(TRACE_IO, "smtp: %p: %s %s", s, io_strevent(evt),
- io_strio(io));
-
- switch (evt) {
-
- case IO_TLSREADY:
- log_info("%016"PRIx64" smtp tls ciphers=%s",
- s->id, ssl_to_text(io_tls(s->io)));
-
- smtp_report_link_tls(s, ssl_to_text(io_tls(s->io)));
-
- s->flags |= SF_SECURE;
- s->helo[0] = '\0';
-
- smtp_cert_verify(s);
- break;
-
- case IO_DATAIN:
- nextline:
- line = io_getline(s->io, &len);
- if ((line == NULL && io_datalen(s->io) >= SMTP_LINE_MAX) ||
- (line && len >= SMTP_LINE_MAX)) {
- s->flags |= SF_BADINPUT;
- smtp_reply(s, "500 %s Line too long",
- esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- io_set_write(io);
- return;
- }
-
- /* No complete line received */
- if (line == NULL)
- return;
-
- /* Strip trailing '\r' */
- if (len && line[len - 1] == '\r')
- line[--len] = '\0';
-
- /* Message body */
- eom = 0;
- if (s->state == STATE_BODY) {
- if (strcmp(line, ".")) {
- s->tx->datain += strlen(line) + 1;
- if (s->tx->datain > env->sc_maxsize)
- s->tx->error = TX_ERROR_SIZE;
- }
- eom = (s->tx->filter == NULL) ?
- smtp_tx_dataline(s->tx, line) :
- smtp_tx_filtered_dataline(s->tx, line);
- if (eom == 0)
- goto nextline;
- }
-
- /* Pipelining not supported */
- if (io_datalen(s->io)) {
- s->flags |= SF_BADINPUT;
- smtp_reply(s, "500 %s %s: Pipelining not supported",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- smtp_enter_state(s, STATE_QUIT);
- io_set_write(io);
- return;
- }
-
- if (eom) {
- io_set_write(io);
- if (s->tx->filter == NULL)
- smtp_tx_eom(s->tx);
- return;
- }
-
- /* Must be a command */
- if (strlcpy(s->cmd, line, sizeof(s->cmd)) >= sizeof(s->cmd)) {
- s->flags |= SF_BADINPUT;
- smtp_reply(s, "500 %s Command line too long",
- esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- io_set_write(io);
- return;
- }
- io_set_write(io);
- smtp_command(s, line);
- break;
-
- case IO_LOWAT:
- if (s->state == STATE_QUIT) {
- log_info("%016"PRIx64" smtp disconnected "
- "reason=quit",
- s->id);
- smtp_free(s, "done");
- break;
- }
-
- /* Wait for the client to start tls */
- if (s->state == STATE_TLS) {
- smtp_cert_init(s);
- break;
- }
-
- io_set_read(io);
- break;
-
- case IO_TIMEOUT:
- log_info("%016"PRIx64" smtp disconnected "
- "reason=timeout",
- s->id);
- smtp_report_timeout(s);
- smtp_free(s, "timeout");
- break;
-
- case IO_DISCONNECTED:
- log_info("%016"PRIx64" smtp disconnected "
- "reason=disconnect",
- s->id);
- smtp_free(s, "disconnected");
- break;
-
- case IO_ERROR:
- log_info("%016"PRIx64" smtp disconnected "
- "reason=\"io-error: %s\"",
- s->id, io_error(io));
- smtp_free(s, "IO error");
- break;
-
- default:
- fatalx("smtp_io()");
- }
-}
-
-static void
-smtp_command(struct smtp_session *s, char *line)
-{
- char *args;
- int cmd, i;
-
- log_trace(TRACE_SMTP, "smtp: %p: <<< %s", s, line);
-
- /*
- * These states are special.
- */
- if (s->state == STATE_AUTH_INIT) {
- smtp_report_protocol_client(s, "********");
- smtp_rfc4954_auth_plain(s, line);
- return;
- }
- if (s->state == STATE_AUTH_USERNAME || s->state == STATE_AUTH_PASSWORD) {
- smtp_report_protocol_client(s, "********");
- smtp_rfc4954_auth_login(s, line);
- return;
- }
-
- if (s->state == STATE_HELO && strncasecmp(line, "AUTH PLAIN ", 11) == 0)
- smtp_report_protocol_client(s, "AUTH PLAIN ********");
- else
- smtp_report_protocol_client(s, line);
-
-
- /*
- * Unlike other commands, "mail from" and "rcpt to" contain a
- * space in the command name.
- */
- if (strncasecmp("mail from:", line, 10) == 0 ||
- strncasecmp("rcpt to:", line, 8) == 0)
- args = strchr(line, ':');
- else
- args = strchr(line, ' ');
-
- if (args) {
- *args++ = '\0';
- while (isspace((unsigned char)*args))
- args++;
- }
-
- cmd = -1;
- for (i = 0; commands[i].code != -1; i++)
- if (!strcasecmp(line, commands[i].cmd)) {
- cmd = commands[i].code;
- break;
- }
-
- s->last_cmd = cmd;
- switch (cmd) {
- /*
- * INIT
- */
- case CMD_HELO:
- if (!smtp_check_helo(s, args))
- break;
- smtp_filter_phase(FILTER_HELO, s, args);
- break;
-
- case CMD_EHLO:
- if (!smtp_check_ehlo(s, args))
- break;
- smtp_filter_phase(FILTER_EHLO, s, args);
- break;
-
- /*
- * SETUP
- */
- case CMD_STARTTLS:
- if (!smtp_check_starttls(s, args))
- break;
-
- smtp_filter_phase(FILTER_STARTTLS, s, NULL);
- break;
-
- case CMD_AUTH:
- if (!smtp_check_auth(s, args))
- break;
- smtp_filter_phase(FILTER_AUTH, s, args);
- break;
-
- case CMD_MAIL_FROM:
- if (!smtp_check_mail_from(s, args))
- break;
- smtp_filter_phase(FILTER_MAIL_FROM, s, args);
- break;
-
- /*
- * TRANSACTION
- */
- case CMD_RCPT_TO:
- if (!smtp_check_rcpt_to(s, args))
- break;
- smtp_filter_phase(FILTER_RCPT_TO, s, args);
- break;
-
- case CMD_RSET:
- if (!smtp_check_rset(s, args))
- break;
- smtp_filter_phase(FILTER_RSET, s, NULL);
- break;
-
- case CMD_DATA:
- if (!smtp_check_data(s, args))
- break;
- smtp_filter_phase(FILTER_DATA, s, NULL);
- break;
-
- /*
- * ANY
- */
- case CMD_QUIT:
- if (!smtp_check_noparam(s, args))
- break;
- smtp_filter_phase(FILTER_QUIT, s, NULL);
- break;
-
- case CMD_NOOP:
- if (!smtp_check_noparam(s, args))
- break;
- smtp_filter_phase(FILTER_NOOP, s, NULL);
- break;
-
- case CMD_HELP:
- if (!smtp_check_noparam(s, args))
- break;
- smtp_proceed_help(s, NULL);
- break;
-
- case CMD_WIZ:
- if (!smtp_check_noparam(s, args))
- break;
- smtp_proceed_wiz(s, NULL);
- break;
-
- default:
- smtp_reply(s, "500 %s %s: Command unrecognized",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- break;
- }
-}
-
-static int
-smtp_check_rset(struct smtp_session *s, const char *args)
-{
- if (!smtp_check_noparam(s, args))
- return 0;
-
- if (s->helo[0] == '\0') {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
- return 1;
-}
-
-static int
-smtp_check_helo(struct smtp_session *s, const char *args)
-{
- if (!s->banner_sent) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->helo[0]) {
- smtp_reply(s, "503 %s %s: Already identified",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (args == NULL) {
- smtp_reply(s, "501 %s %s: HELO requires domain name",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (!valid_domainpart(args)) {
- smtp_reply(s, "501 %s %s: Invalid domain name",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_ehlo(struct smtp_session *s, const char *args)
-{
- if (!s->banner_sent) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->helo[0]) {
- smtp_reply(s, "503 %s %s: Already identified",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (args == NULL) {
- smtp_reply(s, "501 %s %s: EHLO requires domain name",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (!valid_domainpart(args)) {
- smtp_reply(s, "501 %s %s: Invalid domain name",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_auth(struct smtp_session *s, const char *args)
-{
- if (s->helo[0] == '\0' || s->tx) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->flags & SF_AUTHENTICATED) {
- smtp_reply(s, "503 %s %s: Already authenticated",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (!ADVERTISE_AUTH(s)) {
- smtp_reply(s, "503 %s %s: Command not supported",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (args == NULL) {
- smtp_reply(s, "501 %s %s: No parameters given",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_starttls(struct smtp_session *s, const char *args)
-{
- if (s->helo[0] == '\0' || s->tx) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (!(s->listener->flags & F_STARTTLS)) {
- smtp_reply(s, "503 %s %s: Command not supported",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->flags & SF_SECURE) {
- smtp_reply(s, "503 %s %s: Channel already secured",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (args != NULL) {
- smtp_reply(s, "501 %s %s: No parameters allowed",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_mail_from(struct smtp_session *s, const char *args)
-{
- char *copy;
- char tmp[SMTP_LINE_MAX];
- struct mailaddr sender;
-
- (void)strlcpy(tmp, args, sizeof tmp);
- copy = tmp;
-
- if (s->helo[0] == '\0' || s->tx) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->listener->flags & F_STARTTLS_REQUIRE &&
- !(s->flags & SF_SECURE)) {
- smtp_reply(s,
- "530 %s %s: Must issue a STARTTLS command first",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->listener->flags & F_AUTH_REQUIRE &&
- !(s->flags & SF_AUTHENTICATED)) {
- smtp_reply(s,
- "530 %s %s: Must issue an AUTH command first",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->mailcount >= env->sc_session_max_mails) {
- /* we can pretend we had too many recipients */
- smtp_reply(s, "452 %s %s: Too many messages sent",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
- esc_description(ESC_TOO_MANY_RECIPIENTS));
- return 0;
- }
-
- if (smtp_mailaddr(&sender, copy, 1, &copy,
- s->smtpname) == 0) {
- smtp_reply(s, "553 %s Sender address syntax error",
- esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_rcpt_to(struct smtp_session *s, const char *args)
-{
- char *copy;
- char tmp[SMTP_LINE_MAX];
-
- (void)strlcpy(tmp, args, sizeof tmp);
- copy = tmp;
-
- if (s->tx == NULL) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->tx->rcptcount >= env->sc_session_max_rcpt) {
- smtp_reply(s->tx->session, "451 %s %s: Too many recipients",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
- esc_description(ESC_TOO_MANY_RECIPIENTS));
- return 0;
- }
-
- if (smtp_mailaddr(&s->tx->evp.rcpt, copy, 0, &copy,
- s->tx->session->smtpname) == 0) {
- smtp_reply(s->tx->session,
- "501 %s Recipient address syntax error",
- esc_code(ESC_STATUS_PERMFAIL,
- ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_data(struct smtp_session *s, const char *args)
-{
- if (!smtp_check_noparam(s, args))
- return 0;
-
- if (s->tx == NULL) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->tx->rcptcount == 0) {
- smtp_reply(s, "503 %s %s: No recipient specified",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_noparam(struct smtp_session *s, const char *args)
-{
- if (args != NULL) {
- smtp_reply(s, "500 %s %s: command does not accept arguments.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- return 0;
- }
- return 1;
-}
-
-static void
-smtp_query_filters(enum filter_phase phase, struct smtp_session *s, const char *args)
-{
- m_create(p_lka, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_int(p_lka, phase);
- m_add_string(p_lka, args);
- m_close(p_lka);
- tree_xset(&wait_filters, s->id, s);
-}
-
-static void
-smtp_filter_begin(struct smtp_session *s)
-{
- if (!SESSION_FILTERED(s))
- return;
-
- m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_string(p_lka, s->listener->filter_name);
- m_close(p_lka);
-}
-
-static void
-smtp_filter_end(struct smtp_session *s)
-{
- if (!SESSION_FILTERED(s))
- return;
-
- m_create(p_lka, IMSG_FILTER_SMTP_END, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_close(p_lka);
-}
-
-static void
-smtp_filter_data_begin(struct smtp_session *s)
-{
- if (!SESSION_FILTERED(s))
- return;
-
- m_create(p_lka, IMSG_FILTER_SMTP_DATA_BEGIN, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_close(p_lka);
- tree_xset(&wait_filter_fd, s->id, s);
-}
-
-static void
-smtp_filter_data_end(struct smtp_session *s)
-{
- if (!SESSION_FILTERED(s))
- return;
-
- if (s->tx->filter == NULL)
- return;
-
- io_free(s->tx->filter);
- s->tx->filter = NULL;
-
- m_create(p_lka, IMSG_FILTER_SMTP_DATA_END, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_close(p_lka);
-}
-
-static void
-smtp_filter_phase(enum filter_phase phase, struct smtp_session *s, const char *param)
-{
- uint8_t i;
-
- s->filter_phase = phase;
- s->filter_param = param;
-
- if (SESSION_FILTERED(s)) {
- smtp_query_filters(phase, s, param ? param : "");
- return;
- }
-
- if (s->filter_phase == FILTER_CONNECT) {
- smtp_proceed_connected(s);
- return;
- }
-
- for (i = 0; i < nitems(commands); ++i)
- if (commands[i].filter_phase == s->filter_phase) {
- commands[i].proceed(s, param);
- break;
- }
-}
-
-static void
-smtp_proceed_rset(struct smtp_session *s, const char *args)
-{
- smtp_reply(s, "250 %s Reset state",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
-
- if (s->tx) {
- if (s->tx->msgid)
- smtp_tx_rollback(s->tx);
- smtp_tx_free(s->tx);
- }
-}
-
-static void
-smtp_proceed_helo(struct smtp_session *s, const char *args)
-{
- (void)strlcpy(s->helo, args, sizeof(s->helo));
- s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED;
-
- smtp_report_link_identify(s, "HELO", s->helo);
-
- smtp_enter_state(s, STATE_HELO);
-
- smtp_reply(s, "250 %s Hello %s %s%s%s, pleased to meet you",
- s->smtpname,
- s->helo,
- s->ss.ss_family == AF_INET6 ? "" : "[",
- ss_to_text(&s->ss),
- s->ss.ss_family == AF_INET6 ? "" : "]");
-}
-
-static void
-smtp_proceed_ehlo(struct smtp_session *s, const char *args)
-{
- (void)strlcpy(s->helo, args, sizeof(s->helo));
- s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED;
- s->flags |= SF_EHLO;
- s->flags |= SF_8BITMIME;
-
- smtp_report_link_identify(s, "EHLO", s->helo);
-
- smtp_enter_state(s, STATE_HELO);
- smtp_reply(s, "250-%s Hello %s %s%s%s, pleased to meet you",
- s->smtpname,
- s->helo,
- s->ss.ss_family == AF_INET6 ? "" : "[",
- ss_to_text(&s->ss),
- s->ss.ss_family == AF_INET6 ? "" : "]");
-
- smtp_reply(s, "250-8BITMIME");
- smtp_reply(s, "250-ENHANCEDSTATUSCODES");
- smtp_reply(s, "250-SIZE %zu", env->sc_maxsize);
- if (ADVERTISE_EXT_DSN(s))
- smtp_reply(s, "250-DSN");
- if (ADVERTISE_TLS(s))
- smtp_reply(s, "250-STARTTLS");
- if (ADVERTISE_AUTH(s))
- smtp_reply(s, "250-AUTH PLAIN LOGIN");
- smtp_reply(s, "250 HELP");
-}
-
-static void
-smtp_proceed_auth(struct smtp_session *s, const char *args)
-{
- char tmp[SMTP_LINE_MAX];
- char *eom, *method;
-
- (void)strlcpy(tmp, args, sizeof tmp);
-
- method = tmp;
- eom = strchr(tmp, ' ');
- if (eom == NULL)
- eom = strchr(tmp, '\t');
- if (eom != NULL)
- *eom++ = '\0';
- if (strcasecmp(method, "PLAIN") == 0)
- smtp_rfc4954_auth_plain(s, eom);
- else if (strcasecmp(method, "LOGIN") == 0)
- smtp_rfc4954_auth_login(s, eom);
- else
- smtp_reply(s, "504 %s %s: AUTH method \"%s\" not supported",
- esc_code(ESC_STATUS_PERMFAIL, ESC_SECURITY_FEATURES_NOT_SUPPORTED),
- esc_description(ESC_SECURITY_FEATURES_NOT_SUPPORTED),
- method);
-}
-
-static void
-smtp_proceed_starttls(struct smtp_session *s, const char *args)
-{
- smtp_reply(s, "220 %s Ready to start TLS",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
- smtp_enter_state(s, STATE_TLS);
-}
-
-static void
-smtp_proceed_mail_from(struct smtp_session *s, const char *args)
-{
- char *copy;
- char tmp[SMTP_LINE_MAX];
-
- (void)strlcpy(tmp, args, sizeof tmp);
- copy = tmp;
-
- if (!smtp_tx(s)) {
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- return;
- }
-
- if (smtp_mailaddr(&s->tx->evp.sender, copy, 1, &copy,
- s->smtpname) == 0) {
- smtp_reply(s, "553 %s Sender address syntax error",
- esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS));
- smtp_tx_free(s->tx);
- return;
- }
-
- smtp_tx_mail_from(s->tx, args);
-}
-
-static void
-smtp_proceed_rcpt_to(struct smtp_session *s, const char *args)
-{
- smtp_tx_rcpt_to(s->tx, args);
-}
-
-static void
-smtp_proceed_data(struct smtp_session *s, const char *args)
-{
- smtp_tx_open_message(s->tx);
-}
-
-static void
-smtp_proceed_quit(struct smtp_session *s, const char *args)
-{
- smtp_reply(s, "221 %s Bye",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
- smtp_enter_state(s, STATE_QUIT);
-}
-
-static void
-smtp_proceed_noop(struct smtp_session *s, const char *args)
-{
- smtp_reply(s, "250 %s Ok",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
-}
-
-static void
-smtp_proceed_help(struct smtp_session *s, const char *args)
-{
- const char *code = esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS);
-
- smtp_reply(s, "214-%s This is " SMTPD_NAME, code);
- smtp_reply(s, "214-%s To report bugs in the implementation, "
- "please contact bugs@openbsd.org", code);
- smtp_reply(s, "214-%s with full details", code);
- smtp_reply(s, "214 %s End of HELP info", code);
-}
-
-static void
-smtp_proceed_wiz(struct smtp_session *s, const char *args)
-{
- smtp_reply(s, "500 %s %s: this feature is not supported yet ;-)",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
-}
-
-static void
-smtp_proceed_commit(struct smtp_session *s, const char *args)
-{
- smtp_message_end(s->tx);
-}
-
-static void
-smtp_proceed_rollback(struct smtp_session *s, const char *args)
-{
- struct smtp_tx *tx;
-
- tx = s->tx;
-
- fclose(tx->ofile);
- tx->ofile = NULL;
-
- smtp_tx_rollback(tx);
- smtp_tx_free(tx);
- smtp_enter_state(s, STATE_HELO);
-}
-
-static void
-smtp_rfc4954_auth_plain(struct smtp_session *s, char *arg)
-{
- char buf[1024], *user, *pass;
- int len;
-
- switch (s->state) {
- case STATE_HELO:
- if (arg == NULL) {
- smtp_enter_state(s, STATE_AUTH_INIT);
- smtp_reply(s, "334 ");
- return;
- }
- smtp_enter_state(s, STATE_AUTH_INIT);
- /* FALLTHROUGH */
-
- case STATE_AUTH_INIT:
- /* String is not NUL terminated, leave room. */
- if ((len = base64_decode(arg, (unsigned char *)buf,
- sizeof(buf) - 1)) == -1)
- goto abort;
- /* buf is a byte string, NUL terminate. */
- buf[len] = '\0';
-
- /*
- * Skip "foo" in "foo\0user\0pass", if present.
- */
- user = memchr(buf, '\0', len);
- if (user == NULL || user >= buf + len - 2)
- goto abort;
- user++; /* skip NUL */
- if (strlcpy(s->username, user, sizeof(s->username))
- >= sizeof(s->username))
- goto abort;
-
- pass = memchr(user, '\0', len - (user - buf));
- if (pass == NULL || pass >= buf + len - 2)
- goto abort;
- pass++; /* skip NUL */
-
- m_create(p_lka, IMSG_SMTP_AUTHENTICATE, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_string(p_lka, s->listener->authtable);
- m_add_string(p_lka, user);
- m_add_string(p_lka, pass);
- m_close(p_lka);
- tree_xset(&wait_parent_auth, s->id, s);
- return;
-
- default:
- fatal("smtp_rfc4954_auth_plain: unknown state");
- }
-
-abort:
- smtp_reply(s, "501 %s %s: Syntax error",
- esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR),
- esc_description(ESC_SYNTAX_ERROR));
- smtp_enter_state(s, STATE_HELO);
-}
-
-static void
-smtp_rfc4954_auth_login(struct smtp_session *s, char *arg)
-{
- char buf[LINE_MAX];
-
- switch (s->state) {
- case STATE_HELO:
- smtp_enter_state(s, STATE_AUTH_USERNAME);
- if (arg != NULL && *arg != '\0') {
- smtp_rfc4954_auth_login(s, arg);
- return;
- }
- smtp_reply(s, "334 VXNlcm5hbWU6");
- return;
-
- case STATE_AUTH_USERNAME:
- memset(s->username, 0, sizeof(s->username));
- if (base64_decode(arg, (unsigned char *)s->username,
- sizeof(s->username) - 1) == -1)
- goto abort;
-
- smtp_enter_state(s, STATE_AUTH_PASSWORD);
- smtp_reply(s, "334 UGFzc3dvcmQ6");
- return;
-
- case STATE_AUTH_PASSWORD:
- memset(buf, 0, sizeof(buf));
- if (base64_decode(arg, (unsigned char *)buf,
- sizeof(buf)-1) == -1)
- goto abort;
-
- m_create(p_lka, IMSG_SMTP_AUTHENTICATE, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_string(p_lka, s->listener->authtable);
- m_add_string(p_lka, s->username);
- m_add_string(p_lka, buf);
- m_close(p_lka);
- tree_xset(&wait_parent_auth, s->id, s);
- return;
-
- default:
- fatal("smtp_rfc4954_auth_login: unknown state");
- }
-
-abort:
- smtp_reply(s, "501 %s %s: Syntax error",
- esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR),
- esc_description(ESC_SYNTAX_ERROR));
- smtp_enter_state(s, STATE_HELO);
-}
-
-static void
-smtp_lookup_servername(struct smtp_session *s)
-{
- if (s->listener->hostnametable[0]) {
- m_create(p_lka, IMSG_SMTP_LOOKUP_HELO, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_string(p_lka, s->listener->hostnametable);
- m_add_sockaddr(p_lka, (struct sockaddr*)&s->listener->ss);
- m_close(p_lka);
- tree_xset(&wait_lka_helo, s->id, s);
- return;
- }
-
- smtp_connected(s);
-}
-
-static void
-smtp_connected(struct smtp_session *s)
-{
- smtp_enter_state(s, STATE_CONNECTED);
-
- log_info("%016"PRIx64" smtp connected address=%s host=%s",
- s->id, ss_to_text(&s->ss), s->rdns);
-
- smtp_filter_begin(s);
-
- smtp_report_link_connect(s, s->rdns, s->fcrdns, &s->ss,
- &s->listener->ss);
-
- smtp_filter_phase(FILTER_CONNECT, s, ss_to_text(&s->ss));
-}
-
-static void
-smtp_proceed_connected(struct smtp_session *s)
-{
- if (s->listener->flags & F_SMTPS)
- smtp_cert_init(s);
- else
- smtp_send_banner(s);
-}
-
-static void
-smtp_send_banner(struct smtp_session *s)
-{
- smtp_reply(s, "220 %s ESMTP %s", s->smtpname, SMTPD_NAME);
- s->banner_sent = 1;
- smtp_report_link_greeting(s, s->smtpname);
-}
-
-void
-smtp_enter_state(struct smtp_session *s, int newstate)
-{
- log_trace(TRACE_SMTP, "smtp: %p: %s -> %s", s,
- smtp_strstate(s->state),
- smtp_strstate(newstate));
-
- s->state = newstate;
-}
-
-static void
-smtp_reply(struct smtp_session *s, char *fmt, ...)
-{
- va_list ap;
- int n;
- char buf[LINE_MAX*2], tmp[LINE_MAX*2];
-
- va_start(ap, fmt);
- n = vsnprintf(buf, sizeof buf, fmt, ap);
- va_end(ap);
- if (n < 0)
- fatalx("smtp_reply: response format error");
- if (n < 4)
- fatalx("smtp_reply: response too short");
- if (n >= (int)sizeof buf) {
- /* only first three bytes are used by SMTP logic,
- * so if _our_ reply does not fit entirely in the
- * buffer, it's ok to truncate.
- */
- }
-
- log_trace(TRACE_SMTP, "smtp: %p: >>> %s", s, buf);
- smtp_report_protocol_server(s, buf);
-
- switch (buf[0]) {
- case '2':
- if (s->tx) {
- if (s->last_cmd == CMD_MAIL_FROM) {
- smtp_report_tx_begin(s, s->tx->msgid);
- smtp_report_tx_mail(s, s->tx->msgid, s->cmd + 10, 1);
- }
- else if (s->last_cmd == CMD_RCPT_TO)
- smtp_report_tx_rcpt(s, s->tx->msgid, s->cmd + 8, 1);
- }
- break;
- case '3':
- if (s->tx) {
- if (s->last_cmd == CMD_DATA)
- smtp_report_tx_data(s, s->tx->msgid, 1);
- }
- break;
- case '5':
- case '4':
- /* do not report smtp_tx_mail/smtp_tx_rcpt errors
- * if they happened outside of a transaction.
- */
- if (s->tx) {
- if (s->last_cmd == CMD_MAIL_FROM)
- smtp_report_tx_mail(s, s->tx->msgid,
- s->cmd + 10, buf[0] == '4' ? -1 : 0);
- else if (s->last_cmd == CMD_RCPT_TO)
- smtp_report_tx_rcpt(s,
- s->tx->msgid, s->cmd + 8, buf[0] == '4' ? -1 : 0);
- else if (s->last_cmd == CMD_DATA && s->tx->rcptcount)
- smtp_report_tx_data(s, s->tx->msgid,
- buf[0] == '4' ? -1 : 0);
- }
-
- if (s->flags & SF_BADINPUT) {
- log_info("%016"PRIx64" smtp "
- "bad-input result=\"%.*s\"",
- s->id, n, buf);
- }
- else if (s->state == STATE_AUTH_INIT) {
- log_info("%016"PRIx64" smtp "
- "failed-command "
- "command=\"AUTH PLAIN (...)\" result=\"%.*s\"",
- s->id, n, buf);
- }
- else if (s->state == STATE_AUTH_USERNAME) {
- log_info("%016"PRIx64" smtp "
- "failed-command "
- "command=\"AUTH LOGIN (username)\" result=\"%.*s\"",
- s->id, n, buf);
- }
- else if (s->state == STATE_AUTH_PASSWORD) {
- log_info("%016"PRIx64" smtp "
- "failed-command "
- "command=\"AUTH LOGIN (password)\" result=\"%.*s\"",
- s->id, n, buf);
- }
- else {
- strnvis(tmp, s->cmd, sizeof tmp, VIS_SAFE | VIS_CSTYLE);
- log_info("%016"PRIx64" smtp "
- "failed-command command=\"%s\" "
- "result=\"%.*s\"",
- s->id, tmp, n, buf);
- }
- break;
- }
-
- io_xprintf(s->io, "%s\r\n", buf);
-}
-
-static void
-smtp_free(struct smtp_session *s, const char * reason)
-{
- if (s->tx) {
- if (s->tx->msgid)
- smtp_tx_rollback(s->tx);
- smtp_tx_free(s->tx);
- }
-
- smtp_report_link_disconnect(s);
- smtp_filter_end(s);
-
- if (s->flags & SF_SECURE && s->listener->flags & F_SMTPS)
- stat_decrement("smtp.smtps", 1);
- if (s->flags & SF_SECURE && s->listener->flags & F_STARTTLS)
- stat_decrement("smtp.tls", 1);
-
- io_free(s->io);
- free(s);
-
- smtp_collect();
-}
-
-static int
-smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args,
- const char *domain)
-{
- char *p, *e;
-
- if (line == NULL)
- return (0);
-
- if (*line != '<')
- return (0);
-
- e = strchr(line, '>');
- if (e == NULL)
- return (0);
- *e++ = '\0';
- while (*e == ' ')
- e++;
- *args = e;
-
- if (!text_to_mailaddr(maddr, line + 1))
- return (0);
-
- p = strchr(maddr->user, ':');
- if (p != NULL) {
- p++;
- memmove(maddr->user, p, strlen(p) + 1);
- }
-
- /* accept empty return-path in MAIL FROM, required for bounces */
- if (mailfrom && maddr->user[0] == '\0' && maddr->domain[0] == '\0')
- return (1);
-
- /* no or invalid user-part, reject */
- if (maddr->user[0] == '\0' || !valid_localpart(maddr->user))
- return (0);
-
- /* no domain part, local user */
- if (maddr->domain[0] == '\0') {
- (void)strlcpy(maddr->domain, domain,
- sizeof(maddr->domain));
- }
-
- if (!valid_domainpart(maddr->domain))
- return (0);
-
- return (1);
-}
-
-static void
-smtp_cert_init(struct smtp_session *s)
-{
- const char *name;
- int fallback;
-
- if (s->listener->pki_name[0]) {
- name = s->listener->pki_name;
- fallback = 0;
- }
- else {
- name = s->smtpname;
- fallback = 1;
- }
-
- if (cert_init(name, fallback, smtp_cert_init_cb, s))
- tree_xset(&wait_ssl_init, s->id, s);
-}
-
-static void
-smtp_cert_init_cb(void *arg, int status, const char *name, const void *cert,
- size_t cert_len)
-{
- struct smtp_session *s = arg;
- void *ssl, *ssl_ctx;
-
- tree_pop(&wait_ssl_init, s->id);
-
- if (status == CA_FAIL) {
- log_info("%016"PRIx64" smtp disconnected "
- "reason=ca-failure",
- s->id);
- smtp_free(s, "CA failure");
- return;
- }
-
- ssl_ctx = dict_get(env->sc_ssl_dict, name);
- ssl = ssl_smtp_init(ssl_ctx, s->listener->flags & F_TLS_VERIFY);
- io_set_read(s->io);
- io_start_tls(s->io, ssl);
-}
-
-static void
-smtp_cert_verify(struct smtp_session *s)
-{
- const char *name;
- int fallback;
-
- if (s->listener->ca_name[0]) {
- name = s->listener->ca_name;
- fallback = 0;
- }
- else {
- name = s->smtpname;
- fallback = 1;
- }
-
- if (cert_verify(io_tls(s->io), name, fallback, smtp_cert_verify_cb, s)) {
- tree_xset(&wait_ssl_verify, s->id, s);
- io_pause(s->io, IO_IN);
- }
-}
-
-static void
-smtp_cert_verify_cb(void *arg, int status)
-{
- struct smtp_session *s = arg;
- const char *reason = NULL;
- int resume;
-
- resume = tree_pop(&wait_ssl_verify, s->id) != NULL;
-
- switch (status) {
- case CERT_OK:
- reason = "cert-ok";
- s->flags |= SF_VERIFIED;
- break;
- case CERT_NOCA:
- reason = "no-ca";
- break;
- case CERT_NOCERT:
- reason = "no-client-cert";
- break;
- case CERT_INVALID:
- reason = "cert-invalid";
- break;
- default:
- reason = "cert-check-failed";
- break;
- }
-
- log_debug("smtp: %p: smtp_cert_verify_cb: %s", s, reason);
-
- if (!(s->flags & SF_VERIFIED) && (s->listener->flags & F_TLS_VERIFY)) {
- log_info("%016"PRIx64" smtp disconnected "
- " reason=%s", s->id,
- reason);
- smtp_free(s, "SSL certificate check failed");
- return;
- }
-
- smtp_tls_verified(s);
- if (resume)
- io_resume(s->io, IO_IN);
-}
-
-static void
-smtp_auth_failure_resume(int fd, short event, void *p)
-{
- struct smtp_session *s = p;
-
- smtp_reply(s, "535 Authentication failed");
- smtp_enter_state(s, STATE_HELO);
-}
-
-static void
-smtp_auth_failure_pause(struct smtp_session *s)
-{
- struct timeval tv;
-
- tv.tv_sec = 0;
- tv.tv_usec = arc4random_uniform(1000000);
- log_trace(TRACE_SMTP, "smtp: timing-attack protection triggered, "
- "will defer answer for %lu microseconds", tv.tv_usec);
- evtimer_set(&s->pause, smtp_auth_failure_resume, s);
- evtimer_add(&s->pause, &tv);
-}
-
-static int
-smtp_tx(struct smtp_session *s)
-{
- struct smtp_tx *tx;
-
- tx = calloc(1, sizeof(*tx));
- if (tx == NULL)
- return 0;
-
- TAILQ_INIT(&tx->rcpts);
-
- s->tx = tx;
- tx->session = s;
-
- /* setup the envelope */
- tx->evp.ss = s->ss;
- (void)strlcpy(tx->evp.tag, s->listener->tag, sizeof(tx->evp.tag));
- (void)strlcpy(tx->evp.smtpname, s->smtpname, sizeof(tx->evp.smtpname));
- (void)strlcpy(tx->evp.hostname, s->rdns, sizeof tx->evp.hostname);
- (void)strlcpy(tx->evp.helo, s->helo, sizeof(tx->evp.helo));
- (void)strlcpy(tx->evp.username, s->username, sizeof(tx->evp.username));
-
- if (s->flags & SF_BOUNCE)
- tx->evp.flags |= EF_BOUNCE;
- if (s->flags & SF_AUTHENTICATED)
- tx->evp.flags |= EF_AUTHENTICATED;
-
- if ((tx->parser = rfc5322_parser_new()) == NULL) {
- free(tx);
- return 0;
- }
-
- return 1;
-}
-
-static void
-smtp_tx_free(struct smtp_tx *tx)
-{
- struct smtp_rcpt *rcpt;
-
- rfc5322_free(tx->parser);
-
- while ((rcpt = TAILQ_FIRST(&tx->rcpts))) {
- TAILQ_REMOVE(&tx->rcpts, rcpt, entry);
- free(rcpt);
- }
-
- if (tx->ofile)
- fclose(tx->ofile);
-
- tx->session->tx = NULL;
-
- free(tx);
-}
-
-static void
-smtp_tx_mail_from(struct smtp_tx *tx, const char *line)
-{
- char *opt;
- char *copy;
- char tmp[SMTP_LINE_MAX];
-
- (void)strlcpy(tmp, line, sizeof tmp);
- copy = tmp;
-
- if (smtp_mailaddr(&tx->evp.sender, copy, 1, &copy,
- tx->session->smtpname) == 0) {
- smtp_reply(tx->session, "553 %s Sender address syntax error",
- esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS));
- smtp_tx_free(tx);
- return;
- }
-
- while ((opt = strsep(&copy, " "))) {
- if (*opt == '\0')
- continue;
-
- if (strncasecmp(opt, "AUTH=", 5) == 0)
- log_debug("debug: smtp: AUTH in MAIL FROM command");
- else if (strncasecmp(opt, "SIZE=", 5) == 0)
- log_debug("debug: smtp: SIZE in MAIL FROM command");
- else if (strcasecmp(opt, "BODY=7BIT") == 0)
- /* XXX only for this transaction */
- tx->session->flags &= ~SF_8BITMIME;
- else if (strcasecmp(opt, "BODY=8BITMIME") == 0)
- ;
- else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "RET=", 4) == 0) {
- opt += 4;
- if (strcasecmp(opt, "HDRS") == 0)
- tx->evp.dsn_ret = DSN_RETHDRS;
- else if (strcasecmp(opt, "FULL") == 0)
- tx->evp.dsn_ret = DSN_RETFULL;
- } else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "ENVID=", 6) == 0) {
- opt += 6;
- if (strlcpy(tx->evp.dsn_envid, opt, sizeof(tx->evp.dsn_envid))
- >= sizeof(tx->evp.dsn_envid)) {
- smtp_reply(tx->session,
- "503 %s %s: option too large, truncated: %s",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS), opt);
- smtp_tx_free(tx);
- return;
- }
- } else {
- smtp_reply(tx->session, "503 %s %s: Unsupported option %s",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS), opt);
- smtp_tx_free(tx);
- return;
- }
- }
-
- /* only check sendertable if defined and user has authenticated */
- if (tx->session->flags & SF_AUTHENTICATED &&
- tx->session->listener->sendertable[0]) {
- m_create(p_lka, IMSG_SMTP_CHECK_SENDER, 0, 0, -1);
- m_add_id(p_lka, tx->session->id);
- m_add_string(p_lka, tx->session->listener->sendertable);
- m_add_string(p_lka, tx->session->username);
- m_add_mailaddr(p_lka, &tx->evp.sender);
- m_close(p_lka);
- tree_xset(&wait_lka_mail, tx->session->id, tx->session);
- }
- else
- smtp_tx_create_message(tx);
-}
-
-static void
-smtp_tx_create_message(struct smtp_tx *tx)
-{
- m_create(p_queue, IMSG_SMTP_MESSAGE_CREATE, 0, 0, -1);
- m_add_id(p_queue, tx->session->id);
- m_close(p_queue);
- tree_xset(&wait_queue_msg, tx->session->id, tx->session);
-}
-
-static void
-smtp_tx_rcpt_to(struct smtp_tx *tx, const char *line)
-{
- char *opt, *p;
- char *copy;
- char tmp[SMTP_LINE_MAX];
-
- (void)strlcpy(tmp, line, sizeof tmp);
- copy = tmp;
-
- if (tx->rcptcount >= env->sc_session_max_rcpt) {
- smtp_reply(tx->session, "451 %s %s: Too many recipients",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
- esc_description(ESC_TOO_MANY_RECIPIENTS));
- return;
- }
-
- if (smtp_mailaddr(&tx->evp.rcpt, copy, 0, &copy,
- tx->session->smtpname) == 0) {
- smtp_reply(tx->session,
- "501 %s Recipient address syntax error",
- esc_code(ESC_STATUS_PERMFAIL,
- ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX));
- return;
- }
-
- while ((opt = strsep(&copy, " "))) {
- if (*opt == '\0')
- continue;
-
- if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "NOTIFY=", 7) == 0) {
- opt += 7;
- while ((p = strsep(&opt, ","))) {
- if (strcasecmp(p, "SUCCESS") == 0)
- tx->evp.dsn_notify |= DSN_SUCCESS;
- else if (strcasecmp(p, "FAILURE") == 0)
- tx->evp.dsn_notify |= DSN_FAILURE;
- else if (strcasecmp(p, "DELAY") == 0)
- tx->evp.dsn_notify |= DSN_DELAY;
- else if (strcasecmp(p, "NEVER") == 0)
- tx->evp.dsn_notify |= DSN_NEVER;
- }
-
- if (tx->evp.dsn_notify & DSN_NEVER &&
- tx->evp.dsn_notify & (DSN_SUCCESS | DSN_FAILURE |
- DSN_DELAY)) {
- smtp_reply(tx->session,
- "553 NOTIFY option NEVER cannot be"
- " combined with other options");
- return;
- }
- } else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "ORCPT=", 6) == 0) {
- opt += 6;
-
- if (strncasecmp(opt, "rfc822;", 7) == 0)
- opt += 7;
-
- if (!text_to_mailaddr(&tx->evp.dsn_orcpt, opt) ||
- !valid_localpart(tx->evp.dsn_orcpt.user) ||
- !valid_domainpart(tx->evp.dsn_orcpt.domain)) {
- smtp_reply(tx->session,
- "553 ORCPT address syntax error");
- return;
- }
- } else {
- smtp_reply(tx->session, "503 Unsupported option %s", opt);
- return;
- }
- }
-
- m_create(p_lka, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1);
- m_add_id(p_lka, tx->session->id);
- m_add_envelope(p_lka, &tx->evp);
- m_close(p_lka);
- tree_xset(&wait_lka_rcpt, tx->session->id, tx->session);
-}
-
-static void
-smtp_tx_open_message(struct smtp_tx *tx)
-{
- m_create(p_queue, IMSG_SMTP_MESSAGE_OPEN, 0, 0, -1);
- m_add_id(p_queue, tx->session->id);
- m_add_msgid(p_queue, tx->msgid);
- m_close(p_queue);
- tree_xset(&wait_queue_fd, tx->session->id, tx->session);
-}
-
-static void
-smtp_tx_commit(struct smtp_tx *tx)
-{
- m_create(p_queue, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1);
- m_add_id(p_queue, tx->session->id);
- m_add_msgid(p_queue, tx->msgid);
- m_close(p_queue);
- tree_xset(&wait_queue_commit, tx->session->id, tx->session);
- smtp_filter_data_end(tx->session);
-}
-
-static void
-smtp_tx_rollback(struct smtp_tx *tx)
-{
- m_create(p_queue, IMSG_SMTP_MESSAGE_ROLLBACK, 0, 0, -1);
- m_add_msgid(p_queue, tx->msgid);
- m_close(p_queue);
- smtp_report_tx_rollback(tx->session, tx->msgid);
- smtp_report_tx_reset(tx->session, tx->msgid);
- smtp_filter_data_end(tx->session);
-}
-
-static int
-smtp_tx_dataline(struct smtp_tx *tx, const char *line)
-{
- struct rfc5322_result res;
- int r;
-
- log_trace(TRACE_SMTP, "<<< [MSG] %s", line);
-
- if (!strcmp(line, ".")) {
- smtp_report_protocol_client(tx->session, ".");
- log_trace(TRACE_SMTP, "<<< [EOM]");
- if (tx->error)
- return 1;
- line = NULL;
- }
- else {
- /* ignore data line if an error is set */
- if (tx->error)
- return 0;
-
- /* escape lines starting with a '.' */
- if (line[0] == '.')
- line += 1;
- }
-
- if (rfc5322_push(tx->parser, line) == -1) {
- log_warnx("failed to push dataline");
- tx->error = TX_ERROR_INTERNAL;
- return 0;
- }
-
- for(;;) {
- r = rfc5322_next(tx->parser, &res);
- switch (r) {
- case -1:
- if (errno == ENOMEM)
- tx->error = TX_ERROR_INTERNAL;
- else
- tx->error = TX_ERROR_MALFORMED;
- return 0;
-
- case RFC5322_NONE:
- /* Need more data */
- return 0;
-
- case RFC5322_HEADER_START:
- /* ignore bcc */
- if (!strcasecmp("Bcc", res.hdr))
- continue;
-
- if (!strcasecmp("To", res.hdr) ||
- !strcasecmp("Cc", res.hdr) ||
- !strcasecmp("From", res.hdr)) {
- rfc5322_unfold_header(tx->parser);
- continue;
- }
-
- if (!strcasecmp("Received", res.hdr)) {
- if (++tx->rcvcount >= MAX_HOPS_COUNT) {
- log_warnx("warn: loop detected");
- tx->error = TX_ERROR_LOOP;
- return 0;
- }
- }
- else if (!tx->has_date && !strcasecmp("Date", res.hdr))
- tx->has_date = 1;
- else if (!tx->has_message_id &&
- !strcasecmp("Message-Id", res.hdr))
- tx->has_message_id = 1;
-
- smtp_message_printf(tx, "%s:%s\n", res.hdr, res.value);
- break;
-
- case RFC5322_HEADER_CONT:
-
- if (!strcasecmp("Bcc", res.hdr) ||
- !strcasecmp("To", res.hdr) ||
- !strcasecmp("Cc", res.hdr) ||
- !strcasecmp("From", res.hdr))
- continue;
-
- smtp_message_printf(tx, "%s\n", res.value);
- break;
-
- case RFC5322_HEADER_END:
- if (!strcasecmp("To", res.hdr) ||
- !strcasecmp("Cc", res.hdr) ||
- !strcasecmp("From", res.hdr))
- header_domain_append_callback(tx, res.hdr,
- res.value);
- break;
-
- case RFC5322_END_OF_HEADERS:
- if (tx->session->listener->local ||
- tx->session->listener->port == 587) {
-
- if (!tx->has_date) {
- log_debug("debug: %p: adding Date", tx);
- smtp_message_printf(tx, "Date: %s\n",
- time_to_text(tx->time));
- }
-
- if (!tx->has_message_id) {
- log_debug("debug: %p: adding Message-ID", tx);
- smtp_message_printf(tx,
- "Message-ID: <%016"PRIx64"@%s>\n",
- generate_uid(),
- tx->session->listener->hostname);
- }
- }
- break;
-
- case RFC5322_BODY_START:
- case RFC5322_BODY:
- smtp_message_printf(tx, "%s\n", res.value);
- break;
-
- case RFC5322_END_OF_MESSAGE:
- return 1;
-
- default:
- fatalx("%s", __func__);
- }
- }
-}
-
-static int
-smtp_tx_filtered_dataline(struct smtp_tx *tx, const char *line)
-{
- if (!strcmp(line, "."))
- line = NULL;
- else {
- /* ignore data line if an error is set */
- if (tx->error)
- return 0;
- }
- io_printf(tx->filter, "%s\n", line ? line : ".");
- return line ? 0 : 1;
-}
-
-static void
-smtp_tx_eom(struct smtp_tx *tx)
-{
- smtp_filter_phase(FILTER_COMMIT, tx->session, NULL);
-}
-
-static int
-smtp_message_fd(struct smtp_tx *tx, int fd)
-{
- struct smtp_session *s;
-
- s = tx->session;
-
- log_debug("smtp: %p: message fd %d", s, fd);
-
- if ((tx->ofile = fdopen(fd, "w")) == NULL) {
- close(fd);
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- return 0;
- }
- return 1;
-}
-
-static void
-filter_session_io(struct io *io, int evt, void *arg)
-{
- struct smtp_tx*tx = arg;
- char*line = NULL;
- ssize_t len;
-
- log_trace(TRACE_IO, "filter session io (smtp): %p: %s %s", tx, io_strevent(evt),
- io_strio(io));
-
- switch (evt) {
- case IO_DATAIN:
- nextline:
- line = io_getline(tx->filter, &len);
- /* No complete line received */
- if (line == NULL)
- return;
-
- if (smtp_tx_dataline(tx, line)) {
- smtp_tx_eom(tx);
- return;
- }
-
- goto nextline;
- }
-}
-
-static void
-smtp_filter_fd(struct smtp_tx *tx, int fd)
-{
- struct smtp_session *s;
-
- s = tx->session;
-
- log_debug("smtp: %p: filter fd %d", s, fd);
-
- tx->filter = io_new();
- io_set_fd(tx->filter, fd);
- io_set_callback(tx->filter, filter_session_io, tx);
-}
-
-static void
-smtp_message_begin(struct smtp_tx *tx)
-{
- struct smtp_session *s;
- X509 *x;
- int (*m_printf)(struct smtp_tx *, const char *, ...);
-
- m_printf = smtp_message_printf;
- if (tx->filter)
- m_printf = smtp_filter_printf;
-
- s = tx->session;
-
- log_debug("smtp: %p: message begin", s);
-
- smtp_reply(s, "354 Enter mail, end with \".\""
- " on a line by itself");
-
- if (s->junk || (s->tx && s->tx->junk))
- m_printf(tx, "X-Spam: Yes\n");
-
- m_printf(tx, "Received: ");
- if (!(s->listener->flags & F_MASK_SOURCE)) {
- m_printf(tx, "from %s (%s %s%s%s)",
- s->helo,
- s->rdns,
- s->ss.ss_family == AF_INET6 ? "" : "[",
- ss_to_text(&s->ss),
- s->ss.ss_family == AF_INET6 ? "" : "]");
- }
- m_printf(tx, "\n\tby %s (%s) with %sSMTP%s%s id %08x",
- s->smtpname,
- SMTPD_NAME,
- s->flags & SF_EHLO ? "E" : "",
- s->flags & SF_SECURE ? "S" : "",
- s->flags & SF_AUTHENTICATED ? "A" : "",
- tx->msgid);
-
- if (s->flags & SF_SECURE) {
- x = SSL_get_peer_certificate(io_tls(s->io));
- m_printf(tx, " (%s:%s:%d:%s)",
- SSL_get_version(io_tls(s->io)),
- SSL_get_cipher_name(io_tls(s->io)),
- SSL_get_cipher_bits(io_tls(s->io), NULL),
- (s->flags & SF_VERIFIED) ? "YES" : (x ? "FAIL" : "NO"));
- X509_free(x);
-
- if (s->listener->flags & F_RECEIVEDAUTH) {
- m_printf(tx, " auth=%s",
- s->username[0] ? "yes" : "no");
- if (s->username[0])
- m_printf(tx, " user=%s", s->username);
- }
- }
-
- if (tx->rcptcount == 1) {
- m_printf(tx, "\n\tfor <%s@%s>",
- tx->evp.rcpt.user,
- tx->evp.rcpt.domain);
- }
-
- m_printf(tx, ";\n\t%s\n", time_to_text(time(&tx->time)));
-
- smtp_enter_state(s, STATE_BODY);
-}
-
-static void
-smtp_message_end(struct smtp_tx *tx)
-{
- struct smtp_session *s;
-
- s = tx->session;
-
- log_debug("debug: %p: end of message, error=%d", s, tx->error);
-
- fclose(tx->ofile);
- tx->ofile = NULL;
-
- switch(tx->error) {
- case TX_OK:
- smtp_tx_commit(tx);
- return;
-
- case TX_ERROR_SIZE:
- smtp_reply(s, "554 %s %s: Transaction failed, message too big",
- esc_code(ESC_STATUS_PERMFAIL, ESC_MESSAGE_TOO_BIG_FOR_SYSTEM),
- esc_description(ESC_MESSAGE_TOO_BIG_FOR_SYSTEM));
- break;
-
- case TX_ERROR_LOOP:
- smtp_reply(s, "500 %s %s: Loop detected",
- esc_code(ESC_STATUS_PERMFAIL, ESC_ROUTING_LOOP_DETECTED),
- esc_description(ESC_ROUTING_LOOP_DETECTED));
- break;
-
- case TX_ERROR_MALFORMED:
- smtp_reply(s, "550 %s %s: Message is not RFC 2822 compliant",
- esc_code(ESC_STATUS_PERMFAIL, ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED),
- esc_description(ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED));
- break;
-
- case TX_ERROR_IO:
- case TX_ERROR_RESOURCES:
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- break;
-
- default:
- /* fatal? */
- smtp_reply(s, "421 Internal server error");
- }
-
- smtp_tx_rollback(tx);
- smtp_tx_free(tx);
- smtp_enter_state(s, STATE_HELO);
-}
-
-static int
-smtp_filter_printf(struct smtp_tx *tx, const char *fmt, ...)
-{
- va_list ap;
- int len;
-
- if (tx->error)
- return -1;
-
- va_start(ap, fmt);
- len = io_vprintf(tx->filter, fmt, ap);
- va_end(ap);
-
- if (len < 0) {
- log_warn("smtp-in: session %016"PRIx64": vfprintf", tx->session->id);
- tx->error = TX_ERROR_IO;
- }
- else
- tx->odatalen += len;
-
- return len;
-}
-
-static int
-smtp_message_printf(struct smtp_tx *tx, const char *fmt, ...)
-{
- va_list ap;
- int len;
-
- if (tx->error)
- return -1;
-
- va_start(ap, fmt);
- len = vfprintf(tx->ofile, fmt, ap);
- va_end(ap);
-
- if (len == -1) {
- log_warn("smtp-in: session %016"PRIx64": vfprintf", tx->session->id);
- tx->error = TX_ERROR_IO;
- }
- else
- tx->odatalen += len;
-
- return len;
-}
-
-#define CASE(x) case x : return #x
-
-const char *
-smtp_strstate(int state)
-{
- static char buf[32];
-
- switch (state) {
- CASE(STATE_NEW);
- CASE(STATE_CONNECTED);
- CASE(STATE_TLS);
- CASE(STATE_HELO);
- CASE(STATE_AUTH_INIT);
- CASE(STATE_AUTH_USERNAME);
- CASE(STATE_AUTH_PASSWORD);
- CASE(STATE_AUTH_FINALIZE);
- CASE(STATE_BODY);
- CASE(STATE_QUIT);
- default:
- (void)snprintf(buf, sizeof(buf), "STATE_??? (%d)", state);
- return (buf);
- }
-}
-
-
-static void
-smtp_report_link_connect(struct smtp_session *s, const char *rdns, int fcrdns,
- const struct sockaddr_storage *ss_src,
- const struct sockaddr_storage *ss_dest)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_connect("smtp-in", s->id, rdns, fcrdns, ss_src, ss_dest);
-}
-
-static void
-smtp_report_link_greeting(struct smtp_session *s,
- const char *domain)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_greeting("smtp-in", s->id, domain);
-}
-
-static void
-smtp_report_link_identify(struct smtp_session *s, const char *method, const char *identity)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_identify("smtp-in", s->id, method, identity);
-}
-
-static void
-smtp_report_link_tls(struct smtp_session *s, const char *ssl)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_tls("smtp-in", s->id, ssl);
-}
-
-static void
-smtp_report_link_disconnect(struct smtp_session *s)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_disconnect("smtp-in", s->id);
-}
-
-static void
-smtp_report_link_auth(struct smtp_session *s, const char *user, const char *result)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_auth("smtp-in", s->id, user, result);
-}
-
-static void
-smtp_report_tx_reset(struct smtp_session *s, uint32_t msgid)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_reset("smtp-in", s->id, msgid);
-}
-
-static void
-smtp_report_tx_begin(struct smtp_session *s, uint32_t msgid)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_begin("smtp-in", s->id, msgid);
-}
-
-static void
-smtp_report_tx_mail(struct smtp_session *s, uint32_t msgid, const char *address, int ok)
-{
- char mailaddr[SMTPD_MAXMAILADDRSIZE];
- char *p;
-
- if (! SESSION_FILTERED(s))
- return;
-
- if ((p = strchr(address, '<')) == NULL)
- return;
- (void)strlcpy(mailaddr, p + 1, sizeof mailaddr);
- if ((p = strchr(mailaddr, '>')) == NULL)
- return;
- *p = '\0';
-
- report_smtp_tx_mail("smtp-in", s->id, msgid, mailaddr, ok);
-}
-
-static void
-smtp_report_tx_rcpt(struct smtp_session *s, uint32_t msgid, const char *address, int ok)
-{
- char mailaddr[SMTPD_MAXMAILADDRSIZE];
- char *p;
-
- if (! SESSION_FILTERED(s))
- return;
-
- if ((p = strchr(address, '<')) == NULL)
- return;
- (void)strlcpy(mailaddr, p + 1, sizeof mailaddr);
- if ((p = strchr(mailaddr, '>')) == NULL)
- return;
- *p = '\0';
-
- report_smtp_tx_rcpt("smtp-in", s->id, msgid, mailaddr, ok);
-}
-
-static void
-smtp_report_tx_envelope(struct smtp_session *s, uint32_t msgid, uint64_t evpid)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_envelope("smtp-in", s->id, msgid, evpid);
-}
-
-static void
-smtp_report_tx_data(struct smtp_session *s, uint32_t msgid, int ok)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_data("smtp-in", s->id, msgid, ok);
-}
-
-static void
-smtp_report_tx_commit(struct smtp_session *s, uint32_t msgid, size_t msgsz)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_commit("smtp-in", s->id, msgid, msgsz);
-}
-
-static void
-smtp_report_tx_rollback(struct smtp_session *s, uint32_t msgid)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_rollback("smtp-in", s->id, msgid);
-}
-
-static void
-smtp_report_protocol_client(struct smtp_session *s, const char *command)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_protocol_client("smtp-in", s->id, command);
-}
-
-static void
-smtp_report_protocol_server(struct smtp_session *s, const char *response)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_protocol_server("smtp-in", s->id, response);
-}
-
-static void
-smtp_report_filter_response(struct smtp_session *s, int phase, int response, const char *param)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_filter_response("smtp-in", s->id, phase, response, param);
-}
-
-static void
-smtp_report_timeout(struct smtp_session *s)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_timeout("smtp-in", s->id);
-}
diff --git a/smtpd/smtpc.c b/smtpd/smtpc.c
deleted file mode 100644
index 59479703..00000000
--- a/smtpd/smtpc.c
+++ /dev/null
@@ -1,465 +0,0 @@
-/* $OpenBSD: smtpc.c,v 1.10 2019/09/21 09:04:08 semarie Exp $ */
-
-/*
- * Copyright (c) 2018 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <event.h>
-#include <limits.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <resolv.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include <openssl/ssl.h>
-
-#include "smtp.h"
-#include "ssl.h"
-#include "log.h"
-
-static void parse_server(char *);
-static void parse_message(FILE *);
-static void resume(void);
-
-static int verbose = 1;
-static int done = 0;
-static int noaction = 0;
-static struct addrinfo *res0, *ai;
-static struct smtp_params params;
-static struct smtp_mail mail;
-static const char *servname = NULL;
-
-static SSL_CTX *ssl_ctx;
-
-static void
-usage(void)
-{
- extern char *__progname;
-
- fprintf(stderr,
- "usage: %s [-Chnv] [-F from] [-H helo] [-s server] [-S name] rcpt ...\n",
- __progname);
- exit(1);
-}
-
-int
-main(int argc, char **argv)
-{
- char hostname[256];
- int ch, i;
- char *server = "localhost";
- struct passwd *pw;
-
- log_init(1, 0);
-
- if (gethostname(hostname, sizeof(hostname)) == -1)
- fatal("gethostname");
-
- if ((pw = getpwuid(getuid())) == NULL)
- fatal("getpwuid");
-
- memset(&params, 0, sizeof(params));
-
- params.linemax = 16392;
- params.ibufmax = 65536;
- params.obufmax = 65536;
- params.timeout = 100000;
- params.helo = hostname;
-
- params.tls_verify = 1;
-
- memset(&mail, 0, sizeof(mail));
- mail.from = pw->pw_name;
-
- while ((ch = getopt(argc, argv, "CF:H:S:hns:v")) != -1) {
- switch (ch) {
- case 'C':
- params.tls_verify = 0;
- break;
- case 'F':
- mail.from = optarg;
- break;
- case 'H':
- params.helo = optarg;
- break;
- case 'S':
- servname = optarg;
- break;
- case 'h':
- usage();
- break;
- case 'n':
- noaction = 1;
- break;
- case 's':
- server = optarg;
- break;
- case 'v':
- verbose++;
- break;
- default:
- usage();
- }
- }
-
- log_setverbose(verbose);
-
- argc -= optind;
- argv += optind;
-
- if (argc) {
- mail.rcpt = calloc(argc, sizeof(*mail.rcpt));
- if (mail.rcpt == NULL)
- fatal("calloc");
- for (i = 0; i < argc; i++)
- mail.rcpt[i].to = argv[i];
- mail.rcptcount = argc;
- }
-
- ssl_init();
- event_init();
-
- ssl_ctx = ssl_ctx_create(NULL, NULL, 0, NULL);
- if (!SSL_CTX_load_verify_locations(ssl_ctx,
- X509_get_default_cert_file(), NULL))
- fatal("SSL_CTX_load_verify_locations");
- if (!SSL_CTX_set_ssl_version(ssl_ctx, SSLv23_client_method()))
- fatal("SSL_CTX_set_ssl_version");
- SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE , NULL);
-
-#if HAVE_PLEDGE
- if (pledge("stdio inet dns tmppath", NULL) == -1)
- fatal("pledge");
-#endif
-
- if (!noaction)
- parse_message(stdin);
-
-#if HAVE_PLEDGE
- if (pledge("stdio inet dns", NULL) == -1)
- fatal("pledge");
-#endif
-
- parse_server(server);
-
-#if HAVE_PLEDGE
- if (pledge("stdio inet", NULL) == -1)
- fatal("pledge");
-#endif
-
- resume();
-
- log_debug("done...");
-
- return 0;
-}
-
-static void
-parse_server(char *server)
-{
- struct addrinfo hints;
- char *scheme, *creds, *host, *port, *p, *c;
- int error;
-
- creds = NULL;
- host = NULL;
- port = NULL;
- scheme = server;
-
- p = strstr(server, "://");
- if (p) {
- *p = '\0';
- p += 3;
- /* check for credentials */
- c = strchr(p, '@');
- if (c) {
- creds = p;
- *c = '\0';
- host = c + 1;
- } else
- host = p;
- } else {
- /* Assume a simple server name */
- scheme = "smtp";
- host = server;
- }
-
- if (host[0] == '[') {
- /* IPV6 address? */
- p = strchr(host, ']');
- if (p) {
- if (p[1] == ':' || p[1] == '\0') {
- *p++ = '\0'; /* remove ']' */
- host++; /* skip '[' */
- if (*p == ':')
- port = p + 1;
- }
- }
- }
- else {
- port = strchr(host, ':');
- if (port)
- *port++ = '\0';
- }
-
- if (port && port[0] == '\0')
- port = NULL;
-
- if (creds) {
- p = strchr(creds, ':');
- if (p == NULL)
- fatalx("invalid credentials");
- *p = '\0';
-
- params.auth_user = creds;
- params.auth_pass = p + 1;
- }
- params.tls_req = TLS_YES;
-
- if (!strcmp(scheme, "lmtp")) {
- params.lmtp = 1;
- }
- else if (!strcmp(scheme, "lmtp+tls")) {
- params.lmtp = 1;
- params.tls_req = TLS_FORCE;
- }
- else if (!strcmp(scheme, "lmtp+notls")) {
- params.lmtp = 1;
- params.tls_req = TLS_NO;
- }
- else if (!strcmp(scheme, "smtps")) {
- params.tls_req = TLS_SMTPS;
- if (port == NULL)
- port = "smtps";
- }
- else if (!strcmp(scheme, "smtp")) {
- }
- else if (!strcmp(scheme, "smtp+tls")) {
- params.tls_req = TLS_FORCE;
- }
- else if (!strcmp(scheme, "smtp+notls")) {
- params.tls_req = TLS_NO;
- }
- else
- fatalx("invalid url scheme %s", scheme);
-
- if (port == NULL)
- port = "smtp";
-
- if (servname == NULL)
- servname = host;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- error = getaddrinfo(host, port, &hints, &res0);
- if (error)
- fatalx("%s: %s", host, gai_strerror(error));
- ai = res0;
-}
-
-void
-parse_message(FILE *ifp)
-{
- char *line = NULL;
- size_t linesz = 0;
- ssize_t len;
-
- if ((mail.fp = tmpfile()) == NULL)
- fatal("tmpfile");
-
- for (;;) {
- if ((len = getline(&line, &linesz, ifp)) == -1) {
- if (feof(ifp))
- break;
- fatal("getline");
- }
-
- if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n')
- line[--len - 1] = '\n';
-
- if (fwrite(line, 1, len, mail.fp) != (size_t)len)
- fatal("fwrite");
-
- if (line[len - 1] != '\n' && fputc('\n', mail.fp) == EOF)
- fatal("fputc");
- }
-
- fclose(ifp);
- rewind(mail.fp);
-}
-
-void
-resume(void)
-{
- static int started = 0;
- char host[256];
- char serv[16];
-
- if (done) {
- event_loopexit(NULL);
- return;
- }
-
- if (ai == NULL)
- fatalx("no more host");
-
- getnameinfo(ai->ai_addr, SA_LEN(ai->ai_addr),
- host, sizeof(host), serv, sizeof(serv),
- NI_NUMERICHOST | NI_NUMERICSERV);
- log_debug("trying host %s port %s...", host, serv);
-
- params.dst = ai->ai_addr;
- if (smtp_connect(&params, NULL) == NULL)
- fatal("smtp_connect");
-
- if (started == 0) {
- started = 1;
- event_loop(0);
- }
-}
-
-void
-log_trace(int lvl, const char *emsg, ...)
-{
- va_list ap;
-
- if (verbose > lvl) {
- va_start(ap, emsg);
- vlog(LOG_DEBUG, emsg, ap);
- va_end(ap);
- }
-}
-
-void
-smtp_verify_server_cert(void *tag, struct smtp_client *proto, void *ctx)
-{
- SSL *ssl = ctx;
- X509 *cert;
- long res;
- int match;
-
- if ((cert = SSL_get_peer_certificate(ssl))) {
- (void)ssl_check_name(cert, servname, &match);
- X509_free(cert);
- res = SSL_get_verify_result(ssl);
- if (res == X509_V_OK) {
- if (match) {
- log_debug("valid certificate");
- smtp_cert_verified(proto, CERT_OK);
- }
- else {
- log_debug("certificate does not match hostname");
- smtp_cert_verified(proto, CERT_INVALID);
- }
- return;
- }
- log_debug("certificate validation error %ld", res);
- }
- else
- log_debug("no certificate provided");
-
- smtp_cert_verified(proto, CERT_INVALID);
-}
-
-void
-smtp_require_tls(void *tag, struct smtp_client *proto)
-{
- SSL *ssl = NULL;
-
- if ((ssl = SSL_new(ssl_ctx)) == NULL)
- fatal("SSL_new");
- smtp_set_tls(proto, ssl);
-}
-
-void
-smtp_ready(void *tag, struct smtp_client *proto)
-{
- log_debug("connection ready...");
-
- if (done || noaction)
- smtp_quit(proto);
- else
- smtp_sendmail(proto, &mail);
-}
-
-void
-smtp_failed(void *tag, struct smtp_client *proto, int failure, const char *detail)
-{
- switch (failure) {
- case FAIL_INTERNAL:
- log_warnx("internal error: %s", detail);
- break;
- case FAIL_CONN:
- log_warnx("connection error: %s", detail);
- break;
- case FAIL_PROTO:
- log_warnx("protocol error: %s", detail);
- break;
- case FAIL_IMPL:
- log_warnx("missing feature: %s", detail);
- break;
- case FAIL_RESP:
- log_warnx("rejected by server: %s", detail);
- break;
- default:
- fatalx("unknown failure %d: %s", failure, detail);
- }
-}
-
-void
-smtp_status(void *tag, struct smtp_client *proto, struct smtp_status *status)
-{
- log_info("%s: %s: %s", status->rcpt->to, status->cmd, status->status);
-}
-
-void
-smtp_done(void *tag, struct smtp_client *proto, struct smtp_mail *mail)
-{
- int i;
-
- log_debug("mail done...");
-
- if (noaction)
- return;
-
- for (i = 0; i < mail->rcptcount; i++)
- if (!mail->rcpt[i].done)
- return;
-
- done = 1;
-}
-
-void
-smtp_closed(void *tag, struct smtp_client *proto)
-{
- log_debug("connection closed...");
-
- ai = ai->ai_next;
- if (noaction && ai == NULL)
- done = 1;
-
- resume();
-}
diff --git a/smtpd/smtpctl.8 b/smtpd/smtpctl.8
deleted file mode 100644
index 1efcff63..00000000
--- a/smtpd/smtpctl.8
+++ /dev/null
@@ -1,336 +0,0 @@
-.\" $OpenBSD: smtpctl.8,v 1.64 2018/09/18 06:21:45 miko Exp $
-.\"
-.\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
-.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.Dd $Mdocdate: September 18 2018 $
-.Dt SMTPCTL 8
-.Os
-.Sh NAME
-.Nm smtpctl ,
-.Nm mailq
-.Nd control the Simple Mail Transfer Protocol daemon
-.Sh SYNOPSIS
-.Nm
-.Ar command
-.Op Ar argument ...
-.Nm mailq
-.Sh DESCRIPTION
-The
-.Nm
-program controls
-.Xr smtpd 8 .
-Commands may be abbreviated to the minimum unambiguous prefix; for example,
-.Cm sh ro
-for
-.Cm show routes .
-.Pp
-The
-.Nm mailq
-command is provided for compatibility with other MTAs
-and is simply a shortcut for
-.Cm show queue .
-.Pp
-The following commands are available:
-.Bl -tag -width Ds
-.It Cm discover Ar envelope-id | message-id
-Schedule a single envelope, or all envelopes with the same message ID
-that were manually moved to the queue.
-.It Cm encrypt Op Ar string
-Encrypt the password
-.Ar string
-to a representation suitable for user credentials and print it to the
-standard output.
-If
-.Ar string
-is not provided, cleartext passwords are read from standard input.
-.Pp
-It is advised to avoid providing the password as a parameter as it will be
-visible from
-.Xr top 1
-and
-.Xr ps 1
-output.
-.It Cm log brief
-Disable verbose debug logging.
-.It Cm log verbose
-Enable verbose debug logging.
-.It Cm monitor
-Display updates of some
-.Xr smtpd 8
-internal counters in one second intervals.
-Each line reports the increment of all counters since the last update,
-except for some counters which are always absolute values.
-The first line reports the current value of each counter.
-The fields are:
-.Pp
-.Bl -bullet -compact
-.It
-Current number of active SMTP clients (absolute value).
-.It
-New SMTP clients.
-.It
-Disconnected clients.
-.It
-Current number of envelopes in the queue (absolute value).
-.It
-Newly enqueued envelopes.
-.It
-Dequeued envelopes.
-.It
-Successful deliveries.
-.It
-Temporary failures.
-.It
-Permanent failures.
-.It
-Message loops.
-.It
-Expired envelopes.
-.It
-Envelopes removed by the administrator.
-.It
-Generated bounces.
-.El
-.It Cm pause envelope Ar envelope-id | message-id | Cm all
-Temporarily suspend scheduling for the envelope with the given ID,
-envelopes with the given message ID,
-or all envelopes.
-.It Cm pause mda
-Temporarily stop deliveries to local users.
-.It Cm pause mta
-Temporarily stop relaying and deliveries to
-remote users.
-.It Cm pause smtp
-Temporarily stop accepting incoming sessions.
-.It Cm profile Ar subsystem
-Enables real-time profiling of
-.Ar subsystem .
-Supported subsystems are:
-.Pp
-.Bl -bullet -compact
-.It
-queue, to profile cost of queue IO
-.It
-imsg, to profile cost of event handlers
-.El
-.It Cm remove Ar envelope-id | message-id | Cm all
-Remove a single envelope,
-envelopes with the given message ID,
-or all envelopes.
-.It Cm resume envelope Ar envelope-id | message-id | Cm all
-Resume scheduling for the envelope with the given ID,
-envelopes with the given message ID,
-or all envelopes.
-.It Cm resume mda
-Resume deliveries to local users.
-.It Cm resume mta
-Resume relaying and deliveries to remote users.
-.It Cm resume route Ar route-id
-Resume routing on disabled route
-.Ar route-id .
-.It Cm resume smtp
-Resume accepting incoming sessions.
-.It Cm schedule Ar envelope-id | message-id | Cm all
-Mark as ready for immediate delivery
-a single envelope,
-envelopes with the given message ID,
-or all envelopes.
-.It Cm show envelope Ar envelope-id
-Display envelope content for the given ID.
-.It Cm show hosts
-Display the list of known remote MX hosts.
-For each of them, it shows the IP address, the canonical hostname,
-a reference count, the number of active connections to this host,
-and the elapsed time since the last connection.
-.It Cm show hoststats
-Display status of last delivery for domains that have been active in the
-last 4 hours.
-It consists of the following fields, separated by a "|":
-.Pp
-.Bl -bullet -compact
-.It
-Domain.
-.It
-.Ux
-timestamp of last delivery.
-.It
-Status of last delivery.
-.El
-.It Cm show message Ar envelope-id
-Display message content for the given ID.
-.It Cm show queue
-Display information concerning envelopes that are currently in the queue.
-Each line of output describes a single envelope.
-It consists of the following fields, separated by a "|":
-.Pp
-.Bl -bullet -compact
-.It
-Envelope ID.
-.It
-Address family of the client which enqueued the mail.
-.It
-Type of delivery: one of "mta", "mda" or "bounce".
-.It
-Various flags on the envelope.
-.It
-Sender address (return path).
-.It
-The original recipient address.
-.It
-The destination address.
-.It
-Time of creation.
-.It
-Time of expiration.
-.It
-Time of last delivery or relaying attempt.
-.It
-Number of delivery or relaying attempts.
-.It
-Current runstate: either "pending" or "inflight" if
-.Xr smtpd 8
-is running, or "offline" otherwise.
-.It
-Delay in seconds before the next attempt if pending, or time elapsed
-if currently running.
-This field is blank if
-.Xr smtpd 8
-is not running.
-.It
-Error string for the last failed delivery or relay attempt.
-.El
-.It Cm show relays
-Display the list of currently active relays and associated connectors.
-For each relay, it shows a number of counters and information on its
-internal state on a single line.
-Then comes the list of connectors
-(source addresses to connect from for this relay).
-.It Cm show routes
-Display status of routes currently known by
-.Xr smtpd 8 .
-Each line consists of a route number, a source address, a destination
-address, a set of flags, the number of connections on this
-route, the current penalty level which determines the amount of time
-the route is disabled if an error occurs, and the delay before it
-gets reactivated.
-The following flags are defined:
-.Pp
-.Bl -tag -width xx -compact
-.It D
-The route is currently disabled.
-.It N
-The route is new.
-No SMTP session has been established yet.
-.It Q
-The route has a timeout registered to lower its penalty level and possibly
-reactivate or discard it.
-.El
-.It Cm show stats
-Displays runtime statistics concerning
-.Xr smtpd 8 .
-.It Cm show status
-Shows if MTA, MDA and SMTP systems are currently running or paused.
-.It Cm spf walk
-Recursively look up SPF records for the domains read from stdin.
-For example:
-.Bd -literal -offset indent
-# smtpctl spf walk < domains.txt
-.Ed
-.It Cm trace Ar subsystem
-Enables real-time tracing of
-.Ar subsystem .
-Supported subsystems are:
-.Pp
-.Bl -bullet -compact
-.It
-imsg
-.It
-io
-.It
-smtp (incoming sessions)
-.It
-filters
-.It
-mta (outgoing sessions)
-.It
-bounce
-.It
-scheduler
-.It
-expand (aliases/virtual/forward expansion)
-.It
-lookup (user/credentials lookups)
-.It
-stat
-.It
-rules (matched by incoming sessions)
-.It
-mproc
-.It
-all
-.El
-.It Cm unprofile Ar subsystem
-Disables real-time profiling of
-.Ar subsystem .
-.It Cm untrace Ar subsystem
-Disables real-time tracing of
-.Ar subsystem .
-.It Cm update table Ar name
-Updates the contents of table
-.Ar name ,
-for tables using the
-.Dq file
-backend.
-.El
-.Pp
-When
-.Nm smtpd
-receives a message, it generates a
-.Ar message-id
-for the message, and one
-.Ar envelope-id
-per recipient.
-The
-.Ar message-id
-is a 32-bit random identifier that is guaranteed to be
-unique on the host system.
-The
-.Ar envelope-id
-is a 64-bit unique identifier that encodes the
-.Ar message-id
-in the 32 upper bits and a random envelope identifier
-in the 32 lower bits.
-.Pp
-A command which specifies a
-.Ar message-id
-applies to all recipients of a message;
-a command which specifies an
-.Ar envelope-id
-applies to a specific recipient of a message.
-.Sh FILES
-.Bl -tag -width "/var/run/smtpd.sockXXX" -compact
-.It Pa /var/run/smtpd.sock
-.Ux Ns -domain
-socket used for communication with
-.Xr smtpd 8 .
-.El
-.Sh SEE ALSO
-.Xr smtpd 8
-.Sh HISTORY
-The
-.Nm
-program first appeared in
-.Ox 4.6 .
diff --git a/smtpd/smtpctl.c b/smtpd/smtpctl.c
deleted file mode 100644
index 7dba4224..00000000
--- a/smtpd/smtpctl.c
+++ /dev/null
@@ -1,1469 +0,0 @@
-/* $OpenBSD: smtpctl.c,v 1.167 2020/02/24 16:16:07 millert Exp $ */
-
-/*
- * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
- * Copyright (c) 2006 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
- * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
- * Copyright (c) 2003 Henning Brauer <henning@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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/un.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-
-#include <net/if.h>
-/* #include <net/if_media.h> */
-/* #include <net/if_types.h> */
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <fts.h>
-#include <grp.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <time.h>
-#include <unistd.h>
-#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
-#include <vis.h>
-#else
-#include "bsd-vis.h"
-#endif
-#include <limits.h>
-
-#include "smtpd.h"
-#include "parser.h"
-#include "log.h"
-
-#ifndef PATH_GZCAT
-#define PATH_GZCAT "/usr/bin/gzcat"
-#endif
-#define PATH_CAT "/bin/cat"
-#define PATH_QUEUE "/queue"
-#ifndef PATH_ENCRYPT
-#define PATH_ENCRYPT "/usr/bin/encrypt"
-#endif
-
-#ifndef HAVE_DB_API
-#define makemap(x, y, z) 1
-#endif
-
-
-int srv_connect(void);
-int srv_connected(void);
-
-void usage(void);
-static void show_queue_envelope(struct envelope *, int);
-static void getflag(uint *, int, char *, char *, size_t);
-static void display(const char *);
-static int str_to_trace(const char *);
-static int str_to_profile(const char *);
-static void show_offline_envelope(uint64_t);
-static int is_gzip_fp(FILE *);
-static int is_encrypted_fp(FILE *);
-static int is_encrypted_buffer(const char *);
-static int is_gzip_buffer(const char *);
-static FILE *offline_file(void);
-static void sendmail_compat(int, char **);
-
-extern int spfwalk(int, struct parameter *);
-
-extern char *__progname;
-int sendmail;
-struct smtpd *env;
-struct imsgbuf *ibuf;
-struct imsg imsg;
-char *rdata;
-size_t rlen;
-time_t now;
-
-struct queue_backend queue_backend_null;
-struct queue_backend queue_backend_proc;
-struct queue_backend queue_backend_ram;
-
-__dead void
-usage(void)
-{
- if (sendmail)
- fprintf(stderr, "usage: %s [-tv] [-f from] [-F name] to ...\n",
- __progname);
- else
- fprintf(stderr, "usage: %s command [argument ...]\n",
- __progname);
- exit(1);
-}
-
-void stat_increment(const char *k, size_t v)
-{
-}
-
-void stat_decrement(const char *k, size_t v)
-{
-}
-
-int
-srv_connect(void)
-{
- struct sockaddr_un s_un;
- int ctl_sock, saved_errno;
-
- /* connect to smtpd control socket */
- if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
- err(1, "socket");
-
- memset(&s_un, 0, sizeof(s_un));
- s_un.sun_family = AF_UNIX;
- (void)strlcpy(s_un.sun_path, SMTPD_SOCKET, sizeof(s_un.sun_path));
- if (connect(ctl_sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) {
- saved_errno = errno;
- close(ctl_sock);
- errno = saved_errno;
- return (0);
- }
-
- ibuf = xcalloc(1, sizeof(struct imsgbuf));
- imsg_init(ibuf, ctl_sock);
-
- return (1);
-}
-
-int
-srv_connected(void)
-{
- return ibuf != NULL ? 1 : 0;
-}
-
-FILE *
-offline_file(void)
-{
- char path[PATH_MAX];
- int fd;
- FILE *fp;
-
- if (!bsnprintf(path, sizeof(path), "%s%s/%lld.XXXXXXXXXX", PATH_SPOOL,
- PATH_OFFLINE, (long long int) time(NULL)))
- err(EX_UNAVAILABLE, "snprintf");
-
- if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
- if (fd != -1)
- unlink(path);
- err(EX_UNAVAILABLE, "cannot create temporary file %s", path);
- }
-
- if (fchmod(fd, 0600) == -1) {
- unlink(path);
- err(EX_SOFTWARE, "fchmod");
- }
-
- return fp;
-}
-
-
-static void
-srv_flush(void)
-{
- if (imsg_flush(ibuf) == -1)
- err(1, "write error");
-}
-
-static void
-srv_send(int msg, const void *data, size_t len)
-{
- if (ibuf == NULL && !srv_connect())
- errx(1, "smtpd doesn't seem to be running");
- imsg_compose(ibuf, msg, IMSG_VERSION, 0, -1, data, len);
-}
-
-static void
-srv_recv(int type)
-{
- ssize_t n;
-
- srv_flush();
-
- while (1) {
- if ((n = imsg_get(ibuf, &imsg)) == -1)
- errx(1, "imsg_get error");
- if (n) {
- if (imsg.hdr.type == IMSG_CTL_FAIL &&
- imsg.hdr.peerid != 0 &&
- imsg.hdr.peerid != IMSG_VERSION)
- errx(1, "incompatible smtpctl and smtpd");
- if (type != -1 && type != (int)imsg.hdr.type)
- errx(1, "bad message type");
- rdata = imsg.data;
- rlen = imsg.hdr.len - sizeof(imsg.hdr);
- break;
- }
-
- if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
- errx(1, "imsg_read error");
- if (n == 0)
- errx(1, "pipe closed");
- }
-}
-
-static void
-srv_read(void *dst, size_t sz)
-{
- if (sz == 0)
- return;
- if (rlen < sz)
- errx(1, "message too short");
- if (dst)
- memmove(dst, rdata, sz);
- rlen -= sz;
- rdata += sz;
-}
-
-static void
-srv_get_int(int *i)
-{
- srv_read(i, sizeof(*i));
-}
-
-static void
-srv_get_time(time_t *t)
-{
- srv_read(t, sizeof(*t));
-}
-
-static void
-srv_get_evpid(uint64_t *evpid)
-{
- srv_read(evpid, sizeof(*evpid));
-}
-
-static void
-srv_get_string(const char **s)
-{
- const char *end;
- size_t len;
-
- if (rlen == 0)
- errx(1, "message too short");
-
- rlen -= 1;
- if (*rdata++ == '\0') {
- *s = NULL;
- return;
- }
-
- if (rlen == 0)
- errx(1, "bogus string");
-
- end = memchr(rdata, 0, rlen);
- if (end == NULL)
- errx(1, "unterminated string");
-
- len = end + 1 - rdata;
-
- *s = rdata;
- rlen -= len;
- rdata += len;
-}
-
-static void
-srv_get_envelope(struct envelope *evp)
-{
- uint64_t evpid;
- const char *str;
-
- srv_get_evpid(&evpid);
- srv_get_string(&str);
-
- envelope_load_buffer(evp, str, strlen(str));
- evp->id = evpid;
-}
-
-static void
-srv_end(void)
-{
- if (rlen)
- errx(1, "bogus data");
- imsg_free(&imsg);
-}
-
-static int
-srv_check_result(int verbose_)
-{
- srv_recv(-1);
- srv_end();
-
- switch (imsg.hdr.type) {
- case IMSG_CTL_OK:
- if (verbose_)
- printf("command succeeded\n");
- return (0);
- case IMSG_CTL_FAIL:
- if (verbose_) {
- if (rlen)
- printf("command failed: %s\n", rdata);
- else
- printf("command failed\n");
- }
- return (1);
- default:
- errx(1, "wrong message in response: %u", imsg.hdr.type);
- }
- return (0);
-}
-
-static int
-srv_iter_messages(uint32_t *res)
-{
- static uint32_t *msgids = NULL, from = 0;
- static size_t n, curr;
- static int done = 0;
-
- if (done)
- return (0);
-
- if (msgids == NULL) {
- srv_send(IMSG_CTL_LIST_MESSAGES, &from, sizeof(from));
- srv_recv(IMSG_CTL_LIST_MESSAGES);
- if (rlen == 0) {
- srv_end();
- done = 1;
- return (0);
- }
- msgids = malloc(rlen);
- n = rlen / sizeof(*msgids);
- srv_read(msgids, rlen);
- srv_end();
-
- curr = 0;
- from = msgids[n - 1] + 1;
- if (from == 0)
- done = 1;
- }
-
- *res = msgids[curr++];
- if (curr == n) {
- free(msgids);
- msgids = NULL;
- }
-
- return (1);
-}
-
-static int
-srv_iter_envelopes(uint32_t msgid, struct envelope *evp)
-{
- static uint32_t currmsgid = 0;
- static uint64_t from = 0;
- static int done = 0, need_send = 1, found;
- int flags;
- time_t nexttry;
-
- if (currmsgid != msgid) {
- if (currmsgid != 0 && !done)
- errx(1, "must finish current iteration first");
- currmsgid = msgid;
- from = msgid_to_evpid(msgid);
- done = 0;
- found = 0;
- need_send = 1;
- }
-
- if (done)
- return (0);
-
- again:
- if (need_send) {
- found = 0;
- srv_send(IMSG_CTL_LIST_ENVELOPES, &from, sizeof(from));
- }
- need_send = 0;
-
- srv_recv(IMSG_CTL_LIST_ENVELOPES);
- if (rlen == 0) {
- srv_end();
- if (!found || evpid_to_msgid(from) != msgid) {
- done = 1;
- return (0);
- }
- need_send = 1;
- goto again;
- }
-
- srv_get_int(&flags);
- srv_get_time(&nexttry);
- srv_get_envelope(evp);
- srv_end();
-
- evp->flags |= flags;
- evp->nexttry = nexttry;
-
- from = evp->id + 1;
- found++;
- return (1);
-}
-
-static int
-srv_iter_evpids(uint32_t msgid, uint64_t *evpid, int *offset)
-{
- static uint64_t *evpids = NULL, *tmp;
- static int n, tmpalloc, alloc = 0;
- struct envelope evp;
-
- if (*offset == 0) {
- n = 0;
- while (srv_iter_envelopes(msgid, &evp)) {
- if (n == alloc) {
- tmpalloc = alloc ? (alloc * 2) : 128;
- tmp = recallocarray(evpids, alloc, tmpalloc,
- sizeof(*evpids));
- if (tmp == NULL)
- err(1, "recallocarray");
- evpids = tmp;
- alloc = tmpalloc;
- }
- evpids[n++] = evp.id;
- }
- }
-
- if (*offset >= n)
- return (0);
- *evpid = evpids[*offset];
- *offset += 1;
- return (1);
-}
-
-static void
-srv_foreach_envelope(struct parameter *argv, int ctl, size_t *total, size_t *ok)
-{
- uint32_t msgid;
- uint64_t evpid;
- int i;
-
- *total = 0;
- *ok = 0;
-
- if (argv == NULL) {
- while (srv_iter_messages(&msgid)) {
- i = 0;
- while (srv_iter_evpids(msgid, &evpid, &i)) {
- *total += 1;
- srv_send(ctl, &evpid, sizeof(evpid));
- if (srv_check_result(0) == 0)
- *ok += 1;
- }
- }
- } else if (argv->type == P_MSGID) {
- i = 0;
- while (srv_iter_evpids(argv->u.u_msgid, &evpid, &i)) {
- srv_send(ctl, &evpid, sizeof(evpid));
- if (srv_check_result(0) == 0)
- *ok += 1;
- }
- } else {
- *total += 1;
- srv_send(ctl, &argv->u.u_evpid, sizeof(evpid));
- if (srv_check_result(0) == 0)
- *ok += 1;
- }
-}
-
-static void
-srv_show_cmd(int cmd, const void *data, size_t len)
-{
- int done = 0;
-
- srv_send(cmd, data, len);
-
- do {
- srv_recv(cmd);
- if (rlen) {
- printf("%s\n", rdata);
- srv_read(NULL, rlen);
- }
- else
- done = 1;
- srv_end();
- } while (!done);
-}
-
-static void
-droppriv(void)
-{
- struct passwd *pw;
-
- if (geteuid())
- return;
-
- if ((pw = getpwnam(SMTPD_USER)) == NULL)
- errx(1, "unknown user " SMTPD_USER);
-
- if ((setgroups(1, &pw->pw_gid) ||
- setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
- setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)))
- err(1, "cannot drop privileges");
-}
-
-static int
-do_permission_denied(int argc, struct parameter *argv)
-{
- errx(1, "need root privileges");
-}
-
-static int
-do_log_brief(int argc, struct parameter *argv)
-{
- int v = 0;
-
- srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v));
- return srv_check_result(1);
-}
-
-static int
-do_log_verbose(int argc, struct parameter *argv)
-{
- int v = TRACE_DEBUG;
-
- srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v));
- return srv_check_result(1);
-}
-
-static int
-do_monitor(int argc, struct parameter *argv)
-{
- struct stat_digest last, digest;
- size_t count;
-
- memset(&last, 0, sizeof(last));
- count = 0;
-
- while (1) {
- srv_send(IMSG_CTL_GET_DIGEST, NULL, 0);
- srv_recv(IMSG_CTL_GET_DIGEST);
- srv_read(&digest, sizeof(digest));
- srv_end();
-
- if (count % 25 == 0) {
- if (count != 0)
- printf("\n");
- printf("--- client --- "
- "-- envelope -- "
- "---- relay/delivery --- "
- "------- misc -------\n"
- "curr conn disc "
- "curr enq deq "
- "ok tmpfail prmfail loop "
- "expire remove bounce\n");
- }
- printf("%4zu %4zu %4zu "
- "%4zu %4zu %4zu "
- "%4zu %4zu %4zu %4zu "
- "%4zu %4zu %4zu\n",
- digest.clt_connect - digest.clt_disconnect,
- digest.clt_connect - last.clt_connect,
- digest.clt_disconnect - last.clt_disconnect,
-
- digest.evp_enqueued - digest.evp_dequeued,
- digest.evp_enqueued - last.evp_enqueued,
- digest.evp_dequeued - last.evp_dequeued,
-
- digest.dlv_ok - last.dlv_ok,
- digest.dlv_tempfail - last.dlv_tempfail,
- digest.dlv_permfail - last.dlv_permfail,
- digest.dlv_loop - last.dlv_loop,
-
- digest.evp_expired - last.evp_expired,
- digest.evp_removed - last.evp_removed,
- digest.evp_bounce - last.evp_bounce);
-
- last = digest;
- count++;
- sleep(1);
- }
-
- return (0);
-}
-
-static int
-do_pause_envelope(int argc, struct parameter *argv)
-{
- size_t total, ok;
-
- srv_foreach_envelope(argv, IMSG_CTL_PAUSE_EVP, &total, &ok);
- printf("%zu envelope%s paused\n", ok, (ok > 1) ? "s" : "");
-
- return (0);
-}
-
-static int
-do_pause_mda(int argc, struct parameter *argv)
-{
- srv_send(IMSG_CTL_PAUSE_MDA, NULL, 0);
- return srv_check_result(1);
-}
-
-static int
-do_pause_mta(int argc, struct parameter *argv)
-{
- srv_send(IMSG_CTL_PAUSE_MTA, NULL, 0);
- return srv_check_result(1);
-}
-
-static int
-do_pause_smtp(int argc, struct parameter *argv)
-{
- srv_send(IMSG_CTL_PAUSE_SMTP, NULL, 0);
- return srv_check_result(1);
-}
-
-static int
-do_profile(int argc, struct parameter *argv)
-{
- int v;
-
- v = str_to_profile(argv[0].u.u_str);
-
- srv_send(IMSG_CTL_PROFILE_ENABLE, &v, sizeof(v));
- return srv_check_result(1);
-}
-
-static int
-do_remove(int argc, struct parameter *argv)
-{
- size_t total, ok;
-
- srv_foreach_envelope(argv, IMSG_CTL_REMOVE, &total, &ok);
- printf("%zu envelope%s removed\n", ok, (ok > 1) ? "s" : "");
-
- return (0);
-}
-
-static int
-do_resume_envelope(int argc, struct parameter *argv)
-{
- size_t total, ok;
-
- srv_foreach_envelope(argv, IMSG_CTL_RESUME_EVP, &total, &ok);
- printf("%zu envelope%s resumed\n", ok, (ok > 1) ? "s" : "");
-
- return (0);
-}
-
-static int
-do_resume_mda(int argc, struct parameter *argv)
-{
- srv_send(IMSG_CTL_RESUME_MDA, NULL, 0);
- return srv_check_result(1);
-}
-
-static int
-do_resume_mta(int argc, struct parameter *argv)
-{
- srv_send(IMSG_CTL_RESUME_MTA, NULL, 0);
- return srv_check_result(1);
-}
-
-static int
-do_resume_route(int argc, struct parameter *argv)
-{
- uint64_t v;
-
- if (argc == 0)
- v = 0;
- else
- v = argv[0].u.u_routeid;
-
- srv_send(IMSG_CTL_RESUME_ROUTE, &v, sizeof(v));
- return srv_check_result(1);
-}
-
-static int
-do_resume_smtp(int argc, struct parameter *argv)
-{
- srv_send(IMSG_CTL_RESUME_SMTP, NULL, 0);
- return srv_check_result(1);
-}
-
-static int
-do_schedule(int argc, struct parameter *argv)
-{
- size_t total, ok;
-
- srv_foreach_envelope(argv, IMSG_CTL_SCHEDULE, &total, &ok);
- printf("%zu envelope%s scheduled\n", ok, (ok > 1) ? "s" : "");
-
- return (0);
-}
-
-static int
-do_show_envelope(int argc, struct parameter *argv)
-{
- char buf[PATH_MAX];
-
- if (!bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/%016" PRIx64,
- PATH_SPOOL,
- PATH_QUEUE,
- (evpid_to_msgid(argv[0].u.u_evpid) & 0xff000000) >> 24,
- evpid_to_msgid(argv[0].u.u_evpid),
- argv[0].u.u_evpid))
- errx(1, "unable to retrieve envelope");
-
- display(buf);
-
- return (0);
-}
-
-static int
-do_show_hoststats(int argc, struct parameter *argv)
-{
- srv_show_cmd(IMSG_CTL_MTA_SHOW_HOSTSTATS, NULL, 0);
-
- return (0);
-}
-
-static int
-do_show_message(int argc, struct parameter *argv)
-{
- char buf[PATH_MAX];
- uint32_t msgid;
-
- if (argv[0].type == P_EVPID)
- msgid = evpid_to_msgid(argv[0].u.u_evpid);
- else
- msgid = argv[0].u.u_msgid;
-
- if (!bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/message",
- PATH_SPOOL,
- PATH_QUEUE,
- (msgid & 0xff000000) >> 24,
- msgid))
- errx(1, "unable to retrieve message");
-
- display(buf);
-
- return (0);
-}
-
-static int
-do_show_queue(int argc, struct parameter *argv)
-{
- struct envelope evp;
- uint32_t msgid;
- FTS *fts;
- FTSENT *ftse;
- char *qpath[] = {"/queue", NULL};
- char *tmp;
- uint64_t evpid;
-
- now = time(NULL);
-
- if (!srv_connect()) {
- log_init(1, LOG_MAIL);
- queue_init("fs", 0);
- if (chroot(PATH_SPOOL) == -1 || chdir("/") == -1)
- err(1, "%s", PATH_SPOOL);
- fts = fts_open(qpath, FTS_PHYSICAL|FTS_NOCHDIR, NULL);
- if (fts == NULL)
- err(1, "%s/queue", PATH_SPOOL);
-
- while ((ftse = fts_read(fts)) != NULL) {
- switch (ftse->fts_info) {
- case FTS_DP:
- case FTS_DNR:
- break;
- case FTS_F:
- tmp = NULL;
- evpid = strtoull(ftse->fts_name, &tmp, 16);
- if (tmp && *tmp != '\0')
- break;
- show_offline_envelope(evpid);
- }
- }
-
- fts_close(fts);
- return (0);
- }
-
- if (argc == 0) {
- msgid = 0;
- while (srv_iter_messages(&msgid))
- while (srv_iter_envelopes(msgid, &evp))
- show_queue_envelope(&evp, 1);
- } else if (argv[0].type == P_MSGID) {
- while (srv_iter_envelopes(argv[0].u.u_msgid, &evp))
- show_queue_envelope(&evp, 1);
- }
-
- return (0);
-}
-
-static int
-do_show_hosts(int argc, struct parameter *argv)
-{
- srv_show_cmd(IMSG_CTL_MTA_SHOW_HOSTS, NULL, 0);
-
- return (0);
-}
-
-static int
-do_show_relays(int argc, struct parameter *argv)
-{
- srv_show_cmd(IMSG_CTL_MTA_SHOW_RELAYS, NULL, 0);
-
- return (0);
-}
-
-static int
-do_show_routes(int argc, struct parameter *argv)
-{
- srv_show_cmd(IMSG_CTL_MTA_SHOW_ROUTES, NULL, 0);
-
- return (0);
-}
-
-static int
-do_show_stats(int argc, struct parameter *argv)
-{
- struct stat_kv kv;
- time_t duration;
-
- memset(&kv, 0, sizeof kv);
-
- while (1) {
- srv_send(IMSG_CTL_GET_STATS, &kv, sizeof kv);
- srv_recv(IMSG_CTL_GET_STATS);
- srv_read(&kv, sizeof(kv));
- srv_end();
-
- if (kv.iter == NULL)
- break;
-
- if (strcmp(kv.key, "uptime") == 0) {
- duration = time(NULL) - kv.val.u.counter;
- printf("uptime=%lld\n", (long long)duration);
- printf("uptime.human=%s\n",
- duration_to_text(duration));
- }
- else {
- switch (kv.val.type) {
- case STAT_COUNTER:
- printf("%s=%zd\n",
- kv.key, kv.val.u.counter);
- break;
- case STAT_TIMESTAMP:
- printf("%s=%" PRId64 "\n",
- kv.key, (int64_t)kv.val.u.timestamp);
- break;
- case STAT_TIMEVAL:
- printf("%s=%lld.%lld\n",
- kv.key, (long long)kv.val.u.tv.tv_sec,
- (long long)kv.val.u.tv.tv_usec);
- break;
- case STAT_TIMESPEC:
- printf("%s=%lld.%06ld\n",
- kv.key,
- (long long)kv.val.u.ts.tv_sec * 1000000 +
- kv.val.u.ts.tv_nsec / 1000000,
- kv.val.u.ts.tv_nsec % 1000000);
- break;
- }
- }
- }
-
- return (0);
-}
-
-static int
-do_show_status(int argc, struct parameter *argv)
-{
- uint32_t sc_flags;
-
- srv_send(IMSG_CTL_SHOW_STATUS, NULL, 0);
- srv_recv(IMSG_CTL_SHOW_STATUS);
- srv_read(&sc_flags, sizeof(sc_flags));
- srv_end();
- printf("MDA %s\n",
- (sc_flags & SMTPD_MDA_PAUSED) ? "paused" : "running");
- printf("MTA %s\n",
- (sc_flags & SMTPD_MTA_PAUSED) ? "paused" : "running");
- printf("SMTP %s\n",
- (sc_flags & SMTPD_SMTP_PAUSED) ? "paused" : "running");
- return (0);
-}
-
-static int
-do_trace(int argc, struct parameter *argv)
-{
- int v;
-
- v = str_to_trace(argv[0].u.u_str);
-
- srv_send(IMSG_CTL_TRACE_ENABLE, &v, sizeof(v));
- return srv_check_result(1);
-}
-
-static int
-do_unprofile(int argc, struct parameter *argv)
-{
- int v;
-
- v = str_to_profile(argv[0].u.u_str);
-
- srv_send(IMSG_CTL_PROFILE_DISABLE, &v, sizeof(v));
- return srv_check_result(1);
-}
-
-static int
-do_untrace(int argc, struct parameter *argv)
-{
- int v;
-
- v = str_to_trace(argv[0].u.u_str);
-
- srv_send(IMSG_CTL_TRACE_DISABLE, &v, sizeof(v));
- return srv_check_result(1);
-}
-
-static int
-do_update_table(int argc, struct parameter *argv)
-{
- const char *name = argv[0].u.u_str;
-
- srv_send(IMSG_CTL_UPDATE_TABLE, name, strlen(name) + 1);
- return srv_check_result(1);
-}
-
-static int
-do_encrypt(int argc, struct parameter *argv)
-{
- const char *p = NULL;
-
- droppriv();
-
- if (argv)
- p = argv[0].u.u_str;
- execl(PATH_ENCRYPT, "encrypt", "--", p, (char *)NULL);
- errx(1, "execl");
-}
-
-static int
-do_block_mta(int argc, struct parameter *argv)
-{
- struct ibuf *m;
-
- if (ibuf == NULL && !srv_connect())
- errx(1, "smtpd doesn't seem to be running");
- m = imsg_create(ibuf, IMSG_CTL_MTA_BLOCK, IMSG_VERSION, 0,
- sizeof(argv[0].u.u_ss) + strlen(argv[1].u.u_str) + 1);
- if (imsg_add(m, &argv[0].u.u_ss, sizeof(argv[0].u.u_ss)) == -1)
- errx(1, "imsg_add");
- if (imsg_add(m, argv[1].u.u_str, strlen(argv[1].u.u_str) + 1) == -1)
- errx(1, "imsg_add");
- imsg_close(ibuf, m);
-
- return srv_check_result(1);
-}
-
-static int
-do_unblock_mta(int argc, struct parameter *argv)
-{
- struct ibuf *m;
-
- if (ibuf == NULL && !srv_connect())
- errx(1, "smtpd doesn't seem to be running");
-
- m = imsg_create(ibuf, IMSG_CTL_MTA_UNBLOCK, IMSG_VERSION, 0,
- sizeof(argv[0].u.u_ss) + strlen(argv[1].u.u_str) + 1);
- if (imsg_add(m, &argv[0].u.u_ss, sizeof(argv[0].u.u_ss)) == -1)
- errx(1, "imsg_add");
- if (imsg_add(m, argv[1].u.u_str, strlen(argv[1].u.u_str) + 1) == -1)
- errx(1, "imsg_add");
- imsg_close(ibuf, m);
-
- return srv_check_result(1);
-}
-
-static int
-do_show_mta_block(int argc, struct parameter *argv)
-{
- srv_show_cmd(IMSG_CTL_MTA_SHOW_BLOCK, NULL, 0);
-
- return (0);
-}
-
-static int
-do_discover(int argc, struct parameter *argv)
-{
- uint64_t evpid;
- uint32_t msgid;
- size_t n_evp;
-
- if (ibuf == NULL && !srv_connect())
- errx(1, "smtpd doesn't seem to be running");
-
- if (argv[0].type == P_EVPID) {
- evpid = argv[0].u.u_evpid;
- srv_send(IMSG_CTL_DISCOVER_EVPID, &evpid, sizeof evpid);
- srv_recv(IMSG_CTL_DISCOVER_EVPID);
- } else {
- msgid = argv[0].u.u_msgid;
- srv_send(IMSG_CTL_DISCOVER_MSGID, &msgid, sizeof msgid);
- srv_recv(IMSG_CTL_DISCOVER_MSGID);
- }
-
- if (rlen == 0) {
- srv_end();
- return (0);
- } else {
- srv_read(&n_evp, sizeof n_evp);
- srv_end();
- }
-
- printf("%zu envelope%s discovered\n", n_evp, (n_evp != 1) ? "s" : "");
- return (0);
-}
-
-static int
-do_spf_walk(int argc, struct parameter *argv)
-{
- droppriv();
-
- return spfwalk(argc, argv);
-}
-
-#define cmd_install_priv(s, f) \
- cmd_install((s), privileged ? (f) : do_permission_denied)
-
-int
-main(int argc, char **argv)
-{
- gid_t gid;
- struct group *gr;
- int privileged;
- char *argv_mailq[] = { "show", "queue", NULL };
-
-#ifndef HAVE___PROGNAME
- __progname = ssh_get_progname(argv[0]);
-#endif
-
- /* check that smtpctl was installed setgid */
- if ((gr = getgrnam(SMTPD_QUEUE_GROUP)) == NULL)
- errx(1, "unknown group %s", SMTPD_QUEUE_GROUP);
- else if (gr->gr_gid != getegid())
- errx(1, "this program must be setgid %s", SMTPD_QUEUE_GROUP);
-
- sendmail_compat(argc, argv);
- privileged = geteuid() == 0;
-
- gid = getgid();
- if (setresgid(gid, gid, gid) == -1)
- err(1, "setresgid");
-
- /* Privileged commands */
- cmd_install_priv("discover <evpid>", do_discover);
- cmd_install_priv("discover <msgid>", do_discover);
- cmd_install_priv("pause mta from <addr> for <str>", do_block_mta);
- cmd_install_priv("resume mta from <addr> for <str>", do_unblock_mta);
- cmd_install_priv("show mta paused", do_show_mta_block);
- cmd_install_priv("log brief", do_log_brief);
- cmd_install_priv("log verbose", do_log_verbose);
- cmd_install_priv("monitor", do_monitor);
- cmd_install_priv("pause envelope <evpid>", do_pause_envelope);
- cmd_install_priv("pause envelope <msgid>", do_pause_envelope);
- cmd_install_priv("pause envelope all", do_pause_envelope);
- cmd_install_priv("pause mda", do_pause_mda);
- cmd_install_priv("pause mta", do_pause_mta);
- cmd_install_priv("pause smtp", do_pause_smtp);
- cmd_install_priv("profile <str>", do_profile);
- cmd_install_priv("remove <evpid>", do_remove);
- cmd_install_priv("remove <msgid>", do_remove);
- cmd_install_priv("remove all", do_remove);
- cmd_install_priv("resume envelope <evpid>", do_resume_envelope);
- cmd_install_priv("resume envelope <msgid>", do_resume_envelope);
- cmd_install_priv("resume envelope all", do_resume_envelope);
- cmd_install_priv("resume mda", do_resume_mda);
- cmd_install_priv("resume mta", do_resume_mta);
- cmd_install_priv("resume route <routeid>", do_resume_route);
- cmd_install_priv("resume smtp", do_resume_smtp);
- cmd_install_priv("schedule <msgid>", do_schedule);
- cmd_install_priv("schedule <evpid>", do_schedule);
- cmd_install_priv("schedule all", do_schedule);
- cmd_install_priv("show envelope <evpid>", do_show_envelope);
- cmd_install_priv("show hoststats", do_show_hoststats);
- cmd_install_priv("show message <msgid>", do_show_message);
- cmd_install_priv("show message <evpid>", do_show_message);
- cmd_install_priv("show queue", do_show_queue);
- cmd_install_priv("show queue <msgid>", do_show_queue);
- cmd_install_priv("show hosts", do_show_hosts);
- cmd_install_priv("show relays", do_show_relays);
- cmd_install_priv("show routes", do_show_routes);
- cmd_install_priv("show stats", do_show_stats);
- cmd_install_priv("show status", do_show_status);
- cmd_install_priv("trace <str>", do_trace);
- cmd_install_priv("unprofile <str>", do_unprofile);
- cmd_install_priv("untrace <str>", do_untrace);
- cmd_install_priv("update table <str>", do_update_table);
-
- /* Unprivileged commands */
- cmd_install("encrypt", do_encrypt);
- cmd_install("encrypt <str>", do_encrypt);
- cmd_install("spf walk", do_spf_walk);
-
- if (strcmp(__progname, "mailq") == 0)
- return cmd_run(2, argv_mailq);
- if (strcmp(__progname, "smtpctl") == 0)
- return cmd_run(argc - 1, argv + 1);
-
- errx(1, "unsupported mode");
- return (0);
-}
-
-void
-sendmail_compat(int argc, char **argv)
-{
- FILE *offlinefp = NULL;
- gid_t gid;
- int i, r;
-
- if (strcmp(__progname, "sendmail") == 0 ||
- strcmp(__progname, "send-mail") == 0) {
- /*
- * determine whether we are called with flags
- * that should invoke makemap/newaliases.
- */
- for (i = 1; i < argc; i++)
- if (strncmp(argv[i], "-bi", 3) == 0)
- exit(makemap(P_SENDMAIL, argc, argv));
-
- if (!srv_connect())
- offlinefp = offline_file();
-
- gid = getgid();
- if (setresgid(gid, gid, gid) == -1)
- err(1, "setresgid");
-
-#if HAVE_PLEDGE
- /* we'll reduce further down the road */
- if (pledge("stdio rpath wpath cpath tmppath flock "
- "dns getpw recvfd", NULL) == -1)
- err(1, "pledge");
-#endif
-
- sendmail = 1;
- exit(enqueue(argc, argv, offlinefp));
- } else if (strcmp(__progname, "makemap") == 0)
- exit(makemap(P_MAKEMAP, argc, argv));
- else if (strcmp(__progname, "newaliases") == 0) {
- r = makemap(P_NEWALIASES, argc, argv);
- /*
- * if server is available, notify of table update.
- * only makes sense for static tables AND if server is up.
- */
- if (srv_connect()) {
- srv_send(IMSG_CTL_UPDATE_TABLE, "aliases", strlen("aliases") + 1);
- srv_check_result(0);
- }
- exit(r);
- }
-}
-
-static void
-show_queue_envelope(struct envelope *e, int online)
-{
- const char *src = "?", *agent = "?";
- char status[128], runstate[128], errline[LINE_MAX];
-
- status[0] = '\0';
-
- getflag(&e->flags, EF_BOUNCE, "bounce", status, sizeof(status));
- getflag(&e->flags, EF_AUTHENTICATED, "auth", status, sizeof(status));
- getflag(&e->flags, EF_INTERNAL, "internal", status, sizeof(status));
- getflag(&e->flags, EF_SUSPEND, "suspend", status, sizeof(status));
- getflag(&e->flags, EF_HOLD, "hold", status, sizeof(status));
-
- if (online) {
- if (e->flags & EF_PENDING)
- (void)snprintf(runstate, sizeof runstate, "pending|%zd",
- (ssize_t)(e->nexttry - now));
- else if (e->flags & EF_INFLIGHT)
- (void)snprintf(runstate, sizeof runstate,
- "inflight|%zd", (ssize_t)(now - e->lasttry));
- else
- (void)snprintf(runstate, sizeof runstate, "invalid|");
- e->flags &= ~(EF_PENDING|EF_INFLIGHT);
- }
- else
- (void)strlcpy(runstate, "offline|", sizeof runstate);
-
- if (e->flags)
- errx(1, "%016" PRIx64 ": unexpected flags 0x%04x", e->id,
- e->flags);
-
- if (status[0])
- status[strlen(status) - 1] = '\0';
-
- if (e->type == D_MDA)
- agent = "mda";
- else if (e->type == D_MTA)
- agent = "mta";
- else if (e->type == D_BOUNCE)
- agent = "bounce";
-
- if (e->ss.ss_family == AF_LOCAL)
- src = "local";
- else if (e->ss.ss_family == AF_INET)
- src = "inet4";
- else if (e->ss.ss_family == AF_INET6)
- src = "inet6";
-
- strnvis(errline, e->errorline, sizeof(errline), 0);
-
- printf("%016"PRIx64
- "|%s|%s|%s|%s@%s|%s@%s|%s@%s"
- "|%zu|%zu|%zu|%zu|%s|%s\n",
-
- e->id,
-
- src,
- agent,
- status,
- e->sender.user, e->sender.domain,
- e->rcpt.user, e->rcpt.domain,
- e->dest.user, e->dest.domain,
-
- (size_t) e->creation,
- (size_t) (e->creation + e->ttl),
- (size_t) e->lasttry,
- (size_t) e->retry,
- runstate,
- errline);
-}
-
-static void
-getflag(uint *bitmap, int bit, char *bitstr, char *buf, size_t len)
-{
- if (*bitmap & bit) {
- *bitmap &= ~bit;
- (void)strlcat(buf, bitstr, len);
- (void)strlcat(buf, ",", len);
- }
-}
-
-static void
-show_offline_envelope(uint64_t evpid)
-{
- FILE *fp = NULL;
- char pathname[PATH_MAX];
- size_t plen;
- char *p;
- size_t buflen;
- char buffer[sizeof(struct envelope)];
-
- struct envelope evp;
-
- if (!bsnprintf(pathname, sizeof pathname,
- "/queue/%02x/%08x/%016"PRIx64,
- (evpid_to_msgid(evpid) & 0xff000000) >> 24,
- evpid_to_msgid(evpid), evpid))
- goto end;
- fp = fopen(pathname, "r");
- if (fp == NULL)
- goto end;
-
- buflen = fread(buffer, 1, sizeof (buffer) - 1, fp);
- p = buffer;
- plen = buflen;
- buffer[buflen] = '\0';
-
- if (is_encrypted_buffer(p)) {
- warnx("offline encrypted queue is not supported yet");
- goto end;
- }
-
- if (is_gzip_buffer(p)) {
- warnx("offline compressed queue is not supported yet");
- goto end;
- }
-
- if (!envelope_load_buffer(&evp, p, plen))
- goto end;
- evp.id = evpid;
- show_queue_envelope(&evp, 0);
-
-end:
- if (fp)
- fclose(fp);
-}
-
-static void
-display(const char *s)
-{
- FILE *fp;
- char *key;
- int gzipped;
- char *gzcat_argv0 = strrchr(PATH_GZCAT, '/') + 1;
-
- if ((fp = fopen(s, "r")) == NULL)
- err(1, "fopen");
-
- if (is_encrypted_fp(fp)) {
- int i;
- FILE *ofp = NULL;
-
- if ((ofp = tmpfile()) == NULL)
- err(1, "tmpfile");
-
- for (i = 0; i < 3; i++) {
- key = getpass("key> ");
- if (crypto_setup(key, strlen(key)))
- break;
- }
- if (i == 3)
- errx(1, "crypto-setup: invalid key");
-
- if (!crypto_decrypt_file(fp, ofp)) {
- printf("object is encrypted: %s\n", key);
- exit(1);
- }
-
- fclose(fp);
- fp = ofp;
- fseek(fp, 0, SEEK_SET);
- }
- gzipped = is_gzip_fp(fp);
-
- lseek(fileno(fp), 0, SEEK_SET);
- (void)dup2(fileno(fp), STDIN_FILENO);
- if (gzipped)
- execl(PATH_GZCAT, gzcat_argv0, (char *)NULL);
- else
- execl(PATH_CAT, "cat", (char *)NULL);
- err(1, "execl");
-}
-
-static int
-str_to_trace(const char *str)
-{
- if (!strcmp(str, "imsg"))
- return TRACE_IMSG;
- if (!strcmp(str, "io"))
- return TRACE_IO;
- if (!strcmp(str, "smtp"))
- return TRACE_SMTP;
- if (!strcmp(str, "filters"))
- return TRACE_FILTERS;
- if (!strcmp(str, "mta"))
- return TRACE_MTA;
- if (!strcmp(str, "bounce"))
- return TRACE_BOUNCE;
- if (!strcmp(str, "scheduler"))
- return TRACE_SCHEDULER;
- if (!strcmp(str, "lookup"))
- return TRACE_LOOKUP;
- if (!strcmp(str, "stat"))
- return TRACE_STAT;
- if (!strcmp(str, "rules"))
- return TRACE_RULES;
- if (!strcmp(str, "mproc"))
- return TRACE_MPROC;
- if (!strcmp(str, "expand"))
- return TRACE_EXPAND;
- if (!strcmp(str, "all"))
- return ~TRACE_DEBUG;
- errx(1, "invalid trace keyword: %s", str);
- return (0);
-}
-
-static int
-str_to_profile(const char *str)
-{
- if (!strcmp(str, "imsg"))
- return PROFILE_IMSG;
- if (!strcmp(str, "queue"))
- return PROFILE_QUEUE;
- errx(1, "invalid profile keyword: %s", str);
- return (0);
-}
-
-static int
-is_gzip_buffer(const char *buffer)
-{
- uint16_t magic;
-
- memcpy(&magic, buffer, sizeof magic);
-#define GZIP_MAGIC 0x8b1f
- return (magic == GZIP_MAGIC);
-}
-
-static int
-is_gzip_fp(FILE *fp)
-{
- uint8_t magic[2];
- int ret = 0;
-
- if (fread(&magic, 1, sizeof magic, fp) != sizeof magic)
- goto end;
-
- ret = is_gzip_buffer((const char *)&magic);
-end:
- fseek(fp, 0, SEEK_SET);
- return ret;
-}
-
-
-/* XXX */
-/*
- * queue supports transparent encryption.
- * encrypted chunks are prefixed with an API version byte
- * which we ensure is unambiguous with gzipped / plain
- * objects.
- */
-
-static int
-is_encrypted_buffer(const char *buffer)
-{
- uint8_t magic;
-
- magic = *buffer;
-#define ENCRYPTION_MAGIC 0x1
- return (magic == ENCRYPTION_MAGIC);
-}
-
-static int
-is_encrypted_fp(FILE *fp)
-{
- uint8_t magic;
- int ret = 0;
-
- if (fread(&magic, 1, sizeof magic, fp) != sizeof magic)
- goto end;
-
- ret = is_encrypted_buffer((const char *)&magic);
-end:
- fseek(fp, 0, SEEK_SET);
- return ret;
-}
diff --git a/smtpd/smtpctl/Makefile b/smtpd/smtpctl/Makefile
deleted file mode 100644
index ef8148be..00000000
--- a/smtpd/smtpctl/Makefile
+++ /dev/null
@@ -1,56 +0,0 @@
-# $OpenBSD: Makefile,v 1.47 2018/07/03 01:34:43 mortimer Exp $
-
-.PATH: ${.CURDIR}/..
-
-PROG= smtpctl
-BINOWN= root
-BINGRP= _smtpq
-
-BINMODE?=2555
-
-BINDIR= /usr/sbin
-MAN= smtpctl.8 aliases.5 forward.5 makemap.8 newaliases.8
-
-CFLAGS+= -fstack-protector-all
-CFLAGS+= -I${.CURDIR}/..
-CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS+= -Wmissing-declarations
-CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
-CFLAGS+= -Wsign-compare
-CFLAGS+= -Werror-implicit-function-declaration
-CFLAGS+= -DNO_IO
-CFLAGS+= -DCONFIG_MINIMUM
-YFLAGS=
-
-SRCS= enqueue.c
-SRCS+= parser.c
-SRCS+= log.c
-SRCS+= envelope.c
-SRCS+= crypto.c
-SRCS+= queue_backend.c
-SRCS+= queue_fs.c
-SRCS+= smtpctl.c
-SRCS+= util.c
-SRCS+= compress_backend.c
-SRCS+= compress_gzip.c
-SRCS+= to.c
-SRCS+= expand.c
-SRCS+= tree.c
-SRCS+= config.c
-SRCS+= dict.c
-SRCS+= aliases.c
-SRCS+= limit.c
-SRCS+= makemap.c
-SRCS+= parse.y
-SRCS+= mailaddr.c
-SRCS+= table.c
-SRCS+= table_static.c
-SRCS+= table_db.c
-SRCS+= table_getpwnam.c
-SRCS+= table_proc.c
-SRCS+= unpack_dns.c
-SRCS+= spfwalk.c
-
-LDADD+= -levent -lutil -lz -lcrypto
-DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBZ} ${LIBCRYPTO}
-.include <bsd.prog.mk>
diff --git a/smtpd/smtpd-api.h b/smtpd/smtpd-api.h
deleted file mode 100644
index f83edd05..00000000
--- a/smtpd/smtpd-api.h
+++ /dev/null
@@ -1,290 +0,0 @@
-/* $OpenBSD: smtpd-api.h,v 1.36 2018/12/23 16:06:24 gilles Exp $ */
-
-/*
- * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
- * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _SMTPD_API_H_
-#define _SMTPD_API_H_
-
-#include "dict.h"
-#include "tree.h"
-
-struct mailaddr {
- char user[SMTPD_MAXLOCALPARTSIZE];
- char domain[SMTPD_MAXDOMAINPARTSIZE];
-};
-
-#define PROC_QUEUE_API_VERSION 2
-
-enum {
- PROC_QUEUE_OK,
- PROC_QUEUE_FAIL,
- PROC_QUEUE_INIT,
- PROC_QUEUE_CLOSE,
- PROC_QUEUE_MESSAGE_CREATE,
- PROC_QUEUE_MESSAGE_DELETE,
- PROC_QUEUE_MESSAGE_COMMIT,
- PROC_QUEUE_MESSAGE_FD_R,
- PROC_QUEUE_ENVELOPE_CREATE,
- PROC_QUEUE_ENVELOPE_DELETE,
- PROC_QUEUE_ENVELOPE_LOAD,
- PROC_QUEUE_ENVELOPE_UPDATE,
- PROC_QUEUE_ENVELOPE_WALK,
-};
-
-#define PROC_SCHEDULER_API_VERSION 2
-
-struct scheduler_info;
-
-enum {
- PROC_SCHEDULER_OK,
- PROC_SCHEDULER_FAIL,
- PROC_SCHEDULER_INIT,
- PROC_SCHEDULER_INSERT,
- PROC_SCHEDULER_COMMIT,
- PROC_SCHEDULER_ROLLBACK,
- PROC_SCHEDULER_UPDATE,
- PROC_SCHEDULER_DELETE,
- PROC_SCHEDULER_HOLD,
- PROC_SCHEDULER_RELEASE,
- PROC_SCHEDULER_BATCH,
- PROC_SCHEDULER_MESSAGES,
- PROC_SCHEDULER_ENVELOPES,
- PROC_SCHEDULER_SCHEDULE,
- PROC_SCHEDULER_REMOVE,
- PROC_SCHEDULER_SUSPEND,
- PROC_SCHEDULER_RESUME,
-};
-
-enum envelope_flags {
- EF_AUTHENTICATED = 0x01,
- EF_BOUNCE = 0x02,
- EF_INTERNAL = 0x04, /* Internal expansion forward */
-
- /* runstate, not saved on disk */
-
- EF_PENDING = 0x10,
- EF_INFLIGHT = 0x20,
- EF_SUSPEND = 0x40,
- EF_HOLD = 0x80,
-};
-
-struct evpstate {
- uint64_t evpid;
- uint16_t flags;
- uint16_t retry;
- time_t time;
-};
-
-enum delivery_type {
- D_MDA,
- D_MTA,
- D_BOUNCE,
-};
-
-struct scheduler_info {
- uint64_t evpid;
- enum delivery_type type;
- uint16_t retry;
- time_t creation;
- time_t ttl;
- time_t lasttry;
- time_t lastbounce;
- time_t nexttry;
-};
-
-#define SCHED_REMOVE 0x01
-#define SCHED_EXPIRE 0x02
-#define SCHED_UPDATE 0x04
-#define SCHED_BOUNCE 0x08
-#define SCHED_MDA 0x10
-#define SCHED_MTA 0x20
-
-#define PROC_TABLE_API_VERSION 2
-
-struct table_open_params {
- uint32_t version;
- char name[LINE_MAX];
-};
-
-enum table_service {
- K_NONE = 0x000,
- K_ALIAS = 0x001, /* returns struct expand */
- K_DOMAIN = 0x002, /* returns struct destination */
- K_CREDENTIALS = 0x004, /* returns struct credentials */
- K_NETADDR = 0x008, /* returns struct netaddr */
- K_USERINFO = 0x010, /* returns struct userinfo */
- K_SOURCE = 0x020, /* returns struct source */
- K_MAILADDR = 0x040, /* returns struct mailaddr */
- K_ADDRNAME = 0x080, /* returns struct addrname */
- K_MAILADDRMAP = 0x100, /* returns struct maddrmap */
- K_RELAYHOST = 0x200, /* returns struct relayhost */
- K_STRING = 0x400,
- K_REGEX = 0x800,
-};
-#define K_ANY 0xfff
-
-enum {
- PROC_TABLE_OK,
- PROC_TABLE_FAIL,
- PROC_TABLE_OPEN,
- PROC_TABLE_CLOSE,
- PROC_TABLE_UPDATE,
- PROC_TABLE_CHECK,
- PROC_TABLE_LOOKUP,
- PROC_TABLE_FETCH,
-};
-
-enum enhanced_status_code {
- /* 0.0 */
- ESC_OTHER_STATUS = 00,
-
- /* 1.x */
- ESC_OTHER_ADDRESS_STATUS = 10,
- ESC_BAD_DESTINATION_MAILBOX_ADDRESS = 11,
- ESC_BAD_DESTINATION_SYSTEM_ADDRESS = 12,
- ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX = 13,
- ESC_DESTINATION_MAILBOX_ADDRESS_AMBIGUOUS = 14,
- ESC_DESTINATION_ADDRESS_VALID = 15,
- ESC_DESTINATION_MAILBOX_HAS_MOVED = 16,
- ESC_BAD_SENDER_MAILBOX_ADDRESS_SYNTAX = 17,
- ESC_BAD_SENDER_SYSTEM_ADDRESS = 18,
-
- /* 2.x */
- ESC_OTHER_MAILBOX_STATUS = 20,
- ESC_MAILBOX_DISABLED = 21,
- ESC_MAILBOX_FULL = 22,
- ESC_MESSAGE_LENGTH_TOO_LARGE = 23,
- ESC_MAILING_LIST_EXPANSION_PROBLEM = 24,
-
- /* 3.x */
- ESC_OTHER_MAIL_SYSTEM_STATUS = 30,
- ESC_MAIL_SYSTEM_FULL = 31,
- ESC_SYSTEM_NOT_ACCEPTING_MESSAGES = 32,
- ESC_SYSTEM_NOT_CAPABLE_OF_SELECTED_FEATURES = 33,
- ESC_MESSAGE_TOO_BIG_FOR_SYSTEM = 34,
- ESC_SYSTEM_INCORRECTLY_CONFIGURED = 35,
-
- /* 4.x */
- ESC_OTHER_NETWORK_ROUTING_STATUS = 40,
- ESC_NO_ANSWER_FROM_HOST = 41,
- ESC_BAD_CONNECTION = 42,
- ESC_DIRECTORY_SERVER_FAILURE = 43,
- ESC_UNABLE_TO_ROUTE = 44,
- ESC_MAIL_SYSTEM_CONGESTION = 45,
- ESC_ROUTING_LOOP_DETECTED = 46,
- ESC_DELIVERY_TIME_EXPIRED = 47,
-
- /* 5.x */
- ESC_INVALID_RECIPIENT = 50,
- ESC_INVALID_COMMAND = 51,
- ESC_SYNTAX_ERROR = 52,
- ESC_TOO_MANY_RECIPIENTS = 53,
- ESC_INVALID_COMMAND_ARGUMENTS = 54,
- ESC_WRONG_PROTOCOL_VERSION = 55,
-
- /* 6.x */
- ESC_OTHER_MEDIA_ERROR = 60,
- ESC_MEDIA_NOT_SUPPORTED = 61,
- ESC_CONVERSION_REQUIRED_AND_PROHIBITED = 62,
- ESC_CONVERSION_REQUIRED_BUT_NOT_SUPPORTED = 63,
- ESC_CONVERSION_WITH_LOSS_PERFORMED = 64,
- ESC_CONVERSION_FAILED = 65,
-
- /* 7.x */
- ESC_OTHER_SECURITY_STATUS = 70,
- ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED = 71,
- ESC_MAILING_LIST_EXPANSION_PROHIBITED = 72,
- ESC_SECURITY_CONVERSION_REQUIRED_NOT_POSSIBLE = 73,
- ESC_SECURITY_FEATURES_NOT_SUPPORTED = 74,
- ESC_CRYPTOGRAPHIC_FAILURE = 75,
- ESC_CRYPTOGRAPHIC_ALGORITHM_NOT_SUPPORTED = 76,
- ESC_MESSAGE_INTEGRITY_FAILURE = 77,
-};
-
-enum enhanced_status_class {
- ESC_STATUS_OK = 2,
- ESC_STATUS_TEMPFAIL = 4,
- ESC_STATUS_PERMFAIL = 5,
-};
-
-static inline uint32_t
-evpid_to_msgid(uint64_t evpid)
-{
- return (evpid >> 32);
-}
-
-static inline uint64_t
-msgid_to_evpid(uint32_t msgid)
-{
- return ((uint64_t)msgid << 32);
-}
-
-
-/* esc.c */
-const char *esc_code(enum enhanced_status_class, enum enhanced_status_code);
-const char *esc_description(enum enhanced_status_code);
-
-
-/* queue */
-void queue_api_on_close(int(*)(void));
-void queue_api_on_message_create(int(*)(uint32_t *));
-void queue_api_on_message_commit(int(*)(uint32_t, const char*));
-void queue_api_on_message_delete(int(*)(uint32_t));
-void queue_api_on_message_fd_r(int(*)(uint32_t));
-void queue_api_on_envelope_create(int(*)(uint32_t, const char *, size_t, uint64_t *));
-void queue_api_on_envelope_delete(int(*)(uint64_t));
-void queue_api_on_envelope_update(int(*)(uint64_t, const char *, size_t));
-void queue_api_on_envelope_load(int(*)(uint64_t, char *, size_t));
-void queue_api_on_envelope_walk(int(*)(uint64_t *, char *, size_t));
-void queue_api_on_message_walk(int(*)(uint64_t *, char *, size_t,
- uint32_t, int *, void **));
-void queue_api_no_chroot(void);
-void queue_api_set_chroot(const char *);
-void queue_api_set_user(const char *);
-int queue_api_dispatch(void);
-
-/* scheduler */
-void scheduler_api_on_init(int(*)(void));
-void scheduler_api_on_insert(int(*)(struct scheduler_info *));
-void scheduler_api_on_commit(size_t(*)(uint32_t));
-void scheduler_api_on_rollback(size_t(*)(uint32_t));
-void scheduler_api_on_update(int(*)(struct scheduler_info *));
-void scheduler_api_on_delete(int(*)(uint64_t));
-void scheduler_api_on_hold(int(*)(uint64_t, uint64_t));
-void scheduler_api_on_release(int(*)(int, uint64_t, int));
-void scheduler_api_on_batch(int(*)(int, int *, size_t *, uint64_t *, int *));
-void scheduler_api_on_messages(size_t(*)(uint32_t, uint32_t *, size_t));
-void scheduler_api_on_envelopes(size_t(*)(uint64_t, struct evpstate *, size_t));
-void scheduler_api_on_schedule(int(*)(uint64_t));
-void scheduler_api_on_remove(int(*)(uint64_t));
-void scheduler_api_on_suspend(int(*)(uint64_t));
-void scheduler_api_on_resume(int(*)(uint64_t));
-void scheduler_api_no_chroot(void);
-void scheduler_api_set_chroot(const char *);
-void scheduler_api_set_user(const char *);
-int scheduler_api_dispatch(void);
-
-/* table */
-void table_api_on_update(int(*)(void));
-void table_api_on_check(int(*)(int, struct dict *, const char *));
-void table_api_on_lookup(int(*)(int, struct dict *, const char *, char *, size_t));
-void table_api_on_fetch(int(*)(int, struct dict *, char *, size_t));
-int table_api_dispatch(void);
-const char *table_api_get_name(void);
-
-#endif
diff --git a/smtpd/smtpd-defines.h b/smtpd/smtpd-defines.h
deleted file mode 100644
index f22a546f..00000000
--- a/smtpd/smtpd-defines.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* $OpenBSD: smtpd-defines.h,v 1.12 2020/02/24 16:16:08 millert Exp $ */
-
-/*
- * Copyright (c) 2013 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef nitems
-#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
-#endif
-
-#define SMTPD_TABLENAME_SIZE (64 + 1)
-#define SMTPD_TAG_SIZE (32 + 1)
-
-/* buffer sizes for email address components */
-#define SMTPD_MAXLOCALPARTSIZE (255 + 1)
-#define SMTPD_MAXDOMAINPARTSIZE (255 + 1)
-#define SMTPD_MAXMAILADDRSIZE (255 + 1)
-
-/* buffer size for virtual username (can be email addresses) */
-#define SMTPD_VUSERNAME_SIZE (255 + 1)
-#define SMTPD_SUBADDRESS_SIZE (255 + 1)
-
-#ifndef SMTPD_USER
-#define SMTPD_USER "_smtpd"
-#endif
-#ifndef PATH_CHROOT
-#define PATH_CHROOT "/var/empty"
-#endif
-#ifndef SMTPD_QUEUE_USER
-#define SMTPD_QUEUE_USER "_smtpq"
-#endif
-#ifndef SMTPD_QUEUE_GROUP
-#define SMTPD_QUEUE_GROUP "_smtpq"
-#endif
-#ifndef PATH_SPOOL
-#define PATH_SPOOL "/var/spool/smtpd"
-#endif
-
-#ifndef PATH_MAILLOCAL
-#define PATH_MAILLOCAL PATH_LIBEXEC "/mail.local"
-#endif
-
-#ifndef PATH_MAKEMAP
-#define PATH_MAKEMAP "/usr/sbin/makemap"
-#endif
-
-#define SUBADDRESSING_DELIMITER "+"
-
-
-/* sendmail compat */
-
-#define EX_OK 0
-#define EX_NOHOST 68
-#define EX_UNAVAILABLE 69
-#define EX_SOFTWARE 70
-#define EX_TEMPFAIL 75
diff --git a/smtpd/smtpd-filters.7 b/smtpd/smtpd-filters.7
deleted file mode 100644
index 5af7008e..00000000
--- a/smtpd/smtpd-filters.7
+++ /dev/null
@@ -1,653 +0,0 @@
-.\" $OpenBSD: smtpd-filters.7,v 1.6 2020/04/25 09:44:02 eric Exp $
-.\"
-.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org>
-.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
-.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.\"
-.Dd $Mdocdate: April 25 2020 $
-.Dt FILTERS 7
-.Os
-.Sh NAME
-.Nm filters
-.Nd filtering API for the
-.Xr smtpd 8
-daemon
-.Sh DESCRIPTION
-The
-.Xr smtpd 8
-daemon provides a Simple Mail Transfer Protocol (SMTP) implementation
-that allows an ordinary machine to become Mail eXchangers (MX).
-Many features that are commonly used by MX,
-such as delivery reporting or Spam filtering,
-are outside the scope of SMTP and too complex to fit in
-.Xr smtpd 8 .
-.Pp
-Because an MX needs to provide these features,
-.Xr smtpd 8
-provides an API to extend its behavior through pluggable
-.Nm .
-.Pp
-At runtime,
-.Xr smtpd 8
-can report events to
-.Nm
-and query what it should answer to these events.
-This allows the decision logic to rely on third-party programs.
-.Sh DESIGN
-The
-.Nm
-are programs that run as unique standalone processes,
-they do not share
-.Xr smtpd 8
-memory space.
-They are executed by
-.Xr smtpd 8
-at startup and expected to run in an infinite loop,
-reading events and filtering requests from
-.Xr stdin 4 ,
-writing responses to
-.Xr stdout 4
-and logging to
-.Xr stderr 4 .
-They are not allowed to terminate.
-.Pp
-Because
-.Nm
-are standalone programs that communicate with
-.Xr smtpd 8
-through
-.Xr fd 4 ,
-they may run as different users than
-.Xr smtpd 8
-and may be written in any language.
-The
-.Nm
-must not use blocking I/O,
-they must support answering asynchronously to
-.Xr smtpd 8 .
-.Sh REPORT AND FILTER
-The API relies on two streams,
-report and filter.
-.Pp
-The report stream is a one-way stream which allows
-.Xr smtpd 8
-to inform
-.Nm
-in real-time about events that are occurring in the daemon.
-The report events do not expect an answer from
-.Nm ,
-it is just meant to provide them with informations.
-A filter should be able to replicate the
-.Xr smtpd 8
-state for a session by gathering informations coming from report events.
-No decision is ever taken by the report stream.
-.Pp
-The filter stream is a two-way stream which allows
-.Xr smtpd 8
-to query
-.Nm
-about what it should do with a session at a given phase.
-The filter requests expects an answer from
-.Nm ,
-.Xr smtpd 8
-will not let the session move forward until then.
-A decision must always be taken by the filter stream.
-.Pp
-It is sometimes possible to rely on filter requests to gather information,
-but because a reponse is expected by
-.Xr smtpd 8 ,
-this is more costly than using report events.
-The correct pattern for writing filters is to use the report events to
-create a local state for a session,
-then use filter requests to take decisions based on this state.
-The only case when using filter request instead of report events is correct,
-is when a decision is required for the filter request and there is no need for
-more information than that of the event.
-.Sh PROTOCOL
-The protocol is straightforward,
-it consists of a human-readable line exchanges between
-.Nm
-and
-.Xr smtpd 8
-through
-.Xr fd 4 .
-.Pp
-The protocol begins with a handshake.
-First,
-.Xr smtpd 8
-provides
-.Nm
-with general configuration information in the form of key-value lines:
-.Bd -literal -offset indent
-config|smtpd-version|6.6.1
-config|smtp-session-timeout|300
-config|subsystem|smtp-in
-config|ready
-.Ed
-.Pp
-Then,
-.Nm
-register the stream,
-subsystem and event they want to handle:
-.Bd -literal -offset indent
-register|report|smtp-in|link-connect
-register|ready
-.Ed
-.Pp
-Finally,
-.Xr smtpd 8
-will emit report events and filter requests,
-expecting
-.Nm
-to react accordingly either by responding or not depending on the stream:
-.Bd -literal -offset indent
-report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
-report|0.5|1576147242.200225|smtp-in|link-connect|7641dfb3798eb5bf|mail.openbsd.org|pass|199.185.178.25:31205|45.77.67.80:25
-report|0.5|1576148447.982572|smtp-in|link-connect|7641dfc063102cbd|mail.openbsd.org|pass|199.185.178.25:24786|45.77.67.80:25
-.Ed
-.Pp
-The char
-.Dq |
-may only appear in the last field of a payload,
-in which case it should be considered a regular char and not a separator.
-Other fields have strict formatting excluding the possibility of having a
-.Dq | .
-.Pp
-The list of subsystems and events,
-as well as the format of requests and reponses,
-will be documented in the sections below.
-.Sh CONFIGURATION
-During the initial handshake,
-.Xr smtpd 8
-will emit a serie of configuration keys and values.
-The list is meant to be ignored by
-.Nm
-that do not require it and consumed gracefully by filters that do.
-.Pp
-There are currently three keys:
-.Bd -literal -offset indent
-config|smtpd-version|6.6.1
-config|smtp-session-timeout|300
-config|subsystem|smtp-in
-.Ed
-.Pp
-When
-.Xr smtpd 8
-has sent all configuration keys it emits the following line:
-.Bd -literal -offset indent
-config|ready
-.Ed
-.Sh REPORT EVENTS
-There is currently only one subsystem supported in the API:
-smtp-in.
-.Pp
-Each report event is generated by
-.Xr smtpd 8
-as a single line similar to the one below:
-.Bd -literal -offset indent
-report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
-.Ed
-.Pp
-The format consists of a protocol prefix containing the stream,
-the protocol version,
-the timestamp,
-the subsystem,
-the event and the unique session identifier separated by
-.Dq | :
-.Bd -literal -offset indent
-report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00
-.Ed
-.Pp
-It is followed by a suffix containing the event-specific parameters,
-also separated by
-.Dq | :
-.Bd -literal -offset indent
-mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
-.Ed
-.Pp
-The list of events and event-specific parameters are provided here for smtp-in:
-.Bl -tag -width Ds
-.It Ic link-connect : Ar rdns fcrdns src dest
-This event is generated upon connection.
-.Pp
-.Ar rdns
-contains the reverse DNS hostname for the remote end or an empty string if none.
-.Pp
-.Ar fcrdns
-contains the string
-.Dq pass
-or
-.Dq fail
-depending on if the remote end validates FCrDNS.
-.Pp
-.Ar src
-holds either the IP address and port from source address,
-in the format
-.Dq address:port
-or the path to a UNIX socket in the format
-.Dq unix:/path .
-.Pp
-.Ar dest
-holds either the IP address and port from destination address,
-in the format
-.Dq address:port
-or the path to a UNIX socket in the format
-.Dq unix:/path .
-.It Ic link-greeting : Ar hostname
-This event is generated upon display of the server banner.
-.Pp
-.Ar hostname
-contains the hostname displayed in the banner.
-.It Ic link-identify : Ar method identity
-This event is generated upon
-.Dq HELO
-or
-.Dq EHLO
-command from the client.
-.Pp
-.Ar method
-contains the string
-.Dq HELO
-or
-.Dq EHLO
-indicating the method used by the client.
-.Pp
-.Ar identity
-contains the identity provided by the client.
-.It Ic link-tls : Ar tls-string
-This event is generated upon successful negotiation of TLS.
-.Pp
-.Ar tls-string
-contains a colon-separated list of TLS properties including the TLS version,
-the cipher suite used by the session and the cipher strenght in bits.
-.It Ic link-disconnect
-This event is generated upon disconnection of the client.
-.It Ic link-auth : Ar username result
-This event is generated upon authentication attempt of the client.
-.Pp
-.Ar username
-contains the username used for the authentication attempt.
-.Pp
-.Ar result
-contains the string
-.Dq pass ,
-.Dq fail
-or
-.Dq error
-depending on the result of the authentication attempt.
-.It Ic tx-reset : Op message-id
-This event is generated when a transaction is reset.
-.Pp
-If reset happend while in a transaction,
-.Ar message-id
-contains the identifier of the transaction being reset.
-.It Ic tx-begin : Ar message-id
-This event is generated when a transaction is initiated.
-.Pp
-.Ar message-id
-contains the identifier for the transaction.
-.It Ic tx-mail : Ar message-id Ar result address
-This event is generated when client emits
-.Dq MAIL FROM .
-.Pp
-.Ar message-id
-contains the identifier for the transaction.
-.Pp
-.Ar result
-contains
-.Dq ok
-if the sender was accepted,
-.Dq permfail
-if it was rejected
-or
-.Dq tempfail
-if it was rejected for a transient error.
-.Pp
-.Ar address
-contains the e-mail address of the sender.
-The address is normalized and sanitized,
-the protocol
-.Dq <
-and
-.Dq >
-are removed and so are parameters to
-.Dq MAIL FROM .
-.It Ic tx-rcpt : Ar message-id Ar result address
-This event is generated when client emits
-.Dq RCPT TO .
-.Pp
-.Ar message-id
-contains the identifier for the transaction.
-.Pp
-.Ar result
-contains
-.Dq ok
-if the recipient was accepted,
-.Dq permfail
-if it was rejected
-or
-.Dq tempfail
-if it was rejected for a transient error.
-.Pp
-.Ar address
-contains the e-mail address of the recipient.
-The address is normalized and sanitized,
-the protocol
-.Dq <
-and
-.Dq >
-are removed and so are parameters to
-.Dq RCPT TO .
-.It Ic tx-envelope : Ar message-id Ar envelope-id
-This event is generated when an envelope is accepted.
-.Pp
-.Ar envelope-id
-contains the unique identifier for the envelope.
-.It Ic tx-data : Ar message-id Ar result
-This event is generated when client has emitted
-.Dq DATA .
-.Pp
-.Ar message-id
-contains the unique identifier for the transaction.
-.Pp
-.Ar result
-contains
-.Dq ok
-if server accepted to process the message,
-.Dq permfail
-if it has not accepted and
-.Dq tempfail
-if a transient error is preventing the processing of message.
-.It Ic tx-commit : Ar message-id Ar message-size
-This event is generated when a transaction has been accepted by the server.
-.Pp
-.Ar message-id
-contains the unique identifier for the SMTP transaction.
-.Pp
-.Ar message-size
-contains the size of the message submitted in the
-.Dq DATA
-phase of the SMTP transaction.
-.It Ic tx-rollback : Ar message-id
-This event is generated when a transaction has been rejected by the server.
-.Pp
-.Ar message-id
-contains the unique identifier for the SMTP transaction.
-.It Ic protocol-client : Ar command
-This event is generated for every command submitted by the client.
-It contains the raw command as received by the server.
-.Pp
-.Ar command
-contains the command emitted by the client to the server.
-.It Ic protocol-server : Ar response
-This event is generated for every response emitted by the server.
-It contains the raw response as emitted by the server.
-.Pp
-.Ar response
-contains the response emitted by the server to the client.
-.It Ic filter-report : Ar filter-kind Ar name message
-This event is generated when a filter emits a report.
-.Pp
-.Ar filter-kind may be either
-.Dq builtin
-or
-.Dq proc
-depending on if the filter is an
-.Xr smtpd 8
-builtin filter or a proc filter implementing the API.
-.Pp
-.Ar name
-is the name of the filter that generated the report.
-.Pp
-.Ar message
-is a filter-specific message.
-.It Ic filter-response : Ar phase response Op param
-This event is generated when a filter responds to a filtering request.
-.Pp
-.Ar phase
-contains the phase name for the request.
-The phases are documented in the next section.
-.Pp
-.Ar response
-contains the response of the filter to the request,
-it is either one of
-.Dq proceed ,
-.Dq report ,
-.Dq reject ,
-.Dq disconnect ,
-.Dq junk or
-.Dq rewrite .
-.Pp
-If specified,
-.Ar param
-is the parameter to the response.
-.It Ic timeout
-This event is generated when a timeout happens for a session.
-.El
-.Sh FILTER REQUESTS
-There is currently only one subsystem supported in the API:
-smtp-in.
-.Pp
-The filter requests allow
-.Xr smtpd 8
-to query
-.Nm
-about what to do with a session at a particular phase.
-In addition,
-they allow
-.Nm
-to alter the content of a message by adding,
-modifying,
-or suppressing lines of input in a way that is similar to what program like
-.Xr sed 1
-or
-.Xr grep 1
-would do.
-.Pp
-Each filter request is generated by
-.Xr smtpd 8
-as a single line similar to the one below:
-.Bd -literal -offset indent
-filter|0.5|1576146008.006099|smtp-in|connect|7641df9771b4ed00|1ef1c203cc576e5d|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
-.Ed
-.Pp
-The format consists of a protocol prefix containing the stream,
-the protocol version,
-the timestamp,
-the subsystem,
-the filtering phase,
-the unique session identifier and an opaque token separated by
-.Dq |
-that the filter should provide in its response:
-.Bd -literal -offset indent
-filter|0.5|1576146008.006099|smtp-in|connect|7641df9771b4ed00|1ef1c203cc576e5d
-.Ed
-.Pp
-It is followed by a suffix containing the phase-specific parameters to the
-filter request,
-also separated by
-.Dq | :
-.Bd -literal -offset indent
-mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
-.Ed
-.Pp
-Unlike with report events,
-.Xr smtpd 8
-expects answers from filter requests and will not allow a session to move
-forward before the filter has instructed
-.Xr smtpd 8
-what to do with it.
-.Pp
-For all phases,
-excepted
-.Dq data-line ,
-the responses must follow the same construct,
-a message type
-.Dq filter-result ,
-followed by the unique session id,
-the opaque token,
-a decision and optional decision-specific parameters:
-.Bd -literal -offset indent
-filter-result|7641df9771b4ed00|1ef1c203cc576e5d|proceed
-filter-result|7641df9771b4ed00|1ef1c203cc576e5d|reject|550 nope
-.Ed
-.Pp
-The possible decisions to a
-.Dq filter-result
-message will be described below.
-.Pp
-For the
-.Dq data-line
-phase,
-.Nm
-are fed with a stream of lines corresponding to the message to filter,
-and terminated by a single dot:
-.Bd -literal -offset indent
-filter|0.5|1576146008.006099|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|line 1
-filter|0.5|1576146008.006103|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|line 2
-filter|0.5|1576146008.006105|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|.
-.Ed
-.Pp
-They are expected to produce an output stream similarly terminate by a single
-dot.
-A filter may inject,
-suppress,
-modify or echo back the lines it receives.
-Ultimately,
-.Xr smtpd 8
-will assume that the message consists of the output from
-.Nm .
-.Pp
-Note that filters may be chained and the lines that are input into a filter
-are the lines that are output from previous filter.
-.Pp
-The response to
-.Dq data-line
-requests use their own construct.
-A
-.Dq filter-dataline
-prefix,
-followed by the unique session identifier,
-the opaque token and the output line as follows:
-.Bd -literal -offset indent
-filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|line 1
-filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|line 2
-filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|.
-.Ed
-.Pp
-The list of events and event-specific parameters are provided here for smtp-in:
-.Bl -tag -width Ds
-.It Ic connect : Ar rdns fcrdns src dest
-This request is emitted after connection,
-before the banner is displayed.
-.It Ic helo : Ar identity
-This request is emitted after the client has emitted
-.Dq HELO .
-.It Ic ehlo : Ar identity
-This request is emitted after the client has emitted
-.Dq EHLO .
-.It Ic starttls : Ar tls-string
-This request is emitted after the client has requested
-.Dq STARTTLS .
-.It Ic auth : Ar auth
-This request is emitted after the client has requested
-.Dq AUTH .
-.It Ic mail-from : Ar address
-This request is emitted after the client has requested
-.Dq MAIL FROM .
-.It Ic rcpt-to : Ar address
-This request is emitted after the client has requested
-.Dq RCPT TO .
-.It Ic data
-This request is emitted after the client has requested
-.Dq DATA .
-.It Ic data-line : Ar line
-This request is emitted for each line of input in the
-.Dq DATA
-phase.
-The lines are raw dot-escaped SMTP DATA input,
-terminated with a single dot.
-.It Ic commit
-This request is emitted after the final single dot is received.
-.El
-.Pp
-For every filtering phase,
-excepted
-.Dq data-line ,
-the following decisions may be taken by a filter:
-.Bl -tag -width Ds
-.It Ic proceed
-No action is taken,
-session or transaction may be passed to the next filter.
-.It Ic junk
-The session or transaction is marked as Spam.
-.Xr smtpd 8
-will prepend a
-.Dq X-Spam
-header to the message.
-.It Ic reject Ar error
-The command is rejected with the message
-.Ar error .
-The message must be a valid SMTP message including status code,
-5xx or 4xx.
-.Pp
-Messages starting with a 5xx status result in a permanent failure,
-those starting with a 4xx status result in a temporary failure.
-.Pp
-Messages starting with a 421 status will result in a client disconnect.
-.It Ic disconnect Ar error
-The client is disconnected with the message
-.Ar error .
-The message must be a valid SMTP message including status code,
-5xx or 4xx.
-.Pp
-Messages starting with a 5xx status result in a permanent failure,
-those starting with a 4xx status result in a temporary failure.
-.It Ic rewrite Ar parameter
-The command parameter is rewritten.
-.Pp
-This decision allows a filter to perform a rewrite of client-submitted
-commands before they are processed by the SMTP engine.
-.Ar parameter
-is expected to be a valid SMTP parameter for the command.
-.It Ic report Ar parameter
-Generates a report with
-.Ar parameter
-for this filter.
-.El
-.\".Sh EXAMPLES
-.\"This example filter written in
-.\".Xr sh 1
-.\"will echo back...
-.\".Bd -literal -offset indent
-.\"XXX
-.\".Ed
-.\".Pp
-.\"This example filter will filter...
-.\".Bd -literal -offset indent
-.\"XXX
-.\".Ed
-.\".Pp
-.\"Note that libraries may provide a simpler interface to
-.\".Nm
-.\"that does not require implementing the protocol itself.
-.\".Ed
-.Sh SEE ALSO
-.Xr smtpd 8
-.Sh HISTORY
-.Nm
-first appeared in
-.Ox 6.6 .
diff --git a/smtpd/smtpd.8 b/smtpd/smtpd.8
deleted file mode 100644
index e3429f07..00000000
--- a/smtpd/smtpd.8
+++ /dev/null
@@ -1,167 +0,0 @@
-.\" $OpenBSD: smtpd.8,v 1.32 2017/01/03 22:11:39 jmc Exp $
-.\"
-.\" Copyright (c) 2012, Eric Faurot <eric@openbsd.org>
-.\" Copyright (c) 2008, Gilles Chehade <gilles@poolp.org>
-.\" Copyright (c) 2008, Pierre-Yves Ritschard <pyr@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.
-.\"
-.Dd $Mdocdate: January 3 2017 $
-.Dt SMTPD 8
-.Os
-.Sh NAME
-.Nm smtpd
-.Nd Simple Mail Transfer Protocol daemon
-.Sh SYNOPSIS
-.Nm
-.Op Fl dFhnv
-.Op Fl D Ar macro Ns = Ns Ar value
-.Op Fl f Ar file
-.Op Fl P Ar system
-.Op Fl T Ar trace
-.Sh DESCRIPTION
-.Nm
-is a Simple Mail Transfer Protocol
-.Pq SMTP
-daemon which can be used as a machine's primary mail system.
-.Nm
-can listen on a network interface and handle SMTP
-transactions; it can also be fed messages through the standard
-.Xr sendmail 8
-interface.
-It can relay messages through remote mail transfer agents or store them
-locally using either the mbox or maildir format.
-This implementation supports SMTP as defined by RFC 5321 as well as several
-extensions.
-A running
-.Nm
-can be controlled through
-.Xr smtpctl 8 .
-.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl D Ar macro Ns = Ns Ar value
-Define
-.Ar macro
-to be set to
-.Ar value
-on the command line.
-Overrides the definition of
-.Ar macro
-in the configuration file.
-.It Fl d
-Do not daemonize.
-If this option is specified,
-.Nm
-will run in the foreground and log to
-.Em stderr .
-.It Fl F
-Do not daemonize.
-If this option is specified,
-.Nm
-will run in the foreground and log to
-.Xr syslogd 8 .
-.It Fl f Ar file
-Specify an alternative configuration file.
-.It Fl h
-Display version and usage.
-.It Fl n
-Configtest mode.
-Only check the configuration file for validity.
-.It Fl P Ar system
-Pause a specific subsystem at startup.
-Normal operation can be resumed using
-.Xr smtpctl 8 .
-This option can be used multiple times.
-The accepted values are:
-.Pp
-.Bl -tag -width "smtpXXX" -compact
-.It mda
-Do not schedule local deliveries.
-.It mta
-Do not schedule remote transfers.
-.It smtp
-Do not listen on SMTP sockets.
-.El
-.It Fl T Ar trace
-Enables real-time tracing at startup.
-Normal operation can be resumed using
-.Xr smtpctl 8 .
-This option can be used multiple times.
-The accepted values are:
-.Pp
-.Bl -bullet -compact
-.It
-imsg
-.It
-io
-.It
-smtp (incoming sessions)
-.It
-filters
-.It
-transfer (outgoing sessions)
-.It
-bounce
-.It
-scheduler
-.It
-expand (aliases/virtual/forward expansion)
-.It
-lookup (user/credentials lookups)
-.It
-stat
-.It
-rules (matched by incoming sessions)
-.It
-mproc
-.It
-all
-.El
-.It Fl v
-Produce more verbose output.
-.El
-.Sh FILES
-.Bl -tag -width "/etc/mail/smtpd.confXXX" -compact
-.It Pa /etc/mail/mailname
-Alternate server name to use.
-.It Pa /etc/mail/smtpd.conf
-Default
-.Nm
-configuration file.
-.It Pa /var/run/smtpd.sock
-.Ux Ns -domain
-socket used for communication with
-.Xr smtpctl 8 .
-.It Pa /var/spool/smtpd/
-Spool directories for mail during processing.
-.It Pa ~/.forward
-User email forwarding information.
-.El
-.Sh SEE ALSO
-.Xr forward 5 ,
-.Xr smtpd.conf 5 ,
-.Xr mailwrapper 8 ,
-.Xr smtpctl 8
-.Sh STANDARDS
-.Rs
-.%A J. Klensin
-.%D October 2008
-.%R RFC 5321
-.%T Simple Mail Transfer Protocol
-.Re
-.Sh HISTORY
-The
-.Nm
-program first appeared in
-.Ox 4.6 .
diff --git a/smtpd/smtpd.c b/smtpd/smtpd.c
deleted file mode 100644
index f18b1446..00000000
--- a/smtpd/smtpd.c
+++ /dev/null
@@ -1,2328 +0,0 @@
-/* $OpenBSD: smtpd.c,v 1.333 2020/05/06 16:03:30 millert Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/file.h> /* Needed for flock */
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-#include <sys/mman.h>
-
-#ifdef BSD_AUTH
-#include <bsd_auth.h>
-#endif
-
-#ifdef USE_PAM
-#if defined(HAVE_SECURITY_PAM_APPL_H)
-#include <security/pam_appl.h>
-#elif defined (HAVE_PAM_PAM_APPL_H)
-#include <pam/pam_appl.h>
-#endif
-#endif
-
-#ifdef HAVE_CRYPT_H
-#include <crypt.h> /* needed for crypt() */
-#endif
-#include <dirent.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <grp.h> /* needed for setgroups */
-#include <fts.h>
-#include <grp.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <libgen.h>
-#ifdef HAVE_LOGIN_CAP_H
-#include <login_cap.h>
-#endif
-#ifdef HAVE_PATHS_H
-#include <paths.h>
-#endif
-#include <poll.h>
-#include <pwd.h>
-#include <signal.h>
-#ifdef HAVE_SHADOW_H
-#include <shadow.h> /* needed for getspnam() */
-#endif
-#include <stdio.h>
-#include <syslog.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sysexits.h>
-#include <time.h>
-#include <unistd.h>
-#ifdef HAVE_UTIL_H
-#include <util.h>
-#endif
-
-#include <openssl/ssl.h>
-#include <openssl/evp.h>
-
-#include "smtpd.h"
-#include "log.h"
-#include "ssl.h"
-
-extern char *__progname;
-
-#define SMTPD_MAXARG 32
-
-static void parent_imsg(struct mproc *, struct imsg *);
-static void usage(void);
-static int smtpd(void);
-static void parent_shutdown(void);
-static void parent_send_config(int, short, void *);
-static void parent_send_config_lka(void);
-static void parent_send_config_pony(void);
-static void parent_send_config_ca(void);
-static void parent_sig_handler(int, short, void *);
-static void forkmda(struct mproc *, uint64_t, struct deliver *);
-static int parent_forward_open(char *, char *, uid_t, gid_t);
-static struct child *child_add(pid_t, int, const char *);
-static struct mproc *start_child(int, char **, char *);
-static struct mproc *setup_peer(enum smtp_proc_type, pid_t, int);
-static void setup_peers(struct mproc *, struct mproc *);
-static void setup_done(struct mproc *);
-static void setup_proc(void);
-static struct mproc *setup_peer(enum smtp_proc_type, pid_t, int);
-static int imsg_wait(struct imsgbuf *, struct imsg *, int);
-
-static void offline_scan(int, short, void *);
-static int offline_add(char *, uid_t, gid_t);
-static void offline_done(void);
-static int offline_enqueue(char *, uid_t, gid_t);
-
-static void purge_task(void);
-static int parent_auth_user(const char *, const char *);
-static void load_pki_tree(void);
-static void load_pki_keys(void);
-
-static void fork_filter_processes(void);
-static void fork_filter_process(const char *, const char *, const char *, const char *, const char *, uint32_t);
-
-enum child_type {
- CHILD_DAEMON,
- CHILD_MDA,
- CHILD_PROCESSOR,
- CHILD_ENQUEUE_OFFLINE,
-};
-
-struct child {
- pid_t pid;
- enum child_type type;
- const char *title;
- int mda_out;
- uint64_t mda_id;
- char *path;
- char *cause;
-};
-
-struct offline {
- TAILQ_ENTRY(offline) entry;
- uid_t uid;
- gid_t gid;
- char *path;
-};
-
-#define OFFLINE_READMAX 20
-#define OFFLINE_QUEUEMAX 5
-static size_t offline_running = 0;
-TAILQ_HEAD(, offline) offline_q;
-
-static struct event config_ev;
-static struct event offline_ev;
-static struct timeval offline_timeout;
-
-static pid_t purge_pid = -1;
-
-extern char **environ;
-void (*imsg_callback)(struct mproc *, struct imsg *);
-
-enum smtp_proc_type smtpd_process;
-
-struct smtpd *env = NULL;
-
-struct mproc *p_control = NULL;
-struct mproc *p_lka = NULL;
-struct mproc *p_parent = NULL;
-struct mproc *p_queue = NULL;
-struct mproc *p_scheduler = NULL;
-struct mproc *p_pony = NULL;
-struct mproc *p_ca = NULL;
-
-const char *backend_queue = "fs";
-const char *backend_scheduler = "ramqueue";
-const char *backend_stat = "ram";
-
-int profiling = 0;
-int debug = 0;
-int foreground = 0;
-int control_socket = -1;
-
-struct tree children;
-
-/* Saved arguments to main(). */
-char **saved_argv;
-int saved_argc;
-
-static void
-parent_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct forward_req *fwreq;
- struct filter_proc *processor;
- struct deliver deliver;
- struct child *c;
- struct msg m;
- const void *data;
- const char *username, *password, *cause, *procname;
- uint64_t reqid;
- size_t sz;
- void *i;
- int fd, n, v, ret;
-
- if (imsg == NULL)
- fatalx("process %s socket closed", p->name);
-
- switch (imsg->hdr.type) {
- case IMSG_LKA_OPEN_FORWARD:
- CHECK_IMSG_DATA_SIZE(imsg, sizeof *fwreq);
- fwreq = imsg->data;
- fd = parent_forward_open(fwreq->user, fwreq->directory,
- fwreq->uid, fwreq->gid);
- fwreq->status = 0;
- if (fd == -1 && errno != ENOENT) {
- if (errno == EAGAIN)
- fwreq->status = -1;
- }
- else
- fwreq->status = 1;
- m_compose(p, IMSG_LKA_OPEN_FORWARD, 0, 0, fd,
- fwreq, sizeof *fwreq);
- return;
-
- case IMSG_LKA_AUTHENTICATE:
- /*
- * If we reached here, it means we want root to lookup
- * system user.
- */
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &username);
- m_get_string(&m, &password);
- m_end(&m);
-
- ret = parent_auth_user(username, password);
-
- m_create(p, IMSG_LKA_AUTHENTICATE, 0, 0, -1);
- m_add_id(p, reqid);
- m_add_int(p, ret);
- m_close(p);
- return;
-
- case IMSG_MDA_FORK:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_data(&m, &data, &sz);
- m_end(&m);
- if (sz != sizeof(deliver))
- fatalx("expected deliver");
- memmove(&deliver, data, sz);
- forkmda(p, reqid, &deliver);
- return;
-
- case IMSG_MDA_KILL:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_string(&m, &cause);
- m_end(&m);
-
- i = NULL;
- while ((n = tree_iter(&children, &i, NULL, (void**)&c)))
- if (c->type == CHILD_MDA &&
- c->mda_id == reqid &&
- c->cause == NULL)
- break;
- if (!n) {
- log_debug("debug: smtpd: "
- "kill request: proc not found");
- return;
- }
-
- c->cause = xstrdup(cause);
- log_debug("debug: smtpd: kill requested for %u: %s",
- c->pid, c->cause);
- kill(c->pid, SIGTERM);
- return;
-
- case IMSG_CTL_VERBOSE:
- m_msg(&m, imsg);
- m_get_int(&m, &v);
- m_end(&m);
- log_trace_verbose(v);
- return;
-
- case IMSG_CTL_PROFILE:
- m_msg(&m, imsg);
- m_get_int(&m, &v);
- m_end(&m);
- profiling = v;
- return;
-
- case IMSG_LKA_PROCESSOR_ERRFD:
- m_msg(&m, imsg);
- m_get_string(&m, &procname);
- m_end(&m);
-
- processor = dict_xget(env->sc_filter_processes_dict, procname);
- m_create(p_lka, IMSG_LKA_PROCESSOR_ERRFD, 0, 0, processor->errfd);
- m_add_string(p_lka, procname);
- m_close(p_lka);
- return;
- }
-
- errx(1, "parent_imsg: unexpected %s imsg from %s",
- imsg_to_str(imsg->hdr.type), proc_title(p->proc));
-}
-
-static void
-usage(void)
-{
- extern char *__progname;
-
- fprintf(stderr, "usage: %s [-dFhnv] [-D macro=value] "
- "[-f file] [-P system] [-T trace]\n", __progname);
- exit(1);
-}
-
-static void
-parent_shutdown(void)
-{
- pid_t pid;
-
- mproc_clear(p_ca);
- mproc_clear(p_pony);
- mproc_clear(p_control);
- mproc_clear(p_lka);
- mproc_clear(p_scheduler);
- mproc_clear(p_queue);
-
- do {
- pid = waitpid(WAIT_MYPGRP, NULL, 0);
- } while (pid != -1 || (pid == -1 && errno == EINTR));
-
- unlink(SMTPD_SOCKET);
-
- log_info("Exiting");
- exit(0);
-}
-
-static void
-parent_send_config(int fd, short event, void *p)
-{
- parent_send_config_lka();
- parent_send_config_pony();
- parent_send_config_ca();
- purge_config(PURGE_PKI);
-}
-
-static void
-parent_send_config_pony(void)
-{
- log_debug("debug: parent_send_config: configuring pony process");
- m_compose(p_pony, IMSG_CONF_START, 0, 0, -1, NULL, 0);
- m_compose(p_pony, IMSG_CONF_END, 0, 0, -1, NULL, 0);
-}
-
-void
-parent_send_config_lka()
-{
- log_debug("debug: parent_send_config_ruleset: reloading");
- m_compose(p_lka, IMSG_CONF_START, 0, 0, -1, NULL, 0);
- m_compose(p_lka, IMSG_CONF_END, 0, 0, -1, NULL, 0);
-}
-
-static void
-parent_send_config_ca(void)
-{
- log_debug("debug: parent_send_config: configuring ca process");
- m_compose(p_ca, IMSG_CONF_START, 0, 0, -1, NULL, 0);
- m_compose(p_ca, IMSG_CONF_END, 0, 0, -1, NULL, 0);
-}
-
-static void
-parent_sig_handler(int sig, short event, void *p)
-{
- struct child *child;
- int status, fail;
- pid_t pid;
- char *cause;
-
- switch (sig) {
- case SIGTERM:
- case SIGINT:
- log_debug("debug: got signal %d", sig);
- parent_shutdown();
- /* NOT REACHED */
-
- case SIGCHLD:
- do {
- int len;
- enum mda_resp_status mda_status;
- int mda_sysexit;
-
- pid = waitpid(-1, &status, WNOHANG);
- if (pid <= 0)
- continue;
-
- fail = 0;
- if (WIFSIGNALED(status)) {
- fail = 1;
- len = asprintf(&cause, "terminated; signal %d",
- WTERMSIG(status));
- mda_status = MDA_TEMPFAIL;
- mda_sysexit = 0;
- } else if (WIFEXITED(status)) {
- if (WEXITSTATUS(status) != 0) {
- fail = 1;
- len = asprintf(&cause,
- "exited abnormally");
- mda_sysexit = WEXITSTATUS(status);
- if (mda_sysexit == EX_OSERR ||
- mda_sysexit == EX_TEMPFAIL)
- mda_status = MDA_TEMPFAIL;
- else
- mda_status = MDA_PERMFAIL;
- } else {
- len = asprintf(&cause, "exited okay");
- mda_status = MDA_OK;
- mda_sysexit = 0;
- }
- } else
- /* WIFSTOPPED or WIFCONTINUED */
- continue;
-
- if (len == -1)
- fatal("asprintf");
-
- if (pid == purge_pid)
- purge_pid = -1;
-
- child = tree_pop(&children, pid);
- if (child == NULL)
- goto skip;
-
- switch (child->type) {
- case CHILD_PROCESSOR:
- if (fail) {
- log_warnx("warn: lost processor: %s %s",
- child->title, cause);
- parent_shutdown();
- }
- break;
-
- case CHILD_DAEMON:
- if (fail)
- log_warnx("warn: lost child: %s %s",
- child->title, cause);
- break;
-
- case CHILD_MDA:
- if (WIFSIGNALED(status) &&
- WTERMSIG(status) == SIGALRM) {
- char *tmp;
- if (asprintf(&tmp,
- "terminated; timeout") != -1) {
- free(cause);
- cause = tmp;
- }
- }
- else if (child->cause &&
- WIFSIGNALED(status) &&
- WTERMSIG(status) == SIGTERM) {
- free(cause);
- cause = child->cause;
- child->cause = NULL;
- }
- free(child->cause);
- log_debug("debug: smtpd: mda process done "
- "for session %016"PRIx64 ": %s",
- child->mda_id, cause);
-
- m_create(p_pony, IMSG_MDA_DONE, 0, 0,
- child->mda_out);
- m_add_id(p_pony, child->mda_id);
- m_add_int(p_pony, mda_status);
- m_add_int(p_pony, mda_sysexit);
- m_add_string(p_pony, cause);
- m_close(p_pony);
-
- break;
-
- case CHILD_ENQUEUE_OFFLINE:
- if (fail)
- log_warnx("warn: smtpd: "
- "couldn't enqueue offline "
- "message %s; smtpctl %s",
- child->path, cause);
- else
- unlink(child->path);
- free(child->path);
- offline_done();
- break;
-
- default:
- fatalx("smtpd: unexpected child type");
- }
- free(child);
- skip:
- free(cause);
- } while (pid > 0 || (pid == -1 && errno == EINTR));
-
- break;
- default:
- fatalx("smtpd: unexpected signal");
- }
-}
-
-int
-main(int argc, char *argv[])
-{
- int c, i;
- int opts, flags;
- const char *conffile = CONF_FILE;
- int save_argc = argc;
- char **save_argv = argv;
- char *rexec = NULL;
- struct smtpd *conf;
-
-#ifndef HAVE___PROGNAME
- __progname = ssh_get_progname(argv[0]);
-#endif
-
-#ifndef HAVE_SETPROCTITLE
- /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */
- saved_argc = argc;
- saved_argv = xcalloc(argc + 1, sizeof(*saved_argv));
- for (i = 0; i < argc; i++)
- saved_argv[i] = xstrdup(argv[i]);
- saved_argv[i] = NULL;
-
- /* Prepare for later setproctitle emulation */
- compat_init_setproctitle(argc, argv);
- argv = saved_argv;
-
- /* this is to work around GNU getopt + portable setproctitle() fuckery */
- save_argc = saved_argc;
- save_argv = saved_argv;
-#endif
-
- if ((conf = config_default()) == NULL)
- err(1, NULL);
-
- env = conf;
-
- flags = 0;
- opts = 0;
- debug = 0;
- tracing = 0;
-
- log_init(1, LOG_MAIL);
-
- TAILQ_INIT(&offline_q);
-
- while ((c = getopt(argc, argv, "B:dD:hnP:f:FT:vx:")) != -1) {
- switch (c) {
- case 'B':
- if (strstr(optarg, "queue=") == optarg)
- backend_queue = strchr(optarg, '=') + 1;
- else if (strstr(optarg, "scheduler=") == optarg)
- backend_scheduler = strchr(optarg, '=') + 1;
- else if (strstr(optarg, "stat=") == optarg)
- backend_stat = strchr(optarg, '=') + 1;
- else
- log_warnx("warn: "
- "invalid backend specifier %s",
- optarg);
- break;
- case 'd':
- foreground = 1;
- foreground_log = 1;
- break;
- case 'D':
- if (cmdline_symset(optarg) < 0)
- log_warnx("warn: "
- "could not parse macro definition %s",
- optarg);
- break;
- case 'h':
- log_info("version: " SMTPD_NAME " " SMTPD_VERSION);
- usage();
- break;
- case 'n':
- debug = 2;
- opts |= SMTPD_OPT_NOACTION;
- break;
- case 'f':
- conffile = optarg;
- break;
- case 'F':
- foreground = 1;
- break;
-
- case 'T':
- if (!strcmp(optarg, "imsg"))
- tracing |= TRACE_IMSG;
- else if (!strcmp(optarg, "io"))
- tracing |= TRACE_IO;
- else if (!strcmp(optarg, "smtp"))
- tracing |= TRACE_SMTP;
- else if (!strcmp(optarg, "filters"))
- tracing |= TRACE_FILTERS;
- else if (!strcmp(optarg, "mta") ||
- !strcmp(optarg, "transfer"))
- tracing |= TRACE_MTA;
- else if (!strcmp(optarg, "bounce") ||
- !strcmp(optarg, "bounces"))
- tracing |= TRACE_BOUNCE;
- else if (!strcmp(optarg, "scheduler"))
- tracing |= TRACE_SCHEDULER;
- else if (!strcmp(optarg, "lookup"))
- tracing |= TRACE_LOOKUP;
- else if (!strcmp(optarg, "stat") ||
- !strcmp(optarg, "stats"))
- tracing |= TRACE_STAT;
- else if (!strcmp(optarg, "rules"))
- tracing |= TRACE_RULES;
- else if (!strcmp(optarg, "mproc"))
- tracing |= TRACE_MPROC;
- else if (!strcmp(optarg, "expand"))
- tracing |= TRACE_EXPAND;
- else if (!strcmp(optarg, "table") ||
- !strcmp(optarg, "tables"))
- tracing |= TRACE_TABLES;
- else if (!strcmp(optarg, "queue"))
- tracing |= TRACE_QUEUE;
- else if (!strcmp(optarg, "all"))
- tracing |= ~TRACE_DEBUG;
- else if (!strcmp(optarg, "profstat"))
- profiling |= PROFILE_TOSTAT;
- else if (!strcmp(optarg, "profile-imsg"))
- profiling |= PROFILE_IMSG;
- else if (!strcmp(optarg, "profile-queue"))
- profiling |= PROFILE_QUEUE;
- else
- log_warnx("warn: unknown trace flag \"%s\"",
- optarg);
- break;
- case 'P':
- if (!strcmp(optarg, "smtp"))
- flags |= SMTPD_SMTP_PAUSED;
- else if (!strcmp(optarg, "mta"))
- flags |= SMTPD_MTA_PAUSED;
- else if (!strcmp(optarg, "mda"))
- flags |= SMTPD_MDA_PAUSED;
- break;
- case 'v':
- tracing |= TRACE_DEBUG;
- break;
- case 'x':
- rexec = optarg;
- break;
- default:
- usage();
- }
- }
-
- argv += optind;
- argc -= optind;
-
- if (argc || *argv)
- usage();
-
- env->sc_opts |= opts;
-
- ssl_init();
-
- if (parse_config(conf, conffile, opts))
- exit(1);
-
- if (RAND_status() != 1)
- errx(1, "PRNG is not seeded");
-
- if (strlcpy(env->sc_conffile, conffile, PATH_MAX)
- >= PATH_MAX)
- errx(1, "config file exceeds PATH_MAX");
-
- if (env->sc_opts & SMTPD_OPT_NOACTION) {
- if (env->sc_queue_key &&
- crypto_setup(env->sc_queue_key,
- strlen(env->sc_queue_key)) == 0) {
- fatalx("crypto_setup:"
- "invalid key for queue encryption");
- }
- load_pki_tree();
- load_pki_keys();
- fprintf(stderr, "configuration OK\n");
- exit(0);
- }
-
- env->sc_flags |= flags;
-
- /* check for root privileges */
- if (geteuid())
- errx(1, "need root privileges");
-
- log_init(foreground_log, LOG_MAIL);
- log_trace_verbose(tracing);
- load_pki_tree();
- load_pki_keys();
-
- log_debug("debug: using \"%s\" queue backend", backend_queue);
- log_debug("debug: using \"%s\" scheduler backend", backend_scheduler);
- log_debug("debug: using \"%s\" stat backend", backend_stat);
-
- if (env->sc_hostname[0] == '\0')
- errx(1, "machine does not have a hostname set");
- env->sc_uptime = time(NULL);
-
- if (rexec == NULL) {
- smtpd_process = PROC_PARENT;
-
- if (env->sc_queue_flags & QUEUE_ENCRYPTION) {
- if (env->sc_queue_key == NULL) {
- char *password;
-
- password = getpass("queue key: ");
- if (password == NULL)
- err(1, "getpass");
-
- env->sc_queue_key = strdup(password);
- explicit_bzero(password, strlen(password));
- if (env->sc_queue_key == NULL)
- err(1, "strdup");
- }
- else {
- char *buf = NULL;
- size_t sz = 0;
- ssize_t len;
-
- if (strcasecmp(env->sc_queue_key, "stdin") == 0) {
- if ((len = getline(&buf, &sz, stdin)) == -1)
- err(1, "getline");
- if (buf[len - 1] == '\n')
- buf[len - 1] = '\0';
- env->sc_queue_key = buf;
- }
- }
- }
-
- log_info("info: %s %s starting", SMTPD_NAME, SMTPD_VERSION);
-
- if (!foreground)
- if (daemon(0, 0) == -1)
- err(1, "failed to daemonize");
-
- /* setup all processes */
-
- p_ca = start_child(save_argc, save_argv, "ca");
- p_ca->proc = PROC_CA;
-
- p_control = start_child(save_argc, save_argv, "control");
- p_control->proc = PROC_CONTROL;
-
- p_lka = start_child(save_argc, save_argv, "lka");
- p_lka->proc = PROC_LKA;
-
- p_pony = start_child(save_argc, save_argv, "pony");
- p_pony->proc = PROC_PONY;
-
- p_queue = start_child(save_argc, save_argv, "queue");
- p_queue->proc = PROC_QUEUE;
-
- p_scheduler = start_child(save_argc, save_argv, "scheduler");
- p_scheduler->proc = PROC_SCHEDULER;
-
- setup_peers(p_control, p_ca);
- setup_peers(p_control, p_lka);
- setup_peers(p_control, p_pony);
- setup_peers(p_control, p_queue);
- setup_peers(p_control, p_scheduler);
- setup_peers(p_pony, p_ca);
- setup_peers(p_pony, p_lka);
- setup_peers(p_pony, p_queue);
- setup_peers(p_queue, p_lka);
- setup_peers(p_queue, p_scheduler);
-
- if (env->sc_queue_key) {
- if (imsg_compose(&p_queue->imsgbuf, IMSG_SETUP_KEY, 0,
- 0, -1, env->sc_queue_key, strlen(env->sc_queue_key)
- + 1) == -1)
- fatal("imsg_compose");
- if (imsg_flush(&p_queue->imsgbuf) == -1)
- fatal("imsg_flush");
- }
-
- setup_done(p_ca);
- setup_done(p_control);
- setup_done(p_lka);
- setup_done(p_pony);
- setup_done(p_queue);
- setup_done(p_scheduler);
-
- log_debug("smtpd: setup done");
-
- return smtpd();
- }
-
- if (!strcmp(rexec, "ca")) {
- smtpd_process = PROC_CA;
- setup_proc();
-
- return ca();
- }
-
- else if (!strcmp(rexec, "control")) {
- smtpd_process = PROC_CONTROL;
- setup_proc();
-
- /* the control socket ensures that only one smtpd instance is running */
- control_socket = control_create_socket();
-
- env->sc_stat = stat_backend_lookup(backend_stat);
- if (env->sc_stat == NULL)
- errx(1, "could not find stat backend \"%s\"", backend_stat);
-
- return control();
- }
-
- else if (!strcmp(rexec, "lka")) {
- smtpd_process = PROC_LKA;
- setup_proc();
-
- return lka();
- }
-
- else if (!strcmp(rexec, "pony")) {
- smtpd_process = PROC_PONY;
- setup_proc();
-
- return pony();
- }
-
- else if (!strcmp(rexec, "queue")) {
- smtpd_process = PROC_QUEUE;
- setup_proc();
-
- if (env->sc_queue_flags & QUEUE_COMPRESSION)
- env->sc_comp = compress_backend_lookup("gzip");
-
- if (!queue_init(backend_queue, 1))
- errx(1, "could not initialize queue backend");
-
- return queue();
- }
-
- else if (!strcmp(rexec, "scheduler")) {
- smtpd_process = PROC_SCHEDULER;
- setup_proc();
-
- for (i = 0; i < MAX_BOUNCE_WARN; i++) {
- if (env->sc_bounce_warn[i] == 0)
- break;
- log_debug("debug: bounce warning after %s",
- duration_to_text(env->sc_bounce_warn[i]));
- }
-
- return scheduler();
- }
-
- fatalx("bad rexec: %s", rexec);
-
- return (1);
-}
-
-static struct mproc *
-start_child(int save_argc, char **save_argv, char *rexec)
-{
- struct mproc *p;
- char *argv[SMTPD_MAXARG];
- int sp[2], argc = 0;
- pid_t pid;
-
- if (save_argc >= SMTPD_MAXARG - 2)
- fatalx("too many arguments");
-
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1)
- fatal("socketpair");
-
- io_set_nonblocking(sp[0]);
- io_set_nonblocking(sp[1]);
-
- switch (pid = fork()) {
- case -1:
- fatal("%s: fork", save_argv[0]);
- case 0:
- break;
- default:
- close(sp[0]);
- p = calloc(1, sizeof(*p));
- if (p == NULL)
- fatal("calloc");
- if((p->name = strdup(rexec)) == NULL)
- fatal("strdup");
- mproc_init(p, sp[1]);
- p->pid = pid;
- p->handler = parent_imsg;
- return p;
- }
-
- if (sp[0] != 3) {
- if (dup2(sp[0], 3) == -1)
- fatal("%s: dup2", rexec);
- } else if (fcntl(sp[0], F_SETFD, 0) == -1)
- fatal("%s: fcntl", rexec);
-
- xclosefrom(4);
-
- for (argc = 0; argc < save_argc; argc++)
- argv[argc] = save_argv[argc];
- argv[argc++] = "-x";
- argv[argc++] = rexec;
- argv[argc++] = NULL;
-
- execvp(argv[0], argv);
- fatal("%s: execvp", rexec);
-}
-
-static void
-setup_peers(struct mproc *a, struct mproc *b)
-{
- int sp[2];
-
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1)
- fatal("socketpair");
-
- io_set_nonblocking(sp[0]);
- io_set_nonblocking(sp[1]);
-
- if (imsg_compose(&a->imsgbuf, IMSG_SETUP_PEER, b->proc, b->pid, sp[0],
- NULL, 0) == -1)
- fatal("imsg_compose");
- if (imsg_flush(&a->imsgbuf) == -1)
- fatal("imsg_flush");
-
- if (imsg_compose(&b->imsgbuf, IMSG_SETUP_PEER, a->proc, a->pid, sp[1],
- NULL, 0) == -1)
- fatal("imsg_compose");
- if (imsg_flush(&b->imsgbuf) == -1)
- fatal("imsg_flush");
-}
-
-static void
-setup_done(struct mproc *p)
-{
- struct imsg imsg;
-
- if (imsg_compose(&p->imsgbuf, IMSG_SETUP_DONE, 0, 0, -1, NULL, 0) == -1)
- fatal("imsg_compose");
- if (imsg_flush(&p->imsgbuf) == -1)
- fatal("imsg_flush");
-
- if (imsg_wait(&p->imsgbuf, &imsg, 10000) == -1)
- fatal("imsg_wait");
-
- if (imsg.hdr.type != IMSG_SETUP_DONE)
- fatalx("expect IMSG_SETUP_DONE");
-
- log_debug("setup_done: %s[%d] done", p->name, p->pid);
-
- imsg_free(&imsg);
-}
-
-static void
-setup_proc(void)
-{
- struct imsgbuf *ibuf;
- struct imsg imsg;
- int setup = 1;
-
- log_procinit(proc_title(smtpd_process));
-
- p_parent = calloc(1, sizeof(*p_parent));
- if (p_parent == NULL)
- fatal("calloc");
- if((p_parent->name = strdup("parent")) == NULL)
- fatal("strdup");
- p_parent->proc = PROC_PARENT;
- p_parent->handler = imsg_dispatch;
- mproc_init(p_parent, 3);
-
- ibuf = &p_parent->imsgbuf;
-
- while (setup) {
- if (imsg_wait(ibuf, &imsg, 10000) == -1)
- fatal("imsg_wait");
-
- switch (imsg.hdr.type) {
- case IMSG_SETUP_KEY:
- env->sc_queue_key = strdup(imsg.data);
- break;
- case IMSG_SETUP_PEER:
- setup_peer(imsg.hdr.peerid, imsg.hdr.pid, imsg.fd);
- break;
- case IMSG_SETUP_DONE:
- setup = 0;
- break;
- default:
- fatal("bad imsg %d", imsg.hdr.type);
- }
- imsg_free(&imsg);
- }
-
- if (imsg_compose(ibuf, IMSG_SETUP_DONE, 0, 0, -1, NULL, 0) == -1)
- fatal("imsg_compose");
-
- if (imsg_flush(ibuf) == -1)
- fatal("imsg_flush");
-
- log_debug("setup_proc: %s done", proc_title(smtpd_process));
-}
-
-static struct mproc *
-setup_peer(enum smtp_proc_type proc, pid_t pid, int sock)
-{
- struct mproc *p, **pp;
-
- log_debug("setup_peer: %s -> %s[%u] fd=%d", proc_title(smtpd_process),
- proc_title(proc), pid, sock);
-
- if (sock == -1)
- fatalx("peer socket not received");
-
- switch (proc) {
- case PROC_LKA:
- pp = &p_lka;
- break;
- case PROC_QUEUE:
- pp = &p_queue;
- break;
- case PROC_CONTROL:
- pp = &p_control;
- break;
- case PROC_SCHEDULER:
- pp = &p_scheduler;
- break;
- case PROC_PONY:
- pp = &p_pony;
- break;
- case PROC_CA:
- pp = &p_ca;
- break;
- default:
- fatalx("unknown peer");
- }
-
- if (*pp)
- fatalx("peer already set");
-
- p = calloc(1, sizeof(*p));
- if (p == NULL)
- fatal("calloc");
- if((p->name = strdup(proc_title(proc))) == NULL)
- fatal("strdup");
- mproc_init(p, sock);
- p->pid = pid;
- p->proc = proc;
- p->handler = imsg_dispatch;
-
- *pp = p;
-
- return p;
-}
-
-static int
-imsg_wait(struct imsgbuf *ibuf, struct imsg *imsg, int timeout)
-{
- struct pollfd pfd[1];
- ssize_t n;
-
- pfd[0].fd = ibuf->fd;
- pfd[0].events = POLLIN;
-
- while (1) {
- if ((n = imsg_get(ibuf, imsg)) == -1)
- return -1;
- if (n)
- return 1;
-
- n = poll(pfd, 1, timeout);
- if (n == -1)
- return -1;
- if (n == 0) {
- errno = ETIMEDOUT;
- return -1;
- }
-
- if (((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) || n == 0)
- return -1;
- }
-}
-
-int
-smtpd(void) {
- struct event ev_sigint;
- struct event ev_sigterm;
- struct event ev_sigchld;
- struct event ev_sighup;
- struct timeval tv;
-
- imsg_callback = parent_imsg;
-
- tree_init(&children);
-
- child_add(p_queue->pid, CHILD_DAEMON, proc_title(PROC_QUEUE));
- child_add(p_control->pid, CHILD_DAEMON, proc_title(PROC_CONTROL));
- child_add(p_lka->pid, CHILD_DAEMON, proc_title(PROC_LKA));
- child_add(p_scheduler->pid, CHILD_DAEMON, proc_title(PROC_SCHEDULER));
- child_add(p_pony->pid, CHILD_DAEMON, proc_title(PROC_PONY));
- child_add(p_ca->pid, CHILD_DAEMON, proc_title(PROC_CA));
-
- event_init();
-
- signal_set(&ev_sigint, SIGINT, parent_sig_handler, NULL);
- signal_set(&ev_sigterm, SIGTERM, parent_sig_handler, NULL);
- signal_set(&ev_sigchld, SIGCHLD, parent_sig_handler, NULL);
- signal_set(&ev_sighup, SIGHUP, parent_sig_handler, NULL);
- signal_add(&ev_sigint, NULL);
- signal_add(&ev_sigterm, NULL);
- signal_add(&ev_sigchld, NULL);
- signal_add(&ev_sighup, NULL);
- signal(SIGPIPE, SIG_IGN);
-
- config_peer(PROC_CONTROL);
- config_peer(PROC_LKA);
- config_peer(PROC_QUEUE);
- config_peer(PROC_CA);
- config_peer(PROC_PONY);
-
- evtimer_set(&config_ev, parent_send_config, NULL);
- memset(&tv, 0, sizeof(tv));
- evtimer_add(&config_ev, &tv);
-
- /* defer offline scanning for a second */
- evtimer_set(&offline_ev, offline_scan, NULL);
- offline_timeout.tv_sec = 1;
- offline_timeout.tv_usec = 0;
- evtimer_add(&offline_ev, &offline_timeout);
-
- if (pidfile(NULL) < 0)
- err(1, "pidfile");
-
- fork_filter_processes();
-
- purge_task();
-
-#if HAVE_PLEDGE
- if (pledge("stdio rpath wpath cpath fattr tmppath "
- "getpw sendfd proc exec id inet chown unix", NULL) == -1)
- err(1, "pledge");
-#endif
-
- event_dispatch();
- fatalx("exited event loop");
-
- return (0);
-}
-
-static void
-load_pki_tree(void)
-{
- struct pki *pki;
- struct ca *sca;
- const char *k;
- void *iter_dict;
-
- log_debug("debug: init ssl-tree");
- iter_dict = NULL;
- while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) {
- log_debug("info: loading pki information for %s", k);
- if (pki->pki_cert_file == NULL)
- fatalx("load_pki_tree: missing certificate file");
- if (pki->pki_key_file == NULL)
- fatalx("load_pki_tree: missing key file");
-
- if (!ssl_load_certificate(pki, pki->pki_cert_file))
- fatalx("load_pki_tree: failed to load certificate file");
- }
-
- log_debug("debug: init ca-tree");
- iter_dict = NULL;
- while (dict_iter(env->sc_ca_dict, &iter_dict, &k, (void **)&sca)) {
- log_debug("info: loading CA information for %s", k);
- if (!ssl_load_cafile(sca, sca->ca_cert_file))
- fatalx("load_pki_tree: failed to load CA file");
- }
-}
-
-void
-load_pki_keys(void)
-{
- struct pki *pki;
- const char *k;
- void *iter_dict;
-
- log_debug("debug: init ssl-tree");
- iter_dict = NULL;
- while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) {
- log_debug("info: loading pki keys for %s", k);
-
- if (!ssl_load_keyfile(pki, pki->pki_key_file, k))
- fatalx("load_pki_keys: failed to load key file");
- }
-}
-
-int
-fork_proc_backend(const char *key, const char *conf, const char *procname)
-{
- pid_t pid;
- int sp[2];
- char path[PATH_MAX];
- char name[PATH_MAX];
- char *arg;
-
- if (strlcpy(name, conf, sizeof(name)) >= sizeof(name)) {
- log_warnx("warn: %s-proc: conf too long", key);
- return (0);
- }
-
- arg = strchr(name, ':');
- if (arg)
- *arg++ = '\0';
-
- if (snprintf(path, sizeof(path), PATH_LIBEXEC "/%s-%s", key, name) >=
- (ssize_t)sizeof(path)) {
- log_warn("warn: %s-proc: exec path too long", key);
- return (-1);
- }
-
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) {
- log_warn("warn: %s-proc: socketpair", key);
- return (-1);
- }
-
- if ((pid = fork()) == -1) {
- log_warn("warn: %s-proc: fork", key);
- close(sp[0]);
- close(sp[1]);
- return (-1);
- }
-
- if (pid == 0) {
- /* child process */
- dup2(sp[0], STDIN_FILENO);
- closefrom(STDERR_FILENO + 1);
-
- if (procname == NULL)
- procname = name;
-
- execl(path, procname, arg, (char *)NULL);
- err(1, "execl: %s", path);
- }
-
- /* parent process */
- close(sp[0]);
-
- return (sp[1]);
-}
-
-struct child *
-child_add(pid_t pid, int type, const char *title)
-{
- struct child *child;
-
- if ((child = calloc(1, sizeof(*child))) == NULL)
- fatal("smtpd: child_add: calloc");
-
- child->pid = pid;
- child->type = type;
- child->title = title;
-
- tree_xset(&children, pid, child);
-
- return (child);
-}
-
-static void
-purge_task(void)
-{
- struct passwd *pw;
- DIR *d;
- int n;
- uid_t uid;
- gid_t gid;
-
- n = 0;
- if ((d = opendir(PATH_SPOOL PATH_PURGE))) {
- while (readdir(d) != NULL)
- n++;
- closedir(d);
- } else
- log_warn("warn: purge_task: opendir");
-
- if (n > 2) {
- switch (purge_pid = fork()) {
- case -1:
- log_warn("warn: purge_task: fork");
- break;
- case 0:
- if ((pw = getpwnam(SMTPD_QUEUE_USER)) == NULL)
- fatalx("unknown user " SMTPD_QUEUE_USER);
- if (chroot(PATH_SPOOL PATH_PURGE) == -1)
- fatal("smtpd: chroot");
- if (chdir("/") == -1)
- fatal("smtpd: chdir");
- uid = pw->pw_uid;
- gid = pw->pw_gid;
- if (setgroups(1, &gid) ||
- setresgid(gid, gid, gid) ||
- setresuid(uid, uid, uid))
- fatal("smtpd: cannot drop privileges");
- rmtree("/", 1);
- _exit(0);
- break;
- default:
- break;
- }
- }
-}
-
-static void
-fork_filter_processes(void)
-{
- const char *name;
- void *iter;
- const char *fn;
- struct filter_config *fc;
- struct filter_config *fcs;
- struct filter_proc *fp;
- size_t i;
-
- /* For each filter chain, assign the registered subsystem to subfilters */
- iter = NULL;
- while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) {
- if (fc->chain) {
- for (i = 0; i < fc->chain_size; ++i) {
- fcs = dict_xget(env->sc_filters_dict, fc->chain[i]);
- fcs->filter_subsystem |= fc->filter_subsystem;
- }
- }
- }
-
- /* For each filter, assign the registered subsystem to underlying proc */
- iter = NULL;
- while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) {
- if (fc->proc) {
- fp = dict_xget(env->sc_filter_processes_dict, fc->proc);
- fp->filter_subsystem |= fc->filter_subsystem;
- }
- }
-
- iter = NULL;
- while (dict_iter(env->sc_filter_processes_dict, &iter, &name, (void **)&fp))
- fork_filter_process(name, fp->command, fp->user, fp->group, fp->chroot, fp->filter_subsystem);
-}
-
-static void
-fork_filter_process(const char *name, const char *command, const char *user, const char *group, const char *chroot_path, uint32_t subsystems)
-{
- pid_t pid;
- struct filter_proc *processor;
- char buf;
- int sp[2], errfd[2];
- struct passwd *pw;
- struct group *gr;
- char exec[_POSIX_ARG_MAX];
- int execr;
-
- if (user == NULL)
- user = SMTPD_USER;
- if ((pw = getpwnam(user)) == NULL)
- err(1, "getpwnam");
-
- if (group) {
- if ((gr = getgrnam(group)) == NULL)
- err(1, "getgrnam");
- }
- else {
- if ((gr = getgrgid(pw->pw_gid)) == NULL)
- err(1, "getgrgid");
- }
-
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1)
- err(1, "socketpair");
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, errfd) == -1)
- err(1, "socketpair");
-
- if ((pid = fork()) == -1)
- err(1, "fork");
-
- /* parent passes the child fd over to lka */
- if (pid > 0) {
- processor = dict_xget(env->sc_filter_processes_dict, name);
- processor->errfd = errfd[1];
- child_add(pid, CHILD_PROCESSOR, name);
- close(sp[0]);
- close(errfd[0]);
- m_create(p_lka, IMSG_LKA_PROCESSOR_FORK, 0, 0, sp[1]);
- m_add_string(p_lka, name);
- m_add_u32(p_lka, (uint32_t)subsystems);
- m_close(p_lka);
- return;
- }
-
- close(sp[1]);
- close(errfd[1]);
- dup2(sp[0], STDIN_FILENO);
- dup2(sp[0], STDOUT_FILENO);
- dup2(errfd[0], STDERR_FILENO);
-
- if (chroot_path) {
- if (chroot(chroot_path) != 0 || chdir("/") != 0)
- err(1, "chroot: %s", chroot_path);
- }
-
- if (setgroups(1, &gr->gr_gid) ||
- setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid) ||
- setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
- err(1, "fork_filter_process: cannot drop privileges");
-
- xclosefrom(STDERR_FILENO + 1);
-
- if (setsid() < 0)
- err(1, "setsid");
- if (signal(SIGPIPE, SIG_DFL) == SIG_ERR ||
- signal(SIGINT, SIG_DFL) == SIG_ERR ||
- signal(SIGTERM, SIG_DFL) == SIG_ERR ||
- signal(SIGCHLD, SIG_DFL) == SIG_ERR ||
- signal(SIGHUP, SIG_DFL) == SIG_ERR)
- err(1, "signal");
-
- if (command[0] == '/')
- execr = snprintf(exec, sizeof(exec), "exec %s", command);
- else
- execr = snprintf(exec, sizeof(exec), "exec %s/%s",
- PATH_LIBEXEC, command);
- if (execr >= (int) sizeof(exec))
- errx(1, "%s: exec path too long", name);
-
- /*
- * Wait for lka to acknowledge that it received the fd.
- * This prevents a race condition between the filter sending an error
- * message, and exiting and lka not being able to log it because of
- * SIGCHLD.
- * (Ab)use read to determine if the fd is installed; since stderr is
- * never going to be read from we can shutdown(2) the write-end in lka.
- */
- if (read(STDERR_FILENO, &buf, 1) != 0)
- errx(1, "lka didn't properly close write end of error socket");
- if (system(exec) == -1)
- err(1, NULL);
-
- /* there's no successful exit from a processor */
- _exit(1);
-}
-
-static void
-forkmda(struct mproc *p, uint64_t id, struct deliver *deliver)
-{
- char ebuf[128], sfn[32];
- struct dispatcher *dsp;
- struct child *child;
- pid_t pid;
- int allout, pipefd[2];
- struct passwd *pw;
- const char *pw_name;
- uid_t pw_uid;
- gid_t pw_gid;
- const char *pw_dir;
-
- dsp = dict_xget(env->sc_dispatchers, deliver->dispatcher);
- if (dsp->type != DISPATCHER_LOCAL)
- fatalx("non-local dispatcher called from forkmda()");
-
- log_debug("debug: smtpd: forking mda for session %016"PRIx64
- ": %s as %s", id, deliver->userinfo.username,
- dsp->u.local.user ? dsp->u.local.user : deliver->userinfo.username);
-
- if (dsp->u.local.user) {
- if ((pw = getpwnam(dsp->u.local.user)) == NULL) {
- (void)snprintf(ebuf, sizeof ebuf,
- "delivery user '%s' does not exist",
- dsp->u.local.user);
- m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1);
- m_add_id(p_pony, id);
- m_add_int(p_pony, MDA_PERMFAIL);
- m_add_int(p_pony, EX_NOUSER);
- m_add_string(p_pony, ebuf);
- m_close(p_pony);
- return;
- }
- pw_name = pw->pw_name;
- pw_uid = pw->pw_uid;
- pw_gid = pw->pw_gid;
- pw_dir = pw->pw_dir;
- }
- else {
- pw_name = deliver->userinfo.username;
- pw_uid = deliver->userinfo.uid;
- pw_gid = deliver->userinfo.gid;
- pw_dir = deliver->userinfo.directory;
- }
-
- if (pw_uid == 0 && deliver->mda_exec[0]) {
- pw_name = deliver->userinfo.username;
- pw_uid = deliver->userinfo.uid;
- pw_gid = deliver->userinfo.gid;
- pw_dir = deliver->userinfo.directory;
- }
-
- if (pw_uid == 0 && !dsp->u.local.is_mbox) {
- (void)snprintf(ebuf, sizeof ebuf, "not allowed to deliver to: %s",
- deliver->userinfo.username);
- m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1);
- m_add_id(p_pony, id);
- m_add_int(p_pony, MDA_PERMFAIL);
- m_add_int(p_pony, EX_NOPERM);
- m_add_string(p_pony, ebuf);
- m_close(p_pony);
- return;
- }
-
- if (pipe(pipefd) == -1) {
- (void)snprintf(ebuf, sizeof ebuf, "pipe: %s", strerror(errno));
- m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1);
- m_add_id(p_pony, id);
- m_add_int(p_pony, MDA_TEMPFAIL);
- m_add_int(p_pony, EX_OSERR);
- m_add_string(p_pony, ebuf);
- m_close(p_pony);
- return;
- }
-
- /* prepare file which captures stdout and stderr */
- (void)strlcpy(sfn, "/tmp/smtpd.out.XXXXXXXXXXX", sizeof(sfn));
- allout = mkstemp(sfn);
- if (allout == -1) {
- (void)snprintf(ebuf, sizeof ebuf, "mkstemp: %s", strerror(errno));
- m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1);
- m_add_id(p_pony, id);
- m_add_int(p_pony, MDA_TEMPFAIL);
- m_add_int(p_pony, EX_OSERR);
- m_add_string(p_pony, ebuf);
- m_close(p_pony);
- close(pipefd[0]);
- close(pipefd[1]);
- return;
- }
- unlink(sfn);
-
- pid = fork();
- if (pid == -1) {
- (void)snprintf(ebuf, sizeof ebuf, "fork: %s", strerror(errno));
- m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1);
- m_add_id(p_pony, id);
- m_add_int(p_pony, MDA_TEMPFAIL);
- m_add_int(p_pony, EX_OSERR);
- m_add_string(p_pony, ebuf);
- m_close(p_pony);
- close(pipefd[0]);
- close(pipefd[1]);
- close(allout);
- return;
- }
-
- /* parent passes the child fd over to mda */
- if (pid > 0) {
- child = child_add(pid, CHILD_MDA, NULL);
- child->mda_out = allout;
- child->mda_id = id;
- close(pipefd[0]);
- m_create(p, IMSG_MDA_FORK, 0, 0, pipefd[1]);
- m_add_id(p, id);
- m_close(p);
- return;
- }
-
- /* mbox helper, create mailbox before privdrop if it doesn't exist */
- if (dsp->u.local.is_mbox)
- mda_mbox_init(deliver);
-
- if (chdir(pw_dir) == -1 && chdir("/") == -1)
- err(1, "chdir");
- if (setgroups(1, &pw_gid) ||
- setresgid(pw_gid, pw_gid, pw_gid) ||
- setresuid(pw_uid, pw_uid, pw_uid))
- err(1, "forkmda: cannot drop privileges");
- if (dup2(pipefd[0], STDIN_FILENO) == -1 ||
- dup2(allout, STDOUT_FILENO) == -1 ||
- dup2(allout, STDERR_FILENO) == -1)
- err(1, "forkmda: dup2");
- closefrom(STDERR_FILENO + 1);
- if (setsid() < 0)
- err(1, "setsid");
- if (signal(SIGPIPE, SIG_DFL) == SIG_ERR ||
- signal(SIGINT, SIG_DFL) == SIG_ERR ||
- signal(SIGTERM, SIG_DFL) == SIG_ERR ||
- signal(SIGCHLD, SIG_DFL) == SIG_ERR ||
- signal(SIGHUP, SIG_DFL) == SIG_ERR)
- err(1, "signal");
-
- /* avoid hangs by setting 5m timeout */
- alarm(300);
-
- if (dsp->u.local.is_mbox &&
- dsp->u.local.mda_wrapper == NULL &&
- deliver->mda_exec[0] == '\0')
- mda_mbox(deliver);
- else
- mda_unpriv(dsp, deliver, pw_name, pw_dir);
-}
-
-static void
-offline_scan(int fd, short ev, void *arg)
-{
- char *path_argv[2];
- FTS *fts = arg;
- FTSENT *e;
- int n = 0;
-
- path_argv[0] = PATH_SPOOL PATH_OFFLINE;
- path_argv[1] = NULL;
-
- if (fts == NULL) {
- log_debug("debug: smtpd: scanning offline queue...");
- fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
- if (fts == NULL) {
- log_warn("fts_open: %s", path_argv[0]);
- return;
- }
- }
-
- while ((e = fts_read(fts)) != NULL) {
- if (e->fts_info != FTS_F)
- continue;
-
- /* offline files must be at depth 1 */
- if (e->fts_level != 1)
- continue;
-
- /* offline file group must match parent directory group */
- if (e->fts_statp->st_gid != e->fts_parent->fts_statp->st_gid)
- continue;
-
- if (e->fts_statp->st_size == 0) {
- if (unlink(e->fts_accpath) == -1)
- log_warnx("warn: smtpd: could not unlink %s", e->fts_accpath);
- continue;
- }
-
- if (offline_add(e->fts_name, e->fts_statp->st_uid,
- e->fts_statp->st_gid)) {
- log_warnx("warn: smtpd: "
- "could not add offline message %s", e->fts_name);
- continue;
- }
-
- if ((n++) == OFFLINE_READMAX) {
- evtimer_set(&offline_ev, offline_scan, fts);
- offline_timeout.tv_sec = 0;
- offline_timeout.tv_usec = 100000;
- evtimer_add(&offline_ev, &offline_timeout);
- return;
- }
- }
-
- log_debug("debug: smtpd: offline scanning done");
- fts_close(fts);
-}
-
-static int
-offline_enqueue(char *name, uid_t uid, gid_t gid)
-{
- char *path;
- struct stat sb;
- pid_t pid;
- struct child *child;
- struct passwd *pw;
- int pathlen;
-
- pathlen = asprintf(&path, "%s/%s", PATH_SPOOL PATH_OFFLINE, name);
- if (pathlen == -1) {
- log_warnx("warn: smtpd: asprintf");
- return (-1);
- }
-
- if (pathlen >= PATH_MAX) {
- log_warnx("warn: smtpd: pathname exceeds PATH_MAX");
- free(path);
- return (-1);
- }
-
- log_debug("debug: smtpd: enqueueing offline message %s", path);
-
- if ((pid = fork()) == -1) {
- log_warn("warn: smtpd: fork");
- free(path);
- return (-1);
- }
-
- if (pid == 0) {
- char *envp[2], *p = NULL, *tmp;
- int fd;
- FILE *fp;
- size_t sz = 0;
- ssize_t len;
- arglist args;
-
- closefrom(STDERR_FILENO + 1);
-
- memset(&args, 0, sizeof(args));
-
- if ((fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK)) == -1) {
- log_warn("warn: smtpd: open: %s", path);
- _exit(1);
- }
-
- if (fstat(fd, &sb) == -1) {
- log_warn("warn: smtpd: fstat: %s", path);
- _exit(1);
- }
-
- if (!S_ISREG(sb.st_mode)) {
- log_warnx("warn: smtpd: file %s (uid %d) not regular",
- path, sb.st_uid);
- _exit(1);
- }
-
- if (sb.st_nlink != 1) {
- log_warnx("warn: smtpd: file %s is hard-link", path);
- _exit(1);
- }
-
- if (sb.st_uid != uid) {
- log_warnx("warn: smtpd: file %s has bad uid %d",
- path, sb.st_uid);
- _exit(1);
- }
-
- if (sb.st_gid != gid) {
- log_warnx("warn: smtpd: file %s has bad gid %d",
- path, sb.st_gid);
- _exit(1);
- }
-
- pw = getpwuid(sb.st_uid);
- if (pw == NULL) {
- log_warnx("warn: smtpd: getpwuid for uid %d failed",
- sb.st_uid);
- _exit(1);
- }
-
- if (setgroups(1, &pw->pw_gid) ||
- setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
- setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
- _exit(1);
-
- if ((fp = fdopen(fd, "r")) == NULL)
- _exit(1);
-
- if (chdir(pw->pw_dir) == -1 && chdir("/") == -1)
- _exit(1);
-
- if (setsid() == -1 ||
- signal(SIGPIPE, SIG_DFL) == SIG_ERR ||
- dup2(fileno(fp), STDIN_FILENO) == -1)
- _exit(1);
-
- if ((len = getline(&p, &sz, fp)) == -1)
- _exit(1);
-
- if (p[len - 1] != '\n')
- _exit(1);
- p[len - 1] = '\0';
-
- addargs(&args, "%s", "sendmail");
- addargs(&args, "%s", "-S");
-
- while ((tmp = strsep(&p, "|")) != NULL)
- addargs(&args, "%s", tmp);
-
- free(p);
- if (lseek(fileno(fp), len, SEEK_SET) == -1)
- _exit(1);
-
- envp[0] = "PATH=" _PATH_DEFPATH;
- envp[1] = (char *)NULL;
- environ = envp;
-
- execvp(PATH_SMTPCTL, args.list);
- _exit(1);
- }
-
- offline_running++;
- child = child_add(pid, CHILD_ENQUEUE_OFFLINE, NULL);
- child->path = path;
-
- return (0);
-}
-
-static int
-offline_add(char *path, uid_t uid, gid_t gid)
-{
- struct offline *q;
-
- if (offline_running < OFFLINE_QUEUEMAX)
- /* skip queue */
- return offline_enqueue(path, uid, gid);
-
- q = malloc(sizeof(*q) + strlen(path) + 1);
- if (q == NULL)
- return (-1);
- q->uid = uid;
- q->gid = gid;
- q->path = (char *)q + sizeof(*q);
- memmove(q->path, path, strlen(path) + 1);
- TAILQ_INSERT_TAIL(&offline_q, q, entry);
-
- return (0);
-}
-
-static void
-offline_done(void)
-{
- struct offline *q;
-
- offline_running--;
-
- while (offline_running < OFFLINE_QUEUEMAX) {
- if ((q = TAILQ_FIRST(&offline_q)) == NULL)
- break; /* all done */
- TAILQ_REMOVE(&offline_q, q, entry);
- offline_enqueue(q->path, q->uid, q->gid);
- free(q);
- }
-}
-
-static int
-parent_forward_open(char *username, char *directory, uid_t uid, gid_t gid)
-{
- char pathname[PATH_MAX];
- int fd;
- struct stat sb;
-
- if (!bsnprintf(pathname, sizeof (pathname), "%s/.forward",
- directory)) {
- log_warnx("warn: smtpd: %s: pathname too large", pathname);
- return -1;
- }
-
- if (stat(directory, &sb) == -1) {
- log_warn("warn: smtpd: parent_forward_open: %s", directory);
- return -1;
- }
- if (sb.st_mode & S_ISVTX) {
- log_warnx("warn: smtpd: parent_forward_open: %s is sticky",
- directory);
- errno = EAGAIN;
- return -1;
- }
-
- do {
- fd = open(pathname, O_RDONLY|O_NOFOLLOW|O_NONBLOCK);
- } while (fd == -1 && errno == EINTR);
- if (fd == -1) {
- if (errno == ENOENT)
- return -1;
- if (errno == EMFILE || errno == ENFILE || errno == EIO) {
- errno = EAGAIN;
- return -1;
- }
- if (errno == ELOOP)
- log_warnx("warn: smtpd: parent_forward_open: %s: "
- "cannot follow symbolic links", pathname);
- else
- log_warn("warn: smtpd: parent_forward_open: %s", pathname);
- return -1;
- }
-
- if (!secure_file(fd, pathname, directory, uid, 1)) {
- log_warnx("warn: smtpd: %s: unsecure file", pathname);
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
-void
-imsg_dispatch(struct mproc *p, struct imsg *imsg)
-{
- struct timespec t0, t1, dt;
- int msg;
-
- if (imsg == NULL) {
- imsg_callback(p, imsg);
- return;
- }
-
- log_imsg(smtpd_process, p->proc, imsg);
-
- if (profiling & PROFILE_IMSG)
- clock_gettime(CLOCK_MONOTONIC, &t0);
-
- msg = imsg->hdr.type;
- imsg_callback(p, imsg);
-
- if (profiling & PROFILE_IMSG) {
- clock_gettime(CLOCK_MONOTONIC, &t1);
- timespecsub(&t1, &t0, &dt);
-
- log_debug("profile-imsg: %s %s %s %d %lld.%09ld",
- proc_name(smtpd_process),
- proc_name(p->proc),
- imsg_to_str(msg),
- (int)imsg->hdr.len,
- (long long)dt.tv_sec,
- dt.tv_nsec);
-
- if (profiling & PROFILE_TOSTAT) {
- char key[STAT_KEY_SIZE];
- /* can't profstat control process yet */
- if (smtpd_process == PROC_CONTROL)
- return;
-
- if (!bsnprintf(key, sizeof key,
- "profiling.imsg.%s.%s.%s",
- proc_name(smtpd_process),
- proc_name(p->proc),
- imsg_to_str(msg)))
- return;
- stat_set(key, stat_timespec(&dt));
- }
- }
-}
-
-void
-log_imsg(int to, int from, struct imsg *imsg)
-{
-
- if (to == PROC_CONTROL && imsg->hdr.type == IMSG_STAT_SET)
- return;
-
- if (imsg->fd != -1)
- log_trace(TRACE_IMSG, "imsg: %s <- %s: %s (len=%zu, fd=%d)",
- proc_name(to),
- proc_name(from),
- imsg_to_str(imsg->hdr.type),
- imsg->hdr.len - IMSG_HEADER_SIZE,
- imsg->fd);
- else
- log_trace(TRACE_IMSG, "imsg: %s <- %s: %s (len=%zu)",
- proc_name(to),
- proc_name(from),
- imsg_to_str(imsg->hdr.type),
- imsg->hdr.len - IMSG_HEADER_SIZE);
-}
-
-const char *
-proc_title(enum smtp_proc_type proc)
-{
- switch (proc) {
- case PROC_PARENT:
- return "[priv]";
- case PROC_LKA:
- return "lookup";
- case PROC_QUEUE:
- return "queue";
- case PROC_CONTROL:
- return "control";
- case PROC_SCHEDULER:
- return "scheduler";
- case PROC_PONY:
- return "pony express";
- case PROC_CA:
- return "klondike";
- case PROC_CLIENT:
- return "client";
- case PROC_PROCESSOR:
- return "processor";
- }
- return "unknown";
-}
-
-const char *
-proc_name(enum smtp_proc_type proc)
-{
- switch (proc) {
- case PROC_PARENT:
- return "parent";
- case PROC_LKA:
- return "lka";
- case PROC_QUEUE:
- return "queue";
- case PROC_CONTROL:
- return "control";
- case PROC_SCHEDULER:
- return "scheduler";
- case PROC_PONY:
- return "pony";
- case PROC_CA:
- return "ca";
- case PROC_CLIENT:
- return "client-proc";
- default:
- return "unknown";
- }
-}
-
-#define CASE(x) case x : return #x
-
-const char *
-imsg_to_str(int type)
-{
- static char buf[32];
-
- switch (type) {
- CASE(IMSG_NONE);
-
- CASE(IMSG_CTL_OK);
- CASE(IMSG_CTL_FAIL);
-
- CASE(IMSG_CTL_GET_DIGEST);
- CASE(IMSG_CTL_GET_STATS);
- CASE(IMSG_CTL_LIST_MESSAGES);
- CASE(IMSG_CTL_LIST_ENVELOPES);
- CASE(IMSG_CTL_MTA_SHOW_HOSTS);
- CASE(IMSG_CTL_MTA_SHOW_RELAYS);
- CASE(IMSG_CTL_MTA_SHOW_ROUTES);
- CASE(IMSG_CTL_MTA_SHOW_HOSTSTATS);
- CASE(IMSG_CTL_MTA_BLOCK);
- CASE(IMSG_CTL_MTA_UNBLOCK);
- CASE(IMSG_CTL_MTA_SHOW_BLOCK);
- CASE(IMSG_CTL_PAUSE_EVP);
- CASE(IMSG_CTL_PAUSE_MDA);
- CASE(IMSG_CTL_PAUSE_MTA);
- CASE(IMSG_CTL_PAUSE_SMTP);
- CASE(IMSG_CTL_PROFILE);
- CASE(IMSG_CTL_PROFILE_DISABLE);
- CASE(IMSG_CTL_PROFILE_ENABLE);
- CASE(IMSG_CTL_RESUME_EVP);
- CASE(IMSG_CTL_RESUME_MDA);
- CASE(IMSG_CTL_RESUME_MTA);
- CASE(IMSG_CTL_RESUME_SMTP);
- CASE(IMSG_CTL_RESUME_ROUTE);
- CASE(IMSG_CTL_REMOVE);
- CASE(IMSG_CTL_SCHEDULE);
- CASE(IMSG_CTL_SHOW_STATUS);
- CASE(IMSG_CTL_TRACE_DISABLE);
- CASE(IMSG_CTL_TRACE_ENABLE);
- CASE(IMSG_CTL_UPDATE_TABLE);
- CASE(IMSG_CTL_VERBOSE);
- CASE(IMSG_CTL_DISCOVER_EVPID);
- CASE(IMSG_CTL_DISCOVER_MSGID);
-
- CASE(IMSG_CTL_SMTP_SESSION);
-
- CASE(IMSG_GETADDRINFO);
- CASE(IMSG_GETADDRINFO_END);
- CASE(IMSG_GETNAMEINFO);
- CASE(IMSG_RES_QUERY);
-
- CASE(IMSG_CERT_INIT);
- CASE(IMSG_CERT_CERTIFICATE);
- CASE(IMSG_CERT_VERIFY);
-
- CASE(IMSG_SETUP_KEY);
- CASE(IMSG_SETUP_PEER);
- CASE(IMSG_SETUP_DONE);
-
- CASE(IMSG_CONF_START);
- CASE(IMSG_CONF_END);
-
- CASE(IMSG_STAT_INCREMENT);
- CASE(IMSG_STAT_DECREMENT);
- CASE(IMSG_STAT_SET);
-
- CASE(IMSG_LKA_AUTHENTICATE);
- CASE(IMSG_LKA_OPEN_FORWARD);
- CASE(IMSG_LKA_ENVELOPE_SUBMIT);
- CASE(IMSG_LKA_ENVELOPE_COMMIT);
-
- CASE(IMSG_QUEUE_DELIVER);
- CASE(IMSG_QUEUE_DELIVERY_OK);
- CASE(IMSG_QUEUE_DELIVERY_TEMPFAIL);
- CASE(IMSG_QUEUE_DELIVERY_PERMFAIL);
- CASE(IMSG_QUEUE_DELIVERY_LOOP);
- CASE(IMSG_QUEUE_DISCOVER_EVPID);
- CASE(IMSG_QUEUE_DISCOVER_MSGID);
- CASE(IMSG_QUEUE_ENVELOPE_ACK);
- CASE(IMSG_QUEUE_ENVELOPE_COMMIT);
- CASE(IMSG_QUEUE_ENVELOPE_REMOVE);
- CASE(IMSG_QUEUE_ENVELOPE_SCHEDULE);
- CASE(IMSG_QUEUE_ENVELOPE_SUBMIT);
- CASE(IMSG_QUEUE_HOLDQ_HOLD);
- CASE(IMSG_QUEUE_HOLDQ_RELEASE);
- CASE(IMSG_QUEUE_MESSAGE_COMMIT);
- CASE(IMSG_QUEUE_MESSAGE_ROLLBACK);
- CASE(IMSG_QUEUE_SMTP_SESSION);
- CASE(IMSG_QUEUE_TRANSFER);
-
- CASE(IMSG_MDA_DELIVERY_OK);
- CASE(IMSG_MDA_DELIVERY_TEMPFAIL);
- CASE(IMSG_MDA_DELIVERY_PERMFAIL);
- CASE(IMSG_MDA_DELIVERY_LOOP);
- CASE(IMSG_MDA_DELIVERY_HOLD);
- CASE(IMSG_MDA_DONE);
- CASE(IMSG_MDA_FORK);
- CASE(IMSG_MDA_HOLDQ_RELEASE);
- CASE(IMSG_MDA_LOOKUP_USERINFO);
- CASE(IMSG_MDA_KILL);
- CASE(IMSG_MDA_OPEN_MESSAGE);
-
- CASE(IMSG_MTA_DELIVERY_OK);
- CASE(IMSG_MTA_DELIVERY_TEMPFAIL);
- CASE(IMSG_MTA_DELIVERY_PERMFAIL);
- CASE(IMSG_MTA_DELIVERY_LOOP);
- CASE(IMSG_MTA_DELIVERY_HOLD);
- CASE(IMSG_MTA_DNS_HOST);
- CASE(IMSG_MTA_DNS_HOST_END);
- CASE(IMSG_MTA_DNS_MX);
- CASE(IMSG_MTA_DNS_MX_PREFERENCE);
- CASE(IMSG_MTA_HOLDQ_RELEASE);
- CASE(IMSG_MTA_LOOKUP_CREDENTIALS);
- CASE(IMSG_MTA_LOOKUP_SOURCE);
- CASE(IMSG_MTA_LOOKUP_HELO);
- CASE(IMSG_MTA_LOOKUP_SMARTHOST);
- CASE(IMSG_MTA_OPEN_MESSAGE);
- CASE(IMSG_MTA_SCHEDULE);
-
- CASE(IMSG_SCHED_ENVELOPE_BOUNCE);
- CASE(IMSG_SCHED_ENVELOPE_DELIVER);
- CASE(IMSG_SCHED_ENVELOPE_EXPIRE);
- CASE(IMSG_SCHED_ENVELOPE_INJECT);
- CASE(IMSG_SCHED_ENVELOPE_REMOVE);
- CASE(IMSG_SCHED_ENVELOPE_TRANSFER);
-
- CASE(IMSG_SMTP_AUTHENTICATE);
- CASE(IMSG_SMTP_MESSAGE_COMMIT);
- CASE(IMSG_SMTP_MESSAGE_CREATE);
- CASE(IMSG_SMTP_MESSAGE_ROLLBACK);
- CASE(IMSG_SMTP_MESSAGE_OPEN);
- CASE(IMSG_SMTP_CHECK_SENDER);
- CASE(IMSG_SMTP_EXPAND_RCPT);
- CASE(IMSG_SMTP_LOOKUP_HELO);
-
- CASE(IMSG_SMTP_REQ_CONNECT);
- CASE(IMSG_SMTP_REQ_HELO);
- CASE(IMSG_SMTP_REQ_MAIL);
- CASE(IMSG_SMTP_REQ_RCPT);
- CASE(IMSG_SMTP_REQ_DATA);
- CASE(IMSG_SMTP_REQ_EOM);
- CASE(IMSG_SMTP_EVENT_RSET);
- CASE(IMSG_SMTP_EVENT_COMMIT);
- CASE(IMSG_SMTP_EVENT_ROLLBACK);
- CASE(IMSG_SMTP_EVENT_DISCONNECT);
-
- CASE(IMSG_LKA_PROCESSOR_FORK);
- CASE(IMSG_LKA_PROCESSOR_ERRFD);
-
- CASE(IMSG_REPORT_SMTP_LINK_CONNECT);
- CASE(IMSG_REPORT_SMTP_LINK_DISCONNECT);
- CASE(IMSG_REPORT_SMTP_LINK_TLS);
- CASE(IMSG_REPORT_SMTP_LINK_GREETING);
- CASE(IMSG_REPORT_SMTP_LINK_IDENTIFY);
- CASE(IMSG_REPORT_SMTP_LINK_AUTH);
-
- CASE(IMSG_REPORT_SMTP_TX_RESET);
- CASE(IMSG_REPORT_SMTP_TX_BEGIN);
- CASE(IMSG_REPORT_SMTP_TX_ENVELOPE);
- CASE(IMSG_REPORT_SMTP_TX_COMMIT);
- CASE(IMSG_REPORT_SMTP_TX_ROLLBACK);
-
- CASE(IMSG_REPORT_SMTP_PROTOCOL_CLIENT);
- CASE(IMSG_REPORT_SMTP_PROTOCOL_SERVER);
-
- CASE(IMSG_FILTER_SMTP_BEGIN);
- CASE(IMSG_FILTER_SMTP_END);
- CASE(IMSG_FILTER_SMTP_PROTOCOL);
- CASE(IMSG_FILTER_SMTP_DATA_BEGIN);
- CASE(IMSG_FILTER_SMTP_DATA_END);
-
- CASE(IMSG_CA_RSA_PRIVENC);
- CASE(IMSG_CA_RSA_PRIVDEC);
- CASE(IMSG_CA_ECDSA_SIGN);
- default:
- (void)snprintf(buf, sizeof(buf), "IMSG_??? (%d)", type);
-
- return buf;
- }
-}
-
-#ifdef BSD_AUTH
-int
-parent_auth_bsd(const char *username, const char *password)
-{
- char user[LOGIN_NAME_MAX];
- char pass[LINE_MAX];
- int ret;
-
- (void)strlcpy(user, username, sizeof(user));
- (void)strlcpy(pass, password, sizeof(pass));
-
- ret = auth_userokay(user, NULL, "auth-smtp", pass);
- if (ret)
- return LKA_OK;
- return LKA_PERMFAIL;
-}
-#endif
-
-#ifdef USE_PAM
-int
-pam_conv_password(int num_msg, const struct pam_message **msg,
- struct pam_response **respp, void *password)
-{
- struct pam_response *response;
-
- if (num_msg != 1)
- return PAM_CONV_ERR;
-
- response = calloc(1, sizeof(struct pam_response));
- if (response == NULL || (response->resp = strdup(password)) == NULL) {
- free(response);
- return PAM_BUF_ERR;
- }
-
- *respp = response;
- return PAM_SUCCESS;
-}
-int
-parent_auth_pam(const char *username, const char *password)
-{
- int rc;
- pam_handle_t *pamh = NULL;
- struct pam_conv conv = { pam_conv_password, (char *)password };
-
- if ((rc = pam_start(USE_PAM_SERVICE, username, &conv, &pamh)) != PAM_SUCCESS)
- goto end;
- if ((rc = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
- goto end;
- if ((rc = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS)
- goto end;
-
-end:
- pam_end(pamh, rc);
-
- switch (rc) {
- case PAM_SUCCESS:
- return LKA_OK;
- case PAM_SYSTEM_ERR:
- case PAM_ABORT:
- case PAM_AUTHINFO_UNAVAIL:
- return LKA_TEMPFAIL;
- default:
- return LKA_PERMFAIL;
- }
-}
-#endif
-
-#ifdef HAVE_GETSPNAM
-int
-parent_auth_getspnam(const char *username, const char *password)
-{
- struct spwd *pw;
- char *ep;
-
- errno = 0;
- do {
- pw = getspnam(username);
- } while (pw == NULL && errno == EINTR);
-
- if (pw == NULL) {
- if (errno)
- return LKA_TEMPFAIL;
- return LKA_PERMFAIL;
- }
-
- if ((ep = crypt(password, pw->sp_pwdp)) == NULL)
- return LKA_PERMFAIL;
-
- if (strcmp(pw->sp_pwdp, ep) == 0)
- return LKA_OK;
-
- return LKA_PERMFAIL;
-}
-#endif
-
-int
-parent_auth_pwd(const char *username, const char *password)
-{
- struct passwd *pw;
- char *ep;
-
- errno = 0;
- do {
- pw = getpwnam(username);
- } while (pw == NULL && errno == EINTR);
-
- if (pw == NULL) {
- if (errno)
- return LKA_TEMPFAIL;
- return LKA_PERMFAIL;
- }
-
- if ((ep = crypt(password, pw->pw_passwd)) == NULL)
- return LKA_PERMFAIL;
-
- if (strcmp(pw->pw_passwd, ep) == 0)
- return LKA_OK;
-
- return LKA_PERMFAIL;
-}
-
-int
-parent_auth_user(const char *username, const char *password)
-{
-#if defined(BSD_AUTH)
- return (parent_auth_bsd(username, password));
-#elif defined(USE_PAM)
- return (parent_auth_pam(username, password));
-#elif defined(HAVE_GETSPNAM)
- return (parent_auth_getspnam(username, password));
-#else
- return (parent_auth_pwd(username, password));
-#endif
-}
diff --git a/smtpd/smtpd.conf b/smtpd/smtpd.conf
deleted file mode 100644
index a7ba6c64..00000000
--- a/smtpd/smtpd.conf
+++ /dev/null
@@ -1,19 +0,0 @@
-# $OpenBSD: smtpd.conf,v 1.10 2018/05/24 11:40:17 gilles Exp $
-
-# This is the smtpd server system-wide configuration file.
-# See smtpd.conf(5) for more information.
-
-table aliases file:/etc/mail/aliases
-
-# To accept external mail, replace with: listen on all
-#
-listen on localhost
-
-action "local" maildir alias <aliases>
-action "relay" relay
-
-# Uncomment the following to accept external mail for domain "example.org"
-#
-# match from any for domain "example.org" action "local"
-match for local action "local"
-match from local for any action "relay"
diff --git a/smtpd/smtpd.conf.5 b/smtpd/smtpd.conf.5
deleted file mode 100644
index c543c662..00000000
--- a/smtpd/smtpd.conf.5
+++ /dev/null
@@ -1,1240 +0,0 @@
-.\" $OpenBSD: smtpd.conf.5,v 1.250 2020/04/25 09:20:38 eric Exp $
-.\"
-.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org>
-.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
-.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.\"
-.Dd $Mdocdate: April 25 2020 $
-.Dt SMTPD.CONF 5
-.Os
-.Sh NAME
-.Nm smtpd.conf
-.Nd Simple Mail Transfer Protocol daemon configuration file
-.Sh DESCRIPTION
-.Nm
-is the configuration file for the mail daemon
-.Xr smtpd 8 .
-.Pp
-When mail arrives,
-each
-.Dq RCPT TO:
-command generates a mail envelope.
-If an envelope matches
-any of a pre-designated set of criteria
-(using the
-.Ic match
-directive),
-the message is accepted for delivery.
-A copy of the message, as well as its associated envelopes,
-is saved in the mail queue and later dispatched
-according to an associated set of actions
-(using the
-.Ic action
-directive).
-If an envelope does not match any options,
-it is rejected.
-The match rules are evaluated sequentially,
-with the first match winning.
-.Pp
-The format of the configuration file is fairly flexible.
-The current line can be extended over multiple lines using a backslash
-.Pq Sq \e .
-Comments can be put anywhere in the file using a hash mark
-.Pq Sq # ,
-and extend to the end of the current line.
-Care should be taken when commenting out multi-line text:
-the comment is effective until the end of the entire block.
-Argument names not beginning with a letter, digit, or underscore,
-as well as reserved words
-(such as
-.Ic listen ,
-.Ic match ,
-and
-.Cm port ) ,
-must be quoted.
-Arguments containing whitespace should be surrounded by double quotes
-.Pq \&" .
-.Pp
-Macros can be defined that are later expanded in context.
-Macro names must start with a letter, digit, or underscore,
-and may contain any of those characters,
-but may not be reserved words.
-Macros are not expanded inside quotes.
-For example:
-.Bd -literal -offset indent
-lan_addr = "192.168.0.1"
-listen on $lan_addr
-listen on $lan_addr tls auth
-.Ed
-.Pp
-The syntax of
-.Nm
-is described below.
-.Bl -tag -width Ds
-.It Ic action Ar name method Op Ar options
-When the queue runner processes an envelope from the mail queue,
-it carries out the
-.Ic action
-.Ar name ,
-selected by the
-.Ic match No ... Cm action
-directive when the message was received.
-The
-.Ic action
-directive provides configuration data for delivery attempts.
-Required lookups are performed at the time of each delivery attempt.
-Consequently, changing an
-.Ic action
-directive or the files it references and restarting the
-.Xr smtpd 8
-daemon causes the changes to take effect for subsequent delivery
-attempts for the respective dispatcher
-.Ar name ,
-even for messages that were already stuck in the queue
-prior to the configuration changes.
-.Pp
-The delivery
-.Ar method
-parameter may be one of the following:
-.Bl -tag -width Ds
-.It Cm expand-only
-Only accept the message if a delivery method was specified
-in an aliases or
-.Pa .forward
-file.
-.It Cm forward-only
-Only accept the message if the recipient results in a remote address
-after the processing of aliases or forward file.
-.It Cm lmtp Ar destination Op Ar rcpt-to
-Deliver the message to an LMTP server at
-.Ar destination .
-The location may be expressed as host:port or as a UNIX socket.
-.Pp
-Optionally,
-.Ar rcpt-to
-might be specified to use the
-recipient email address (after expansion) instead of the
-local user in the LMTP session as RCPT TO.
-.It Cm maildir Op Ar pathname Op Cm junk
-Deliver the message to the maildir in
-.Ar pathname
-if specified, or by default to
-.Pa ~/Maildir .
-.Pp
-The
-.Ar pathname
-may contain format specifiers that are expanded before use
-.Pq see Sx FORMAT SPECIFIERS .
-.Pp
-If the
-.Cm junk
-argument is provided, the message will be moved to the
-.Ql Junk
-folder if it contains a positive
-.Ql X-Spam
-header.
-This folder will be created under
-.Ar pathname
-if it does not yet exist.
-.It Cm mbox
-Deliver the message to the user's mbox with
-.Xr mail.local 8 .
-.It Cm mda Ar command
-Delegate the delivery to a
-.Ar command
-that receives the message on its standard input.
-.Pp
-The
-.Ar command
-may contain format specifiers that are expanded before use
-.Pq see Sx FORMAT SPECIFIERS .
-.It Cm relay
-Relay the message to another SMTP server.
-.El
-.Pp
-The local delivery methods support additional options:
-.Bl -tag -width Ds
-.It Cm alias Pf < Ar table Ns >
-Use the mapping
-.Ar table
-for
-.Xr aliases 5
-expansion.
-.It Xo
-.Cm ttl
-.Sm off
-.Ar n
-.Brq Cm s | m | h | d
-.Sm on
-.Xc
-Specify how long a message may remain in the queue.
-.It Cm user Ar username
-Specify the
-.Ar username
-for performing the delivery, to be looked up with
-.Xr getpwnam 3 .
-.Pp
-This is used for virtual hosting where a single username
-is in charge of handling delivery for all virtual users.
-.Pp
-This option is not usable with the
-.Cm mbox
-delivery method.
-.It Cm userbase Pf < Ar table Ns >
-Use the mapping
-.Ar table
-for user lookups instead of the
-.Xr getpwnam 3
-function.
-.Pp
-The
-.Cm userbase
-does not apply for the
-.Cm user
-option.
-.It Cm virtual Pf < Ar table Ns >
-Use the mapping
-.Ar table
-for virtual expansion.
-The aliasing table format is described in
-.Xr table 5 .
-.It Cm wrapper Ar name
-Use the wrapper specified in
-.Cm mda wrapper .
-.El
-.Pp
-The relay delivery methods also support additional options:
-.Bl -tag -width Ds
-.It Cm backup
-Operate as a backup mail exchanger delivering messages to any mail exchanger
-with higher priority.
-.It Cm backup mx Ar name
-Operate as a backup mail exchanger delivering messages to any mail exchanger
-with higher priority than mail exchanger identified as
-.Ar name .
-.It Cm helo Ar heloname
-Advertise
-.Ar heloname
-as the hostname to other mail exchangers during the HELO phase.
-.It Cm helo-src Pf < Ar table Ns >
-Use the mapping
-.Ar table
-to look up a hostname matching the source address,
-to advertise during the HELO phase.
-.It Cm domain Pf < Ar domains Ns >
-Do not perform MX lookups but look up destination domain in
-.Ar domains
-and use matching relay url as relay host.
-.It Cm host Ar relay-url
-Do not perform MX lookups but relay messages to the relay host described by
-.Ar relay-url .
-The format for
-.Ar relay-url
-is
-.Sm off
-.Op Ar proto No :// Op Ar label No @
-.Ar host Op : Ar port .
-.Sm on
-The following protocols are available:
-.Pp
-.Bl -tag -width "smtp+notls" -compact
-.It smtp
-Normal SMTP session with opportunistic STARTTLS
-(the default).
-.It smtp+tls
-Normal SMTP session with mandatory STARTTLS.
-.It smtp+notls
-Plain text SMTP session without TLS.
-.It lmtp
-LMTP session.
-.Ar port
-is required.
-.It smtps
-SMTP session with forced TLS on connection, default port is 465.
-.El
-Unless noted,
-.Ar port
-defaults to 25.
-.Pp
-The
-.Ar label
-corresponds to an entry in a credentials table,
-as documented in
-.Xr table 5 .
-It is used with the
-.Dq smtp+tls
-and
-.Dq smtps
-protocols for authentication.
-Server certificates for those protocols are verified by default.
-.It Cm srs
-When relaying a mail resulting from a forward,
-use the Sender Rewriting Scheme to rewrite sender address.
-.It Cm tls Op Cm no-verify
-Require TLS to be used when relaying, using mandatory STARTTLS by default.
-When used with a smarthost, the protocol must not be
-.Dq smtp+notls:// .
-If
-.Cm no-verify
-is specified, do not require a valid certificate.
-.It Cm auth Pf < Ar table Ns >
-Use the mapping
-.Ar table
-for connecting to
-.Ar relay-url
-using credentials.
-This option is usable only with
-.Cm host
-option.
-The credential table format is described in
-.Xr table 5 .
-.It Cm mail-from Ar mailaddr
-Use
-.Ar mailaddr
-as the MAIL FROM address within the SMTP transaction.
-.It Cm src Ar sourceaddr | Pf < Ar sourceaddr Ns >
-Use the string or list table
-.Ar sourceaddr
-for the source IP address,
-which is useful on machines with multiple interfaces.
-If the list contains more than one address, all of them are used
-in such a way that traffic is routed as efficiently as possible.
-.El
-.It Ic bounce Cm warn-interval Ar delay Op , Ar delay ...
-Send warning messages to the envelope sender when temporary delivery
-failures cause a message to remain on the queue for longer than
-.Ar delay .
-Each
-.Ar delay
-parameter consists of a positive decimal integer and a unit
-.Cm s , m , h ,
-or
-.Cm d .
-At most four
-.Ar delay
-parameters can be specified.
-The default is
-.Qq Ic bounce Cm warn-interval No 4h ,
-sending a single warning after four hours.
-.It Ic ca Ar caname Cm cert Ar cafile
-Associate the Certificate Authority (CA) certificate file
-.Ar cafile
-with host
-.Ar caname ,
-and use that file as the CA certificate for that host.
-.Ar caname
-is the server's name,
-derived from the default hostname
-or set using either
-.Pa /etc/mail/mailname
-or using the
-.Ic hostname
-directive.
-.It Ic filter Ar chain-name Ic chain Brq Ar filter-name Op , Ar ...
-Register a chain of filters
-.Ar chain-name ,
-consisting of the filters listed from
-.Ar filter-name .
-Filters part of a filter chain are executed in order of declaration for
-each phase that they are registered for.
-A filter chain may be used in place of a filter for any directive but
-filter chains themselves.
-.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions decision
-Register a filter
-.Ar filter-name .
-A
-.Ar decision
-about what to do with the mail is taken at phase
-.Ar phase-name
-when matching
-.Ar conditions .
-Phases, matching conditions, and decisions are described in
-.Sx MAIL FILTERING ,
-below.
-.It Ic filter Ar filter-name Ic proc Ar proc-name
-Register
-.Qq proc
-filter
-.Ar filter-name
-backed by the
-.Ar proc-name
-process.
-.It Ic filter Ar filter-name Ic proc-exec Ar command
-Register and execute
-.Qq proc
-filter
-.Ar filter-name
-from
-.Ar command .
-If
-.Ar command
-starts with a slash it is executed with an absolute path,
-else it will be run from
-.Dq /usr/local/libexec/smtpd/ .
-.It Ic include Qq Ar pathname
-Replace this directive with the content of the additional configuration
-file at the absolute
-.Ar pathname .
-.It Ic listen on Ar interface Oo Ar family Oc Op Ar options
-Listen on the
-.Ar interface
-for incoming connections, using the same syntax as for
-.Xr ifconfig 8 .
-The
-.Ar interface
-parameter may also be an interface group, an IP address, or a domain name.
-Listening can optionally be restricted to a specific address
-.Ar family ,
-which can be either
-.Cm inet4
-or
-.Cm inet6 .
-.Pp
-The
-.Ar options
-are as follows:
-.Bl -tag -width Ds
-.It Cm auth Op Pf < Ar authtable Ns >
-Support SMTPAUTH: clients may only start SMTP transactions
-after successful authentication.
-Users are authenticated against either their own normal login credentials
-or a credentials table
-.Ar authtable ,
-the format of which is described in
-.Xr table 5 .
-.It Cm auth-optional Op Pf < Ar authtable Ns >
-Support SMTPAUTH optionally:
-clients need not authenticate, but may do so.
-This allows a
-.Ic listen on
-directive to both accept incoming mail from untrusted senders
-and permit outgoing mail from authenticated users
-(using
-.Cm match auth ) .
-It can be used in situations where it is not possible to listen on a separate port
-(usually the submission port, 587)
-for users to authenticate.
-.It Ic ca Ar caname
-For secure connections,
-use the CA certificate associated with
-.Ar caname
-(declared in a
-.Ic ca
-directive)
-as the CA certificate when verifying client certificates.
-.It Ic filter Ar name
-Apply filter
-.Ar name
-on connections handled by this listener.
-.It Cm hostname Ar hostname
-Use
-.Ar hostname
-in the greeting banner instead of the default server name.
-.It Cm hostnames Pf < Ar names Ns >
-Override the server name for specific addresses.
-The
-.Ar names
-table contains a mapping of IP addresses to hostnames.
-If the address on which the connection arrives appears in the mapping,
-the associated hostname is used.
-.It Cm mask-src
-Omit the
-.Sy from
-part when prepending
-.Dq Received
-headers.
-.It Cm no-dsn
-Disable the DSN (Delivery Status Notification) extension.
-.It Cm pki Ar pkiname
-For secure connections,
-use the certificate associated with
-.Ar pkiname
-(declared in a
-.Ic pki
-directive)
-to prove a mail server's identity.
-.It Cm port Op Ar port
-Listen on the given
-.Ar port
-instead of the default port 25.
-.It Cm proxy-v2
-Support the PROXYv2 protocol,
-rewriting appropriately source address received from proxy.
-.It Cm received-auth
-In
-.Dq Received
-headers, report whether the session was authenticated
-and by which local user.
-.It Cm senders Pf < Ar users Ns > Op Cm masquerade
-Look up the authenticated user in the
-.Ar users
-mapping table to find the email addresses that user is allowed
-to submit mail as.
-In addition, if the
-.Cm masquerade
-option is provided,
-the From header is rewritten
-to match the sender provided in the SMTP session.
-.It Cm smtps
-Support SMTPS, by default on port 465.
-Mutually exclusive with
-.Cm tls .
-.It Cm tag Ar tag
-Clients connecting to the listener are tagged with the given
-.Ar tag .
-.It Cm tls
-Support STARTTLS, by default on port 25.
-Mutually exclusive with
-.Cm smtps .
-.It Cm tls-require Op Cm verify
-Like
-.Cm tls ,
-but force clients to establish a secure connection
-before being allowed to start an SMTP transaction.
-With the
-.Cm verify
-option, clients must also provide a valid certificate
-to establish an SMTP session.
-.El
-.It Ic listen on Cm socket Op Ar options
-Listen for incoming SMTP connections on the Unix domain socket
-.Pa /var/run/smtpd.sock .
-This is done by default, even if the directive is absent.
-.Pp
-The
-.Ar options
-are as follows:
-.Bl -tag -width Ds
-.It Ic filter Ar name
-Apply filter
-.Ar name
-on connections handled by this listener.
-.It Cm mask-src
-Omit the
-.Sy from
-part when prepending
-.Dq Received
-headers.
-.It Cm tag Ar tag
-Clients connecting to the listener are tagged with the given
-.Ar tag .
-.El
-.It Ic match Ar options Cm action Ar name
-If at least one mail envelope matches the
-.Ar options
-of one
-.Ic match Cm action
-directive, receive the incoming message, put a copy into each
-matching envelope, and atomically save the envelopes to the mail
-spool for later processing by the respective dispatcher
-.Ar name .
-.Pp
-The following matching options are supported and can all be negated:
-.Bl -tag -width Ds
-.It Xo
-.Op Ic \&!
-.Cm for any
-.Xc
-Specify that session may address any destination.
-.It Xo
-.Op Ic \&!
-.Cm for local
-.Xc
-Specify that session may address any local domain.
-This is the default, and may be omitted.
-.It Xo
-.Op Ic \&!
-.Cm for domain
-.Ar domain | Pf < Ar domain Ns >
-.Xc
-Specify that session may address the string or list table
-.Ar domain .
-.It Xo
-.Op Ic \&!
-.Cm for domain regex
-.Ar domain | Pf < Ar domain Ns >
-.Xc
-Specify that session may address the regex or regex table
-.Ar domain .
-.It Xo
-.Op Ic \&!
-.Cm for rcpt-to
-.Ar recipient | Pf < Ar recipient Ns >
-.Xc
-Specify that session may address the string or list table
-.Ar recipient .
-.It Xo
-.Op Ic \&!
-.Cm for rcpt-to regex
-.Ar recipient | Pf < Ar recipient Ns >
-.Xc
-Specify that session may address the regex or regex table
-.Ar recipient .
-.It Xo
-.Op Ic \&!
-.Cm from any
-.Xc
-Specify that session may originate from any source.
-.It Xo
-.Op Ic \&!
-.Cm from auth
-.Xc
-Specify that session may originate from any authenticated user,
-no matter the source IP address.
-.It Xo
-.Op Ic \&!
-.Cm from auth
-.Ar user | Pf < Ar user Ns >
-.Xc
-Specify that session may originate from authenticated user or user list
-.Ar user ,
-no matter the source IP address.
-.It Xo
-.Op Ic \&!
-.Cm from auth
-.Ar user | Pf < Ar user Ns >
-.Xc
-Specify that session may originate from authenticated regex or regex list
-.Ar user ,
-no matter the source IP address.
-.It Xo
-.Op Ic \&!
-.Cm from local
-.Xc
-Specify that session may only originate from a local IP address,
-or from the local enqueuer.
-This is the default, and may be omitted.
-.It Xo
-.Op Ic \&!
-.Cm from mail-from
-.Ar sender | Pf < Ar sender Ns >
-.Xc
-Specify that session may originate from sender or sender list
-.Ar sender ,
-no matter the source IP address.
-.It Xo
-.Op Ic \&!
-.Cm from mail-from regex
-.Ar sender | Pf < Ar sender Ns >
-.Xc
-Specify that session may originate from regex or regex list
-.Ar sender ,
-no matter the source IP address.
-.It Xo
-.Op Ic \&!
-.Cm from rdns
-.Xc
-Specify that session may only originate from an IP address that
-resolves to a reverse DNS.
-.It Xo
-.Op Ic \&!
-.Cm from rdns
-.Ar hostname | Pf < Ar hostname Ns >
-.Xc
-Specify that session may only originate from an IP address that
-resolves to a reverse DNS matching string or list string
-.Ar hostname .
-.It Xo
-.Op Ic \&!
-.Cm from rdns regex
-.Ar hostname | Pf < Ar hostname Ns >
-.Xc
-Specify that session may only originate from an IP address that
-resolves to a reverse DNS matching regex or list regex
-.Ar hostname .
-.It Xo
-.Op Ic \&!
-.Cm from socket
-.Xc
-Specify that session may only originate from the local enqueuer.
-.It Xo
-.Op Ic \&!
-.Cm from src
-.Ar address | Pf < Ar address Ns >
-.Xc
-Specify that session may only originate from string or list table
-.Ar address
-which can be a specific address or a subnet expressed in CIDR-notation.
-.It Xo
-.Op Ic \&!
-.Cm from src regex
-.Ar address | Pf < Ar address Ns >
-.Xc
-Specify that session may only originate from regex or regex table
-.Ar address
-which can be a specific address or a subnet expressed in CIDR-notation.
-.El
-.Pp
-In addition, the following transaction options:
-.Bl -tag -width Ds
-.It Xo
-.Op Ic \&!
-.Cm auth
-.Xc
-Matches transactions which have been authenticated.
-.It Xo
-.Op Ic \&!
-.Cm auth
-.Ar username | Pf < Ar username Ns >
-.Xc
-Matches transactions which have been authenticated for user or user list
-.Ar username .
-.It Xo
-.Op Ic \&!
-.Cm auth regex
-.Ar username | Pf < Ar username Ns >
-.Xc
-Matches transactions which have been authenticated for regex or regex list
-.Ar username .
-.It Xo
-.Op Ic \&!
-.Cm helo
-.Ar helo-name | Pf < Ar helo-name Ns >
-.Xc
-Specify that session's HELO / EHLO should match the string or list table
-.Ar helo-name .
-.It Xo
-.Op Ic \&!
-.Cm helo regex
-.Ar helo-name | Pf < Ar helo-name Ns >
-.Xc
-Specify that session's HELO / EHLO should match the regex or regex table
-.Ar helo-name .
-.It Xo
-.Op Ic \&!
-.Cm mail-from
-.Ar sender | Pf < Ar sender Ns >
-.Xc
-Specify that transactions's MAIL FROM should match the string or list table
-.Ar sender .
-.It Xo
-.Op Ic \&!
-.Cm mail-from regex
-.Ar sender | Pf < Ar sender Ns >
-.Xc
-Specify that transactions's MAIL FROM should match the regex or regex table
-.Ar sender .
-.It Xo
-.Op Ic \&!
-.Cm rcpt-to
-.Ar recipient | Pf < Ar recipient Ns >
-.Xc
-Specify that transaction's RCPT TO should match the string or list table
-.Ar recipient .
-.It Xo
-.Op Ic \&!
-.Cm rcpt-to regex
-.Ar recipient | Pf < Ar recipient Ns >
-.Xc
-Specify that transaction's RCPT TO should match the regex or regex table
-.Ar recipient .
-.It Xo
-.Op Ic \&!
-.Cm tag Ar tag
-.Xc
-Matches transactions tagged with the given
-.Ar tag .
-.It Xo
-.Op Ic \&!
-.Cm tag regex Ar tag
-.Xc
-Matches transactions tagged with the given
-.Ar tag
-regex.
-.It Xo
-.Op Ic \&!
-.Cm tls
-.Xc
-Specify that transaction should take place in a TLS channel.
-.El
-.It Ic match Ar options Cm reject
-Reject the incoming message during the SMTP dialogue.
-The same
-.Ar options
-are supported as for the
-.Ic match Cm action
-directive.
-.It Ic mda Cm wrapper Ar name command
-Associate
-.Ar command
-with the mail delivery agent wrapper named
-.Ar name .
-When a local delivery specifies a wrapper, the
-.Ar command
-associated with the wrapper will be executed instead.
-The command may contain format specifiers
-.Pq see Sx FORMAT SPECIFIERS .
-.It Ic mta Cm max-deferred Ar number
-When delivery to a given host is suspended due to temporary failures,
-cache at most
-.Ar number
-envelopes for that host such that they can be delivered
-as soon as another delivery succeeds to that host.
-The default is 100.
-.It Ic pki Ar pkiname Cm cert Ar certfile
-Associate certificate file
-.Ar certfile
-with host
-.Ar pkiname ,
-and use that file to prove the identity of the mail server to clients.
-.Ar pkiname
-is the server's name,
-derived from the default hostname
-or set using either
-.Pa /etc/mail/mailname
-or using the
-.Ic hostname
-directive.
-If a fallback certificate or SNI is wanted, the
-.Sq *
-wildcard may be used as
-.Ar pkiname .
-.Pp
-A certificate chain may be created by appending one or many certificates,
-including a Certificate Authority certificate,
-to
-.Ar certfile .
-The creation of certificates is documented in
-.Xr starttls 8 .
-.It Ic pki Ar pkiname Cm key Ar keyfile
-Associate the key located in
-.Ar keyfile
-with host
-.Ar pkiname .
-.It Ic pki Ar pkiname Cm dhe Ar params
-Specify the DHE parameters to use for DHE cipher suites with host
-.Ar pkiname .
-Valid parameter values are
-.Cm none ,
-.Cm legacy ,
-and
-.Cm auto .
-For
-.Cm legacy ,
-a fixed key length of 1024 bits is used, whereas for
-.Cm auto ,
-the key length is determined automatically.
-The default is
-.Cm none ,
-which disables DHE cipher suites.
-.It Ic proc Ar proc-name Ar command
-Register an external process named
-.Ar proc-name
-from
-.Ar command .
-Such processes may be used to share the same instance between multiple filters.
-If
-.Ar command
-starts with a slash it is executed with an absolute path,
-else it will be run from
-.Dq /usr/local/libexec/smtpd/ .
-.It Ic queue Cm compression
-Store queue files in a compressed format.
-This may be useful to save disk space.
-.It Ic queue Cm encryption Op Ar key
-Encrypt queue files with
-.Xr EVP_aes_256_gcm 3 .
-If no
-.Ar key
-is specified, it is read with
-.Xr getpass 3 .
-If the string
-.Cm stdin
-or a single dash
-.Pq Ql -
-is given instead of a
-.Ar key ,
-the key is read from the standard input.
-.It Ic queue Cm ttl Ar delay
-Set the default expiration time for temporarily undeliverable
-messages, given as a positive decimal integer followed by a unit
-.Cm s , m , h ,
-or
-.Cm d .
-The default is four days
-.Pq 4d .
-.It Ic smtp Cm ciphers Ar control
-Set the
-.Ar control
-string for
-.Xr SSL_CTX_set_cipher_list 3 .
-The default is
-.Qq HIGH:!aNULL:!MD5 .
-.It Ic smtp limit Cm max-mails Ar count
-Limit the number of messages to
-.Ar count
-for each session.
-The default is 100.
-.It Ic smtp limit Cm max-rcpt Ar count
-Limit the number of recipients to
-.Ar count
-for each transaction.
-The default is 1000.
-.It Ic smtp Cm max-message-size Ar size
-Reject messages larger than
-.Ar size ,
-given as a positive number of bytes or as a string to be parsed with
-.Xr scan_scaled 3 .
-The default is
-.Qq 35M .
-.It Ic smtp Cm sub-addr-delim Ar character
-When resolving the local part of a local email address, ignore the ASCII
-.Ar character
-and all characters following it.
-The default is
-.Ql + .
-.It Ic srs Cm key Ar secret
-Set the secret key to use for SRS,
-the Sender Rewriting Scheme.
-.It Ic srs Cm key backup Ar secret
-Set a backup secret key to use as a fallback for SRS.
-This can be used to implement SRS key rotation.
-.It Ic srs Cm ttl Ar delay
-Set the time-to-live delay for SRS envelopes.
-After this delay,
-a bounce reply to the SRS address will be discarded to limit risks of forged addresses.
-The default is four days
-.Pq 4d .
-.It Ic table Ar name Oo Ar type : Oc Ns Ar pathname
-Tables provide additional configuration information for
-.Xr smtpd 8
-in the form of lists or key-value mappings.
-The format of the entries depends on what the table is used for.
-Refer to
-.Xr table 5
-for the exhaustive documentation.
-.Pp
-Each table is identified by an arbitrary, unique
-.Ar name .
-.Pp
-If the
-.Ar type
-is
-.Cm db ,
-information is stored in a file created with
-.Xr makemap 8 ;
-if it is
-.Cm file
-or omitted, information is stored in a plain text file
-using the format described in
-.Xr table 5 .
-The
-.Ar pathname
-to the file must be absolute.
-.It Ic table Ar name Brq Ar value Op , Ar ...
-Instead of using a separate file, declare a list table
-containing the given static
-.Ar value Ns s .
-The table must contain at least one value and may declare multiple values as a
-comma-separated (whitespace optional) list.
-.It Ic table Ar name Brq Ar key Ns = Ns Ar value Op , Ar ...
-Instead of using a separate file, declare a mapping table
-containing the given static
-.Ar key Ns - Ns Ar value
-pairs.
-The table must contain at least one key-value pair and may declare
-multiple pairs as a comma-separated (whitespace optional) list.
-.El
-.Ss MAIL FILTERING
-In a regular workflow,
-.Xr smtpd 8
-may accept or reject a message based only on the content of envelopes.
-Its decisions are about the handling of the message,
-not about the handling of an active session.
-.Pp
-Filtering extends the decision making process by allowing
-.Xr smtpd 8
-to stop at each phase of an SMTP session,
-check that conditions are met,
-then decide if a session is allowed to move forward.
-.Pp
-With filtering,
-a session may be interrupted at any phase before an envelope is complete.
-A message may also be rejected after being submitted,
-regardless of whether the envelope was accepted or not.
-.Pp
-The following phases are currently supported:
-.Bl -column mail-from -offset indent
-.It connect Ta upon connection, before a banner is displayed
-.It helo Ta after HELO command is submitted
-.It ehlo Ta after EHLO command is submitted
-.It mail-from Ta after MAIL FROM command is submitted
-.It rcpt-to Ta after RCPT TO command is submitted
-.It data Ta after DATA command is submitted
-.It commit Ta after message is fully is submitted
-.El
-.Pp
-At each phase, various conditions may be matched.
-The fcrdns, rdns, and src data are available in all phases,
-but other data must have been already submitted before they are available.
-.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent
-.It fcrdns Ta forward-confirmed reverse DNS is valid
-.It rdns Ta session has a reverse DNS
-.It rdns Pf < Ar table Ns > Ta session has a reverse DNS in table
-.It src Pf < Ar table Ns > Ta source address is in table
-.It helo Pf < Ar table Ns > Ta helo name is in table
-.It auth Ta session is authenticated
-.It auth Pf < Ar table Ns > Ta session username is in table
-.It mail-from Pf < Ar table Ns > Ta sender address is in table
-.It rcpt-to Pf < Ar table Ns > Ta recipient address is in table
-.El
-.Pp
-These conditions may all be negated by prefixing them with an exclamation mark:
-.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent
-.It !fcrdns Ta forward-confirmed reverse DNS is invalid
-.El
-.Pp
-Any conditions using a table may indicate that tables hold regex by
-prefixing the table name with the keyword regex.
-.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent
-.It helo regex Pf < Ar table Ns > Ta helo name matches a regex in table
-.El
-.Pp
-Finally, a number of decisions may be taken:
-.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent
-.It bypass Ta the session or transaction bypasses filters
-.It disconnect Ar message Ta the session is disconnected with message
-.It junk Ta the session or transaction is junked, i.e., an
-.Ql X-Spam: yes
-header is added to any messages
-.It reject Ar message Ta the command is rejected with message
-.It rewrite Ar value Ta the command parameter is rewritten with value
-.El
-.Pp
-Decisions that involve a message require that the message be RFC valid,
-meaning that they should either start with a 4xx or 5xx status code.
-Descisions can be taken at any phase,
-though junking can only happen before a message is committed.
-.Ss FORMAT SPECIFIERS
-Some configuration directives support expansion of their parameters at runtime.
-Such directives (for example
-.Ic action Cm maildir ,
-.Ic action Cm mda )
-may use format specifiers which are expanded before delivery or
-relaying.
-The following formats are currently supported:
-.Bl -column %{user.directory} -offset indent
-.It %{sender} Ta sender email address, may be empty string
-.It %{sender.user} Ta user part of the sender email address, may be empty
-.It %{sender.domain} Ta domain part of the sender email address, may be empty
-.It %{rcpt} Ta recipient email address
-.It %{rcpt.user} Ta user part of the recipient email address
-.It %{rcpt.domain} Ta domain part of the recipient email address
-.It %{dest} Ta recipient email address after expansion
-.It %{dest.user} Ta user part after expansion
-.It %{dest.domain} Ta domain part after expansion
-.It %{user.username} Ta local user
-.It %{user.directory} Ta home directory of the local user
-.It %{mbox.from} Ta name used in mbox From separator lines
-.It %{mda} Ta mda command, only available for mda wrappers
-.El
-.Pp
-Expansion formats also support partial expansion using the optional
-bracket notations with substring offset.
-For example, with recipient domain
-.Dq example.org :
-.Bl -column %{rcpt.domain[0:-4]} -offset indent
-.It %{rcpt.domain[0]} Ta expands to Dq e
-.It %{rcpt.domain[1]} Ta expands to Dq x
-.It %{rcpt.domain[8:]} Ta expands to Dq org
-.It %{rcpt.domain[-3:]} Ta expands to Dq org
-.It %{rcpt.domain[0:6]} Ta expands to Dq example
-.It %{rcpt.domain[0:-4]} Ta expands to Dq example
-.El
-.Pp
-In addition, modifiers may be applied to the token.
-For example, with recipient
-.Dq User+Tag@Example.org :
-.Bl -column %{rcpt:lowercase|strip} -offset indent
-.It %{rcpt:lowercase} Ta expands to Dq user+tag@example.org
-.It %{rcpt:uppercase} Ta expands to Dq USER+TAG@EXAMPLE.ORG
-.It %{rcpt:strip} Ta expands to Dq User@Example.org
-.It %{rcpt:lowercase|strip} Ta expands to Dq user@example.org
-.El
-.Pp
-For security concerns, expanded values are sanitized and potentially
-dangerous characters are replaced with
-.Sq \&: .
-In situations where they are desirable, the
-.Dq raw
-modifier may be applied.
-For example, with recipient
-.Dq user+t?g@example.org :
-.Bl -column %{rcpt:raw} -offset indent
-.It %{rcpt} Ta expands to Dq user+t:g@example.org
-.It %{rcpt:raw} Ta expands to Dq user+t?g@example.org
-.El
-.Sh FILES
-.Bl -tag -width "/etc/mail/smtpd.confXXX" -compact
-.It Pa /etc/mail/smtpd.conf
-Default
-.Xr smtpd 8
-configuration file.
-.It Pa /etc/mail/mailname
-If this file exists,
-the first line is used as the server name.
-Otherwise, the server name is derived from the local hostname returned by
-.Xr gethostname 3 ,
-either directly if it is a fully qualified domain name,
-or by retrieving the associated canonical name through
-.Xr getaddrinfo 3 .
-.It Pa /var/run/smtpd.sock
-Unix domain socket for incoming SMTP connections.
-.It Pa /var/spool/smtpd/
-Spool directories for mail during processing.
-.El
-.Sh EXAMPLES
-The default
-.Nm
-file which ships with
-.Ox
-listens on the loopback network interface
-.Pq Pa lo0
-and allows for mail from users and daemons on the local machine,
-as well as permitting email to remote servers.
-Some more complex configurations are given below.
-.Pp
-This first example is the same as the default configuration,
-but all outgoing mail is forwarded to a remote SMTP server.
-A secrets file is needed to specify a username and password:
-.Bd -literal -offset indent
-# touch /etc/mail/secrets
-# chmod 640 /etc/mail/secrets
-# chown root:_smtpd /etc/mail/secrets
-# echo "bob username:password" > /etc/mail/secrets
-.Ed
-.Pp
-.Nm
-would look like this:
-.Bd -literal -offset indent
-table aliases file:/etc/mail/aliases
-table secrets file:/etc/mail/secrets
-
-listen on lo0
-
-action "local_mail" mbox alias <aliases>
-action "outbound" relay host smtp+tls://bob@smtp.example.com \e
- auth <secrets>
-
-match from local for local action "local_mail"
-match from local for any action "outbound"
-.Ed
-.Pp
-In this second example,
-the aim is to permit mail delivery and relaying only for users that can authenticate
-(using their normal login credentials).
-An RSA certificate must be provided to prove the server's identity.
-The mail server listens on all interfaces the default routes point to.
-Mail with a local destination is sent to an external MDA.
-First, the RSA certificate is created:
-.Bd -literal -offset indent
-# openssl genrsa \-out /etc/ssl/private/mail.example.com.key 4096
-# openssl req \-new \-x509 \-key /etc/ssl/private/mail.example.com.key \e
- \-out /etc/ssl/mail.example.com.crt \-days 365
-# chmod 600 /etc/ssl/mail.example.com.crt
-# chmod 600 /etc/ssl/private/mail.example.com.key
-.Ed
-.Pp
-In the example above,
-a certificate valid for one year was created.
-The configuration file would look like this:
-.Bd -literal -offset indent
-pki mail.example.com cert "/etc/ssl/mail.example.com.crt"
-pki mail.example.com key "/etc/ssl/private/mail.example.com.key"
-
-table aliases file:/etc/mail/aliases
-
-listen on lo0
-listen on egress tls pki mail.example.com auth
-
-action mda_with_aliases mda "/path/to/mda \-f \-" alias <aliases>
-action mda_without_aliases mda "/path/to/mda \-f \-"
-action "outbound" relay
-
-match for local action mda_with_aliases
-match from any for domain example.com action mda_without_aliases
-match for any action "outbound"
-match auth from any for any action "outbound"
-.Ed
-.Pp
-For sites that wish to sign messages using DKIM,
-the following example uses
-.Sy opensmtpd-filter-dkimsign
-for DKIM signing:
-.Bd -literal -offset indent
-table aliases file:/etc/mail/aliases
-
-filter "dkimsign" proc-exec "filter-dkimsign -d <domain> -s <selector> \e
- -k /etc/mail/dkim/private.key" user _dkimsign group _dkimsign
-
-listen on socket filter "dkimsign"
-listen on lo0 filter "dkimsign"
-
-action "local_mail" mbox alias <aliases>
-action "outbound" relay
-
-match for local action "local_mail"
-match for any action "outbound"
-.Ed
-.Pp
-Alternatively, the
-.Sy opensmtpd-filter-rspamd
-package may be used to provide integration with
-.Sy rspamd ,
-a third-party daemon which provides multiple antispam features
-as well as DKIM signing.
-As well as configuring
-.Sy rspamd
-itself,
-it requires use of the
-.Cm proc-exec
-keyword:
-.Bd -literal -offset indent
-filter "rspamd" proc-exec "filter-rspamd"
-.Ed
-.Pp
-Sites that accept non-local messages may be able to cut down on the
-volume of spam received by rejecting forged messages that claim
-to be from the local domain.
-The following example uses a list table
-.Em other-relays
-to specify the IP addresses of relays that may legitimately
-originate mail with the owner's domain as the sender.
-.Bd -literal -offset indent
-table aliases file:/etc/mail/aliases
-table other-relays file:/etc/mail/other-relays
-
-listen on lo0
-listen on egress
-
-action "local_mail" mbox alias <aliases>
-action "outbound" relay
-
-match for local action "local_mail"
-match for any action "outbound"
-match !from src <other-relays> mail\-from "@example.com" for any \e
- reject
-match from any for domain example.com action "local_mail"
-.Ed
-.Sh SEE ALSO
-.Xr mailer.conf 5 ,
-.Xr table 5 ,
-.Xr makemap 8 ,
-.Xr smtpd 8
-.Sh HISTORY
-.Xr smtpd 8
-first appeared in
-.Ox 4.6 .
diff --git a/smtpd/smtpd.h b/smtpd/smtpd.h
deleted file mode 100644
index 4385f747..00000000
--- a/smtpd/smtpd.h
+++ /dev/null
@@ -1,1784 +0,0 @@
-/* $OpenBSD: smtpd.h,v 1.656 2020/04/08 07:30:44 eric Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * 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 <event.h>
-
-#include <imsg.h>
-
-#include "openbsd-compat.h"
-
-#ifndef nitems
-#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
-#endif
-
-#include <netinet/in.h>
-#include <netdb.h>
-#include <event.h>
-
-#include "smtpd-defines.h"
-#include "smtpd-api.h"
-#include "ioev.h"
-
-#define CHECK_IMSG_DATA_SIZE(imsg, expected_sz) do { \
- if ((imsg)->hdr.len - IMSG_HEADER_SIZE != (expected_sz)) \
- fatalx("smtpd: imsg %d: data size expected %zd got %zd",\
- (imsg)->hdr.type, \
- (expected_sz), (imsg)->hdr.len - IMSG_HEADER_SIZE); \
-} while (0)
-
-#ifndef SMTPD_CONFDIR
-#define SMTPD_CONFDIR "/etc"
-#endif
-#define CONF_FILE SMTPD_CONFDIR "/smtpd.conf"
-#define MAILNAME_FILE SMTPD_CONFDIR "/mailname"
-#ifndef CA_FILE
-#define CA_FILE "/etc/ssl/cert.pem"
-#endif
-
-#define PROC_COUNT 7
-
-#define MAX_HOPS_COUNT 100
-#define DEFAULT_MAX_BODY_SIZE (35*1024*1024)
-
-#define EXPAND_BUFFER 1024
-
-#define SMTPD_QUEUE_EXPIRY (4 * 24 * 60 * 60)
-#ifndef SMTPD_USER
-#define SMTPD_USER "_smtpd"
-#endif
-#ifndef SMTPD_QUEUE_USER
-#define SMTPD_QUEUE_USER "_smtpq"
-#endif
-#ifndef SMTPD_SOCKDIR
-#define SMTPD_SOCKDIR "/var/run"
-#endif
-#define SMTPD_SOCKET SMTPD_SOCKDIR "/smtpd.sock"
-#ifndef SMTPD_NAME
-#define SMTPD_NAME "OpenSMTPD"
-#endif
-#define SMTPD_VERSION "6.7.0-portable"
-#define SMTPD_SESSION_TIMEOUT 300
-#define SMTPD_BACKLOG 5
-
-#ifndef PATH_SMTPCTL
-#define PATH_SMTPCTL "/usr/sbin/smtpctl"
-#endif
-
-#define PATH_OFFLINE "/offline"
-#define PATH_PURGE "/purge"
-#define PATH_TEMPORARY "/temporary"
-
-#ifndef PATH_LIBEXEC
-#define PATH_LIBEXEC "/usr/local/libexec/smtpd"
-#endif
-
-
-/*
- * RFC 5322 defines these characters as valid, some of them are
- * potentially dangerous and need to be escaped.
- */
-#define MAILADDR_ALLOWED "!#$%&'*/?^`{|}~+-=_"
-#define MAILADDR_ESCAPE "!#$%&'*?`{|}~"
-
-
-#define F_STARTTLS 0x01
-#define F_SMTPS 0x02
-#define F_SSL (F_STARTTLS | F_SMTPS)
-#define F_AUTH 0x08
-#define F_STARTTLS_REQUIRE 0x20
-#define F_AUTH_REQUIRE 0x40
-#define F_MASK_SOURCE 0x100
-#define F_TLS_VERIFY 0x200
-#define F_EXT_DSN 0x400
-#define F_RECEIVEDAUTH 0x800
-#define F_MASQUERADE 0x1000
-#define F_FILTERED 0x2000
-#define F_PROXY 0x4000
-
-#define RELAY_TLS_OPPORTUNISTIC 0
-#define RELAY_TLS_STARTTLS 1
-#define RELAY_TLS_SMTPS 2
-#define RELAY_TLS_NO 3
-
-#define RELAY_AUTH 0x08
-#define RELAY_LMTP 0x80
-#define RELAY_TLS_VERIFY 0x200
-
-#define MTA_EXT_DSN 0x400
-
-
-#define P_SENDMAIL 0
-#define P_NEWALIASES 1
-#define P_MAKEMAP 2
-
-#define CERT_ERROR -1
-#define CERT_OK 0
-#define CERT_NOCA 1
-#define CERT_NOCERT 2
-#define CERT_INVALID 3
-
-struct userinfo {
- char username[SMTPD_VUSERNAME_SIZE];
- char directory[PATH_MAX];
- uid_t uid;
- gid_t gid;
-};
-
-struct netaddr {
- struct sockaddr_storage ss;
- int bits;
-};
-
-struct relayhost {
- uint16_t flags;
- int tls;
- char hostname[HOST_NAME_MAX+1];
- uint16_t port;
- char authlabel[PATH_MAX];
-};
-
-struct credentials {
- char username[LINE_MAX];
- char password[LINE_MAX];
-};
-
-struct destination {
- char name[HOST_NAME_MAX+1];
-};
-
-struct source {
- struct sockaddr_storage addr;
-};
-
-struct addrname {
- struct sockaddr_storage addr;
- char name[HOST_NAME_MAX+1];
-};
-
-union lookup {
- struct expand *expand;
- struct credentials creds;
- struct netaddr netaddr;
- struct source source;
- struct destination domain;
- struct userinfo userinfo;
- struct mailaddr mailaddr;
- struct addrname addrname;
- struct maddrmap *maddrmap;
- char relayhost[LINE_MAX];
-};
-
-/*
- * Bump IMSG_VERSION whenever a change is made to enum imsg_type.
- * This will ensure that we can never use a wrong version of smtpctl with smtpd.
- */
-#define IMSG_VERSION 16
-
-enum imsg_type {
- IMSG_NONE,
-
- IMSG_CTL_OK,
- IMSG_CTL_FAIL,
-
- IMSG_CTL_GET_DIGEST,
- IMSG_CTL_GET_STATS,
- IMSG_CTL_LIST_MESSAGES,
- IMSG_CTL_LIST_ENVELOPES,
- IMSG_CTL_MTA_SHOW_HOSTS,
- IMSG_CTL_MTA_SHOW_RELAYS,
- IMSG_CTL_MTA_SHOW_ROUTES,
- IMSG_CTL_MTA_SHOW_HOSTSTATS,
- IMSG_CTL_MTA_BLOCK,
- IMSG_CTL_MTA_UNBLOCK,
- IMSG_CTL_MTA_SHOW_BLOCK,
- IMSG_CTL_PAUSE_EVP,
- IMSG_CTL_PAUSE_MDA,
- IMSG_CTL_PAUSE_MTA,
- IMSG_CTL_PAUSE_SMTP,
- IMSG_CTL_PROFILE,
- IMSG_CTL_PROFILE_DISABLE,
- IMSG_CTL_PROFILE_ENABLE,
- IMSG_CTL_RESUME_EVP,
- IMSG_CTL_RESUME_MDA,
- IMSG_CTL_RESUME_MTA,
- IMSG_CTL_RESUME_SMTP,
- IMSG_CTL_RESUME_ROUTE,
- IMSG_CTL_REMOVE,
- IMSG_CTL_SCHEDULE,
- IMSG_CTL_SHOW_STATUS,
- IMSG_CTL_TRACE_DISABLE,
- IMSG_CTL_TRACE_ENABLE,
- IMSG_CTL_UPDATE_TABLE,
- IMSG_CTL_VERBOSE,
- IMSG_CTL_DISCOVER_EVPID,
- IMSG_CTL_DISCOVER_MSGID,
-
- IMSG_CTL_SMTP_SESSION,
-
- IMSG_GETADDRINFO,
- IMSG_GETADDRINFO_END,
- IMSG_GETNAMEINFO,
- IMSG_RES_QUERY,
-
- IMSG_CERT_INIT,
- IMSG_CERT_CERTIFICATE,
- IMSG_CERT_VERIFY,
-
- IMSG_SETUP_KEY,
- IMSG_SETUP_PEER,
- IMSG_SETUP_DONE,
-
- IMSG_CONF_START,
- IMSG_CONF_END,
-
- IMSG_STAT_INCREMENT,
- IMSG_STAT_DECREMENT,
- IMSG_STAT_SET,
-
- IMSG_LKA_AUTHENTICATE,
- IMSG_LKA_OPEN_FORWARD,
- IMSG_LKA_ENVELOPE_SUBMIT,
- IMSG_LKA_ENVELOPE_COMMIT,
-
- IMSG_QUEUE_DELIVER,
- IMSG_QUEUE_DELIVERY_OK,
- IMSG_QUEUE_DELIVERY_TEMPFAIL,
- IMSG_QUEUE_DELIVERY_PERMFAIL,
- IMSG_QUEUE_DELIVERY_LOOP,
- IMSG_QUEUE_DISCOVER_EVPID,
- IMSG_QUEUE_DISCOVER_MSGID,
- IMSG_QUEUE_ENVELOPE_ACK,
- IMSG_QUEUE_ENVELOPE_COMMIT,
- IMSG_QUEUE_ENVELOPE_REMOVE,
- IMSG_QUEUE_ENVELOPE_SCHEDULE,
- IMSG_QUEUE_ENVELOPE_SUBMIT,
- IMSG_QUEUE_HOLDQ_HOLD,
- IMSG_QUEUE_HOLDQ_RELEASE,
- IMSG_QUEUE_MESSAGE_COMMIT,
- IMSG_QUEUE_MESSAGE_ROLLBACK,
- IMSG_QUEUE_SMTP_SESSION,
- IMSG_QUEUE_TRANSFER,
-
- IMSG_MDA_DELIVERY_OK,
- IMSG_MDA_DELIVERY_TEMPFAIL,
- IMSG_MDA_DELIVERY_PERMFAIL,
- IMSG_MDA_DELIVERY_LOOP,
- IMSG_MDA_DELIVERY_HOLD,
- IMSG_MDA_DONE,
- IMSG_MDA_FORK,
- IMSG_MDA_HOLDQ_RELEASE,
- IMSG_MDA_LOOKUP_USERINFO,
- IMSG_MDA_KILL,
- IMSG_MDA_OPEN_MESSAGE,
-
- IMSG_MTA_DELIVERY_OK,
- IMSG_MTA_DELIVERY_TEMPFAIL,
- IMSG_MTA_DELIVERY_PERMFAIL,
- IMSG_MTA_DELIVERY_LOOP,
- IMSG_MTA_DELIVERY_HOLD,
- IMSG_MTA_DNS_HOST,
- IMSG_MTA_DNS_HOST_END,
- IMSG_MTA_DNS_MX,
- IMSG_MTA_DNS_MX_PREFERENCE,
- IMSG_MTA_HOLDQ_RELEASE,
- IMSG_MTA_LOOKUP_CREDENTIALS,
- IMSG_MTA_LOOKUP_SOURCE,
- IMSG_MTA_LOOKUP_HELO,
- IMSG_MTA_LOOKUP_SMARTHOST,
- IMSG_MTA_OPEN_MESSAGE,
- IMSG_MTA_SCHEDULE,
-
- IMSG_SCHED_ENVELOPE_BOUNCE,
- IMSG_SCHED_ENVELOPE_DELIVER,
- IMSG_SCHED_ENVELOPE_EXPIRE,
- IMSG_SCHED_ENVELOPE_INJECT,
- IMSG_SCHED_ENVELOPE_REMOVE,
- IMSG_SCHED_ENVELOPE_TRANSFER,
-
- IMSG_SMTP_AUTHENTICATE,
- IMSG_SMTP_MESSAGE_COMMIT,
- IMSG_SMTP_MESSAGE_CREATE,
- IMSG_SMTP_MESSAGE_ROLLBACK,
- IMSG_SMTP_MESSAGE_OPEN,
- IMSG_SMTP_CHECK_SENDER,
- IMSG_SMTP_EXPAND_RCPT,
- IMSG_SMTP_LOOKUP_HELO,
-
- IMSG_SMTP_REQ_CONNECT,
- IMSG_SMTP_REQ_HELO,
- IMSG_SMTP_REQ_MAIL,
- IMSG_SMTP_REQ_RCPT,
- IMSG_SMTP_REQ_DATA,
- IMSG_SMTP_REQ_EOM,
- IMSG_SMTP_EVENT_RSET,
- IMSG_SMTP_EVENT_COMMIT,
- IMSG_SMTP_EVENT_ROLLBACK,
- IMSG_SMTP_EVENT_DISCONNECT,
-
- IMSG_LKA_PROCESSOR_FORK,
- IMSG_LKA_PROCESSOR_ERRFD,
-
- IMSG_REPORT_SMTP_LINK_CONNECT,
- IMSG_REPORT_SMTP_LINK_DISCONNECT,
- IMSG_REPORT_SMTP_LINK_GREETING,
- IMSG_REPORT_SMTP_LINK_IDENTIFY,
- IMSG_REPORT_SMTP_LINK_TLS,
- IMSG_REPORT_SMTP_LINK_AUTH,
- IMSG_REPORT_SMTP_TX_RESET,
- IMSG_REPORT_SMTP_TX_BEGIN,
- IMSG_REPORT_SMTP_TX_MAIL,
- IMSG_REPORT_SMTP_TX_RCPT,
- IMSG_REPORT_SMTP_TX_ENVELOPE,
- IMSG_REPORT_SMTP_TX_DATA,
- IMSG_REPORT_SMTP_TX_COMMIT,
- IMSG_REPORT_SMTP_TX_ROLLBACK,
- IMSG_REPORT_SMTP_PROTOCOL_CLIENT,
- IMSG_REPORT_SMTP_PROTOCOL_SERVER,
- IMSG_REPORT_SMTP_FILTER_RESPONSE,
- IMSG_REPORT_SMTP_TIMEOUT,
-
- IMSG_FILTER_SMTP_BEGIN,
- IMSG_FILTER_SMTP_END,
- IMSG_FILTER_SMTP_PROTOCOL,
- IMSG_FILTER_SMTP_DATA_BEGIN,
- IMSG_FILTER_SMTP_DATA_END,
-
- IMSG_CA_RSA_PRIVENC,
- IMSG_CA_RSA_PRIVDEC,
- IMSG_CA_ECDSA_SIGN,
-};
-
-enum smtp_proc_type {
- PROC_PARENT = 0,
- PROC_LKA,
- PROC_QUEUE,
- PROC_CONTROL,
- PROC_SCHEDULER,
- PROC_PONY,
- PROC_CA,
- PROC_PROCESSOR,
- PROC_CLIENT,
-};
-
-enum table_type {
- T_NONE = 0,
- T_DYNAMIC = 0x01, /* table with external source */
- T_LIST = 0x02, /* table holding a list */
- T_HASH = 0x04, /* table holding a hash table */
-};
-
-struct table {
- char t_name[LINE_MAX];
- enum table_type t_type;
- char t_config[PATH_MAX];
-
- void *t_handle;
- struct table_backend *t_backend;
-};
-
-struct table_backend {
- const char *name;
- const unsigned int services;
- int (*config)(struct table *);
- int (*add)(struct table *, const char *, const char *);
- void (*dump)(struct table *);
- int (*open)(struct table *);
- int (*update)(struct table *);
- void (*close)(struct table *);
- int (*lookup)(struct table *, enum table_service, const char *, char **);
- int (*fetch)(struct table *, enum table_service, char **);
-};
-
-
-enum bounce_type {
- B_FAILED,
- B_DELAYED,
- B_DELIVERED
-};
-
-enum dsn_ret {
- DSN_RETFULL = 1,
- DSN_RETHDRS
-};
-
-struct delivery_bounce {
- enum bounce_type type;
- time_t delay;
- time_t ttl;
- enum dsn_ret dsn_ret;
- int mta_without_dsn;
-};
-
-enum expand_type {
- EXPAND_INVALID,
- EXPAND_USERNAME,
- EXPAND_FILENAME,
- EXPAND_FILTER,
- EXPAND_INCLUDE,
- EXPAND_ADDRESS,
- EXPAND_ERROR,
-};
-
-enum filter_phase {
- FILTER_CONNECT,
- FILTER_HELO,
- FILTER_EHLO,
- FILTER_STARTTLS,
- FILTER_AUTH,
- FILTER_MAIL_FROM,
- FILTER_RCPT_TO,
- FILTER_DATA,
- FILTER_DATA_LINE,
- FILTER_RSET,
- FILTER_QUIT,
- FILTER_NOOP,
- FILTER_HELP,
- FILTER_WIZ,
- FILTER_COMMIT,
- FILTER_PHASES_COUNT /* must be last */
-};
-
-struct expandnode {
- RB_ENTRY(expandnode) entry;
- TAILQ_ENTRY(expandnode) tq_entry;
- enum expand_type type;
- int sameuser;
- int realuser;
- int forwarded;
- struct rule *rule;
- struct expandnode *parent;
- unsigned int depth;
- union {
- /*
- * user field handles both expansion user and system user
- * so we MUST make it large enough to fit a mailaddr user
- */
- char user[SMTPD_MAXLOCALPARTSIZE];
- char buffer[EXPAND_BUFFER];
- struct mailaddr mailaddr;
- } u;
- char subaddress[SMTPD_SUBADDRESS_SIZE];
-};
-
-struct expand {
- RB_HEAD(expandtree, expandnode) tree;
- TAILQ_HEAD(xnodes, expandnode) *queue;
- size_t nb_nodes;
- struct rule *rule;
- struct expandnode *parent;
-};
-
-struct maddrnode {
- TAILQ_ENTRY(maddrnode) entries;
- struct mailaddr mailaddr;
-};
-
-struct maddrmap {
- TAILQ_HEAD(xmaddr, maddrnode) queue;
-};
-
-#define DSN_SUCCESS 0x01
-#define DSN_FAILURE 0x02
-#define DSN_DELAY 0x04
-#define DSN_NEVER 0x08
-
-#define DSN_ENVID_LEN 100
-
-#define SMTPD_ENVELOPE_VERSION 3
-struct envelope {
- TAILQ_ENTRY(envelope) entry;
-
- char dispatcher[HOST_NAME_MAX+1];
-
- char tag[SMTPD_TAG_SIZE];
-
- uint32_t version;
- uint64_t id;
- enum envelope_flags flags;
-
- char smtpname[HOST_NAME_MAX+1];
- char helo[HOST_NAME_MAX+1];
- char hostname[HOST_NAME_MAX+1];
- char username[SMTPD_MAXMAILADDRSIZE];
- char errorline[LINE_MAX];
- struct sockaddr_storage ss;
-
- struct mailaddr sender;
- struct mailaddr rcpt;
- struct mailaddr dest;
-
- char mda_user[SMTPD_VUSERNAME_SIZE];
- char mda_subaddress[SMTPD_SUBADDRESS_SIZE];
- char mda_exec[LINE_MAX];
-
- enum delivery_type type;
- union {
- struct delivery_bounce bounce;
- } agent;
-
- uint16_t retry;
- time_t creation;
- time_t ttl;
- time_t lasttry;
- time_t nexttry;
- time_t lastbounce;
-
- struct mailaddr dsn_orcpt;
- char dsn_envid[DSN_ENVID_LEN+1];
- uint8_t dsn_notify;
- enum dsn_ret dsn_ret;
-
- uint8_t esc_class;
- uint8_t esc_code;
-};
-
-struct listener {
- uint16_t flags;
- int fd;
- struct sockaddr_storage ss;
- in_port_t port;
- struct timeval timeout;
- struct event ev;
- char filter_name[PATH_MAX];
- char pki_name[PATH_MAX];
- char ca_name[PATH_MAX];
- char tag[SMTPD_TAG_SIZE];
- char authtable[LINE_MAX];
- char hostname[HOST_NAME_MAX+1];
- char hostnametable[PATH_MAX];
- char sendertable[PATH_MAX];
-
- TAILQ_ENTRY(listener) entry;
-
- int local; /* there must be a better way */
-};
-
-struct smtpd {
- char sc_conffile[PATH_MAX];
- size_t sc_maxsize;
-
-#define SMTPD_OPT_VERBOSE 0x00000001
-#define SMTPD_OPT_NOACTION 0x00000002
- uint32_t sc_opts;
-
-#define SMTPD_EXITING 0x00000001 /* unused */
-#define SMTPD_MDA_PAUSED 0x00000002
-#define SMTPD_MTA_PAUSED 0x00000004
-#define SMTPD_SMTP_PAUSED 0x00000008
-#define SMTPD_MDA_BUSY 0x00000010
-#define SMTPD_MTA_BUSY 0x00000020
-#define SMTPD_BOUNCE_BUSY 0x00000040
-#define SMTPD_SMTP_DISABLED 0x00000080
- uint32_t sc_flags;
-
-#define QUEUE_COMPRESSION 0x00000001
-#define QUEUE_ENCRYPTION 0x00000002
-#define QUEUE_EVPCACHE 0x00000004
- uint32_t sc_queue_flags;
- char *sc_queue_key;
- size_t sc_queue_evpcache_size;
-
- size_t sc_session_max_rcpt;
- size_t sc_session_max_mails;
-
- struct dict *sc_mda_wrappers;
- size_t sc_mda_max_session;
- size_t sc_mda_max_user_session;
- size_t sc_mda_task_hiwat;
- size_t sc_mda_task_lowat;
- size_t sc_mda_task_release;
-
- size_t sc_mta_max_deferred;
-
- size_t sc_scheduler_max_inflight;
- size_t sc_scheduler_max_evp_batch_size;
- size_t sc_scheduler_max_msg_batch_size;
- size_t sc_scheduler_max_schedule;
-
- struct dict *sc_filter_processes_dict;
-
- int sc_ttl;
-#define MAX_BOUNCE_WARN 4
- time_t sc_bounce_warn[MAX_BOUNCE_WARN];
- char sc_hostname[HOST_NAME_MAX+1];
- struct stat_backend *sc_stat;
- struct compress_backend *sc_comp;
-
- time_t sc_uptime;
-
- /* This is a listener for a local socket used by smtp_enqueue(). */
- struct listener *sc_sock_listener;
-
- TAILQ_HEAD(listenerlist, listener) *sc_listeners;
-
- TAILQ_HEAD(rulelist, rule) *sc_rules;
-
-
- struct dict *sc_filters_dict;
- struct dict *sc_dispatchers;
- struct dispatcher *sc_dispatcher_bounce;
-
- struct dict *sc_ca_dict;
- struct dict *sc_pki_dict;
- struct dict *sc_ssl_dict;
-
- struct dict *sc_tables_dict; /* keyed lookup */
-
- struct dict *sc_limits_dict;
-
- char *sc_tls_ciphers;
-
- char *sc_subaddressing_delim;
-
- char *sc_srs_key;
- char *sc_srs_key_backup;
- int sc_srs_ttl;
-};
-
-#define TRACE_DEBUG 0x0001
-#define TRACE_IMSG 0x0002
-#define TRACE_IO 0x0004
-#define TRACE_SMTP 0x0008
-#define TRACE_FILTERS 0x0010
-#define TRACE_MTA 0x0020
-#define TRACE_BOUNCE 0x0040
-#define TRACE_SCHEDULER 0x0080
-#define TRACE_LOOKUP 0x0100
-#define TRACE_STAT 0x0200
-#define TRACE_RULES 0x0400
-#define TRACE_MPROC 0x0800
-#define TRACE_EXPAND 0x1000
-#define TRACE_TABLES 0x2000
-#define TRACE_QUEUE 0x4000
-
-#define PROFILE_TOSTAT 0x0001
-#define PROFILE_IMSG 0x0002
-#define PROFILE_QUEUE 0x0004
-
-struct forward_req {
- uint64_t id;
- uint8_t status;
-
- char user[SMTPD_VUSERNAME_SIZE];
- uid_t uid;
- gid_t gid;
- char directory[PATH_MAX];
-};
-
-struct deliver {
- char dispatcher[EXPAND_BUFFER];
-
- struct mailaddr sender;
- struct mailaddr rcpt;
- struct mailaddr dest;
-
- char mda_subaddress[SMTPD_SUBADDRESS_SIZE];
- char mda_exec[LINE_MAX];
-
- struct userinfo userinfo;
-};
-
-struct mta_host {
- SPLAY_ENTRY(mta_host) entry;
- struct sockaddr *sa;
- char *ptrname;
- int refcount;
- size_t nconn;
- time_t lastconn;
- time_t lastptrquery;
-
-#define HOST_IGNORE 0x01
- int flags;
-};
-
-struct mta_mx {
- TAILQ_ENTRY(mta_mx) entry;
- struct mta_host *host;
- char *mxname;
- int preference;
-};
-
-struct mta_domain {
- SPLAY_ENTRY(mta_domain) entry;
- char *name;
- int as_host;
- TAILQ_HEAD(, mta_mx) mxs;
- int mxstatus;
- int refcount;
- size_t nconn;
- time_t lastconn;
- time_t lastmxquery;
-};
-
-struct mta_source {
- SPLAY_ENTRY(mta_source) entry;
- struct sockaddr *sa;
- int refcount;
- size_t nconn;
- time_t lastconn;
-};
-
-struct mta_connector {
- struct mta_source *source;
- struct mta_relay *relay;
-
-#define CONNECTOR_ERROR_FAMILY 0x0001
-#define CONNECTOR_ERROR_SOURCE 0x0002
-#define CONNECTOR_ERROR_MX 0x0004
-#define CONNECTOR_ERROR_ROUTE_NET 0x0008
-#define CONNECTOR_ERROR_ROUTE_SMTP 0x0010
-#define CONNECTOR_ERROR_ROUTE 0x0018
-#define CONNECTOR_ERROR_BLOCKED 0x0020
-#define CONNECTOR_ERROR 0x00ff
-
-#define CONNECTOR_LIMIT_HOST 0x0100
-#define CONNECTOR_LIMIT_ROUTE 0x0200
-#define CONNECTOR_LIMIT_SOURCE 0x0400
-#define CONNECTOR_LIMIT_RELAY 0x0800
-#define CONNECTOR_LIMIT_CONN 0x1000
-#define CONNECTOR_LIMIT_DOMAIN 0x2000
-#define CONNECTOR_LIMIT 0xff00
-
-#define CONNECTOR_NEW 0x10000
-#define CONNECTOR_WAIT 0x20000
- int flags;
-
- int refcount;
- size_t nconn;
- time_t lastconn;
-};
-
-struct mta_route {
- SPLAY_ENTRY(mta_route) entry;
- uint64_t id;
- struct mta_source *src;
- struct mta_host *dst;
-#define ROUTE_NEW 0x01
-#define ROUTE_RUNQ 0x02
-#define ROUTE_KEEPALIVE 0x04
-#define ROUTE_DISABLED 0xf0
-#define ROUTE_DISABLED_NET 0x10
-#define ROUTE_DISABLED_SMTP 0x20
- int flags;
- int nerror;
- int penalty;
- int refcount;
- size_t nconn;
- time_t lastconn;
- time_t lastdisc;
- time_t lastpenalty;
-};
-
-struct mta_limits {
- size_t maxconn_per_host;
- size_t maxconn_per_route;
- size_t maxconn_per_source;
- size_t maxconn_per_connector;
- size_t maxconn_per_relay;
- size_t maxconn_per_domain;
-
- time_t conndelay_host;
- time_t conndelay_route;
- time_t conndelay_source;
- time_t conndelay_connector;
- time_t conndelay_relay;
- time_t conndelay_domain;
-
- time_t discdelay_route;
-
- size_t max_mail_per_session;
- time_t sessdelay_transaction;
- time_t sessdelay_keepalive;
-
- size_t max_failures_per_session;
-
- int family;
-
- int task_hiwat;
- int task_lowat;
- int task_release;
-};
-
-struct mta_relay {
- SPLAY_ENTRY(mta_relay) entry;
- uint64_t id;
-
- struct dispatcher *dispatcher;
- struct mta_domain *domain;
- struct mta_limits *limits;
- int tls;
- int flags;
- char *backupname;
- int backuppref;
- char *sourcetable;
- uint16_t port;
- char *pki_name;
- char *ca_name;
- char *authtable;
- char *authlabel;
- char *helotable;
- char *heloname;
- char *secret;
- int srs;
-
- int state;
- size_t ntask;
- TAILQ_HEAD(, mta_task) tasks;
-
- struct tree connectors;
- size_t sourceloop;
- time_t lastsource;
- time_t nextsource;
-
- int fail;
- char *failstr;
-
-#define RELAY_WAIT_MX 0x01
-#define RELAY_WAIT_PREFERENCE 0x02
-#define RELAY_WAIT_SECRET 0x04
-#define RELAY_WAIT_LIMITS 0x08
-#define RELAY_WAIT_SOURCE 0x10
-#define RELAY_WAIT_CONNECTOR 0x20
-#define RELAY_WAIT_SMARTHOST 0x40
-#define RELAY_WAITMASK 0x7f
- int status;
-
- int refcount;
- size_t nconn;
- size_t nconn_ready;
- time_t lastconn;
-};
-
-struct mta_envelope {
- TAILQ_ENTRY(mta_envelope) entry;
- uint64_t id;
- uint64_t session;
- time_t creation;
- char *smtpname;
- char *dest;
- char *rcpt;
- struct mta_task *task;
- int delivery;
-
- int ext;
- char *dsn_orcpt;
- char dsn_envid[DSN_ENVID_LEN+1];
- uint8_t dsn_notify;
- enum dsn_ret dsn_ret;
-
- char status[LINE_MAX];
-};
-
-struct mta_task {
- TAILQ_ENTRY(mta_task) entry;
- struct mta_relay *relay;
- uint32_t msgid;
- TAILQ_HEAD(, mta_envelope) envelopes;
- char *sender;
-};
-
-struct passwd;
-
-struct queue_backend {
- int (*init)(struct passwd *, int, const char *);
-};
-
-struct compress_backend {
- size_t (*compress_chunk)(void *, size_t, void *, size_t);
- size_t (*uncompress_chunk)(void *, size_t, void *, size_t);
- int (*compress_file)(FILE *, FILE *);
- int (*uncompress_file)(FILE *, FILE *);
-};
-
-/* auth structures */
-enum auth_type {
- AUTH_BSD,
- AUTH_PWD,
-};
-
-struct auth_backend {
- int (*authenticate)(char *, char *);
-};
-
-struct scheduler_backend {
- int (*init)(const char *);
-
- int (*insert)(struct scheduler_info *);
- size_t (*commit)(uint32_t);
- size_t (*rollback)(uint32_t);
-
- int (*update)(struct scheduler_info *);
- int (*delete)(uint64_t);
- int (*hold)(uint64_t, uint64_t);
- int (*release)(int, uint64_t, int);
-
- int (*batch)(int, int*, size_t*, uint64_t*, int*);
-
- size_t (*messages)(uint32_t, uint32_t *, size_t);
- size_t (*envelopes)(uint64_t, struct evpstate *, size_t);
- int (*schedule)(uint64_t);
- int (*remove)(uint64_t);
- int (*suspend)(uint64_t);
- int (*resume)(uint64_t);
- int (*query)(uint64_t);
-};
-
-enum stat_type {
- STAT_COUNTER,
- STAT_TIMESTAMP,
- STAT_TIMEVAL,
- STAT_TIMESPEC,
-};
-
-struct stat_value {
- enum stat_type type;
- union stat_v {
- size_t counter;
- time_t timestamp;
- struct timeval tv;
- struct timespec ts;
- } u;
-};
-
-#define STAT_KEY_SIZE 1024
-struct stat_kv {
- void *iter;
- char key[STAT_KEY_SIZE];
- struct stat_value val;
-};
-
-struct stat_backend {
- void (*init)(void);
- void (*close)(void);
- void (*increment)(const char *, size_t);
- void (*decrement)(const char *, size_t);
- void (*set)(const char *, const struct stat_value *);
- int (*iter)(void **, char **, struct stat_value *);
-};
-
-struct stat_digest {
- time_t startup;
- time_t timestamp;
-
- size_t clt_connect;
- size_t clt_disconnect;
-
- size_t evp_enqueued;
- size_t evp_dequeued;
-
- size_t evp_expired;
- size_t evp_removed;
- size_t evp_bounce;
-
- size_t dlv_ok;
- size_t dlv_permfail;
- size_t dlv_tempfail;
- size_t dlv_loop;
-};
-
-
-struct mproc {
- pid_t pid;
- char *name;
- int proc;
- void (*handler)(struct mproc *, struct imsg *);
- struct imsgbuf imsgbuf;
-
- char *m_buf;
- size_t m_alloc;
- size_t m_pos;
- uint32_t m_type;
- uint32_t m_peerid;
- pid_t m_pid;
- int m_fd;
-
- int enable;
- short events;
- struct event ev;
- void *data;
-};
-
-struct msg {
- const uint8_t *pos;
- const uint8_t *end;
-};
-
-extern enum smtp_proc_type smtpd_process;
-
-extern int tracing;
-extern int foreground_log;
-extern int profiling;
-
-extern struct mproc *p_control;
-extern struct mproc *p_parent;
-extern struct mproc *p_lka;
-extern struct mproc *p_queue;
-extern struct mproc *p_scheduler;
-extern struct mproc *p_pony;
-extern struct mproc *p_ca;
-
-extern struct smtpd *env;
-extern void (*imsg_callback)(struct mproc *, struct imsg *);
-
-/* inter-process structures */
-
-struct bounce_req_msg {
- uint64_t evpid;
- time_t timestamp;
- struct delivery_bounce bounce;
-};
-
-enum dns_error {
- DNS_OK = 0,
- DNS_RETRY,
- DNS_EINVAL,
- DNS_ENONAME,
- DNS_ENOTFOUND,
-};
-
-enum lka_resp_status {
- LKA_OK,
- LKA_TEMPFAIL,
- LKA_PERMFAIL
-};
-
-enum filter_type {
- FILTER_TYPE_BUILTIN,
- FILTER_TYPE_PROC,
- FILTER_TYPE_CHAIN,
-};
-
-enum filter_subsystem {
- FILTER_SUBSYSTEM_SMTP_IN = 1<<0,
- FILTER_SUBSYSTEM_SMTP_OUT = 1<<1,
-};
-
-struct filter_proc {
- const char *command;
- const char *user;
- const char *group;
- const char *chroot;
- int errfd;
- enum filter_subsystem filter_subsystem;
-};
-
-struct filter_config {
- char *name;
- enum filter_subsystem filter_subsystem;
- enum filter_type filter_type;
- enum filter_phase phase;
- char *reject;
- char *disconnect;
- char *rewrite;
- char *report;
- uint8_t junk;
- uint8_t bypass;
- char *proc;
-
- const char **chain;
- size_t chain_size;
- struct dict chain_procs;
-
- int8_t not_fcrdns;
- int8_t fcrdns;
-
- int8_t not_rdns;
- int8_t rdns;
-
- int8_t not_rdns_table;
- struct table *rdns_table;
-
- int8_t not_rdns_regex;
- struct table *rdns_regex;
-
- int8_t not_src_table;
- struct table *src_table;
-
- int8_t not_src_regex;
- struct table *src_regex;
-
- int8_t not_helo_table;
- struct table *helo_table;
-
- int8_t not_helo_regex;
- struct table *helo_regex;
-
- int8_t not_auth;
- int8_t auth;
-
- int8_t not_auth_table;
- struct table *auth_table;
-
- int8_t not_auth_regex;
- struct table *auth_regex;
-
- int8_t not_mail_from_table;
- struct table *mail_from_table;
-
- int8_t not_mail_from_regex;
- struct table *mail_from_regex;
-
- int8_t not_rcpt_to_table;
- struct table *rcpt_to_table;
-
- int8_t not_rcpt_to_regex;
- struct table *rcpt_to_regex;
-
-};
-
-enum filter_status {
- FILTER_PROCEED,
- FILTER_REWRITE,
- FILTER_REJECT,
- FILTER_DISCONNECT,
- FILTER_JUNK,
-};
-
-enum ca_resp_status {
- CA_OK,
- CA_FAIL
-};
-
-enum mda_resp_status {
- MDA_OK,
- MDA_TEMPFAIL,
- MDA_PERMFAIL
-};
-
-struct msg_walkinfo {
- struct event ev;
- uint32_t msgid;
- uint32_t peerid;
- size_t n_evp;
- void *data;
- int done;
-};
-
-
-enum dispatcher_type {
- DISPATCHER_LOCAL,
- DISPATCHER_REMOTE,
- DISPATCHER_BOUNCE,
-};
-
-struct dispatcher_local {
- uint8_t is_mbox; /* only for MBOX */
-
- uint8_t expand_only;
- uint8_t forward_only;
-
- char *mda_wrapper;
- char *command;
-
- char *table_alias;
- char *table_virtual;
- char *table_userbase;
-
- char *user;
-};
-
-struct dispatcher_remote {
- char *helo;
- char *helo_source;
-
- char *source;
-
- char *ca;
- char *pki;
-
- char *mail_from;
-
- char *smarthost;
- int smarthost_domain;
-
- char *auth;
- int tls_required;
- int tls_noverify;
-
- int backup;
- char *backupmx;
-
- char *filtername;
-
- int srs;
-};
-
-struct dispatcher_bounce {
-};
-
-struct dispatcher {
- enum dispatcher_type type;
- union dispatcher_agent {
- struct dispatcher_local local;
- struct dispatcher_remote remote;
- struct dispatcher_bounce bounce;
- } u;
-
- time_t ttl;
-};
-
-struct rule {
- TAILQ_ENTRY(rule) r_entry;
-
- uint8_t reject;
-
- int8_t flag_tag;
- int8_t flag_from;
- int8_t flag_for;
- int8_t flag_from_rdns;
- int8_t flag_from_socket;
-
- int8_t flag_tag_regex;
- int8_t flag_from_regex;
- int8_t flag_for_regex;
-
- int8_t flag_smtp_helo;
- int8_t flag_smtp_starttls;
- int8_t flag_smtp_auth;
- int8_t flag_smtp_mail_from;
- int8_t flag_smtp_rcpt_to;
-
- int8_t flag_smtp_helo_regex;
- int8_t flag_smtp_starttls_regex;
- int8_t flag_smtp_auth_regex;
- int8_t flag_smtp_mail_from_regex;
- int8_t flag_smtp_rcpt_to_regex;
-
-
- char *table_tag;
- char *table_from;
- char *table_for;
-
- char *table_smtp_helo;
- char *table_smtp_auth;
- char *table_smtp_mail_from;
- char *table_smtp_rcpt_to;
-
- char *dispatcher;
-};
-
-
-/* aliases.c */
-int aliases_get(struct expand *, const char *);
-int aliases_virtual_get(struct expand *, const struct mailaddr *);
-int alias_parse(struct expandnode *, const char *);
-
-
-/* auth.c */
-struct auth_backend *auth_backend_lookup(enum auth_type);
-
-
-/* bounce.c */
-void bounce_add(uint64_t);
-void bounce_fd(int);
-
-
-/* ca.c */
-int ca(void);
-int ca_X509_verify(void *, void *, const char *, const char *, const char **);
-void ca_imsg(struct mproc *, struct imsg *);
-void ca_init(void);
-void ca_engine_init(void);
-
-
-/* cert.c */
-int cert_init(const char *, int,
- void (*)(void *, int, const char *, const void *, size_t), void *);
-int cert_verify(const void *, const char *, int, void (*)(void *, int), void *);
-void cert_dispatch_request(struct mproc *, struct imsg *);
-void cert_dispatch_result(struct mproc *, struct imsg *);
-
-
-/* compress_backend.c */
-struct compress_backend *compress_backend_lookup(const char *);
-size_t compress_chunk(void *, size_t, void *, size_t);
-size_t uncompress_chunk(void *, size_t, void *, size_t);
-int compress_file(FILE *, FILE *);
-int uncompress_file(FILE *, FILE *);
-
-/* config.c */
-#define PURGE_LISTENERS 0x01
-#define PURGE_TABLES 0x02
-#define PURGE_RULES 0x04
-#define PURGE_PKI 0x08
-#define PURGE_PKI_KEYS 0x10
-#define PURGE_DISPATCHERS 0x20
-#define PURGE_EVERYTHING 0xff
-struct smtpd *config_default(void);
-void purge_config(uint8_t);
-void config_process(enum smtp_proc_type);
-void config_peer(enum smtp_proc_type);
-
-
-/* control.c */
-int control(void);
-int control_create_socket(void);
-
-
-/* crypto.c */
-int crypto_setup(const char *, size_t);
-int crypto_encrypt_file(FILE *, FILE *);
-int crypto_decrypt_file(FILE *, FILE *);
-size_t crypto_encrypt_buffer(const char *, size_t, char *, size_t);
-size_t crypto_decrypt_buffer(const char *, size_t, char *, size_t);
-
-
-/* dns.c */
-void dns_imsg(struct mproc *, struct imsg *);
-
-
-/* enqueue.c */
-int enqueue(int, char **, FILE *);
-
-
-/* envelope.c */
-void envelope_set_errormsg(struct envelope *, char *, ...);
-void envelope_set_esc_class(struct envelope *, enum enhanced_status_class);
-void envelope_set_esc_code(struct envelope *, enum enhanced_status_code);
-int envelope_load_buffer(struct envelope *, const char *, size_t);
-int envelope_dump_buffer(const struct envelope *, char *, size_t);
-
-
-/* expand.c */
-int expand_cmp(struct expandnode *, struct expandnode *);
-void expand_insert(struct expand *, struct expandnode *);
-struct expandnode *expand_lookup(struct expand *, struct expandnode *);
-void expand_clear(struct expand *);
-void expand_free(struct expand *);
-int expand_line(struct expand *, const char *, int);
-int expand_to_text(struct expand *, char *, size_t);
-RB_PROTOTYPE(expandtree, expandnode, nodes, expand_cmp);
-
-
-/* forward.c */
-int forwards_get(int, struct expand *);
-
-
-/* limit.c */
-void limit_mta_set_defaults(struct mta_limits *);
-int limit_mta_set(struct mta_limits *, const char*, int64_t);
-
-
-/* lka.c */
-int lka(void);
-
-
-/* lka_proc.c */
-int lka_proc_ready(void);
-void lka_proc_forked(const char *, uint32_t, int);
-void lka_proc_errfd(const char *, int);
-struct io *lka_proc_get_io(const char *);
-
-
-/* lka_report.c */
-void lka_report_init(void);
-void lka_report_register_hook(const char *, const char *);
-void lka_report_smtp_link_connect(const char *, struct timeval *, uint64_t, const char *, int,
- const struct sockaddr_storage *, const struct sockaddr_storage *);
-void lka_report_smtp_link_disconnect(const char *, struct timeval *, uint64_t);
-void lka_report_smtp_link_greeting(const char *, uint64_t, struct timeval *,
- const char *);
-void lka_report_smtp_link_identify(const char *, struct timeval *, uint64_t, const char *, const char *);
-void lka_report_smtp_link_tls(const char *, struct timeval *, uint64_t, const char *);
-void lka_report_smtp_link_auth(const char *, struct timeval *, uint64_t, const char *, const char *);
-void lka_report_smtp_tx_reset(const char *, struct timeval *, uint64_t, uint32_t);
-void lka_report_smtp_tx_begin(const char *, struct timeval *, uint64_t, uint32_t);
-void lka_report_smtp_tx_mail(const char *, struct timeval *, uint64_t, uint32_t, const char *, int);
-void lka_report_smtp_tx_rcpt(const char *, struct timeval *, uint64_t, uint32_t, const char *, int);
-void lka_report_smtp_tx_envelope(const char *, struct timeval *, uint64_t, uint32_t, uint64_t);
-void lka_report_smtp_tx_commit(const char *, struct timeval *, uint64_t, uint32_t, size_t);
-void lka_report_smtp_tx_data(const char *, struct timeval *, uint64_t, uint32_t, int);
-void lka_report_smtp_tx_rollback(const char *, struct timeval *, uint64_t, uint32_t);
-void lka_report_smtp_protocol_client(const char *, struct timeval *, uint64_t, const char *);
-void lka_report_smtp_protocol_server(const char *, struct timeval *, uint64_t, const char *);
-void lka_report_smtp_filter_response(const char *, struct timeval *, uint64_t,
- int, int, const char *);
-void lka_report_smtp_timeout(const char *, struct timeval *, uint64_t);
-void lka_report_filter_report(uint64_t, const char *, int, const char *,
- struct timeval *, const char *);
-void lka_report_proc(const char *, const char *);
-
-
-/* lka_filter.c */
-void lka_filter_init(void);
-void lka_filter_register_hook(const char *, const char *);
-void lka_filter_ready(void);
-int lka_filter_proc_in_session(uint64_t, const char *);
-void lka_filter_begin(uint64_t, const char *);
-void lka_filter_end(uint64_t);
-void lka_filter_protocol(uint64_t, enum filter_phase, const char *);
-void lka_filter_data_begin(uint64_t);
-void lka_filter_data_end(uint64_t);
-int lka_filter_response(uint64_t, const char *, const char *);
-
-
-/* lka_session.c */
-void lka_session(uint64_t, struct envelope *);
-void lka_session_forward_reply(struct forward_req *, int);
-
-
-/* log.c */
-void vlog(int, const char *, va_list);
-void logit(int, const char *, ...) __attribute__((format (printf, 2, 3)));
-
-
-/* mda.c */
-void mda_postfork(void);
-void mda_postprivdrop(void);
-void mda_imsg(struct mproc *, struct imsg *);
-
-
-/* mda_mbox.c */
-void mda_mbox_init(struct deliver *);
-void mda_mbox(struct deliver *);
-
-
-/* mda_unpriv.c */
-void mda_unpriv(struct dispatcher *, struct deliver *, const char *, const char *);
-
-
-/* mda_variables.c */
-ssize_t mda_expand_format(char *, size_t, const struct deliver *,
- const struct userinfo *, const char *);
-
-
-/* makemap.c */
-int makemap(int, int, char **);
-
-
-/* mailaddr.c */
-int mailaddr_line(struct maddrmap *, const char *);
-void maddrmap_init(struct maddrmap *);
-void maddrmap_insert(struct maddrmap *, struct maddrnode *);
-void maddrmap_free(struct maddrmap *);
-
-
-/* mproc.c */
-int mproc_fork(struct mproc *, const char*, char **);
-void mproc_init(struct mproc *, int);
-void mproc_clear(struct mproc *);
-void mproc_enable(struct mproc *);
-void mproc_disable(struct mproc *);
-void mproc_event_add(struct mproc *);
-void m_compose(struct mproc *, uint32_t, uint32_t, pid_t, int, void *, size_t);
-void m_composev(struct mproc *, uint32_t, uint32_t, pid_t, int,
- const struct iovec *, int);
-void m_forward(struct mproc *, struct imsg *);
-void m_create(struct mproc *, uint32_t, uint32_t, pid_t, int);
-void m_add(struct mproc *, const void *, size_t);
-void m_add_int(struct mproc *, int);
-void m_add_u32(struct mproc *, uint32_t);
-void m_add_size(struct mproc *, size_t);
-void m_add_time(struct mproc *, time_t);
-void m_add_timeval(struct mproc *, struct timeval *tv);
-void m_add_string(struct mproc *, const char *);
-void m_add_data(struct mproc *, const void *, size_t);
-void m_add_evpid(struct mproc *, uint64_t);
-void m_add_msgid(struct mproc *, uint32_t);
-void m_add_id(struct mproc *, uint64_t);
-void m_add_sockaddr(struct mproc *, const struct sockaddr *);
-void m_add_mailaddr(struct mproc *, const struct mailaddr *);
-void m_add_envelope(struct mproc *, const struct envelope *);
-void m_add_params(struct mproc *, struct dict *);
-void m_close(struct mproc *);
-void m_flush(struct mproc *);
-
-void m_msg(struct msg *, struct imsg *);
-int m_is_eom(struct msg *);
-void m_end(struct msg *);
-void m_get_int(struct msg *, int *);
-void m_get_size(struct msg *, size_t *);
-void m_get_u32(struct msg *, uint32_t *);
-void m_get_time(struct msg *, time_t *);
-void m_get_timeval(struct msg *, struct timeval *);
-void m_get_string(struct msg *, const char **);
-void m_get_data(struct msg *, const void **, size_t *);
-void m_get_evpid(struct msg *, uint64_t *);
-void m_get_msgid(struct msg *, uint32_t *);
-void m_get_id(struct msg *, uint64_t *);
-void m_get_sockaddr(struct msg *, struct sockaddr *);
-void m_get_mailaddr(struct msg *, struct mailaddr *);
-void m_get_envelope(struct msg *, struct envelope *);
-void m_get_params(struct msg *, struct dict *);
-void m_clear_params(struct dict *);
-
-
-/* mta.c */
-void mta_postfork(void);
-void mta_postprivdrop(void);
-void mta_imsg(struct mproc *, struct imsg *);
-void mta_route_ok(struct mta_relay *, struct mta_route *);
-void mta_route_error(struct mta_relay *, struct mta_route *);
-void mta_route_down(struct mta_relay *, struct mta_route *);
-void mta_route_collect(struct mta_relay *, struct mta_route *);
-void mta_source_error(struct mta_relay *, struct mta_route *, const char *);
-void mta_delivery_log(struct mta_envelope *, const char *, const char *, int, const char *);
-void mta_delivery_notify(struct mta_envelope *);
-struct mta_task *mta_route_next_task(struct mta_relay *, struct mta_route *);
-const char *mta_host_to_text(struct mta_host *);
-const char *mta_relay_to_text(struct mta_relay *);
-
-
-/* mta_session.c */
-void mta_session(struct mta_relay *, struct mta_route *, const char *);
-void mta_session_imsg(struct mproc *, struct imsg *);
-
-
-/* parse.y */
-int parse_config(struct smtpd *, const char *, int);
-int cmdline_symset(char *);
-
-
-/* queue.c */
-int queue(void);
-
-
-/* queue_backend.c */
-uint32_t queue_generate_msgid(void);
-uint64_t queue_generate_evpid(uint32_t);
-int queue_init(const char *, int);
-int queue_close(void);
-int queue_message_create(uint32_t *);
-int queue_message_delete(uint32_t);
-int queue_message_commit(uint32_t);
-int queue_message_fd_r(uint32_t);
-int queue_message_fd_rw(uint32_t);
-int queue_envelope_create(struct envelope *);
-int queue_envelope_delete(uint64_t);
-int queue_envelope_load(uint64_t, struct envelope *);
-int queue_envelope_update(struct envelope *);
-int queue_envelope_walk(struct envelope *);
-int queue_message_walk(struct envelope *, uint32_t, int *, void **);
-
-
-/* report_smtp.c */
-void report_smtp_link_connect(const char *, uint64_t, const char *, int,
- const struct sockaddr_storage *, const struct sockaddr_storage *);
-void report_smtp_link_disconnect(const char *, uint64_t);
-void report_smtp_link_greeting(const char *, uint64_t, const char *);
-void report_smtp_link_identify(const char *, uint64_t, const char *, const char *);
-void report_smtp_link_tls(const char *, uint64_t, const char *);
-void report_smtp_link_auth(const char *, uint64_t, const char *, const char *);
-void report_smtp_tx_reset(const char *, uint64_t, uint32_t);
-void report_smtp_tx_begin(const char *, uint64_t, uint32_t);
-void report_smtp_tx_mail(const char *, uint64_t, uint32_t, const char *, int);
-void report_smtp_tx_rcpt(const char *, uint64_t, uint32_t, const char *, int);
-void report_smtp_tx_envelope(const char *, uint64_t, uint32_t, uint64_t);
-void report_smtp_tx_data(const char *, uint64_t, uint32_t, int);
-void report_smtp_tx_commit(const char *, uint64_t, uint32_t, size_t);
-void report_smtp_tx_rollback(const char *, uint64_t, uint32_t);
-void report_smtp_protocol_client(const char *, uint64_t, const char *);
-void report_smtp_protocol_server(const char *, uint64_t, const char *);
-void report_smtp_filter_response(const char *, uint64_t, int, int, const char *);
-void report_smtp_timeout(const char *, uint64_t);
-
-
-/* ruleset.c */
-struct rule *ruleset_match(const struct envelope *);
-
-
-/* scheduler.c */
-int scheduler(void);
-
-
-/* scheduler_bakend.c */
-struct scheduler_backend *scheduler_backend_lookup(const char *);
-void scheduler_info(struct scheduler_info *, struct envelope *);
-
-
-/* pony.c */
-int pony(void);
-void pony_imsg(struct mproc *, struct imsg *);
-
-
-/* resolver.c */
-void resolver_getaddrinfo(const char *, const char *, const struct addrinfo *,
- void(*)(void *, int, struct addrinfo*), void *);
-void resolver_getnameinfo(const struct sockaddr *, int,
- void(*)(void *, int, const char *, const char *), void *);
-void resolver_res_query(const char *, int, int,
- void (*cb)(void *, int, int, int, const void *, int), void *);
-void resolver_dispatch_request(struct mproc *, struct imsg *);
-void resolver_dispatch_result(struct mproc *, struct imsg *);
-
-
-/* smtp.c */
-void smtp_postfork(void);
-void smtp_postprivdrop(void);
-void smtp_imsg(struct mproc *, struct imsg *);
-void smtp_configure(void);
-void smtp_collect(void);
-
-
-/* smtp_session.c */
-int smtp_session(struct listener *, int, const struct sockaddr_storage *,
- const char *, struct io *);
-void smtp_session_imsg(struct mproc *, struct imsg *);
-
-
-/* smtpf_session.c */
-int smtpf_session(struct listener *, int, const struct sockaddr_storage *,
- const char *);
-void smtpf_session_imsg(struct mproc *, struct imsg *);
-
-
-/* smtpd.c */
-void imsg_dispatch(struct mproc *, struct imsg *);
-const char *proc_name(enum smtp_proc_type);
-const char *proc_title(enum smtp_proc_type);
-const char *imsg_to_str(int);
-void log_imsg(int, int, struct imsg *);
-int fork_proc_backend(const char *, const char *, const char *);
-
-
-/* srs.c */
-const char *srs_encode(const char *, const char *);
-const char *srs_decode(const char *);
-
-
-/* ssl_smtpd.c */
-void *ssl_mta_init(void *, char *, off_t, const char *);
-void *ssl_smtp_init(void *, int);
-
-
-/* stat_backend.c */
-struct stat_backend *stat_backend_lookup(const char *);
-void stat_increment(const char *, size_t);
-void stat_decrement(const char *, size_t);
-void stat_set(const char *, const struct stat_value *);
-struct stat_value *stat_counter(size_t);
-struct stat_value *stat_timestamp(time_t);
-struct stat_value *stat_timeval(struct timeval *);
-struct stat_value *stat_timespec(struct timespec *);
-
-
-/* table.c */
-struct table *table_find(struct smtpd *, const char *);
-struct table *table_create(struct smtpd *, const char *, const char *,
- const char *);
-int table_config(struct table *);
-int table_open(struct table *);
-int table_update(struct table *);
-void table_close(struct table *);
-void table_dump(struct table *);
-int table_check_use(struct table *, uint32_t, uint32_t);
-int table_check_type(struct table *, uint32_t);
-int table_check_service(struct table *, uint32_t);
-int table_match(struct table *, enum table_service, const char *);
-int table_lookup(struct table *, enum table_service, const char *,
- union lookup *);
-int table_fetch(struct table *, enum table_service, union lookup *);
-void table_destroy(struct smtpd *, struct table *);
-void table_add(struct table *, const char *, const char *);
-int table_domain_match(const char *, const char *);
-int table_netaddr_match(const char *, const char *);
-int table_mailaddr_match(const char *, const char *);
-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 *);
-
-
-/* to.c */
-int email_to_mailaddr(struct mailaddr *, char *);
-int text_to_netaddr(struct netaddr *, const char *);
-int text_to_mailaddr(struct mailaddr *, const char *);
-int text_to_relayhost(struct relayhost *, const char *);
-int text_to_userinfo(struct userinfo *, const char *);
-int text_to_credentials(struct credentials *, const char *);
-int text_to_expandnode(struct expandnode *, const char *);
-uint64_t text_to_evpid(const char *);
-uint32_t text_to_msgid(const char *);
-const char *sa_to_text(const struct sockaddr *);
-const char *ss_to_text(const struct sockaddr_storage *);
-const char *time_to_text(time_t);
-const char *duration_to_text(time_t);
-const char *rule_to_text(struct rule *);
-const char *sockaddr_to_text(struct sockaddr *);
-const char *mailaddr_to_text(const struct mailaddr *);
-const char *expandnode_to_text(struct expandnode *);
-
-
-/* util.c */
-typedef struct arglist arglist;
-struct arglist {
- char **list;
- uint num;
- uint nalloc;
-};
-void addargs(arglist *, char *, ...)
- __attribute__((format(printf, 2, 3)));
-int bsnprintf(char *, size_t, const char *, ...)
- __attribute__((format (printf, 3, 4)));
-int safe_fclose(FILE *);
-int hostname_match(const char *, const char *);
-int mailaddr_match(const struct mailaddr *, const struct mailaddr *);
-int valid_localpart(const char *);
-int valid_domainpart(const char *);
-int valid_domainname(const char *);
-int valid_smtp_response(const char *);
-int secure_file(int, char *, char *, uid_t, int);
-int lowercase(char *, const char *, size_t);
-void xlowercase(char *, const char *, size_t);
-int uppercase(char *, const char *, size_t);
-uint64_t generate_uid(void);
-int availdesc(void);
-int ckdir(const char *, mode_t, uid_t, gid_t, int);
-int rmtree(char *, int);
-int mvpurge(char *, char *);
-int mktmpfile(void);
-const char *parse_smtp_response(char *, size_t, char **, int *);
-int xasprintf(char **, const char *, ...)
- __attribute__((__format__ (printf, 2, 3)));
-void *xmalloc(size_t);
-void *xcalloc(size_t, size_t);
-char *xstrdup(const char *);
-void *xmemdup(const void *, size_t);
-char *strip(char *);
-int io_xprint(struct io *, const char *);
-int io_xprintf(struct io *, const char *, ...)
- __attribute__((__format__ (printf, 2, 3)));
-void log_envelope(const struct envelope *, const char *, const char *,
- const char *);
-int session_socket_error(int);
-int getmailname(char *, size_t);
-int base64_encode(unsigned char const *, size_t, char *, size_t);
-int base64_decode(char const *, unsigned char *, size_t);
-int base64_encode_rfc3548(unsigned char const *, size_t,
- char *, size_t);
-void xclosefrom(int);
-
-void log_trace_verbose(int);
-void log_trace(int, const char *, ...)
- __attribute__((format (printf, 2, 3)));
-
-/* waitq.c */
-int waitq_wait(void *, void (*)(void *, void *, void *), void *);
-void waitq_run(void *, void *);
-
-
-/* runq.c */
-struct runq;
-
-int runq_init(struct runq **, void (*)(struct runq *, void *));
-int runq_schedule(struct runq *, time_t, void *);
-int runq_schedule_at(struct runq *, time_t, void *);
-int runq_cancel(struct runq *, void *);
-int runq_pending(struct runq *, void *, time_t *);
diff --git a/smtpd/smtpd/Makefile b/smtpd/smtpd/Makefile
deleted file mode 100644
index 34a4dc74..00000000
--- a/smtpd/smtpd/Makefile
+++ /dev/null
@@ -1,102 +0,0 @@
-# $OpenBSD: Makefile,v 1.91 2018/06/03 14:04:06 gilles Exp $
-
-.PATH: ${.CURDIR}/..
-
-PROG= smtpd
-
-SRCS= aliases.c
-SRCS+= bounce.c
-SRCS+= ca.c
-SRCS+= cert.c
-SRCS+= compress_backend.c
-SRCS+= config.c
-SRCS+= control.c
-SRCS+= crypto.c
-SRCS+= dict.c
-SRCS+= dns.c
-SRCS+= unpack_dns.c
-SRCS+= envelope.c
-SRCS+= esc.c
-SRCS+= expand.c
-SRCS+= forward.c
-SRCS+= iobuf.c
-SRCS+= ioev.c
-SRCS+= limit.c
-SRCS+= lka.c
-SRCS+= lka_filter.c
-SRCS+= lka_session.c
-SRCS+= log.c
-SRCS+= mailaddr.c
-SRCS+= mda.c
-SRCS+= mda_mbox.c
-SRCS+= mda_unpriv.c
-SRCS+= mda_variables.c
-SRCS+= mproc.c
-SRCS+= mta.c
-SRCS+= mta_session.c
-SRCS+= parse.y
-SRCS+= pony.c
-SRCS+= proxy.c
-SRCS+= queue.c
-SRCS+= queue_backend.c
-SRCS+= report_smtp.c
-SRCS+= resolver.c
-SRCS+= ruleset.c
-SRCS+= runq.c
-SRCS+= scheduler.c
-SRCS+= scheduler_backend.c
-SRCS+= smtp.c
-SRCS+= smtp_session.c
-SRCS+= smtpd.c
-SRCS+= srs.c
-SRCS+= ssl.c
-SRCS+= ssl_smtpd.c
-SRCS+= ssl_verify.c
-SRCS+= stat_backend.c
-SRCS+= table.c
-SRCS+= to.c
-SRCS+= tree.c
-SRCS+= util.c
-SRCS+= waitq.c
-
-# RFC parsers
-SRCS+= rfc5322.c
-
-# backends
-SRCS+= compress_gzip.c
-
-SRCS+= table_db.c
-SRCS+= table_getpwnam.c
-SRCS+= table_proc.c
-SRCS+= table_static.c
-
-SRCS+= queue_fs.c
-SRCS+= queue_null.c
-SRCS+= queue_proc.c
-SRCS+= queue_ram.c
-
-SRCS+= scheduler_ramqueue.c
-SRCS+= scheduler_null.c
-SRCS+= scheduler_proc.c
-
-SRCS+= stat_ramstat.c
-
-MAN= sendmail.8 smtpd.8 smtpd.conf.5 table.5
-BINDIR= /usr/sbin
-
-LDADD+= -levent -lutil -lssl -lcrypto -lm -lz
-DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} ${LIBM} ${LIBZ}
-
-CFLAGS+= -fstack-protector-all
-CFLAGS+= -I${.CURDIR}/..
-CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS+= -Wmissing-declarations
-CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
-CFLAGS+= -Wsign-compare
-CFLAGS+= -Werror-implicit-function-declaration
-#CFLAGS+= -Werror # during development phase (breaks some archs)
-CFLAGS+= -DIO_TLS
-CFLAGS+= -DQUEUE_PROFILING
-YFLAGS=
-
-.include <bsd.prog.mk>
diff --git a/smtpd/spfwalk.c b/smtpd/spfwalk.c
deleted file mode 100644
index 0832d1bc..00000000
--- a/smtpd/spfwalk.c
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * Copyright (c) 2017 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/tree.h>
-
-#ifdef HAVE_ARPA_NAMESER_COMPAT_H
-#include <arpa/nameser_compat.h>
-#endif
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-#include <netinet/in.h>
-#include <netdb.h>
-
-#include <asr.h>
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include "smtpd-defines.h"
-#include "smtpd-api.h"
-#include "unpack_dns.h"
-#include "parser.h"
-
-struct target {
- void (*dispatch)(struct dns_rr *, struct target *);
- int cidr4;
- int cidr6;
-};
-
-int spfwalk(int, struct parameter *);
-
-static void dispatch_txt(struct dns_rr *, struct target *);
-static void dispatch_mx(struct dns_rr *, struct target *);
-static void dispatch_a(struct dns_rr *, struct target *);
-static void dispatch_aaaa(struct dns_rr *, struct target *);
-static void lookup_record(int, const char *, struct target *);
-static void dispatch_record(struct asr_result *, void *);
-static ssize_t parse_txt(const char *, size_t, char *, size_t);
-static int parse_target(char *, struct target *);
-void *xmalloc(size_t size);
-
-int ip_v4 = 0;
-int ip_v6 = 0;
-int ip_both = 1;
-
-struct dict seen;
-
-int
-spfwalk(int argc, struct parameter *argv)
-{
- struct target tgt;
- const char *ip_family = NULL;
- char *line = NULL;
- size_t linesize = 0;
- ssize_t linelen;
-
- if (argv)
- ip_family = argv[0].u.u_str;
-
- if (ip_family) {
- if (strcmp(ip_family, "-4") == 0) {
- ip_both = 0;
- ip_v4 = 1;
- } else if (strcmp(ip_family, "-6") == 0) {
- ip_both = 0;
- ip_v6 = 1;
- } else
- errx(1, "invalid ip_family");
- }
-
- dict_init(&seen);
- event_init();
-
- tgt.cidr4 = tgt.cidr6 = -1;
- tgt.dispatch = dispatch_txt;
-
- while ((linelen = getline(&line, &linesize, stdin)) != -1) {
- while (linelen-- > 0 && isspace((unsigned char)line[linelen]))
- line[linelen] = '\0';
-
- if (linelen > 0)
- lookup_record(T_TXT, line, &tgt);
- }
-
- free(line);
-
-#if HAVE_PLEDGE
- if (pledge("dns stdio", NULL) == -1)
- err(1, "pledge");
-#endif
-
- event_dispatch();
-
- return 0;
-}
-
-void
-lookup_record(int type, const char *record, struct target *tgt)
-{
- struct asr_query *as;
- struct target *ntgt;
-
- as = res_query_async(record, C_IN, type, NULL);
- if (as == NULL)
- err(1, "res_query_async");
- ntgt = xmalloc(sizeof(*ntgt));
- *ntgt = *tgt;
- event_asr_run(as, dispatch_record, (void *)ntgt);
-}
-
-void
-dispatch_record(struct asr_result *ar, void *arg)
-{
- struct target *tgt = arg;
- struct unpack pack;
- struct dns_header h;
- struct dns_query q;
- struct dns_rr rr;
-
- /* best effort */
- if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA)
- goto end;
-
- unpack_init(&pack, ar->ar_data, ar->ar_datalen);
- unpack_header(&pack, &h);
- unpack_query(&pack, &q);
-
- for (; h.ancount; h.ancount--) {
- unpack_rr(&pack, &rr);
- /**/
- tgt->dispatch(&rr, tgt);
- }
-end:
- free(tgt);
-}
-
-void
-dispatch_txt(struct dns_rr *rr, struct target *tgt)
-{
- char buf[4096];
- char *argv[512];
- char buf2[512];
- struct target ltgt;
- struct in6_addr ina;
- char **ap = argv;
- char *in = buf;
- char *record, *end;
- ssize_t n;
-
- if (rr->rr_type != T_TXT)
- return;
- n = parse_txt(rr->rr.other.rdata, rr->rr.other.rdlen, buf, sizeof(buf));
- if (n == -1 || n == sizeof(buf))
- return;
- buf[n] = '\0';
-
- if (strncasecmp("v=spf1 ", buf, 7))
- return;
-
- while ((*ap = strsep(&in, " ")) != NULL) {
- if (strcasecmp(*ap, "v=spf1") == 0)
- continue;
-
- end = *ap + strlen(*ap)-1;
- if (*end == '.')
- *end = '\0';
-
- if (dict_set(&seen, *ap, &seen))
- continue;
-
- if (**ap == '-' || **ap == '~')
- continue;
-
- if (**ap == '+' || **ap == '?')
- (*ap)++;
-
- ltgt.cidr4 = ltgt.cidr6 = -1;
-
- if (strncasecmp("ip4:", *ap, 4) == 0) {
- if ((ip_v4 == 1 || ip_both == 1) &&
- inet_net_pton(AF_INET, *(ap) + 4,
- &ina, sizeof(ina)) != -1)
- printf("%s\n", *(ap) + 4);
- continue;
- }
- if (strncasecmp("ip6:", *ap, 4) == 0) {
- if ((ip_v6 == 1 || ip_both == 1) &&
- inet_net_pton(AF_INET6, *(ap) + 4,
- &ina, sizeof(ina)) != -1)
- printf("%s\n", *(ap) + 4);
- continue;
- }
- if (strcasecmp("a", *ap) == 0) {
- print_dname(rr->rr_dname, buf2, sizeof(buf2));
- buf2[strlen(buf2) - 1] = '\0';
- ltgt.dispatch = dispatch_a;
- lookup_record(T_A, buf2, &ltgt);
- ltgt.dispatch = dispatch_aaaa;
- lookup_record(T_AAAA, buf2, &ltgt);
- continue;
- }
- if (strncasecmp("a:", *ap, 2) == 0) {
- record = *(ap) + 2;
- if (parse_target(record, &ltgt) < 0)
- continue;
- ltgt.dispatch = dispatch_a;
- lookup_record(T_A, record, &ltgt);
- ltgt.dispatch = dispatch_aaaa;
- lookup_record(T_AAAA, record, &ltgt);
- continue;
- }
- if (strncasecmp("exists:", *ap, 7) == 0) {
- ltgt.dispatch = dispatch_a;
- lookup_record(T_A, *(ap) + 7, &ltgt);
- continue;
- }
- if (strncasecmp("include:", *ap, 8) == 0) {
- ltgt.dispatch = dispatch_txt;
- lookup_record(T_TXT, *(ap) + 8, &ltgt);
- continue;
- }
- if (strncasecmp("redirect=", *ap, 9) == 0) {
- ltgt.dispatch = dispatch_txt;
- lookup_record(T_TXT, *(ap) + 9, &ltgt);
- continue;
- }
- if (strcasecmp("mx", *ap) == 0) {
- print_dname(rr->rr_dname, buf2, sizeof(buf2));
- buf2[strlen(buf2) - 1] = '\0';
- ltgt.dispatch = dispatch_mx;
- lookup_record(T_MX, buf2, &ltgt);
- continue;
- }
- if (strncasecmp("mx:", *ap, 3) == 0) {
- record = *(ap) + 3;
- if (parse_target(record, &ltgt) < 0)
- continue;
- ltgt.dispatch = dispatch_mx;
- lookup_record(T_MX, record, &ltgt);
- continue;
- }
- }
- *ap = NULL;
-}
-
-void
-dispatch_mx(struct dns_rr *rr, struct target *tgt)
-{
- char buf[512];
- struct target ltgt;
-
- if (rr->rr_type != T_MX)
- return;
-
- print_dname(rr->rr.mx.exchange, buf, sizeof(buf));
- buf[strlen(buf) - 1] = '\0';
- if (buf[strlen(buf) - 1] == '.')
- buf[strlen(buf) - 1] = '\0';
-
- ltgt = *tgt;
- ltgt.dispatch = dispatch_a;
- lookup_record(T_A, buf, &ltgt);
- ltgt.dispatch = dispatch_aaaa;
- lookup_record(T_AAAA, buf, &ltgt);
-}
-
-void
-dispatch_a(struct dns_rr *rr, struct target *tgt)
-{
- char buffer[512];
- const char *ptr;
-
- if (rr->rr_type != T_A)
- return;
-
- if ((ptr = inet_ntop(AF_INET, &rr->rr.in_a.addr,
- buffer, sizeof buffer))) {
- if (tgt->cidr4 >= 0)
- printf("%s/%d\n", ptr, tgt->cidr4);
- else
- printf("%s\n", ptr);
- }
-}
-
-void
-dispatch_aaaa(struct dns_rr *rr, struct target *tgt)
-{
- char buffer[512];
- const char *ptr;
-
- if (rr->rr_type != T_AAAA)
- return;
-
- if ((ptr = inet_ntop(AF_INET6, &rr->rr.in_aaaa.addr6,
- buffer, sizeof buffer))) {
- if (tgt->cidr6 >= 0)
- printf("%s/%d\n", ptr, tgt->cidr6);
- else
- printf("%s\n", ptr);
- }
-}
-
-ssize_t
-parse_txt(const char *rdata, size_t rdatalen, char *dst, size_t dstsz)
-{
- size_t len;
- ssize_t r = 0;
-
- while (rdatalen) {
- len = *(const unsigned char *)rdata;
- if (len >= rdatalen) {
- errno = EINVAL;
- return -1;
- }
-
- rdata++;
- rdatalen--;
-
- if (len == 0)
- continue;
-
- if (len >= dstsz) {
- errno = EOVERFLOW;
- return -1;
- }
- memmove(dst, rdata, len);
- dst += len;
- dstsz -= len;
-
- rdata += len;
- rdatalen -= len;
- r += len;
- }
-
- return r;
-}
-
-int
-parse_target(char *record, struct target *tgt)
-{
- const char *err;
- char *m4, *m6;
-
- m4 = record;
- strsep(&m4, "/");
- if (m4 == NULL)
- return 0;
-
- m6 = m4;
- strsep(&m6, "/");
-
- if (*m4) {
- tgt->cidr4 = strtonum(m4, 0, 32, &err);
- if (err)
- return tgt->cidr4 = -1;
- }
-
- if (m6 == NULL)
- return 0;
-
- tgt->cidr6 = strtonum(m6, 0, 128, &err);
- if (err)
- return tgt->cidr6 = -1;
-
- return 0;
-}
diff --git a/smtpd/srs.c b/smtpd/srs.c
deleted file mode 100644
index bb4f4d9e..00000000
--- a/smtpd/srs.c
+++ /dev/null
@@ -1,379 +0,0 @@
-/* $OpenBSD: srs.c,v 1.3 2019/09/29 10:03:49 gilles Exp $ */
-
-/*
- * Copyright (c) 2019 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <netdb.h>
-#include <limits.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <openssl/sha.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static uint8_t base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
-
-static int
-minrange(uint16_t tref, uint16_t t2, int drift, int mod)
-{
- if (tref > drift) {
- /* t2 must fall in between tref and tref - drift */
- if (t2 <= tref && t2>= tref - drift)
- return 1;
- }
- else {
- /* t2 must fall in between 0 and tref, or wrap */
- if (t2 <= tref || t2 >= mod - (drift - tref))
- return 1;
- }
- return 0;
-}
-
-static int
-maxrange(uint16_t tref, uint16_t t2, int drift, int mod)
-{
- if (tref + drift < 1024) {
- /* t2 must fall in between tref and tref + drift */
- if (t2 >= tref && t2 <= tref + drift)
- return 1;
- }
- else {
- /* t2 must fall in between tref + drift, or wrap */
- if (t2 >= tref || t2 <= (tref + drift) % 1024)
- return 1;
- }
- return 0;
-}
-
-static int
-timestamp_check_range(uint16_t tref, uint16_t t2)
-{
- if (! minrange(tref, t2, env->sc_srs_ttl, 1024) &&
- ! maxrange(tref, t2, 1, 1024))
- return 0;
-
- return 1;
-}
-
-static const unsigned char *
-srs_hash(const char *key, const char *value)
-{
- SHA_CTX c;
- static unsigned char md[SHA_DIGEST_LENGTH];
-
- SHA1_Init(&c);
- SHA1_Update(&c, key, strlen(key));
- SHA1_Update(&c, value, strlen(value));
- SHA1_Final(md, &c);
- return md;
-}
-
-static const char *
-srs0_encode(const char *sender, const char *rcpt_domain)
-{
- static char dest[SMTPD_MAXMAILADDRSIZE];
- char tmp[SMTPD_MAXMAILADDRSIZE];
- char md[SHA_DIGEST_LENGTH*4+1];
- struct mailaddr maddr;
- uint16_t timestamp;
- int ret;
-
- /* compute 10 bits timestamp according to spec */
- timestamp = (time(NULL) / (60 * 60 * 24)) % 1024;
-
- /* parse sender into user and domain */
- if (! text_to_mailaddr(&maddr, sender))
- return sender;
-
- /* TT=<orig_domainpart>=<orig_userpart>@<new_domainpart> */
- ret = snprintf(tmp, sizeof tmp, "%c%c=%s=%s@%s",
- base32[(timestamp>>5) & 0x1F],
- base32[timestamp & 0x1F],
- maddr.domain, maddr.user, rcpt_domain);
- if (ret == -1 || ret >= (int)sizeof tmp)
- return sender;
-
- /* compute HHHH */
- base64_encode_rfc3548(srs_hash(env->sc_srs_key, tmp), SHA_DIGEST_LENGTH,
- md, sizeof md);
-
- /* prepend SRS0=HHHH= prefix */
- ret = snprintf(dest, sizeof dest, "SRS0=%c%c%c%c=%s",
- md[0], md[1], md[2], md[3], tmp);
- if (ret == -1 || ret >= (int)sizeof dest)
- return sender;
-
- return dest;
-}
-
-static const char *
-srs1_encode_srs0(const char *sender, const char *rcpt_domain)
-{
- static char dest[SMTPD_MAXMAILADDRSIZE];
- char tmp[SMTPD_MAXMAILADDRSIZE];
- char md[SHA_DIGEST_LENGTH*4+1];
- struct mailaddr maddr;
- int ret;
-
- /* parse sender into user and domain */
- if (! text_to_mailaddr(&maddr, sender))
- return sender;
-
- /* <last_domainpart>==<SRS0_userpart>@<new_domainpart> */
- ret = snprintf(tmp, sizeof tmp, "%s==%s@%s",
- maddr.domain, maddr.user, rcpt_domain);
- if (ret == -1 || ret >= (int)sizeof tmp)
- return sender;
-
- /* compute HHHH */
- base64_encode_rfc3548(srs_hash(env->sc_srs_key, tmp), SHA_DIGEST_LENGTH,
- md, sizeof md);
-
- /* prepend SRS1=HHHH= prefix */
- ret = snprintf(dest, sizeof dest, "SRS1=%c%c%c%c=%s",
- md[0], md[1], md[2], md[3], tmp);
- if (ret == -1 || ret >= (int)sizeof dest)
- return sender;
-
- return dest;
-}
-
-static const char *
-srs1_encode_srs1(const char *sender, const char *rcpt_domain)
-{
- static char dest[SMTPD_MAXMAILADDRSIZE];
- char tmp[SMTPD_MAXMAILADDRSIZE];
- char md[SHA_DIGEST_LENGTH*4+1];
- struct mailaddr maddr;
- int ret;
-
- /* parse sender into user and domain */
- if (! text_to_mailaddr(&maddr, sender))
- return sender;
-
- /* <SRS1_userpart>@<new_domainpart> */
- ret = snprintf(tmp, sizeof tmp, "%s@%s", maddr.user, rcpt_domain);
- if (ret == -1 || ret >= (int)sizeof tmp)
- return sender;
-
- /* sanity check: there's at least room for a checksum
- * with allowed delimiter =, + or -
- */
- if (strlen(tmp) < 5)
- return sender;
- if (tmp[4] != '=' && tmp[4] != '+' && tmp[4] != '-')
- return sender;
-
- /* compute HHHH */
- base64_encode_rfc3548(srs_hash(env->sc_srs_key, tmp + 5), SHA_DIGEST_LENGTH,
- md, sizeof md);
-
- /* prepend SRS1=HHHH= prefix skipping previous hops' HHHH */
- ret = snprintf(dest, sizeof dest, "SRS1=%c%c%c%c=%s",
- md[0], md[1], md[2], md[3], tmp + 5);
- if (ret == -1 || ret >= (int)sizeof dest)
- return sender;
-
- return dest;
-}
-
-const char *
-srs_encode(const char *sender, const char *rcpt_domain)
-{
- if (strncasecmp(sender, "SRS0=", 5) == 0)
- return srs1_encode_srs0(sender+5, rcpt_domain);
- if (strncasecmp(sender, "SRS1=", 5) == 0)
- return srs1_encode_srs1(sender+5, rcpt_domain);
- return srs0_encode(sender, rcpt_domain);
-}
-
-static const char *
-srs0_decode(const char *rcpt)
-{
- static char dest[SMTPD_MAXMAILADDRSIZE];
- char md[SHA_DIGEST_LENGTH*4+1];
- struct mailaddr maddr;
- char *p;
- uint8_t *idx;
- int ret;
- uint16_t timestamp, srs_timestamp;
-
- /* sanity check: we have room for a checksum and delimiter */
- if (strlen(rcpt) < 5)
- return NULL;
-
- /* compute checksum */
- base64_encode_rfc3548(srs_hash(env->sc_srs_key, rcpt+5), SHA_DIGEST_LENGTH,
- md, sizeof md);
-
- /* compare prefix checksum with computed checksum */
- if (strncmp(md, rcpt, 4) != 0) {
- if (env->sc_srs_key_backup == NULL)
- return NULL;
- base64_encode_rfc3548(srs_hash(env->sc_srs_key_backup, rcpt+5),
- SHA_DIGEST_LENGTH, md, sizeof md);
- if (strncmp(md, rcpt, 4) != 0)
- return NULL;
- }
- rcpt += 5;
-
- /* sanity check: we have room for a timestamp and delimiter */
- if (strlen(rcpt) < 3)
- return NULL;
-
- /* decode timestamp */
- if ((idx = strchr(base32, rcpt[0])) == NULL)
- return NULL;
- srs_timestamp = ((idx - base32) << 5);
-
- if ((idx = strchr(base32, rcpt[1])) == NULL)
- return NULL;
- srs_timestamp |= (idx - base32);
- rcpt += 3;
-
- /* compute current 10 bits timestamp */
- timestamp = (time(NULL) / (60 * 60 * 24)) % 1024;
-
- /* check that SRS timestamp isn't too far from current */
- if (timestamp != srs_timestamp)
- if (! timestamp_check_range(timestamp, srs_timestamp))
- return NULL;
-
- if (! text_to_mailaddr(&maddr, rcpt))
- return NULL;
-
- /* sanity check: we have at least one SRS separator */
- if ((p = strchr(maddr.user, '=')) == NULL)
- return NULL;
- *p++ = '\0';
-
- /* maddr.user holds "domain\0user", with p pointing at user */
- ret = snprintf(dest, sizeof dest, "%s@%s", p, maddr.user);
- if (ret == -1 || ret >= (int)sizeof dest)
- return NULL;
-
- return dest;
-}
-
-static const char *
-srs1_decode(const char *rcpt)
-{
- static char dest[SMTPD_MAXMAILADDRSIZE];
- char md[SHA_DIGEST_LENGTH*4+1];
- struct mailaddr maddr;
- char *p;
- uint8_t *idx;
- int ret;
- uint16_t timestamp, srs_timestamp;
-
- /* sanity check: we have room for a checksum and delimiter */
- if (strlen(rcpt) < 5)
- return NULL;
-
- /* compute checksum */
- base64_encode_rfc3548(srs_hash(env->sc_srs_key, rcpt+5), SHA_DIGEST_LENGTH,
- md, sizeof md);
-
- /* compare prefix checksum with computed checksum */
- if (strncmp(md, rcpt, 4) != 0) {
- if (env->sc_srs_key_backup == NULL)
- return NULL;
- base64_encode_rfc3548(srs_hash(env->sc_srs_key_backup, rcpt+5),
- SHA_DIGEST_LENGTH, md, sizeof md);
- if (strncmp(md, rcpt, 4) != 0)
- return NULL;
- }
- rcpt += 5;
-
- if (! text_to_mailaddr(&maddr, rcpt))
- return NULL;
-
- /* sanity check: we have at least one SRS separator */
- if ((p = strchr(maddr.user, '=')) == NULL)
- return NULL;
- *p++ = '\0';
-
- /* maddr.user holds "domain\0user", with p pointing at user */
- ret = snprintf(dest, sizeof dest, "SRS0%s@%s", p, maddr.user);
- if (ret == -1 || ret >= (int)sizeof dest)
- return NULL;
-
-
- /* we're ready to return decoded address, but let's check if
- * SRS0 timestamp is valid.
- */
-
- /* first, get rid of SRS0 checksum (=HHHH=), we can't check it */
- if (strlen(p) < 6)
- return NULL;
- p += 6;
-
- /* we should be pointing to a timestamp, check that we're indeed */
- if (strlen(p) < 3)
- return NULL;
- if (p[2] != '=' && p[2] != '+' && p[2] != '-')
- return NULL;
- p[2] = '\0';
-
- if ((idx = strchr(base32, p[0])) == NULL)
- return NULL;
- srs_timestamp = ((idx - base32) << 5);
-
- if ((idx = strchr(base32, p[1])) == NULL)
- return NULL;
- srs_timestamp |= (idx - base32);
-
- /* compute current 10 bits timestamp */
- timestamp = (time(NULL) / (60 * 60 * 24)) % 1024;
-
- /* check that SRS timestamp isn't too far from current */
- if (timestamp != srs_timestamp)
- if (! timestamp_check_range(timestamp, srs_timestamp))
- return NULL;
-
- return dest;
-}
-
-const char *
-srs_decode(const char *rcpt)
-{
- if (strncasecmp(rcpt, "SRS0=", 5) == 0)
- return srs0_decode(rcpt + 5);
- if (strncasecmp(rcpt, "SRS1=", 5) == 0)
- return srs1_decode(rcpt + 5);
-
- return NULL;
-}
diff --git a/smtpd/ssl.c b/smtpd/ssl.c
deleted file mode 100644
index a37d6fea..00000000
--- a/smtpd/ssl.c
+++ /dev/null
@@ -1,458 +0,0 @@
-/* $OpenBSD: ssl.c,v 1.93 2019/06/05 06:40:13 gilles Exp $ */
-
-/*
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org>
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <limits.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <openssl/ssl.h>
-#include <openssl/engine.h>
-#include <openssl/err.h>
-#include <openssl/rsa.h>
-#include <openssl/ecdsa.h>
-#include <openssl/dh.h>
-#include <openssl/bn.h>
-
-#include "log.h"
-#include "ssl.h"
-
-void
-ssl_init(void)
-{
- static int inited = 0;
-
- if (inited)
- return;
-
- SSL_library_init();
- SSL_load_error_strings();
-
- OpenSSL_add_all_algorithms();
-
- /* Init hardware crypto engines. */
- ENGINE_load_builtin_engines();
- ENGINE_register_all_complete();
- inited = 1;
-}
-
-int
-ssl_setup(SSL_CTX **ctxp, struct pki *pki,
- int (*sni_cb)(SSL *,int *,void *), const char *ciphers)
-{
- SSL_CTX *ctx;
- uint8_t sid[SSL_MAX_SID_CTX_LENGTH];
-
- ctx = ssl_ctx_create(pki->pki_name, pki->pki_cert, pki->pki_cert_len, ciphers);
-
- /*
- * Set session ID context to a random value. We don't support
- * persistent caching of sessions so it is OK to set a temporary
- * session ID context that is valid during run time.
- */
- arc4random_buf(sid, sizeof(sid));
- if (!SSL_CTX_set_session_id_context(ctx, sid, sizeof(sid)))
- goto err;
-
- if (sni_cb)
- SSL_CTX_set_tlsext_servername_callback(ctx, sni_cb);
-
- SSL_CTX_set_dh_auto(ctx, 0);
-
- SSL_CTX_set_ecdh_auto(ctx, 1);
-
- *ctxp = ctx;
- return 1;
-
-err:
- SSL_CTX_free(ctx);
- ssl_error("ssl_setup");
- return 0;
-}
-
-char *
-ssl_load_file(const char *name, off_t *len, mode_t perm)
-{
- struct stat st;
- off_t size;
- char *buf = NULL;
- int fd, saved_errno;
- char mode[12];
-
- if ((fd = open(name, O_RDONLY)) == -1)
- return (NULL);
- if (fstat(fd, &st) != 0)
- goto fail;
- if (st.st_uid != 0) {
- log_warnx("warn: %s: not owned by uid 0", name);
- errno = EACCES;
- goto fail;
- }
- if (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO) & ~perm) {
- strmode(perm, mode);
- log_warnx("warn: %s: insecure permissions: must be at most %s",
- name, &mode[1]);
- errno = EACCES;
- goto fail;
- }
- size = st.st_size;
- if ((buf = calloc(1, size + 1)) == NULL)
- goto fail;
- if (read(fd, buf, size) != size)
- goto fail;
- close(fd);
-
- *len = size + 1;
- return (buf);
-
-fail:
- free(buf);
- saved_errno = errno;
- close(fd);
- errno = saved_errno;
- return (NULL);
-}
-
-#if 0
-static int
-ssl_password_cb(char *buf, int size, int rwflag, void *u)
-{
- size_t len;
- if (u == NULL) {
- explicit_bzero(buf, size);
- return (0);
- }
- if ((len = strlcpy(buf, u, size)) >= (size_t)size)
- return (0);
- return (len);
-}
-#endif
-
-static int
-ssl_password_cb(char *buf, int size, int rwflag, void *u)
-{
- int ret = 0;
- size_t len;
- char *pass;
-
- pass = getpass((const char *)u);
- if (pass == NULL)
- return 0;
- len = strlen(pass);
- if (strlcpy(buf, pass, size) >= (size_t)size)
- goto end;
- ret = len;
-end:
- if (len)
- explicit_bzero(pass, len);
- return ret;
-}
-
-char *
-ssl_load_key(const char *name, off_t *len, char *pass, mode_t perm, const char *pkiname)
-{
- FILE *fp = NULL;
- EVP_PKEY *key = NULL;
- BIO *bio = NULL;
- long size;
- char *data, *buf, *filebuf;
- struct stat st;
- char mode[12];
- char prompt[2048];
-
- /* Initialize SSL library once */
- ssl_init();
-
- /*
- * Read (possibly) encrypted key from file
- */
- if ((fp = fopen(name, "r")) == NULL)
- return (NULL);
- if ((filebuf = malloc_conceal(BUFSIZ)) == NULL)
- goto fail;
- setvbuf(fp, filebuf, _IOFBF, BUFSIZ);
-
- if (fstat(fileno(fp), &st) != 0)
- goto fail;
- if (st.st_uid != 0) {
- log_warnx("warn: %s: not owned by uid 0", name);
- errno = EACCES;
- goto fail;
- }
- if (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO) & ~perm) {
- strmode(perm, mode);
- log_warnx("warn: %s: insecure permissions: must be at most %s",
- name, &mode[1]);
- errno = EACCES;
- goto fail;
- }
-
- (void)snprintf(prompt, sizeof prompt, "passphrase for %s: ", pkiname);
- key = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, prompt);
- fclose(fp);
- fp = NULL;
- freezero(filebuf, BUFSIZ);
- filebuf = NULL;
- if (key == NULL)
- goto fail;
- /*
- * Write unencrypted key to memory buffer
- */
- if ((bio = BIO_new(BIO_s_mem())) == NULL)
- goto fail;
- if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL))
- goto fail;
- if ((size = BIO_get_mem_data(bio, &data)) <= 0)
- goto fail;
- if ((buf = calloc_conceal(1, size + 1)) == NULL)
- goto fail;
- memcpy(buf, data, size);
-
- BIO_free_all(bio);
- EVP_PKEY_free(key);
-
- *len = (off_t)size + 1;
- return (buf);
-
-fail:
- ssl_error("ssl_load_key");
- BIO_free_all(bio);
- EVP_PKEY_free(key);
- if (fp)
- fclose(fp);
- freezero(filebuf, BUFSIZ);
- return (NULL);
-}
-
-SSL_CTX *
-ssl_ctx_create(const char *pkiname, char *cert, off_t cert_len, const char *ciphers)
-{
- SSL_CTX *ctx;
- size_t pkinamelen = 0;
-
- ctx = SSL_CTX_new(SSLv23_method());
- if (ctx == NULL) {
- ssl_error("ssl_ctx_create");
- fatal("ssl_ctx_create: could not create SSL context");
- }
-
- SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
- SSL_CTX_set_timeout(ctx, SSL_SESSION_TIMEOUT);
- SSL_CTX_set_options(ctx,
- SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TICKET);
- SSL_CTX_set_options(ctx,
- SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
- SSL_CTX_set_options(ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);
- SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
- if (ciphers == NULL)
- ciphers = SSL_CIPHERS;
- if (!SSL_CTX_set_cipher_list(ctx, ciphers)) {
- ssl_error("ssl_ctx_create");
- fatal("ssl_ctx_create: could not set cipher list");
- }
-
- if (cert != NULL) {
- if (pkiname != NULL)
- pkinamelen = strlen(pkiname) + 1;
- if (!SSL_CTX_use_certificate_chain_mem(ctx, cert, cert_len)) {
- ssl_error("ssl_ctx_create");
- fatal("ssl_ctx_create: invalid certificate chain");
- } else if (!ssl_ctx_fake_private_key(ctx,
- pkiname, pkinamelen, cert, cert_len, NULL, NULL)) {
- ssl_error("ssl_ctx_create");
- fatal("ssl_ctx_create: could not fake private key");
- } else if (!SSL_CTX_check_private_key(ctx)) {
- ssl_error("ssl_ctx_create");
- fatal("ssl_ctx_create: invalid private key");
- }
- }
-
- return (ctx);
-}
-
-int
-ssl_load_certificate(struct pki *p, const char *pathname)
-{
- p->pki_cert = ssl_load_file(pathname, &p->pki_cert_len, 0755);
- if (p->pki_cert == NULL)
- return 0;
- return 1;
-}
-
-int
-ssl_load_keyfile(struct pki *p, const char *pathname, const char *pkiname)
-{
- char pass[1024];
-
- p->pki_key = ssl_load_key(pathname, &p->pki_key_len, pass, 0740, pkiname);
- if (p->pki_key == NULL)
- return 0;
- return 1;
-}
-
-int
-ssl_load_cafile(struct ca *c, const char *pathname)
-{
- c->ca_cert = ssl_load_file(pathname, &c->ca_cert_len, 0755);
- if (c->ca_cert == NULL)
- return 0;
- return 1;
-}
-
-const char *
-ssl_to_text(const SSL *ssl)
-{
- static char buf[256];
-
- (void)snprintf(buf, sizeof buf, "%s:%s:%d",
- SSL_get_version(ssl),
- SSL_get_cipher_name(ssl),
- SSL_get_cipher_bits(ssl, NULL));
-
- return (buf);
-}
-
-void
-ssl_error(const char *where)
-{
- unsigned long code;
- char errbuf[128];
-
- for (; (code = ERR_get_error()) != 0 ;) {
- ERR_error_string_n(code, errbuf, sizeof(errbuf));
- log_debug("debug: SSL library error: %s: %s", where, errbuf);
- }
-}
-
-int
-ssl_load_pkey(const void *data, size_t datalen, char *buf, off_t len,
- X509 **x509ptr, EVP_PKEY **pkeyptr)
-{
- BIO *in;
- X509 *x509 = NULL;
- EVP_PKEY *pkey = NULL;
- RSA *rsa = NULL;
- EC_KEY *eckey = NULL;
- void *exdata = NULL;
-
- if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
- SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_BUF_LIB);
- return (0);
- }
-
- if ((x509 = PEM_read_bio_X509(in, NULL,
- ssl_password_cb, NULL)) == NULL) {
- SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_PEM_LIB);
- goto fail;
- }
-
- if ((pkey = X509_get_pubkey(x509)) == NULL) {
- SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_X509_LIB);
- goto fail;
- }
-
- BIO_free(in);
- in = NULL;
-
- if (data != NULL && datalen) {
- if (((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL &&
- (eckey = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) ||
- (exdata = malloc(datalen)) == NULL) {
- SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_R_EVP_LIB);
- goto fail;
- }
-
- memcpy(exdata, data, datalen);
- if (rsa)
- RSA_set_ex_data(rsa, 0, exdata);
-#if defined(SUPPORT_ECDSA)
- if (eckey)
- ECDSA_set_ex_data(eckey, 0, exdata);
-#endif
- RSA_free(rsa); /* dereference, will be cleaned up with pkey */
-#if defined(SUPPORT_ECDSA)
- EC_KEY_free(eckey); /* dereference, will be cleaned up with pkey */
-#endif
- }
-
- *x509ptr = x509;
- *pkeyptr = pkey;
-
- return (1);
-
- fail:
- RSA_free(rsa);
- EC_KEY_free(eckey);
- BIO_free(in);
- EVP_PKEY_free(pkey);
- X509_free(x509);
- free(exdata);
-
- return (0);
-}
-
-int
-ssl_ctx_fake_private_key(SSL_CTX *ctx, const void *data, size_t datalen,
- char *buf, off_t len, X509 **x509ptr, EVP_PKEY **pkeyptr)
-{
- int ret = 0;
- EVP_PKEY *pkey = NULL;
- X509 *x509 = NULL;
-
- if (!ssl_load_pkey(data, datalen, buf, len, &x509, &pkey))
- return (0);
-
- /*
- * Use the public key as the "private" key - the secret key
- * parameters are hidden in an extra process that will be
- * contacted by the RSA engine. The SSL/TLS library needs at
- * least the public key parameters in the current process.
- */
- ret = SSL_CTX_use_PrivateKey(ctx, pkey);
- if (!ret)
- SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY, ERR_LIB_SSL);
-
- if (pkeyptr != NULL)
- *pkeyptr = pkey;
- else
- EVP_PKEY_free(pkey);
-
- if (x509ptr != NULL)
- *x509ptr = x509;
- else
- X509_free(x509);
-
- return (ret);
-}
diff --git a/smtpd/ssl.h b/smtpd/ssl.h
deleted file mode 100644
index 11c80c68..00000000
--- a/smtpd/ssl.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/* $OpenBSD: ssl.h,v 1.21 2019/09/18 11:26:30 eric Exp $ */
-/*
- * Copyright (c) 2013 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#define SSL_CIPHERS "HIGH:!aNULL:!MD5"
-#define SSL_SESSION_TIMEOUT 300
-
-struct pki {
- char pki_name[HOST_NAME_MAX+1];
-
- char *pki_cert_file;
- char *pki_cert;
- off_t pki_cert_len;
-
- char *pki_key_file;
- char *pki_key;
- off_t pki_key_len;
-
- EVP_PKEY *pki_pkey;
-
- int pki_dhe;
-};
-
-struct ca {
- char ca_name[HOST_NAME_MAX+1];
-
- char *ca_cert_file;
- char *ca_cert;
- off_t ca_cert_len;
-};
-
-
-/* ssl.c */
-void ssl_init(void);
-int ssl_setup(SSL_CTX **, struct pki *,
- int (*)(SSL *, int *, void *), const char *);
-SSL_CTX *ssl_ctx_create(const char *, char *, off_t, const char *);
-int ssl_cmp(struct pki *, struct pki *);
-char *ssl_load_file(const char *, off_t *, mode_t);
-char *ssl_load_key(const char *, off_t *, char *, mode_t, const char *);
-
-const char *ssl_to_text(const SSL *);
-void ssl_error(const char *);
-
-int ssl_load_certificate(struct pki *, const char *);
-int ssl_load_keyfile(struct pki *, const char *, const char *);
-int ssl_load_cafile(struct ca *, const char *);
-int ssl_load_pkey(const void *, size_t, char *, off_t,
- X509 **, EVP_PKEY **);
-int ssl_ctx_fake_private_key(SSL_CTX *, const void *, size_t,
- char *, off_t, X509 **, EVP_PKEY **);
-
-/* ssl_privsep.c */
-int ssl_by_mem_ctrl(X509_LOOKUP *, int, const char *, long, char **);
-int SSL_CTX_use_certificate_chain_mem(SSL_CTX *, void *, int);
-
-/* ssl_verify.c */
-int ssl_check_name(X509 *, const char *, int *);
diff --git a/smtpd/ssl_smtpd.c b/smtpd/ssl_smtpd.c
deleted file mode 100644
index 4e5b7e75..00000000
--- a/smtpd/ssl_smtpd.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/* $OpenBSD: ssl_smtpd.c,v 1.13 2015/12/30 16:02:08 benno Exp $ */
-
-/*
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org>
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <ctype.h>
-#include <event.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <imsg.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <openssl/ssl.h>
-#include <openssl/engine.h>
-#include <openssl/err.h>
-
-#include "smtpd.h"
-#include "log.h"
-#include "ssl.h"
-
-
-void *
-ssl_mta_init(void *pkiname, char *cert, off_t cert_len, const char *ciphers)
-{
- SSL_CTX *ctx = NULL;
- SSL *ssl = NULL;
-
- ctx = ssl_ctx_create(pkiname, cert, cert_len, ciphers);
-
- if ((ssl = SSL_new(ctx)) == NULL)
- goto err;
- if (!SSL_set_ssl_method(ssl, SSLv23_client_method()))
- goto err;
-
- SSL_CTX_free(ctx);
- return (void *)(ssl);
-
-err:
- SSL_free(ssl);
- SSL_CTX_free(ctx);
- ssl_error("ssl_mta_init");
- return (NULL);
-}
-
-/* dummy_verify */
-static int
-dummy_verify(int ok, X509_STORE_CTX *store)
-{
- /*
- * We *want* SMTP to request an optional client certificate, however we don't want the
- * verification to take place in the SMTP process. This dummy verify will allow us to
- * asynchronously verify in the lookup process.
- */
- return 1;
-}
-
-void *
-ssl_smtp_init(void *ssl_ctx, int verify)
-{
- SSL *ssl = NULL;
-
- log_debug("debug: session_start_ssl: switching to SSL");
-
- if (verify)
- SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, dummy_verify);
-
- if ((ssl = SSL_new(ssl_ctx)) == NULL)
- goto err;
- if (!SSL_set_ssl_method(ssl, SSLv23_server_method()))
- goto err;
-
- return (void *)(ssl);
-
-err:
- SSL_free(ssl);
- ssl_error("ssl_smtp_init");
- return (NULL);
-}
diff --git a/smtpd/ssl_verify.c b/smtpd/ssl_verify.c
deleted file mode 100644
index 2e784b97..00000000
--- a/smtpd/ssl_verify.c
+++ /dev/null
@@ -1,297 +0,0 @@
-/* $OpenBSD: ssl_verify.c,v 1.2 2019/11/02 03:16:45 gilles Exp $ */
-/*
- * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@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.
- */
-
-/* Adapted from lib/libtls/tls_verify.c */
-
-#include "includes.h"
-
-#include <sys/socket.h>
-
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-#include <limits.h>
-#include <string.h>
-
-#include <openssl/x509v3.h>
-
-#if 0
-#include <tls.h>
-#include "tls_internal.h"
-#endif
-
-#include "ssl.h"
-#include "log.h"
-
-struct tls;
-#define tls_set_errorx(ctx, ...) log_warnx(__VA_ARGS__)
-union tls_addr {
- struct in_addr in;
- struct in6_addr in6;
-};
-
-static int
-tls_match_name(const char *cert_name, const char *name)
-{
- const char *cert_domain, *domain, *next_dot;
-
- if (strcasecmp(cert_name, name) == 0)
- return 0;
-
- /* Wildcard match? */
- if (cert_name[0] == '*') {
- /*
- * Valid wildcards:
- * - "*.domain.tld"
- * - "*.sub.domain.tld"
- * - etc.
- * Reject "*.tld".
- * No attempt to prevent the use of eg. "*.co.uk".
- */
- cert_domain = &cert_name[1];
- /* Disallow "*" */
- if (cert_domain[0] == '\0')
- return -1;
- /* Disallow "*foo" */
- if (cert_domain[0] != '.')
- return -1;
- /* Disallow "*.." */
- if (cert_domain[1] == '.')
- return -1;
- next_dot = strchr(&cert_domain[1], '.');
- /* Disallow "*.bar" */
- if (next_dot == NULL)
- return -1;
- /* Disallow "*.bar.." */
- if (next_dot[1] == '.')
- return -1;
-
- domain = strchr(name, '.');
-
- /* No wildcard match against a name with no host part. */
- if (name[0] == '.')
- return -1;
- /* No wildcard match against a name with no domain part. */
- if (domain == NULL || strlen(domain) == 1)
- return -1;
-
- if (strcasecmp(cert_domain, domain) == 0)
- return 0;
- }
-
- return -1;
-}
-
-/*
- * See RFC 5280 section 4.2.1.6 for SubjectAltName details.
- * alt_match is set to 1 if a matching alternate name is found.
- * alt_exists is set to 1 if any known alternate name exists in the certificate.
- */
-static int
-tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
- int *alt_match, int *alt_exists)
-{
- STACK_OF(GENERAL_NAME) *altname_stack = NULL;
- union tls_addr addrbuf;
- int addrlen, type;
- int count, i;
- int rv = 0;
-
- *alt_match = 0;
- *alt_exists = 0;
-
- altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name,
- NULL, NULL);
- if (altname_stack == NULL)
- return 0;
-
- if (inet_pton(AF_INET, name, &addrbuf) == 1) {
- type = GEN_IPADD;
- addrlen = 4;
- } else if (inet_pton(AF_INET6, name, &addrbuf) == 1) {
- type = GEN_IPADD;
- addrlen = 16;
- } else {
- type = GEN_DNS;
- addrlen = 0;
- }
-
- count = sk_GENERAL_NAME_num(altname_stack);
- for (i = 0; i < count; i++) {
- GENERAL_NAME *altname;
-
- altname = sk_GENERAL_NAME_value(altname_stack, i);
-
- if (altname->type == GEN_DNS || altname->type == GEN_IPADD)
- *alt_exists = 1;
-
- if (altname->type != type)
- continue;
-
- if (type == GEN_DNS) {
- const unsigned char *data;
- int format, len;
-
- format = ASN1_STRING_type(altname->d.dNSName);
- if (format == V_ASN1_IA5STRING) {
- data = ASN1_STRING_get0_data(altname->d.dNSName);
- len = ASN1_STRING_length(altname->d.dNSName);
-
- if (len < 0 || (size_t)len != strlen(data)) {
- tls_set_errorx(ctx,
- "error verifying name '%s': "
- "NUL byte in subjectAltName, "
- "probably a malicious certificate",
- name);
- rv = -1;
- break;
- }
-
- /*
- * Per RFC 5280 section 4.2.1.6:
- * " " is a legal domain name, but that
- * dNSName must be rejected.
- */
- if (strcmp(data, " ") == 0) {
- tls_set_errorx(ctx,
- "error verifying name '%s': "
- "a dNSName of \" \" must not be "
- "used", name);
- rv = -1;
- break;
- }
-
- if (tls_match_name(data, name) == 0) {
- *alt_match = 1;
- break;
- }
- } else {
-#ifdef DEBUG
- fprintf(stdout, "%s: unhandled subjectAltName "
- "dNSName encoding (%d)\n", getprogname(),
- format);
-#endif
- }
-
- } else if (type == GEN_IPADD) {
- const unsigned char *data;
- int datalen;
-
- datalen = ASN1_STRING_length(altname->d.iPAddress);
- data = ASN1_STRING_get0_data(altname->d.iPAddress);
-
- if (datalen < 0) {
- tls_set_errorx(ctx,
- "Unexpected negative length for an "
- "IP address: %d", datalen);
- rv = -1;
- break;
- }
-
- /*
- * Per RFC 5280 section 4.2.1.6:
- * IPv4 must use 4 octets and IPv6 must use 16 octets.
- */
- if (datalen == addrlen &&
- memcmp(data, &addrbuf, addrlen) == 0) {
- *alt_match = 1;
- break;
- }
- }
- }
-
- sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free);
- return rv;
-}
-
-static int
-tls_check_common_name(struct tls *ctx, X509 *cert, const char *name,
- int *cn_match)
-{
- X509_NAME *subject_name;
- char *common_name = NULL;
- union tls_addr addrbuf;
- int common_name_len;
- int rv = 0;
-
- *cn_match = 0;
-
- subject_name = X509_get_subject_name(cert);
- if (subject_name == NULL)
- goto done;
-
- common_name_len = X509_NAME_get_text_by_NID(subject_name,
- NID_commonName, NULL, 0);
- if (common_name_len < 0)
- goto done;
-
- common_name = calloc(common_name_len + 1, 1);
- if (common_name == NULL)
- goto done;
-
- X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name,
- common_name_len + 1);
-
- /* NUL bytes in CN? */
- if (common_name_len < 0 ||
- (size_t)common_name_len != strlen(common_name)) {
- tls_set_errorx(ctx, "error verifying name '%s': "
- "NUL byte in Common Name field, "
- "probably a malicious certificate", name);
- rv = -1;
- goto done;
- }
-
- /*
- * We don't want to attempt wildcard matching against IP addresses,
- * so perform a simple comparison here.
- */
- if (inet_pton(AF_INET, name, &addrbuf) == 1 ||
- inet_pton(AF_INET6, name, &addrbuf) == 1) {
- if (strcmp(common_name, name) == 0)
- *cn_match = 1;
- goto done;
- }
-
- if (tls_match_name(common_name, name) == 0)
- *cn_match = 1;
-
- done:
- free(common_name);
- return rv;
-}
-
-int
-ssl_check_name(X509 *cert, const char *name, int *match)
-{
- int alt_exists;
-
- *match = 0;
-
- if (tls_check_subject_altname(NULL, cert, name, match,
- &alt_exists) == -1)
- return -1;
-
- /*
- * As per RFC 6125 section 6.4.4, if any known alternate name existed
- * in the certificate, we do not attempt to match on the CN.
- */
- if (*match || alt_exists)
- return 0;
-
- return tls_check_common_name(NULL, cert, name, match);
-}
diff --git a/smtpd/stat_backend.c b/smtpd/stat_backend.c
deleted file mode 100644
index 30cb299b..00000000
--- a/smtpd/stat_backend.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/* $OpenBSD: stat_backend.c,v 1.11 2018/12/27 10:35:26 gilles Exp $ */
-
-/*
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-
-#include <event.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <string.h>
-#include <limits.h>
-
-#include "log.h"
-#include "smtpd.h"
-
-extern struct stat_backend stat_backend_ramstat;
-
-struct stat_backend *
-stat_backend_lookup(const char *name)
-{
- return &stat_backend_ramstat;
-}
-
-void
-stat_increment(const char *key, size_t count)
-{
- struct stat_value *value;
-
- if (count == 0)
- return;
-
- value = stat_counter(count);
-
- m_create(p_control, IMSG_STAT_INCREMENT, 0, 0, -1);
- m_add_string(p_control, key);
- m_add_data(p_control, value, sizeof(*value));
- m_close(p_control);
-}
-
-void
-stat_decrement(const char *key, size_t count)
-{
- struct stat_value *value;
-
- if (count == 0)
- return;
-
- value = stat_counter(count);
-
- m_create(p_control, IMSG_STAT_DECREMENT, 0, 0, -1);
- m_add_string(p_control, key);
- m_add_data(p_control, value, sizeof(*value));
- m_close(p_control);
-}
-
-void
-stat_set(const char *key, const struct stat_value *value)
-{
- m_create(p_control, IMSG_STAT_SET, 0, 0, -1);
- m_add_string(p_control, key);
- m_add_data(p_control, value, sizeof(*value));
- m_close(p_control);
-}
-
-/* helpers */
-
-struct stat_value *
-stat_counter(size_t counter)
-{
- static struct stat_value value;
-
- value.type = STAT_COUNTER;
- value.u.counter = counter;
- return &value;
-}
-
-struct stat_value *
-stat_timestamp(time_t timestamp)
-{
- static struct stat_value value;
-
- value.type = STAT_TIMESTAMP;
- value.u.timestamp = timestamp;
- return &value;
-}
-
-struct stat_value *
-stat_timeval(struct timeval *tv)
-{
- static struct stat_value value;
-
- value.type = STAT_TIMEVAL;
- value.u.tv = *tv;
- return &value;
-}
-
-struct stat_value *
-stat_timespec(struct timespec *ts)
-{
- static struct stat_value value;
-
- value.type = STAT_TIMESPEC;
- value.u.ts = *ts;
- return &value;
-}
diff --git a/smtpd/stat_ramstat.c b/smtpd/stat_ramstat.c
deleted file mode 100644
index bbf1541a..00000000
--- a/smtpd/stat_ramstat.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/* $OpenBSD: stat_ramstat.c,v 1.11 2018/05/31 21:06:12 gilles Exp $ */
-
-/*
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-
-#include <event.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-
-static void ramstat_init(void);
-static void ramstat_close(void);
-static void ramstat_increment(const char *, size_t);
-static void ramstat_decrement(const char *, size_t);
-static void ramstat_set(const char *, const struct stat_value *);
-static int ramstat_iter(void **, char **, struct stat_value *);
-
-struct ramstat_entry {
- RB_ENTRY(ramstat_entry) entry;
- char key[STAT_KEY_SIZE];
- struct stat_value value;
-};
-RB_HEAD(stats_tree, ramstat_entry) stats;
-RB_PROTOTYPE(stats_tree, ramstat_entry, entry, ramstat_entry_cmp);
-
-struct stat_backend stat_backend_ramstat = {
- ramstat_init,
- ramstat_close,
- ramstat_increment,
- ramstat_decrement,
- ramstat_set,
- ramstat_iter
-};
-
-static void
-ramstat_init(void)
-{
- log_trace(TRACE_STAT, "ramstat: init");
-
- RB_INIT(&stats);
-
- /* ramstat_set() should be called for each key we want
- * to have displayed by smtpctl show stats at startup.
- */
- ramstat_set("uptime", stat_timestamp(env->sc_uptime));
-}
-
-static void
-ramstat_close(void)
-{
- log_trace(TRACE_STAT, "ramstat: close");
-}
-
-static void
-ramstat_increment(const char *name, size_t val)
-{
- struct ramstat_entry *np, lk;
-
- log_trace(TRACE_STAT, "ramstat: increment: %s", name);
- (void)strlcpy(lk.key, name, sizeof (lk.key));
- np = RB_FIND(stats_tree, &stats, &lk);
- if (np == NULL) {
- np = xcalloc(1, sizeof *np);
- (void)strlcpy(np->key, name, sizeof (np->key));
- RB_INSERT(stats_tree, &stats, np);
- }
- log_trace(TRACE_STAT, "ramstat: %s (%p): %zd -> %zd",
- name, name, np->value.u.counter, np->value.u.counter + val);
- np->value.u.counter += val;
-}
-
-static void
-ramstat_decrement(const char *name, size_t val)
-{
- struct ramstat_entry *np, lk;
-
- log_trace(TRACE_STAT, "ramstat: decrement: %s", name);
- (void)strlcpy(lk.key, name, sizeof (lk.key));
- np = RB_FIND(stats_tree, &stats, &lk);
- if (np == NULL) {
- np = xcalloc(1, sizeof *np);
- (void)strlcpy(np->key, name, sizeof (np->key));
- RB_INSERT(stats_tree, &stats, np);
- }
- log_trace(TRACE_STAT, "ramstat: %s (%p): %zd -> %zd",
- name, name, np->value.u.counter, np->value.u.counter - val);
- np->value.u.counter -= val;
-}
-
-static void
-ramstat_set(const char *name, const struct stat_value *val)
-{
- struct ramstat_entry *np, lk;
-
- log_trace(TRACE_STAT, "ramstat: set: %s", name);
- (void)strlcpy(lk.key, name, sizeof (lk.key));
- np = RB_FIND(stats_tree, &stats, &lk);
- if (np == NULL) {
- np = xcalloc(1, sizeof *np);
- (void)strlcpy(np->key, name, sizeof (np->key));
- RB_INSERT(stats_tree, &stats, np);
- }
- log_trace(TRACE_STAT, "ramstat: %s: n/a -> n/a", name);
- np->value = *val;
-}
-
-static int
-ramstat_iter(void **iter, char **name, struct stat_value *val)
-{
- struct ramstat_entry *np;
-
- log_trace(TRACE_STAT, "ramstat: iter");
- if (RB_EMPTY(&stats))
- return 0;
-
- if (*iter == NULL)
- np = RB_MIN(stats_tree, &stats);
- else
- np = RB_NEXT(stats_tree, &stats, *iter);
-
- *iter = np;
- if (np == NULL)
- return 0;
-
- *name = np->key;
- *val = np->value;
- return 1;
-}
-
-
-static int
-ramstat_entry_cmp(struct ramstat_entry *e1, struct ramstat_entry *e2)
-{
- return strcmp(e1->key, e2->key);
-}
-
-RB_GENERATE(stats_tree, ramstat_entry, entry, ramstat_entry_cmp);
diff --git a/smtpd/table.5 b/smtpd/table.5
deleted file mode 100644
index e9d4fa4b..00000000
--- a/smtpd/table.5
+++ /dev/null
@@ -1,258 +0,0 @@
-.\" $OpenBSD: table.5,v 1.11 2019/08/11 13:00:57 gilles Exp $
-.\"
-.\" Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
-.\" Copyright (c) 2013 Gilles Chehade <gilles@poolp.org>
-.\"
-.\" Permission to use, copy, modify, and distribute this software for any
-.\" purpose with or without fee is hereby granted, provided that the above
-.\" copyright notice and this permission notice appear in all copies.
-.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.\"
-.\"
-.Dd $Mdocdate: August 11 2019 $
-.Dt TABLE 5
-.Os
-.Sh NAME
-.Nm table
-.Nd format description for smtpd tables
-.Sh DESCRIPTION
-This manual page documents the file format for the various tables used in the
-.Xr smtpd 8
-mail daemon.
-.Pp
-The format described here applies to tables as defined in
-.Xr smtpd.conf 5 .
-.Sh TABLE TYPES
-There are two types of tables: lists and mappings.
-A list consists of a series of values,
-while a mapping consists of a series of keys and their associated values.
-The following illustrates how to declare them as static tables:
-.Bd -literal -offset indent
-table mylist { value1, value2, value3 }
-table mymapping { key1 = value1, key2 = value2, key3 = value3 }
-.Ed
-.Pp
-When using a
-.Ql file
-table, a list will be written with each value on a line by itself.
-Comments can be put anywhere in the file using a hash mark
-.Pq Sq # ,
-and extend to the end of the current line.
-.Bd -literal -offset indent
-value1
-value2
-value3
-.Ed
-.Pp
-A mapping will be written with each key and value on a line,
-whitespaces separating both columns:
-.Bd -literal -offset indent
-key1 value1
-key2 value2
-key3 value3
-.Ed
-.Pp
-A file table can be converted to a Berkeley database using the
-.Xr makemap 8
-utility with no syntax change.
-.Pp
-Tables using a
-.Ql file
-or Berkeley DB backend will be referenced as follows:
-.Bd -unfilled -offset indent
-.Ic table Ar name Cm file : Ns Pa /path/to/file
-.Ic table Ar name Cm db : Ns Pa /path/to/file.db
-.Ed
-.Ss Aliasing tables
-Aliasing tables are mappings that associate a recipient to one or many
-destinations.
-They can be used in two contexts: primary domain aliases and virtual domain
-mapping.
-.Bd -unfilled -offset indent
-.Ic action Ar name method Cm alias Pf < table Ns >
-.Ic action Ar name method Cm virtual Pf < table Ns >
-.Ed
-.Pp
-In a primary domain context, the key is the user part of the recipient address,
-whilst the value is one or many recipients as described in
-.Xr aliases 5 :
-.Bd -literal -offset indent
-user1 otheruser
-user2 otheruser1,otheruser2
-user3 otheruser@example.com
-.Ed
-.Pp
-In a virtual domain context, the key is either a user part, a full email
-address or a catch all, following selection rules described in
-.Xr smtpd.conf 5 ,
-and the value is one or many recipients as described in
-.Xr aliases 5 :
-.Bd -literal -offset indent
-user1 otheruser
-user2@example.org otheruser1,otheruser2
-@example.org otheruser@example.com
-@ catchall@example.com
-.Ed
-.Pp
-The following directive shares the same table format,
-but with a different meaning.
-Here, the user is allowed to send mail from the listed addresses:
-.Bd -unfilled -offset indent
-.Ic listen on Ar interface Cm auth Oo Ar ... Oc Cm senders Pf < Ar table Ns >
-.Ed
-.Ss Domain tables
-Domain tables are simple lists of domains or hosts.
-.Bd -unfilled -offset indent
-.Ic match Cm for domain Pf < table Ns > Cm action Ar name
-.Ic match Cm helo Pf < table Ns > Oo Ar ... Oc Cm action Ar name
-.Ed
-.Pp
-In that context, the list of domains will be matched against the recipient
-domain or against the HELO name advertised by the sending host,
-respectively.
-For
-.Ql static ,
-.Ql file
-and
-.Xr dbopen 3
-backends, a wildcard may be used so the domain table may contain:
-.Bd -literal -offset indent
-example.org
-*.example.org
-.Ed
-.Ss Credentials tables
-Credentials tables are mappings of credentials.
-They can be used in two contexts:
-.Bd -unfilled -offset indent
-.Ic listen on Ar interface Cm tls Oo Ar ... Oc Cm auth Pf < Ar table Ns >
-.Ic action Ar name Cm relay host Ar relay-url Cm auth Pf < Ar table Ns >
-.Ed
-.Pp
-In a listener context, the credentials are a mapping of username and encrypted
-passwords:
-.Bd -literal -offset indent
-user1 $2b$10$hIJ4QfMcp.90nJwKqGbKM.MybArjHOTpEtoTV.DgLYAiThuoYmTSe
-user2 $2b$10$bwSmUOBGcZGamIfRuXGTvuTo3VLbPG9k5yeKNMBtULBhksV5KdGsK
-.Ed
-.Pp
-The passwords are to be encrypted using the
-.Xr smtpctl 8
-encrypt subcommand.
-.Pp
-In a relay context, the credentials are a mapping of labels and
-username:password pairs:
-.Bd -literal -offset indent
-label1 user:password
-.Ed
-.Pp
-The label must be unique and is used as a selector for the proper credentials
-when multiple credentials are valid for a single destination.
-The password is not encrypted as it must be provided to the remote host.
-.Ss Netaddr tables
-Netaddr tables are lists of IPv4 and IPv6 network addresses.
-They can only be used in the following context:
-.Pp
-.D1 Ic match Cm from src Pf < Ar table Ns > Cm action Ar name
-.Pp
-When used as a "from source", the address of a client is compared to the list
-of addresses in the table until a match is found.
-.Pp
-A netaddr table can contain exact addresses or netmasks, and looks as follow:
-.Bd -literal -offset indent
-192.168.1.1
-::1
-ipv6:::1
-192.168.1.0/24
-.Ed
-.Ss Userinfo tables
-User info tables are used in rule context to specify an alternate user base,
-mapping virtual users to local system users by UID, GID and home directory.
-.Pp
-.D1 Ic action Ar name method Cm userbase Pf < Ar table Ns >
-.Pp
-A userinfo table looks as follows:
-.Bd -literal -offset indent
-joe 1000:100:/home/virtual/joe
-jack 1000:100:/home/virtual/jack
-.Ed
-.Pp
-In this example, both joe and jack are virtual users mapped to the local
-system user with UID 1000 and GID 100, but different home directories.
-These directories may contain a
-.Xr forward 5
-file.
-This can be used in conjunction with an alias table
-that maps an email address or the domain part to the desired virtual
-username.
-For example:
-.Bd -literal -offset indent
-joe@example.org joe
-jack@example.com jack
-.Ed
-.Ss Source tables
-Source tables are lists of IPv4 and IPv6 addresses.
-They can only be used in the following context:
-.Pp
-.D1 Ic action Ar name Cm relay src Pf < Ar table Ns >
-.Pp
-Successive queries to the source table will return the elements one by one.
-.Pp
-A source table looks as follow:
-.Bd -literal -offset indent
-192.168.1.2
-192.168.1.3
-::1
-::2
-ipv6:::3
-ipv6:::4
-.Ed
-.Ss Mailaddr tables
-Mailaddr tables are lists of email addresses.
-They can be used in the following contexts:
-.Bd -unfilled -offset indent
-.Ic match Cm mail\-from Pf < Ar table Ns > Cm action Ar name
-.Ic match Cm rcpt\-to Pf < Ar table Ns > Cm action Ar name
-.Ed
-.Pp
-A mailaddr entry is used to match an email address against a username,
-a domain or a full email address.
-A "*" wildcard may be used in part of the domain name.
-.Pp
-A mailaddr table looks as follow:
-.Bd -literal -offset indent
-user
-@domain
-user@domain
-user@*.domain
-.Ed
-.Ss Addrname tables
-Addrname tables are used to map IP addresses to hostnames.
-They can be used in both listen context and relay context:
-.Bd -unfilled -offset indent
-.Ic listen on Ar interface Cm hostnames Pf < Ar table Ns >
-.Ic action Ar name Cm relay helo\-src Pf < Ar table Ns >
-.Ed
-.Pp
-In listen context, the table is used to look up the server name to advertise
-depending on the local address of the socket on which a connection is accepted.
-In relay context, the table is used to determine the hostname for the HELO
-sequence of the SMTP protocol, depending on the local address used for the
-outgoing connection.
-.Pp
-The format is a mapping from inet4 or inet6 addresses to hostnames:
-.Bd -literal -offset indent
-::1 localhost
-127.0.0.1 localhost
-88.190.23.165 www.opensmtpd.org
-.Ed
-.Sh SEE ALSO
-.Xr smtpd.conf 5 ,
-.Xr makemap 8 ,
-.Xr smtpd 8
diff --git a/smtpd/table.c b/smtpd/table.c
deleted file mode 100644
index 469eeee1..00000000
--- a/smtpd/table.c
+++ /dev/null
@@ -1,709 +0,0 @@
-/* $OpenBSD: table.c,v 1.48 2019/01/10 07:40:52 eric Exp $ */
-
-/*
- * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <net/if.h>
-
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <regex.h>
-#include <limits.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-struct table_backend *table_backend_lookup(const char *);
-
-extern struct table_backend table_backend_static;
-#ifdef HAVE_DB_API
-extern struct table_backend table_backend_db;
-#endif
-extern struct table_backend table_backend_getpwnam;
-extern struct table_backend table_backend_proc;
-
-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 *);
-
-static unsigned int last_table_id = 0;
-
-static struct table_backend *backends[] = {
- &table_backend_static,
-#ifdef HAVE_DB_API
- &table_backend_db,
-#endif
- &table_backend_getpwnam,
- &table_backend_proc,
- NULL
-};
-
-struct table_backend *
-table_backend_lookup(const char *backend)
-{
- int i;
-
- if (!strcmp(backend, "file"))
- backend = "static";
-
- for (i = 0; backends[i]; i++)
- if (!strcmp(backends[i]->name, backend))
- return (backends[i]);
-
- return NULL;
-}
-
-static const char *
-table_service_name(enum table_service s)
-{
- switch (s) {
- case K_NONE: return "NONE";
- case K_ALIAS: return "ALIAS";
- case K_DOMAIN: return "DOMAIN";
- case K_CREDENTIALS: return "CREDENTIALS";
- case K_NETADDR: return "NETADDR";
- case K_USERINFO: return "USERINFO";
- case K_SOURCE: return "SOURCE";
- case K_MAILADDR: return "MAILADDR";
- case K_ADDRNAME: return "ADDRNAME";
- case K_MAILADDRMAP: return "MAILADDRMAP";
- case K_RELAYHOST: return "RELAYHOST";
- case K_STRING: return "STRING";
- case K_REGEX: return "REGEX";
- }
- return "???";
-}
-
-struct table *
-table_find(struct smtpd *conf, const char *name)
-{
- return dict_get(conf->sc_tables_dict, name);
-}
-
-int
-table_match(struct table *table, enum table_service kind, const char *key)
-{
- return table_lookup(table, kind, key, NULL);
-}
-
-int
-table_lookup(struct table *table, enum table_service kind, const char *key,
- union lookup *lk)
-{
- char lkey[1024], *buf = NULL;
- int r;
-
- r = -1;
- if (table->t_backend->lookup == NULL)
- errno = ENOTSUP;
- else if (!lowercase(lkey, key, sizeof lkey)) {
- log_warnx("warn: lookup key too long: %s", key);
- errno = EINVAL;
- }
- else
- r = table->t_backend->lookup(table, kind, lkey, lk ? &buf : NULL);
-
- if (r == 1) {
- log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s",
- lk ? "lookup" : "match",
- key,
- table_service_name(kind),
- table->t_backend->name,
- table->t_name,
- lk ? "\"" : "",
- lk ? buf : "true",
- lk ? "\"" : "");
- if (buf)
- r = table_parse_lookup(kind, lkey, buf, lk);
- }
- else
- log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s",
- lk ? "lookup" : "match",
- key,
- table_service_name(kind),
- table->t_backend->name,
- table->t_name,
- (r == -1) ? "error: " : (lk ? "none" : "false"),
- (r == -1) ? strerror(errno) : "");
-
- free(buf);
-
- return (r);
-}
-
-int
-table_fetch(struct table *table, enum table_service kind, union lookup *lk)
-{
- char *buf = NULL;
- int r;
-
- r = -1;
- if (table->t_backend->fetch == NULL)
- errno = ENOTSUP;
- else
- r = table->t_backend->fetch(table, kind, &buf);
-
- if (r == 1) {
- log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> \"%s\"",
- table_service_name(kind),
- table->t_backend->name,
- table->t_name,
- buf);
- r = table_parse_lookup(kind, NULL, buf, lk);
- }
- else
- log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> %s%s",
- table_service_name(kind),
- table->t_backend->name,
- table->t_name,
- (r == -1) ? "error: " : "none",
- (r == -1) ? strerror(errno) : "");
-
- free(buf);
-
- return (r);
-}
-
-struct table *
-table_create(struct smtpd *conf, const char *backend, const char *name,
- const char *config)
-{
- struct table *t;
- struct table_backend *tb;
- char path[LINE_MAX];
- size_t n;
- struct stat sb;
-
- if (name && table_find(conf, name))
- fatalx("table_create: table \"%s\" already defined", name);
-
- if ((tb = table_backend_lookup(backend)) == NULL) {
- if ((size_t)snprintf(path, sizeof(path), PATH_LIBEXEC"/table-%s",
- backend) >= sizeof(path)) {
- fatalx("table_create: path too long \""
- PATH_LIBEXEC"/table-%s\"", backend);
- }
- if (stat(path, &sb) == 0) {
- tb = table_backend_lookup("proc");
- (void)strlcpy(path, backend, sizeof(path));
- if (config) {
- (void)strlcat(path, ":", sizeof(path));
- if (strlcat(path, config, sizeof(path))
- >= sizeof(path))
- fatalx("table_create: config file path too long");
- }
- config = path;
- }
- }
-
- if (tb == NULL)
- fatalx("table_create: backend \"%s\" does not exist", backend);
-
- t = xcalloc(1, sizeof(*t));
- t->t_backend = tb;
-
- if (config) {
- if (strlcpy(t->t_config, config, sizeof t->t_config)
- >= sizeof t->t_config)
- fatalx("table_create: table config \"%s\" too large",
- t->t_config);
- }
-
- if (strcmp(tb->name, "static") != 0)
- t->t_type = T_DYNAMIC;
-
- if (name == NULL)
- (void)snprintf(t->t_name, sizeof(t->t_name), "<dynamic:%u>",
- last_table_id++);
- else {
- n = strlcpy(t->t_name, name, sizeof(t->t_name));
- if (n >= sizeof(t->t_name))
- fatalx("table_create: table name too long");
- }
-
- dict_set(conf->sc_tables_dict, t->t_name, t);
-
- return (t);
-}
-
-void
-table_destroy(struct smtpd *conf, struct table *t)
-{
- dict_xpop(conf->sc_tables_dict, t->t_name);
- free(t);
-}
-
-int
-table_config(struct table *t)
-{
- if (t->t_backend->config == NULL)
- return (1);
- return (t->t_backend->config(t));
-}
-
-void
-table_add(struct table *t, const char *key, const char *val)
-{
- if (t->t_backend->add == NULL)
- fatalx("table_add: cannot add to table");
-
- if (t->t_backend->add(t, key, val) == 0)
- log_warnx("warn: failed to add \"%s\" in table \"%s\"", key, t->t_name);
-}
-
-void
-table_dump(struct table *t)
-{
- const char *type;
- char buf[LINE_MAX];
-
- switch(t->t_type) {
- case T_NONE:
- type = "NONE";
- break;
- case T_DYNAMIC:
- type = "DYNAMIC";
- break;
- case T_LIST:
- type = "LIST";
- break;
- case T_HASH:
- type = "HASH";
- break;
- default:
- type = "???";
- break;
- }
-
- if (t->t_config[0])
- snprintf(buf, sizeof(buf), " config=\"%s\"", t->t_config);
- else
- buf[0] = '\0';
-
- log_debug("TABLE \"%s\" backend=%s type=%s%s", t->t_name,
- t->t_backend->name, type, buf);
-
- if (t->t_backend->dump)
- t->t_backend->dump(t);
-}
-
-int
-table_check_type(struct table *t, uint32_t mask)
-{
- return t->t_type & mask;
-}
-
-int
-table_check_service(struct table *t, uint32_t mask)
-{
- return t->t_backend->services & mask;
-}
-
-int
-table_check_use(struct table *t, uint32_t tmask, uint32_t smask)
-{
- return table_check_type(t, tmask) && table_check_service(t, smask);
-}
-
-int
-table_open(struct table *t)
-{
- if (t->t_backend->open == NULL)
- return (1);
- return (t->t_backend->open(t));
-}
-
-void
-table_close(struct table *t)
-{
- if (t->t_backend->close)
- t->t_backend->close(t);
-}
-
-int
-table_update(struct table *t)
-{
- if (t->t_backend->update == NULL)
- return (1);
- return (t->t_backend->update(t));
-}
-
-
-/*
- * quick reminder:
- * in *_match() s1 comes from session, s2 comes from table
- */
-
-int
-table_domain_match(const char *s1, const char *s2)
-{
- return hostname_match(s1, s2);
-}
-
-int
-table_mailaddr_match(const char *s1, const char *s2)
-{
- struct mailaddr m1;
- struct mailaddr m2;
-
- if (!text_to_mailaddr(&m1, s1))
- return 0;
- if (!text_to_mailaddr(&m2, s2))
- return 0;
- return mailaddr_match(&m1, &m2);
-}
-
-static int table_match_mask(struct sockaddr_storage *, struct netaddr *);
-static int table_inet4_match(struct sockaddr_in *, struct netaddr *);
-static int table_inet6_match(struct sockaddr_in6 *, struct netaddr *);
-
-int
-table_netaddr_match(const char *s1, const char *s2)
-{
- struct netaddr n1;
- struct netaddr n2;
-
- if (strcasecmp(s1, s2) == 0)
- return 1;
- if (!text_to_netaddr(&n1, s1))
- return 0;
- if (!text_to_netaddr(&n2, s2))
- return 0;
- if (n1.ss.ss_family != n2.ss.ss_family)
- return 0;
- if (SS_LEN(&n1.ss) != SS_LEN(&n2.ss))
- return 0;
- return table_match_mask(&n1.ss, &n2);
-}
-
-static int
-table_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask)
-{
- if (ss->ss_family == AF_INET)
- return table_inet4_match((struct sockaddr_in *)ss, ssmask);
-
- if (ss->ss_family == AF_INET6)
- return table_inet6_match((struct sockaddr_in6 *)ss, ssmask);
-
- return (0);
-}
-
-static int
-table_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask)
-{
- in_addr_t mask;
- int i;
-
- /* a.b.c.d/8 -> htonl(0xff000000) */
- mask = 0;
- for (i = 0; i < ssmask->bits; ++i)
- mask = (mask >> 1) | 0x80000000;
- mask = htonl(mask);
-
- /* (addr & mask) == (net & mask) */
- if ((ss->sin_addr.s_addr & mask) ==
- (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask))
- return 1;
-
- return 0;
-}
-
-static int
-table_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask)
-{
- struct in6_addr *in;
- struct in6_addr *inmask;
- struct in6_addr mask;
- int i;
-
- memset(&mask, 0, sizeof(mask));
- for (i = 0; i < ssmask->bits / 8; i++)
- mask.s6_addr[i] = 0xff;
- i = ssmask->bits % 8;
- if (i)
- mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i;
-
- in = &ss->sin6_addr;
- inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr;
-
- for (i = 0; i < 16; i++) {
- if ((in->s6_addr[i] & mask.s6_addr[i]) !=
- (inmask->s6_addr[i] & mask.s6_addr[i]))
- return (0);
- }
-
- return (1);
-}
-
-int
-table_regex_match(const char *string, const char *pattern)
-{
- regex_t preg;
- int cflags = REG_EXTENDED|REG_NOSUB;
-
- if (strncmp(pattern, "(?i)", 4) == 0) {
- cflags |= REG_ICASE;
- pattern += 4;
- }
-
- if (regcomp(&preg, pattern, cflags) != 0)
- return (0);
-
- if (regexec(&preg, string, 0, NULL, 0) != 0)
- return (0);
-
- return (1);
-}
-
-void
-table_dump_all(struct smtpd *conf)
-{
- struct table *t;
- void *iter;
-
- iter = NULL;
- while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t))
- table_dump(t);
-}
-
-void
-table_open_all(struct smtpd *conf)
-{
- struct table *t;
- void *iter;
-
- iter = NULL;
- while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t))
- if (!table_open(t))
- fatalx("failed to open table %s", t->t_name);
-}
-
-void
-table_close_all(struct smtpd *conf)
-{
- struct table *t;
- void *iter;
-
- iter = NULL;
- while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t))
- table_close(t);
-}
-
-static int
-table_parse_lookup(enum table_service service, const char *key,
- const char *line, union lookup *lk)
-{
- char buffer[LINE_MAX], *p;
- size_t len;
-
- len = strlen(line);
-
- switch (service) {
- case K_ALIAS:
- lk->expand = calloc(1, sizeof(*lk->expand));
- if (lk->expand == NULL)
- return (-1);
- if (!expand_line(lk->expand, line, 1)) {
- expand_free(lk->expand);
- return (-1);
- }
- return (1);
-
- case K_DOMAIN:
- if (strlcpy(lk->domain.name, line, sizeof(lk->domain.name))
- >= sizeof(lk->domain.name))
- return (-1);
- return (1);
-
- case K_CREDENTIALS:
-
- /* credentials are stored as user:password */
- if (len < 3)
- return (-1);
-
- /* too big to fit in a smtp session line */
- if (len >= LINE_MAX)
- return (-1);
-
- p = strchr(line, ':');
- if (p == NULL) {
- if (strlcpy(lk->creds.username, key, sizeof (lk->creds.username))
- >= sizeof (lk->creds.username))
- return (-1);
- if (strlcpy(lk->creds.password, line, sizeof(lk->creds.password))
- >= sizeof(lk->creds.password))
- return (-1);
- return (1);
- }
-
- if (p == line || p == line + len - 1)
- return (-1);
-
- memmove(lk->creds.username, line, p - line);
- lk->creds.username[p - line] = '\0';
-
- if (strlcpy(lk->creds.password, p+1, sizeof(lk->creds.password))
- >= sizeof(lk->creds.password))
- return (-1);
-
- return (1);
-
- case K_NETADDR:
- if (!text_to_netaddr(&lk->netaddr, line))
- return (-1);
- return (1);
-
- case K_USERINFO:
- if (!bsnprintf(buffer, sizeof(buffer), "%s:%s", key, line))
- return (-1);
- if (!text_to_userinfo(&lk->userinfo, buffer))
- return (-1);
- return (1);
-
- case K_SOURCE:
- if (parse_sockaddr((struct sockaddr *)&lk->source.addr,
- PF_UNSPEC, line) == -1)
- return (-1);
- return (1);
-
- case K_MAILADDR:
- if (!text_to_mailaddr(&lk->mailaddr, line))
- return (-1);
- return (1);
-
- case K_MAILADDRMAP:
- lk->maddrmap = calloc(1, sizeof(*lk->maddrmap));
- if (lk->maddrmap == NULL)
- return (-1);
- maddrmap_init(lk->maddrmap);
- if (!mailaddr_line(lk->maddrmap, line)) {
- maddrmap_free(lk->maddrmap);
- return (-1);
- }
- return (1);
-
- case K_ADDRNAME:
- if (parse_sockaddr((struct sockaddr *)&lk->addrname.addr,
- PF_UNSPEC, key) == -1)
- return (-1);
- if (strlcpy(lk->addrname.name, line, sizeof(lk->addrname.name))
- >= sizeof(lk->addrname.name))
- return (-1);
- return (1);
-
- case K_RELAYHOST:
- if (strlcpy(lk->relayhost, line, sizeof(lk->relayhost))
- >= sizeof(lk->relayhost))
- return (-1);
- return (1);
-
- default:
- return (-1);
- }
-}
-
-static int
-parse_sockaddr(struct sockaddr *sa, int family, const char *str)
-{
- struct in_addr ina;
- struct in6_addr in6a;
- struct sockaddr_in *sin;
- struct sockaddr_in6 *sin6;
- char *cp, *str2;
- const char *errstr;
-
- switch (family) {
- case PF_UNSPEC:
- if (parse_sockaddr(sa, PF_INET, str) == 0)
- return (0);
- return parse_sockaddr(sa, PF_INET6, str);
-
- case PF_INET:
- if (inet_pton(PF_INET, str, &ina) != 1)
- return (-1);
-
- sin = (struct sockaddr_in *)sa;
- memset(sin, 0, sizeof *sin);
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- sin->sin_len = sizeof(struct sockaddr_in);
-#endif
- sin->sin_family = PF_INET;
- sin->sin_addr.s_addr = ina.s_addr;
- return (0);
-
- case PF_INET6:
- if (strncasecmp("ipv6:", str, 5) == 0)
- str += 5;
- cp = strchr(str, SCOPE_DELIMITER);
- if (cp) {
- str2 = strdup(str);
- if (str2 == NULL)
- return (-1);
- str2[cp - str] = '\0';
- if (inet_pton(PF_INET6, str2, &in6a) != 1) {
- free(str2);
- return (-1);
- }
- cp++;
- free(str2);
- } else if (inet_pton(PF_INET6, str, &in6a) != 1)
- return (-1);
-
- sin6 = (struct sockaddr_in6 *)sa;
- memset(sin6, 0, sizeof *sin6);
-#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
- sin6->sin6_len = sizeof(struct sockaddr_in6);
-#endif
- sin6->sin6_family = PF_INET6;
- sin6->sin6_addr = in6a;
-
- if (cp == NULL)
- return (0);
-
- if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
- IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
- IN6_IS_ADDR_MC_NODELOCAL(&in6a))
- if ((sin6->sin6_scope_id = if_nametoindex(cp)))
- return (0);
-
- sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
- if (errstr)
- return (-1);
- return (0);
-
- default:
- break;
- }
-
- return (-1);
-}
diff --git a/smtpd/table_db.c b/smtpd/table_db.c
deleted file mode 100644
index f7d766dd..00000000
--- a/smtpd/table_db.c
+++ /dev/null
@@ -1,282 +0,0 @@
-/* $OpenBSD: table_db.c,v 1.21 2019/06/28 13:32:51 deraadt Exp $ */
-
-/*
- * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#ifdef HAVE_DB_H
-#include <db.h>
-#elif defined(HAVE_DB1_DB_H)
-#include <db1/db.h>
-#elif defined(HAVE_DB_185_H)
-#include <db_185.h>
-#endif
-#include <ctype.h>
-#include <err.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-
-/* db(3) backend */
-static int table_db_config(struct table *);
-static int table_db_update(struct table *);
-static int table_db_open(struct table *);
-static void *table_db_open2(struct table *);
-static int table_db_lookup(struct table *, enum table_service, const char *, char **);
-static int table_db_fetch(struct table *, enum table_service, char **);
-static void table_db_close(struct table *);
-static void table_db_close2(void *);
-
-static char *table_db_get_entry(void *, const char *, size_t *);
-static char *table_db_get_entry_match(void *, const char *, size_t *,
- int(*)(const char *, const char *));
-
-struct table_backend table_backend_db = {
- "db",
- K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP,
- table_db_config,
- NULL,
- NULL,
- table_db_open,
- table_db_update,
- table_db_close,
- table_db_lookup,
- table_db_fetch,
-};
-
-static struct keycmp {
- enum table_service service;
- int (*func)(const char *, const char *);
-} keycmp[] = {
- { K_DOMAIN, table_domain_match },
- { K_NETADDR, table_netaddr_match },
- { K_MAILADDR, table_mailaddr_match }
-};
-
-struct dbhandle {
- DB *db;
- char pathname[PATH_MAX];
- time_t mtime;
- int iter;
-};
-
-static int
-table_db_config(struct table *table)
-{
- struct dbhandle *handle;
-
- handle = table_db_open2(table);
- if (handle == NULL)
- return 0;
-
- table_db_close2(handle);
- return 1;
-}
-
-static int
-table_db_update(struct table *table)
-{
- struct dbhandle *handle;
-
- handle = table_db_open2(table);
- if (handle == NULL)
- return 0;
-
- table_db_close2(table->t_handle);
- table->t_handle = handle;
- return 1;
-}
-
-static int
-table_db_open(struct table *table)
-{
- table->t_handle = table_db_open2(table);
- if (table->t_handle == NULL)
- return 0;
- return 1;
-}
-
-static void
-table_db_close(struct table *table)
-{
- table_db_close2(table->t_handle);
- table->t_handle = NULL;
-}
-
-static void *
-table_db_open2(struct table *table)
-{
- struct dbhandle *handle;
- struct stat sb;
-
- handle = xcalloc(1, sizeof *handle);
- if (strlcpy(handle->pathname, table->t_config, sizeof handle->pathname)
- >= sizeof handle->pathname)
- goto error;
-
- if (stat(handle->pathname, &sb) == -1)
- goto error;
-
- handle->mtime = sb.st_mtime;
- handle->db = dbopen(table->t_config, O_RDONLY, 0600, DB_HASH, NULL);
- if (handle->db == NULL)
- goto error;
-
- return handle;
-
-error:
- if (handle->db)
- handle->db->close(handle->db);
- free(handle);
- return NULL;
-}
-
-static void
-table_db_close2(void *hdl)
-{
- struct dbhandle *handle = hdl;
- handle->db->close(handle->db);
- free(handle);
-}
-
-static int
-table_db_lookup(struct table *table, enum table_service service, const char *key,
- char **dst)
-{
- struct dbhandle *handle = table->t_handle;
- char *line;
- size_t len = 0;
- int ret;
- int (*match)(const char *, const char *) = NULL;
- size_t i;
- struct stat sb;
-
- if (stat(handle->pathname, &sb) == -1)
- return -1;
-
- /* DB has changed, close and reopen */
- if (sb.st_mtime != handle->mtime) {
- table_db_update(table);
- handle = table->t_handle;
- }
-
- for (i = 0; i < nitems(keycmp); ++i)
- if (keycmp[i].service == service)
- match = keycmp[i].func;
-
- if (match == NULL)
- line = table_db_get_entry(handle, key, &len);
- else
- line = table_db_get_entry_match(handle, key, &len, match);
- if (line == NULL)
- return 0;
-
- ret = 1;
- if (dst)
- *dst = line;
- else
- free(line);
-
- return ret;
-}
-
-static int
-table_db_fetch(struct table *table, enum table_service service, char **dst)
-{
- struct dbhandle *handle = table->t_handle;
- DBT dbk;
- DBT dbd;
- int r;
-
- if (handle->iter == 0)
- r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST);
- else
- r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT);
- handle->iter = 1;
- if (!r) {
- r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST);
- if (!r)
- return 0;
- }
-
- *dst = strdup(dbk.data);
- if (*dst == NULL)
- return -1;
-
- return 1;
-}
-
-
-static char *
-table_db_get_entry_match(void *hdl, const char *key, size_t *len,
- int(*func)(const char *, const char *))
-{
- struct dbhandle *handle = hdl;
- DBT dbk;
- DBT dbd;
- int r;
- char *buf = NULL;
-
- for (r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST); !r;
- r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT)) {
- buf = xmemdup(dbk.data, dbk.size);
- if (func(key, buf)) {
- *len = dbk.size;
- return buf;
- }
- free(buf);
- }
- return NULL;
-}
-
-static char *
-table_db_get_entry(void *hdl, const char *key, size_t *len)
-{
- struct dbhandle *handle = hdl;
- int ret;
- DBT dbk;
- DBT dbv;
- char pkey[LINE_MAX];
-
- /* workaround the stupidity of the DB interface */
- if (strlcpy(pkey, key, sizeof pkey) >= sizeof pkey)
- errx(1, "table_db_get_entry: key too long");
- dbk.data = pkey;
- dbk.size = strlen(pkey) + 1;
-
- if ((ret = handle->db->get(handle->db, &dbk, &dbv, 0)) != 0)
- return NULL;
-
- *len = dbv.size;
-
- return xmemdup(dbv.data, dbv.size);
-}
diff --git a/smtpd/table_getpwnam.c b/smtpd/table_getpwnam.c
deleted file mode 100644
index ccf889be..00000000
--- a/smtpd/table_getpwnam.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/* $OpenBSD: table_getpwnam.c,v 1.12 2018/12/27 14:23:41 eric Exp $ */
-
-/*
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <string.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-
-/* getpwnam(3) backend */
-static int table_getpwnam_config(struct table *);
-static int table_getpwnam_update(struct table *);
-static int table_getpwnam_open(struct table *);
-static int table_getpwnam_lookup(struct table *, enum table_service, const char *,
- char **);
-static void table_getpwnam_close(struct table *);
-
-struct table_backend table_backend_getpwnam = {
- "getpwnam",
- K_USERINFO,
- table_getpwnam_config,
- NULL,
- NULL,
- table_getpwnam_open,
- table_getpwnam_update,
- table_getpwnam_close,
- table_getpwnam_lookup,
-};
-
-
-static int
-table_getpwnam_config(struct table *table)
-{
- if (table->t_config[0])
- return 0;
- return 1;
-}
-
-static int
-table_getpwnam_update(struct table *table)
-{
- return 1;
-}
-
-static int
-table_getpwnam_open(struct table *table)
-{
- return 1;
-}
-
-static void
-table_getpwnam_close(struct table *table)
-{
- return;
-}
-
-static int
-table_getpwnam_lookup(struct table *table, enum table_service kind, const char *key,
- char **dst)
-{
- struct passwd *pw;
-
- if (kind != K_USERINFO)
- return -1;
-
- errno = 0;
- do {
- pw = getpwnam(key);
- } while (pw == NULL && errno == EINTR);
-
- if (pw == NULL) {
- if (errno)
- return -1;
- return 0;
- }
- if (dst == NULL)
- return 1;
-
- if (asprintf(dst, "%d:%d:%s",
- pw->pw_uid,
- pw->pw_gid,
- pw->pw_dir) == -1) {
- *dst = NULL;
- return -1;
- }
-
- return (1);
-}
diff --git a/smtpd/table_proc.c b/smtpd/table_proc.c
deleted file mode 100644
index 44589bd7..00000000
--- a/smtpd/table_proc.c
+++ /dev/null
@@ -1,283 +0,0 @@
-/* $OpenBSD: table_proc.c,v 1.16 2019/10/03 04:51:15 gilles Exp $ */
-
-/*
- * Copyright (c) 2013 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#ifdef HAVE_PATHS_H
-#include <paths.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-struct table_proc_priv {
- pid_t pid;
- struct imsgbuf ibuf;
-};
-
-static struct imsg imsg;
-static size_t rlen;
-static char *rdata;
-
-extern char **environ;
-
-static void
-table_proc_call(struct table_proc_priv *p)
-{
- ssize_t n;
-
- if (imsg_flush(&p->ibuf) == -1) {
- log_warn("warn: table-proc: imsg_flush");
- fatalx("table-proc: exiting");
- }
-
- while (1) {
- if ((n = imsg_get(&p->ibuf, &imsg)) == -1) {
- log_warn("warn: table-proc: imsg_get");
- break;
- }
- if (n) {
- rlen = imsg.hdr.len - IMSG_HEADER_SIZE;
- rdata = imsg.data;
-
- if (imsg.hdr.type != PROC_TABLE_OK) {
- log_warnx("warn: table-proc: bad response");
- break;
- }
- return;
- }
-
- if ((n = imsg_read(&p->ibuf)) == -1 && errno != EAGAIN) {
- log_warn("warn: table-proc: imsg_read");
- break;
- }
-
- if (n == 0) {
- log_warnx("warn: table-proc: pipe closed");
- break;
- }
- }
-
- fatalx("table-proc: exiting");
-}
-
-static void
-table_proc_read(void *dst, size_t len)
-{
- if (len > rlen) {
- log_warnx("warn: table-proc: bad msg len");
- fatalx("table-proc: exiting");
- }
-
- if (dst)
- memmove(dst, rdata, len);
-
- rlen -= len;
- rdata += len;
-}
-
-static void
-table_proc_end(void)
-{
- if (rlen) {
- log_warnx("warn: table-proc: bogus data");
- fatalx("table-proc: exiting");
- }
- imsg_free(&imsg);
-}
-
-/*
- * API
- */
-
-static int
-table_proc_open(struct table *table)
-{
- struct table_proc_priv *priv;
- struct table_open_params op;
- int fd;
-
- priv = xcalloc(1, sizeof(*priv));
-
- fd = fork_proc_backend("table", table->t_config, table->t_name);
- if (fd == -1)
- fatalx("table-proc: exiting");
-
- imsg_init(&priv->ibuf, fd);
-
- memset(&op, 0, sizeof op);
- op.version = PROC_TABLE_API_VERSION;
- (void)strlcpy(op.name, table->t_name, sizeof op.name);
- imsg_compose(&priv->ibuf, PROC_TABLE_OPEN, 0, 0, -1, &op, sizeof op);
-
- table_proc_call(priv);
- table_proc_end();
-
- table->t_handle = priv;
-
- return (1);
-}
-
-static int
-table_proc_update(struct table *table)
-{
- struct table_proc_priv *priv = table->t_handle;
- int r;
-
- imsg_compose(&priv->ibuf, PROC_TABLE_UPDATE, 0, 0, -1, NULL, 0);
-
- table_proc_call(priv);
- table_proc_read(&r, sizeof(r));
- table_proc_end();
-
- return (r);
-}
-
-static void
-table_proc_close(struct table *table)
-{
- struct table_proc_priv *priv = table->t_handle;
-
- imsg_compose(&priv->ibuf, PROC_TABLE_CLOSE, 0, 0, -1, NULL, 0);
- if (imsg_flush(&priv->ibuf) == -1)
- fatal("imsg_flush");
-
- table->t_handle = NULL;
-}
-
-static int
-imsg_add_params(struct ibuf *buf)
-{
- size_t count = 0;
-
- if (imsg_add(buf, &count, sizeof(count)) == -1)
- return (-1);
-
- return (0);
-}
-
-static int
-table_proc_lookup(struct table *table, enum table_service s, const char *k, char **dst)
-{
- struct table_proc_priv *priv = table->t_handle;
- struct ibuf *buf;
- int r;
-
- buf = imsg_create(&priv->ibuf,
- dst ? PROC_TABLE_LOOKUP : PROC_TABLE_CHECK, 0, 0,
- sizeof(s) + strlen(k) + 1);
-
- if (buf == NULL)
- return (-1);
- if (imsg_add(buf, &s, sizeof(s)) == -1)
- return (-1);
- if (imsg_add_params(buf) == -1)
- return (-1);
- if (imsg_add(buf, k, strlen(k) + 1) == -1)
- return (-1);
- imsg_close(&priv->ibuf, buf);
-
- table_proc_call(priv);
- table_proc_read(&r, sizeof(r));
-
- if (r == 1 && dst) {
- if (rlen == 0) {
- log_warnx("warn: table-proc: empty response");
- fatalx("table-proc: exiting");
- }
- if (rdata[rlen - 1] != '\0') {
- log_warnx("warn: table-proc: not NUL-terminated");
- fatalx("table-proc: exiting");
- }
- *dst = strdup(rdata);
- if (*dst == NULL)
- r = -1;
- table_proc_read(NULL, rlen);
- }
-
- table_proc_end();
-
- return (r);
-}
-
-static int
-table_proc_fetch(struct table *table, enum table_service s, char **dst)
-{
- struct table_proc_priv *priv = table->t_handle;
- struct ibuf *buf;
- int r;
-
- buf = imsg_create(&priv->ibuf, PROC_TABLE_FETCH, 0, 0, sizeof(s));
- if (buf == NULL)
- return (-1);
- if (imsg_add(buf, &s, sizeof(s)) == -1)
- return (-1);
- if (imsg_add_params(buf) == -1)
- return (-1);
- imsg_close(&priv->ibuf, buf);
-
- table_proc_call(priv);
- table_proc_read(&r, sizeof(r));
-
- if (r == 1) {
- if (rlen == 0) {
- log_warnx("warn: table-proc: empty response");
- fatalx("table-proc: exiting");
- }
- if (rdata[rlen - 1] != '\0') {
- log_warnx("warn: table-proc: not NUL-terminated");
- fatalx("table-proc: exiting");
- }
- *dst = strdup(rdata);
- if (*dst == NULL)
- r = -1;
- table_proc_read(NULL, rlen);
- }
-
- table_proc_end();
-
- return (r);
-}
-
-struct table_backend table_backend_proc = {
- "proc",
- K_ANY,
- NULL,
- NULL,
- NULL,
- table_proc_open,
- table_proc_update,
- table_proc_close,
- table_proc_lookup,
- table_proc_fetch,
-};
diff --git a/smtpd/table_static.c b/smtpd/table_static.c
deleted file mode 100644
index 8f78ae11..00000000
--- a/smtpd/table_static.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/* $OpenBSD: table_static.c,v 1.32 2018/12/28 14:21:02 eric Exp $ */
-
-/*
- * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <ctype.h>
-#include <errno.h>
-
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <string.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-struct table_static_priv {
- int type;
- struct dict dict;
- void *iter;
-};
-
-/* static backend */
-static int table_static_config(struct table *);
-static int table_static_add(struct table *, const char *, const char *);
-static void table_static_dump(struct table *);
-static int table_static_update(struct table *);
-static int table_static_open(struct table *);
-static int table_static_lookup(struct table *, enum table_service, const char *,
- char **);
-static int table_static_fetch(struct table *, enum table_service, char **);
-static void table_static_close(struct table *);
-
-struct table_backend table_backend_static = {
- "static",
- K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|
- K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST|
- K_STRING|K_REGEX,
- table_static_config,
- table_static_add,
- table_static_dump,
- table_static_open,
- table_static_update,
- table_static_close,
- table_static_lookup,
- table_static_fetch
-};
-
-static struct keycmp {
- enum table_service service;
- int (*func)(const char *, const char *);
-} keycmp[] = {
- { K_DOMAIN, table_domain_match },
- { K_NETADDR, table_netaddr_match },
- { K_MAILADDR, table_mailaddr_match },
- { K_REGEX, table_regex_match },
-};
-
-
-static void
-table_static_priv_free(struct table_static_priv *priv)
-{
- void *p;
-
- while (dict_poproot(&priv->dict, (void **)&p))
- if (p != priv)
- free(p);
- free(priv);
-}
-
-static int
-table_static_priv_add(struct table_static_priv *priv, const char *key, const char *val)
-{
- char lkey[1024];
- void *old, *new = NULL;
-
- if (!lowercase(lkey, key, sizeof lkey)) {
- errno = ENAMETOOLONG;
- return (-1);
- }
-
- if (val) {
- new = strdup(val);
- if (new == NULL)
- return (-1);
- }
-
- /* use priv if value is null, so we can detect duplicate entries */
- old = dict_set(&priv->dict, lkey, new ? new : priv);
- if (old) {
- if (old != priv)
- free(old);
- return (1);
- }
-
- return (0);
-}
-
-static int
-table_static_priv_load(struct table_static_priv *priv, const char *path)
-{
- FILE *fp;
- char *buf = NULL, *p;
- int lineno = 0;
- size_t sz = 0;
- ssize_t flen;
- char *keyp;
- char *valp;
- int ret = 0;
-
- if ((fp = fopen(path, "r")) == NULL) {
- log_warn("%s: fopen", path);
- return 0;
- }
-
- while ((flen = getline(&buf, &sz, fp)) != -1) {
- lineno++;
- if (buf[flen - 1] == '\n')
- buf[--flen] = '\0';
-
- keyp = buf;
- while (isspace((unsigned char)*keyp)) {
- ++keyp;
- --flen;
- }
- if (*keyp == '\0')
- continue;
- while (isspace((unsigned char)keyp[flen - 1]))
- keyp[--flen] = '\0';
- if (*keyp == '#') {
- if (priv->type == T_NONE) {
- keyp++;
- while (isspace((unsigned char)*keyp))
- ++keyp;
- if (!strcmp(keyp, "@list"))
- priv->type = T_LIST;
- }
- continue;
- }
-
- if (priv->type == T_NONE) {
- for (p = keyp; *p; p++) {
- if (*p == ' ' || *p == '\t' || *p == ':') {
- priv->type = T_HASH;
- break;
- }
- }
- if (priv->type == T_NONE)
- priv->type = T_LIST;
- }
-
- if (priv->type == T_LIST) {
- table_static_priv_add(priv, keyp, NULL);
- continue;
- }
-
- /* T_HASH */
- valp = keyp;
- strsep(&valp, " \t:");
- if (valp) {
- while (*valp) {
- if (!isspace((unsigned char)*valp) &&
- !(*valp == ':' &&
- isspace((unsigned char)*(valp + 1))))
- break;
- ++valp;
- }
- if (*valp == '\0')
- valp = NULL;
- }
- if (valp == NULL) {
- log_warnx("%s: invalid map entry line %d",
- path, lineno);
- goto end;
- }
-
- table_static_priv_add(priv, keyp, valp);
- }
-
- if (ferror(fp)) {
- log_warn("%s: getline", path);
- goto end;
- }
-
- /* Accept empty alias files; treat them as hashes */
- if (priv->type == T_NONE)
- priv->type = T_HASH;
-
- ret = 1;
-end:
- free(buf);
- fclose(fp);
- return ret;
-}
-
-static int
-table_static_config(struct table *t)
-{
- struct table_static_priv *priv, *old;
-
- /* already up, and no config file? ok */
- if (t->t_handle && *t->t_config == '\0')
- return 1;
-
- /* new config */
- priv = calloc(1, sizeof(*priv));
- if (priv == NULL)
- return 0;
- priv->type = t->t_type;
- dict_init(&priv->dict);
-
- if (*t->t_config) {
- /* load the config file */
- if (table_static_priv_load(priv, t->t_config) == 0) {
- table_static_priv_free(priv);
- return 0;
- }
- }
-
- if ((old = t->t_handle))
- table_static_priv_free(old);
- t->t_handle = priv;
- t->t_type = priv->type;
-
- return 1;
-}
-
-static int
-table_static_add(struct table *table, const char *key, const char *val)
-{
- struct table_static_priv *priv = table->t_handle;
- int r;
-
- /* cannot add to a table read from a file */
- if (*table->t_config)
- return 0;
-
- if (table->t_type == T_NONE)
- table->t_type = val ? T_HASH : T_LIST;
- else if (table->t_type == T_LIST && val)
- return 0;
- else if (table->t_type == T_HASH && val == NULL)
- return 0;
-
- if (priv == NULL) {
- if (table_static_config(table) == 0)
- return 0;
- priv = table->t_handle;
- }
-
- r = table_static_priv_add(priv, key, val);
- if (r == -1)
- return 0;
- return 1;
-}
-
-static void
-table_static_dump(struct table *table)
-{
- struct table_static_priv *priv = table->t_handle;
- const char *key;
- char *value;
- void *iter;
-
- iter = NULL;
- while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) {
- if (value && (void*)value != (void*)priv)
- log_debug(" \"%s\" -> \"%s\"", key, value);
- else
- log_debug(" \"%s\"", key);
- }
-}
-
-static int
-table_static_update(struct table *table)
-{
- if (table_static_config(table) == 1) {
- log_info("info: Table \"%s\" successfully updated", table->t_name);
- return 1;
- }
-
- log_info("info: Failed to update table \"%s\"", table->t_name);
- return 0;
-}
-
-static int
-table_static_open(struct table *table)
-{
- if (table->t_handle == NULL)
- return table_static_config(table);
- return 1;
-}
-
-static void
-table_static_close(struct table *table)
-{
- struct table_static_priv *priv = table->t_handle;
-
- if (priv)
- table_static_priv_free(priv);
- table->t_handle = NULL;
-}
-
-static int
-table_static_lookup(struct table *table, enum table_service service, const char *key,
- char **dst)
-{
- struct table_static_priv *priv = table->t_handle;
- char *line;
- int ret;
- int (*match)(const char *, const char *) = NULL;
- size_t i;
- void *iter;
- const char *k;
- char *v;
-
- for (i = 0; i < nitems(keycmp); ++i)
- if (keycmp[i].service == service)
- match = keycmp[i].func;
-
- line = NULL;
- iter = NULL;
- ret = 0;
- while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) {
- if (match) {
- if (match(key, k)) {
- line = v;
- ret = 1;
- }
- }
- else {
- if (strcmp(key, k) == 0) {
- line = v;
- ret = 1;
- }
- }
- if (ret)
- break;
- }
-
- if (dst == NULL)
- return ret ? 1 : 0;
-
- if (ret == 0)
- return 0;
-
- *dst = strdup(line);
- if (*dst == NULL)
- return -1;
-
- return 1;
-}
-
-static int
-table_static_fetch(struct table *t, enum table_service service, char **dst)
-{
- struct table_static_priv *priv = t->t_handle;
- const char *k;
-
- if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) {
- priv->iter = NULL;
- if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL))
- return 0;
- }
-
- *dst = strdup(k);
- if (*dst == NULL)
- return -1;
-
- return 1;
-}
diff --git a/smtpd/to.c b/smtpd/to.c
deleted file mode 100644
index 81a1bb54..00000000
--- a/smtpd/to.c
+++ /dev/null
@@ -1,880 +0,0 @@
-/* $OpenBSD: to.c,v 1.44 2019/11/12 20:21:46 gilles Exp $ */
-
-/*
- * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
- * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <imsg.h>
-#include <limits.h>
-#include <inttypes.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static const char *in6addr_to_text(const struct in6_addr *);
-static int alias_is_filter(struct expandnode *, const char *, size_t);
-static int alias_is_username(struct expandnode *, const char *, size_t);
-static int alias_is_address(struct expandnode *, const char *, size_t);
-static int alias_is_filename(struct expandnode *, const char *, size_t);
-static int alias_is_include(struct expandnode *, const char *, size_t);
-static int alias_is_error(struct expandnode *, const char *, size_t);
-
-static int broken_inet_net_pton_ipv6(const char *, void *, size_t);
-
-const char *
-sockaddr_to_text(struct sockaddr *sa)
-{
- static char buf[NI_MAXHOST];
-
- if (getnameinfo(sa, SA_LEN(sa), buf, sizeof(buf), NULL, 0,
- NI_NUMERICHOST))
- return ("(unknown)");
- else
- return (buf);
-}
-
-static const char *
-in6addr_to_text(const struct in6_addr *addr)
-{
- struct sockaddr_in6 sa_in6;
- uint16_t tmp16;
-
- memset(&sa_in6, 0, sizeof(sa_in6));
-#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
- sa_in6.sin6_len = sizeof(sa_in6);
-#endif
- sa_in6.sin6_family = AF_INET6;
- memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr));
-
- /* XXX thanks, KAME, for this ugliness... adopted from route/show.c */
- if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) ||
- IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) {
- memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16));
- sa_in6.sin6_scope_id = ntohs(tmp16);
- sa_in6.sin6_addr.s6_addr[2] = 0;
- sa_in6.sin6_addr.s6_addr[3] = 0;
- }
-
- return (sockaddr_to_text((struct sockaddr *)&sa_in6));
-}
-
-int
-text_to_mailaddr(struct mailaddr *maddr, const char *email)
-{
- char *username;
- char *hostname;
- char buffer[LINE_MAX];
-
- if (strlcpy(buffer, email, sizeof buffer) >= sizeof buffer)
- return 0;
-
- memset(maddr, 0, sizeof *maddr);
-
- username = buffer;
- hostname = strrchr(username, '@');
-
- if (hostname == NULL) {
- if (strlcpy(maddr->user, username, sizeof maddr->user)
- >= sizeof maddr->user)
- return 0;
- }
- else if (username == hostname) {
- *hostname++ = '\0';
- if (strlcpy(maddr->domain, hostname, sizeof maddr->domain)
- >= sizeof maddr->domain)
- return 0;
- }
- else {
- *hostname++ = '\0';
- if (strlcpy(maddr->user, username, sizeof maddr->user)
- >= sizeof maddr->user)
- return 0;
- if (strlcpy(maddr->domain, hostname, sizeof maddr->domain)
- >= sizeof maddr->domain)
- return 0;
- }
-
- return 1;
-}
-
-const char *
-mailaddr_to_text(const struct mailaddr *maddr)
-{
- static char buffer[LINE_MAX];
-
- (void)strlcpy(buffer, maddr->user, sizeof buffer);
- (void)strlcat(buffer, "@", sizeof buffer);
- if (strlcat(buffer, maddr->domain, sizeof buffer) >= sizeof buffer)
- return NULL;
-
- return buffer;
-}
-
-
-const char *
-sa_to_text(const struct sockaddr *sa)
-{
- static char buf[NI_MAXHOST + 5];
- char *p;
-
- buf[0] = '\0';
- p = buf;
-
- if (sa->sa_family == AF_LOCAL)
- (void)strlcpy(buf, "local", sizeof buf);
- else if (sa->sa_family == AF_INET) {
- in_addr_t addr;
-
- addr = ((const struct sockaddr_in *)sa)->sin_addr.s_addr;
- addr = ntohl(addr);
- (void)bsnprintf(p, NI_MAXHOST, "%d.%d.%d.%d",
- (addr >> 24) & 0xff, (addr >> 16) & 0xff,
- (addr >> 8) & 0xff, addr & 0xff);
- }
- else if (sa->sa_family == AF_INET6) {
- const struct sockaddr_in6 *in6;
- const struct in6_addr *in6_addr;
-
- in6 = (const struct sockaddr_in6 *)sa;
- p = buf;
- in6_addr = &in6->sin6_addr;
- (void)bsnprintf(p, NI_MAXHOST, "[%s]", in6addr_to_text(in6_addr));
- }
-
- return (buf);
-}
-
-const char *
-ss_to_text(const struct sockaddr_storage *ss)
-{
- return (sa_to_text((const struct sockaddr*)ss));
-}
-
-const char *
-time_to_text(time_t when)
-{
- struct tm *lt;
- static char buf[40];
- char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
- char *month[] = {"Jan","Feb","Mar","Apr","May","Jun",
- "Jul","Aug","Sep","Oct","Nov","Dec"};
- const char *tz;
- long offset;
-
- lt = localtime(&when);
- if (lt == NULL || when == 0)
- fatalx("time_to_text: localtime");
-
-#if HAVE_STRUCT_TM_TM_GMTOFF
- offset = lt->tm_gmtoff;
- tz = lt->tm_zone;
-#elif defined HAVE_DECL_ALTZONE && defined HAVE_DECL_TIMEZONE
- offset = lt->tm_isdst > 0 ? altzone : timezone;
- tz = lt->tm_isdst > 0 ? tzname[1] : tzname[0];
-#endif
-
- /* We do not use strftime because it is subject to locale substitution*/
- if (!bsnprintf(buf, sizeof(buf),
- "%s, %d %s %d %02d:%02d:%02d %c%02d%02d (%s)",
- day[lt->tm_wday], lt->tm_mday, month[lt->tm_mon],
- lt->tm_year + 1900,
- lt->tm_hour, lt->tm_min, lt->tm_sec,
- offset >= 0 ? '+' : '-',
- abs((int)offset / 3600),
- abs((int)offset % 3600) / 60,
- tz))
- fatalx("time_to_text: bsnprintf");
-
- return buf;
-}
-
-const char *
-duration_to_text(time_t t)
-{
- static char dst[64];
- char buf[64];
- int h, m, s;
- long long d;
-
- if (t == 0) {
- (void)strlcpy(dst, "0s", sizeof dst);
- return (dst);
- }
-
- dst[0] = '\0';
- if (t < 0) {
- (void)strlcpy(dst, "-", sizeof dst);
- t = -t;
- }
-
- s = t % 60;
- t /= 60;
- m = t % 60;
- t /= 60;
- h = t % 24;
- d = t / 24;
-
- if (d) {
- (void)snprintf(buf, sizeof buf, "%lldd", d);
- (void)strlcat(dst, buf, sizeof dst);
- }
- if (h) {
- (void)snprintf(buf, sizeof buf, "%dh", h);
- (void)strlcat(dst, buf, sizeof dst);
- }
- if (m) {
- (void)snprintf(buf, sizeof buf, "%dm", m);
- (void)strlcat(dst, buf, sizeof dst);
- }
- if (s) {
- (void)snprintf(buf, sizeof buf, "%ds", s);
- (void)strlcat(dst, buf, sizeof dst);
- }
-
- return (dst);
-}
-
-int
-text_to_netaddr(struct netaddr *netaddr, const char *s)
-{
- struct sockaddr_storage ss;
- struct sockaddr_in ssin;
- struct sockaddr_in6 ssin6;
- int bits;
- char buf[NI_MAXHOST];
- size_t len;
-
- memset(&ssin, 0, sizeof(struct sockaddr_in));
- memset(&ssin6, 0, sizeof(struct sockaddr_in6));
-
- if (strncasecmp("IPv6:", s, 5) == 0)
- s += 5;
-
- bits = inet_net_pton(AF_INET, s, &ssin.sin_addr,
- sizeof(struct in_addr));
- if (bits != -1) {
- ssin.sin_family = AF_INET;
- memcpy(&ss, &ssin, sizeof(ssin));
-#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
- ss.ss_len = sizeof(struct sockaddr_in);
-#endif
- } else {
- if (s[0] != '[') {
- if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf)
- return 0;
- }
- else {
- s++;
- if (strncasecmp("IPv6:", s, 5) == 0)
- s += 5;
- if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf)
- return 0;
- if (buf[len-1] != ']')
- return 0;
- buf[len-1] = 0;
- }
- bits = inet_net_pton(AF_INET6, buf, &ssin6.sin6_addr,
- sizeof(struct in6_addr));
- if (bits == -1) {
- if (errno != EAFNOSUPPORT)
- return 0;
- bits = broken_inet_net_pton_ipv6(buf, &ssin6.sin6_addr,
- sizeof(struct in6_addr));
- if (bits == -1)
- return 0;
- }
- ssin6.sin6_family = AF_INET6;
- memcpy(&ss, &ssin6, sizeof(ssin6));
-#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
- ss.ss_len = sizeof(struct sockaddr_in6);
-#endif
- }
-
- netaddr->ss = ss;
- netaddr->bits = bits;
- return 1;
-}
-
-int
-text_to_relayhost(struct relayhost *relay, const char *s)
-{
- static const struct schema {
- const char *name;
- int tls;
- uint16_t flags;
- uint16_t port;
- } schemas [] = {
- /*
- * new schemas should be *appended* otherwise the default
- * schema index needs to be updated later in this function.
- */
- { "smtp://", RELAY_TLS_OPPORTUNISTIC, 0, 25 },
- { "smtp+tls://", RELAY_TLS_STARTTLS, 0, 25 },
- { "smtp+notls://", RELAY_TLS_NO, 0, 25 },
- /* need to specify an explicit port for LMTP */
- { "lmtp://", RELAY_TLS_NO, RELAY_LMTP, 0 },
- { "smtps://", RELAY_TLS_SMTPS, 0, 465 }
- };
- const char *errstr = NULL;
- char *p, *q;
- char buffer[1024];
- char *beg, *end;
- size_t i;
- size_t len;
-
- memset(buffer, 0, sizeof buffer);
- if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
- return 0;
-
- for (i = 0; i < nitems(schemas); ++i)
- if (strncasecmp(schemas[i].name, s,
- strlen(schemas[i].name)) == 0)
- break;
-
- if (i == nitems(schemas)) {
- /* there is a schema, but it's not recognized */
- if (strstr(buffer, "://"))
- return 0;
-
- /* no schema, default to smtp:// */
- i = 0;
- p = buffer;
- }
- else
- p = buffer + strlen(schemas[i].name);
-
- relay->tls = schemas[i].tls;
- relay->flags = schemas[i].flags;
- relay->port = schemas[i].port;
-
- /* first, we extract the label if any */
- if ((q = strchr(p, '@')) != NULL) {
- *q = 0;
- if (strlcpy(relay->authlabel, p, sizeof (relay->authlabel))
- >= sizeof (relay->authlabel))
- return 0;
- p = q + 1;
- }
-
- /* then, we extract the mail exchanger */
- beg = end = p;
- if (*beg == '[') {
- if ((end = strchr(beg, ']')) == NULL)
- return 0;
- /* skip ']', it has to be included in the relay hostname */
- ++end;
- len = end - beg;
- }
- else {
- for (end = beg; *end; ++end)
- if (!isalnum((unsigned char)*end) &&
- *end != '_' && *end != '.' && *end != '-')
- break;
- len = end - beg;
- }
- if (len >= sizeof relay->hostname)
- return 0;
- for (i = 0; i < len; ++i)
- relay->hostname[i] = beg[i];
- relay->hostname[i] = 0;
-
- /* finally, we extract the port */
- p = beg + len;
- if (*p == ':') {
- relay->port = strtonum(p+1, 1, IPPORT_HILASTAUTO, &errstr);
- if (errstr)
- return 0;
- }
-
- if (!valid_domainpart(relay->hostname))
- return 0;
- if ((relay->flags & RELAY_LMTP) && (relay->port == 0))
- return 0;
- if (relay->authlabel[0]) {
- /* disallow auth on non-tls scheme. */
- if (relay->tls != RELAY_TLS_STARTTLS &&
- relay->tls != RELAY_TLS_SMTPS)
- return 0;
- relay->flags |= RELAY_AUTH;
- }
-
- return 1;
-}
-
-uint64_t
-text_to_evpid(const char *s)
-{
- uint64_t ulval;
- char *ep;
-
- errno = 0;
- ulval = strtoull(s, &ep, 16);
- if (s[0] == '\0' || *ep != '\0')
- return 0;
- if (errno == ERANGE && ulval == ULLONG_MAX)
- return 0;
- if (ulval == 0)
- return 0;
- return (ulval);
-}
-
-uint32_t
-text_to_msgid(const char *s)
-{
- uint64_t ulval;
- char *ep;
-
- errno = 0;
- ulval = strtoull(s, &ep, 16);
- if (s[0] == '\0' || *ep != '\0')
- return 0;
- if (errno == ERANGE && ulval == ULLONG_MAX)
- return 0;
- if (ulval == 0)
- return 0;
- if (ulval > 0xffffffff)
- return 0;
- return (ulval & 0xffffffff);
-}
-
-const char *
-rule_to_text(struct rule *r)
-{
- static char buf[4096];
-
- memset(buf, 0, sizeof buf);
- (void)strlcpy(buf, "match", sizeof buf);
- if (r->flag_tag) {
- if (r->flag_tag < 0)
- (void)strlcat(buf, " !", sizeof buf);
- (void)strlcat(buf, " tag ", sizeof buf);
- (void)strlcat(buf, r->table_tag, sizeof buf);
- }
-
- if (r->flag_from) {
- if (r->flag_from < 0)
- (void)strlcat(buf, " !", sizeof buf);
- if (r->flag_from_socket)
- (void)strlcat(buf, " from socket", sizeof buf);
- else if (r->flag_from_rdns) {
- (void)strlcat(buf, " from rdns", sizeof buf);
- if (r->table_from) {
- (void)strlcat(buf, " ", sizeof buf);
- (void)strlcat(buf, r->table_from, sizeof buf);
- }
- }
- else if (strcmp(r->table_from, "<anyhost>") == 0)
- (void)strlcat(buf, " from any", sizeof buf);
- else if (strcmp(r->table_from, "<localhost>") == 0)
- (void)strlcat(buf, " from local", sizeof buf);
- else {
- (void)strlcat(buf, " from src ", sizeof buf);
- (void)strlcat(buf, r->table_from, sizeof buf);
- }
- }
-
- if (r->flag_for) {
- if (r->flag_for < 0)
- (void)strlcat(buf, " !", sizeof buf);
- if (strcmp(r->table_for, "<anydestination>") == 0)
- (void)strlcat(buf, " for any", sizeof buf);
- else if (strcmp(r->table_for, "<localnames>") == 0)
- (void)strlcat(buf, " for local", sizeof buf);
- else {
- (void)strlcat(buf, " for domain ", sizeof buf);
- (void)strlcat(buf, r->table_for, sizeof buf);
- }
- }
-
- if (r->flag_smtp_helo) {
- if (r->flag_smtp_helo < 0)
- (void)strlcat(buf, " !", sizeof buf);
- (void)strlcat(buf, " helo ", sizeof buf);
- (void)strlcat(buf, r->table_smtp_helo, sizeof buf);
- }
-
- if (r->flag_smtp_auth) {
- if (r->flag_smtp_auth < 0)
- (void)strlcat(buf, " !", sizeof buf);
- (void)strlcat(buf, " auth", sizeof buf);
- if (r->table_smtp_auth) {
- (void)strlcat(buf, " ", sizeof buf);
- (void)strlcat(buf, r->table_smtp_auth, sizeof buf);
- }
- }
-
- if (r->flag_smtp_starttls) {
- if (r->flag_smtp_starttls < 0)
- (void)strlcat(buf, " !", sizeof buf);
- (void)strlcat(buf, " tls", sizeof buf);
- }
-
- if (r->flag_smtp_mail_from) {
- if (r->flag_smtp_mail_from < 0)
- (void)strlcat(buf, " !", sizeof buf);
- (void)strlcat(buf, " mail-from ", sizeof buf);
- (void)strlcat(buf, r->table_smtp_mail_from, sizeof buf);
- }
-
- if (r->flag_smtp_rcpt_to) {
- if (r->flag_smtp_rcpt_to < 0)
- (void)strlcat(buf, " !", sizeof buf);
- (void)strlcat(buf, " rcpt-to ", sizeof buf);
- (void)strlcat(buf, r->table_smtp_rcpt_to, sizeof buf);
- }
- (void)strlcat(buf, " action ", sizeof buf);
- if (r->reject)
- (void)strlcat(buf, "reject", sizeof buf);
- else
- (void)strlcat(buf, r->dispatcher, sizeof buf);
- return buf;
-}
-
-
-int
-text_to_userinfo(struct userinfo *userinfo, const char *s)
-{
- char buf[PATH_MAX];
- char *p;
- const char *errstr;
-
- memset(buf, 0, sizeof buf);
- p = buf;
- while (*s && *s != ':')
- *p++ = *s++;
- if (*s++ != ':')
- goto error;
-
- if (strlcpy(userinfo->username, buf,
- sizeof userinfo->username) >= sizeof userinfo->username)
- goto error;
-
- memset(buf, 0, sizeof buf);
- p = buf;
- while (*s && *s != ':')
- *p++ = *s++;
- if (*s++ != ':')
- goto error;
- userinfo->uid = strtonum(buf, 0, UID_MAX, &errstr);
- if (errstr)
- goto error;
-
- memset(buf, 0, sizeof buf);
- p = buf;
- while (*s && *s != ':')
- *p++ = *s++;
- if (*s++ != ':')
- goto error;
- userinfo->gid = strtonum(buf, 0, GID_MAX, &errstr);
- if (errstr)
- goto error;
-
- if (strlcpy(userinfo->directory, s,
- sizeof userinfo->directory) >= sizeof userinfo->directory)
- goto error;
-
- return 1;
-
-error:
- return 0;
-}
-
-int
-text_to_credentials(struct credentials *creds, const char *s)
-{
- char *p;
- char buffer[LINE_MAX];
- size_t offset;
-
- p = strchr(s, ':');
- if (p == NULL) {
- creds->username[0] = '\0';
- if (strlcpy(creds->password, s, sizeof creds->password)
- >= sizeof creds->password)
- return 0;
- return 1;
- }
-
- offset = p - s;
-
- memset(buffer, 0, sizeof buffer);
- if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer)
- return 0;
- p = buffer + offset;
- *p = '\0';
-
- if (strlcpy(creds->username, buffer, sizeof creds->username)
- >= sizeof creds->username)
- return 0;
- if (strlcpy(creds->password, p+1, sizeof creds->password)
- >= sizeof creds->password)
- return 0;
-
- return 1;
-}
-
-int
-text_to_expandnode(struct expandnode *expandnode, const char *s)
-{
- size_t l;
-
- l = strlen(s);
- if (alias_is_error(expandnode, s, l) ||
- alias_is_include(expandnode, s, l) ||
- alias_is_filter(expandnode, s, l) ||
- alias_is_filename(expandnode, s, l) ||
- alias_is_address(expandnode, s, l) ||
- alias_is_username(expandnode, s, l))
- return (1);
-
- return (0);
-}
-
-const char *
-expandnode_to_text(struct expandnode *expandnode)
-{
- switch (expandnode->type) {
- case EXPAND_FILTER:
- case EXPAND_FILENAME:
- case EXPAND_INCLUDE:
- case EXPAND_ERROR:
- case EXPAND_USERNAME:
- return expandnode->u.user;
- case EXPAND_ADDRESS:
- return mailaddr_to_text(&expandnode->u.mailaddr);
- case EXPAND_INVALID:
- break;
- }
-
- return NULL;
-}
-
-/******/
-static int
-alias_is_filter(struct expandnode *alias, const char *line, size_t len)
-{
- int v = 0;
-
- if (*line == '"')
- v = 1;
- if (*(line+v) == '|') {
- if (strlcpy(alias->u.buffer, line + v + 1,
- sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer))
- return 0;
- if (v) {
- v = strlen(alias->u.buffer);
- if (v == 0)
- return (0);
- if (alias->u.buffer[v-1] != '"')
- return (0);
- alias->u.buffer[v-1] = '\0';
- }
- alias->type = EXPAND_FILTER;
- return (1);
- }
- return (0);
-}
-
-static int
-alias_is_username(struct expandnode *alias, const char *line, size_t len)
-{
- memset(alias, 0, sizeof *alias);
-
- if (strlcpy(alias->u.user, line,
- sizeof(alias->u.user)) >= sizeof(alias->u.user))
- return 0;
-
- while (*line) {
- if (!isalnum((unsigned char)*line) &&
- *line != '_' && *line != '.' && *line != '-' && *line != '+')
- return 0;
- ++line;
- }
-
- alias->type = EXPAND_USERNAME;
- return 1;
-}
-
-static int
-alias_is_address(struct expandnode *alias, const char *line, size_t len)
-{
- char *domain;
-
- memset(alias, 0, sizeof *alias);
-
- if (len < 3) /* x@y */
- return 0;
-
- domain = strchr(line, '@');
- if (domain == NULL)
- return 0;
-
- /* @ cannot start or end an address */
- if (domain == line || domain == line + len - 1)
- return 0;
-
- /* scan pre @ for disallowed chars */
- *domain++ = '\0';
- (void)strlcpy(alias->u.mailaddr.user, line, sizeof(alias->u.mailaddr.user));
- (void)strlcpy(alias->u.mailaddr.domain, domain,
- sizeof(alias->u.mailaddr.domain));
-
- while (*line) {
- char allowedset[] = "!#$%*/?|^{}`~&'+-=_.";
- if (!isalnum((unsigned char)*line) &&
- strchr(allowedset, *line) == NULL)
- return 0;
- ++line;
- }
-
- while (*domain) {
- char allowedset[] = "-.";
- if (!isalnum((unsigned char)*domain) &&
- strchr(allowedset, *domain) == NULL)
- return 0;
- ++domain;
- }
-
- alias->type = EXPAND_ADDRESS;
- return 1;
-}
-
-static int
-alias_is_filename(struct expandnode *alias, const char *line, size_t len)
-{
- memset(alias, 0, sizeof *alias);
-
- if (*line != '/')
- return 0;
-
- if (strlcpy(alias->u.buffer, line,
- sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer))
- return 0;
- alias->type = EXPAND_FILENAME;
- return 1;
-}
-
-static int
-alias_is_include(struct expandnode *alias, const char *line, size_t len)
-{
- size_t skip;
-
- memset(alias, 0, sizeof *alias);
-
- if (strncasecmp(":include:", line, 9) == 0)
- skip = 9;
- else if (strncasecmp("include:", line, 8) == 0)
- skip = 8;
- else
- return 0;
-
- if (!alias_is_filename(alias, line + skip, len - skip))
- return 0;
-
- alias->type = EXPAND_INCLUDE;
- return 1;
-}
-
-static int
-alias_is_error(struct expandnode *alias, const char *line, size_t len)
-{
- size_t skip;
-
- memset(alias, 0, sizeof *alias);
-
- if (strncasecmp(":error:", line, 7) == 0)
- skip = 7;
- else if (strncasecmp("error:", line, 6) == 0)
- skip = 6;
- else
- return 0;
-
- if (strlcpy(alias->u.buffer, line + skip,
- sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer))
- return 0;
-
- if (strlen(alias->u.buffer) < 5)
- return 0;
-
- /* [45][0-9]{2} [a-zA-Z0-9].* */
- if (alias->u.buffer[3] != ' ' ||
- !isalnum((unsigned char)alias->u.buffer[4]) ||
- (alias->u.buffer[0] != '4' && alias->u.buffer[0] != '5') ||
- !isdigit((unsigned char)alias->u.buffer[1]) ||
- !isdigit((unsigned char)alias->u.buffer[2]))
- return 0;
-
- alias->type = EXPAND_ERROR;
- return 1;
-}
-
-static int
-broken_inet_net_pton_ipv6(const char *src, void *dst, size_t size)
-{
- int ret;
- int bits;
- char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128")];
- char *sep;
- const char *errstr;
-
- if (strlcpy(buf, src, sizeof buf) >= sizeof buf) {
- errno = EMSGSIZE;
- return (-1);
- }
-
- sep = strchr(buf, '/');
- if (sep != NULL)
- *sep++ = '\0';
-
- ret = inet_pton(AF_INET6, buf, dst);
- if (ret != 1)
- return (-1);
-
- if (sep == NULL)
- return 128;
-
- bits = strtonum(sep, 0, 128, &errstr);
- if (errstr)
- return (-1);
-
- return bits;
-}
diff --git a/smtpd/tree.c b/smtpd/tree.c
deleted file mode 100644
index 1d720a59..00000000
--- a/smtpd/tree.c
+++ /dev/null
@@ -1,259 +0,0 @@
-/* $OpenBSD: tree.c,v 1.6 2018/12/23 16:06:24 gilles 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/tree.h>
-
-#include <err.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <limits.h>
-
-#include "tree.h"
-
-struct treeentry {
- SPLAY_ENTRY(treeentry) entry;
- uint64_t id;
- void *data;
-};
-
-static int treeentry_cmp(struct treeentry *, struct treeentry *);
-
-SPLAY_PROTOTYPE(_tree, treeentry, entry, treeentry_cmp);
-
-int
-tree_check(struct tree *t, uint64_t id)
-{
- struct treeentry key;
-
- key.id = id;
- return (SPLAY_FIND(_tree, &t->tree, &key) != NULL);
-}
-
-void *
-tree_set(struct tree *t, uint64_t id, void *data)
-{
- struct treeentry *entry, key;
- char *old;
-
- key.id = id;
- if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL) {
- if ((entry = malloc(sizeof *entry)) == NULL)
- err(1, "tree_set: malloc");
- entry->id = id;
- SPLAY_INSERT(_tree, &t->tree, entry);
- old = NULL;
- t->count += 1;
- } else
- old = entry->data;
-
- entry->data = data;
-
- return (old);
-}
-
-void
-tree_xset(struct tree *t, uint64_t id, void *data)
-{
- struct treeentry *entry;
-
- if ((entry = malloc(sizeof *entry)) == NULL)
- err(1, "tree_xset: malloc");
- entry->id = id;
- entry->data = data;
- if (SPLAY_INSERT(_tree, &t->tree, entry))
- errx(1, "tree_xset(%p, 0x%016"PRIx64 ")", t, id);
- t->count += 1;
-}
-
-void *
-tree_get(struct tree *t, uint64_t id)
-{
- struct treeentry key, *entry;
-
- key.id = id;
- if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL)
- return (NULL);
-
- return (entry->data);
-}
-
-void *
-tree_xget(struct tree *t, uint64_t id)
-{
- struct treeentry key, *entry;
-
- key.id = id;
- if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL)
- errx(1, "tree_get(%p, 0x%016"PRIx64 ")", t, id);
-
- return (entry->data);
-}
-
-void *
-tree_pop(struct tree *t, uint64_t id)
-{
- struct treeentry key, *entry;
- void *data;
-
- key.id = id;
- if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL)
- return (NULL);
-
- data = entry->data;
- SPLAY_REMOVE(_tree, &t->tree, entry);
- free(entry);
- t->count -= 1;
-
- return (data);
-}
-
-void *
-tree_xpop(struct tree *t, uint64_t id)
-{
- struct treeentry key, *entry;
- void *data;
-
- key.id = id;
- if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL)
- errx(1, "tree_xpop(%p, 0x%016" PRIx64 ")", t, id);
-
- data = entry->data;
- SPLAY_REMOVE(_tree, &t->tree, entry);
- free(entry);
- t->count -= 1;
-
- return (data);
-}
-
-int
-tree_poproot(struct tree *t, uint64_t *id, void **data)
-{
- struct treeentry *entry;
-
- entry = SPLAY_ROOT(&t->tree);
- if (entry == NULL)
- return (0);
- if (id)
- *id = entry->id;
- if (data)
- *data = entry->data;
- SPLAY_REMOVE(_tree, &t->tree, entry);
- free(entry);
- t->count -= 1;
-
- return (1);
-}
-
-int
-tree_root(struct tree *t, uint64_t *id, void **data)
-{
- struct treeentry *entry;
-
- entry = SPLAY_ROOT(&t->tree);
- if (entry == NULL)
- return (0);
- if (id)
- *id = entry->id;
- if (data)
- *data = entry->data;
- return (1);
-}
-
-int
-tree_iter(struct tree *t, void **hdl, uint64_t *id, void **data)
-{
- struct treeentry *curr = *hdl;
-
- if (curr == NULL)
- curr = SPLAY_MIN(_tree, &t->tree);
- else
- curr = SPLAY_NEXT(_tree, &t->tree, curr);
-
- if (curr) {
- *hdl = curr;
- if (id)
- *id = curr->id;
- if (data)
- *data = curr->data;
- return (1);
- }
-
- return (0);
-}
-
-int
-tree_iterfrom(struct tree *t, void **hdl, uint64_t k, uint64_t *id, void **data)
-{
- struct treeentry *curr = *hdl, key;
-
- if (curr == NULL) {
- if (k == 0)
- curr = SPLAY_MIN(_tree, &t->tree);
- else {
- key.id = k;
- curr = SPLAY_FIND(_tree, &t->tree, &key);
- if (curr == NULL) {
- SPLAY_INSERT(_tree, &t->tree, &key);
- curr = SPLAY_NEXT(_tree, &t->tree, &key);
- SPLAY_REMOVE(_tree, &t->tree, &key);
- }
- }
- } else
- curr = SPLAY_NEXT(_tree, &t->tree, curr);
-
- if (curr) {
- *hdl = curr;
- if (id)
- *id = curr->id;
- if (data)
- *data = curr->data;
- return (1);
- }
-
- return (0);
-}
-
-void
-tree_merge(struct tree *dst, struct tree *src)
-{
- struct treeentry *entry;
-
- while (!SPLAY_EMPTY(&src->tree)) {
- entry = SPLAY_ROOT(&src->tree);
- SPLAY_REMOVE(_tree, &src->tree, entry);
- if (SPLAY_INSERT(_tree, &dst->tree, entry))
- errx(1, "tree_merge: duplicate");
- }
- dst->count += src->count;
- src->count = 0;
-}
-
-static int
-treeentry_cmp(struct treeentry *a, struct treeentry *b)
-{
- if (a->id < b->id)
- return (-1);
- if (a->id > b->id)
- return (1);
- return (0);
-}
-
-SPLAY_GENERATE(_tree, treeentry, entry, treeentry_cmp);
diff --git a/smtpd/tree.h b/smtpd/tree.h
deleted file mode 100644
index 3d719f09..00000000
--- a/smtpd/tree.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* $OpenBSD: tree.h,v 1.1 2018/12/23 16:06:24 gilles Exp $ */
-
-/*
- * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
- * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _TREE_H_
-#define _TREE_H_
-
-SPLAY_HEAD(_tree, treeentry);
-
-struct tree {
- struct _tree tree;
- size_t count;
-};
-
-
-/* tree.c */
-#define tree_init(t) do { SPLAY_INIT(&((t)->tree)); (t)->count = 0; } while(0)
-#define tree_empty(t) SPLAY_EMPTY(&((t)->tree))
-#define tree_count(t) ((t)->count)
-int tree_check(struct tree *, uint64_t);
-void *tree_set(struct tree *, uint64_t, void *);
-void tree_xset(struct tree *, uint64_t, void *);
-void *tree_get(struct tree *, uint64_t);
-void *tree_xget(struct tree *, uint64_t);
-void *tree_pop(struct tree *, uint64_t);
-void *tree_xpop(struct tree *, uint64_t);
-int tree_poproot(struct tree *, uint64_t *, void **);
-int tree_root(struct tree *, uint64_t *, void **);
-int tree_iter(struct tree *, void **, uint64_t *, void **);
-int tree_iterfrom(struct tree *, void **, uint64_t, uint64_t *, void **);
-void tree_merge(struct tree *, struct tree *);
-
-#endif
diff --git a/smtpd/unpack_dns.c b/smtpd/unpack_dns.c
deleted file mode 100644
index 974d5727..00000000
--- a/smtpd/unpack_dns.c
+++ /dev/null
@@ -1,300 +0,0 @@
-/* $OpenBSD: unpack_dns.c,v 1.1 2018/01/06 07:57:53 sunil Exp $ */
-
-/*
- * Copyright (c) 2011-2014 Eric Faurot <eric@faurot.net>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#ifdef HAVE_ARPA_NAMESER_COMPAT_H
-#include <arpa/nameser_compat.h>
-#endif
-#include <arpa/inet.h>
-
-#include <string.h>
-
-#include "unpack_dns.h"
-
-static int unpack_data(struct unpack *, void *, size_t);
-static int unpack_u16(struct unpack *, uint16_t *);
-static int unpack_u32(struct unpack *, uint32_t *);
-static int unpack_inaddr(struct unpack *, struct in_addr *);
-static int unpack_in6addr(struct unpack *, struct in6_addr *);
-static int unpack_dname(struct unpack *, char *, size_t);
-
-void
-unpack_init(struct unpack *unpack, const char *buf, size_t len)
-{
- unpack->buf = buf;
- unpack->len = len;
- unpack->offset = 0;
- unpack->err = NULL;
-}
-
-int
-unpack_header(struct unpack *p, struct dns_header *h)
-{
- if (unpack_data(p, h, HFIXEDSZ) == -1)
- return (-1);
-
- h->flags = ntohs(h->flags);
- h->qdcount = ntohs(h->qdcount);
- h->ancount = ntohs(h->ancount);
- h->nscount = ntohs(h->nscount);
- h->arcount = ntohs(h->arcount);
-
- return (0);
-}
-
-int
-unpack_query(struct unpack *p, struct dns_query *q)
-{
- unpack_dname(p, q->q_dname, sizeof(q->q_dname));
- unpack_u16(p, &q->q_type);
- unpack_u16(p, &q->q_class);
-
- return (p->err) ? (-1) : (0);
-}
-
-int
-unpack_rr(struct unpack *p, struct dns_rr *rr)
-{
- uint16_t rdlen;
- size_t save_offset;
-
- unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname));
- unpack_u16(p, &rr->rr_type);
- unpack_u16(p, &rr->rr_class);
- unpack_u32(p, &rr->rr_ttl);
- unpack_u16(p, &rdlen);
-
- if (p->err)
- return (-1);
-
- if (p->len - p->offset < rdlen) {
- p->err = "too short";
- return (-1);
- }
-
- save_offset = p->offset;
-
- switch (rr->rr_type) {
-
- case T_CNAME:
- unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname));
- break;
-
- case T_MX:
- unpack_u16(p, &rr->rr.mx.preference);
- unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange));
- break;
-
- case T_NS:
- unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname));
- break;
-
- case T_PTR:
- unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname));
- break;
-
- case T_SOA:
- unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname));
- unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname));
- unpack_u32(p, &rr->rr.soa.serial);
- unpack_u32(p, &rr->rr.soa.refresh);
- unpack_u32(p, &rr->rr.soa.retry);
- unpack_u32(p, &rr->rr.soa.expire);
- unpack_u32(p, &rr->rr.soa.minimum);
- break;
-
- case T_A:
- if (rr->rr_class != C_IN)
- goto other;
- unpack_inaddr(p, &rr->rr.in_a.addr);
- break;
-
- case T_AAAA:
- if (rr->rr_class != C_IN)
- goto other;
- unpack_in6addr(p, &rr->rr.in_aaaa.addr6);
- break;
- default:
- other:
- rr->rr.other.rdata = p->buf + p->offset;
- rr->rr.other.rdlen = rdlen;
- p->offset += rdlen;
- }
-
- if (p->err)
- return (-1);
-
- /* make sure that the advertised rdlen is really ok */
- if (p->offset - save_offset != rdlen)
- p->err = "bad dlen";
-
- return (p->err) ? (-1) : (0);
-}
-
-ssize_t
-dname_expand(const unsigned char *data, size_t len, size_t offset,
- size_t *newoffset, char *dst, size_t max)
-{
- size_t n, count, end, ptr, start;
- ssize_t res;
-
- if (offset >= len)
- return (-1);
-
- res = 0;
- end = start = offset;
-
- for (; (n = data[offset]); ) {
- if ((n & 0xc0) == 0xc0) {
- if (offset + 2 > len)
- return (-1);
- ptr = 256 * (n & ~0xc0) + data[offset + 1];
- if (ptr >= start)
- return (-1);
- if (end < offset + 2)
- end = offset + 2;
- offset = start = ptr;
- continue;
- }
- if (offset + n + 1 > len)
- return (-1);
-
- /* copy n + at offset+1 */
- if (dst != NULL && max != 0) {
- count = (max < n + 1) ? (max) : (n + 1);
- memmove(dst, data + offset, count);
- dst += count;
- max -= count;
- }
- res += n + 1;
- offset += n + 1;
- if (end < offset)
- end = offset;
- }
- if (end < offset + 1)
- end = offset + 1;
-
- if (dst != NULL && max != 0)
- dst[0] = 0;
- if (newoffset)
- *newoffset = end;
- return (res + 1);
-}
-
-char *
-print_dname(const char *_dname, char *buf, size_t max)
-{
- const unsigned char *dname = _dname;
- char *res;
- size_t left, n, count;
-
- if (_dname[0] == 0) {
- (void)strlcpy(buf, ".", max);
- return buf;
- }
-
- res = buf;
- left = max - 1;
- for (n = 0; dname[0] && left; n += dname[0]) {
- count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
- memmove(buf, dname + 1, count);
- dname += dname[0] + 1;
- left -= count;
- buf += count;
- if (left) {
- left -= 1;
- *buf++ = '.';
- }
- }
- buf[0] = 0;
-
- return (res);
-}
-
-static int
-unpack_data(struct unpack *p, void *data, size_t len)
-{
- if (p->err)
- return (-1);
-
- if (p->len - p->offset < len) {
- p->err = "too short";
- return (-1);
- }
-
- memmove(data, p->buf + p->offset, len);
- p->offset += len;
-
- return (0);
-}
-
-static int
-unpack_u16(struct unpack *p, uint16_t *u16)
-{
- if (unpack_data(p, u16, 2) == -1)
- return (-1);
-
- *u16 = ntohs(*u16);
-
- return (0);
-}
-
-static int
-unpack_u32(struct unpack *p, uint32_t *u32)
-{
- if (unpack_data(p, u32, 4) == -1)
- return (-1);
-
- *u32 = ntohl(*u32);
-
- return (0);
-}
-
-static int
-unpack_inaddr(struct unpack *p, struct in_addr *a)
-{
- return (unpack_data(p, a, 4));
-}
-
-static int
-unpack_in6addr(struct unpack *p, struct in6_addr *a6)
-{
- return (unpack_data(p, a6, 16));
-}
-
-static int
-unpack_dname(struct unpack *p, char *dst, size_t max)
-{
- ssize_t e;
-
- if (p->err)
- return (-1);
-
- e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max);
- if (e == -1) {
- p->err = "bad domain name";
- return (-1);
- }
- if (e < 0 || e > MAXDNAME) {
- p->err = "domain name too long";
- return (-1);
- }
-
- return (0);
-}
diff --git a/smtpd/unpack_dns.h b/smtpd/unpack_dns.h
deleted file mode 100644
index 2318a0c5..00000000
--- a/smtpd/unpack_dns.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/* $OpenBSD: unpack_dns.h,v 1.1 2018/01/06 07:57:53 sunil Exp $ */
-
-/*
- * Copyright (c) 2011-2014 Eric Faurot <eric@faurot.net>
- *
- * 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 <netinet/in.h>
-
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-
-struct unpack {
- const char *buf;
- size_t len;
- size_t offset;
- const char *err;
-};
-
-struct dns_header {
- uint16_t id;
- uint16_t flags;
- uint16_t qdcount;
- uint16_t ancount;
- uint16_t nscount;
- uint16_t arcount;
-};
-
-struct dns_query {
- char q_dname[MAXDNAME];
- uint16_t q_type;
- uint16_t q_class;
-};
-
-struct dns_rr {
- char rr_dname[MAXDNAME];
- uint16_t rr_type;
- uint16_t rr_class;
- uint32_t rr_ttl;
- union {
- struct {
- char cname[MAXDNAME];
- } cname;
- struct {
- uint16_t preference;
- char exchange[MAXDNAME];
- } mx;
- struct {
- char nsname[MAXDNAME];
- } ns;
- struct {
- char ptrname[MAXDNAME];
- } ptr;
- struct {
- char mname[MAXDNAME];
- char rname[MAXDNAME];
- uint32_t serial;
- uint32_t refresh;
- uint32_t retry;
- uint32_t expire;
- uint32_t minimum;
- } soa;
- struct {
- struct in_addr addr;
- } in_a;
- struct {
- struct in6_addr addr6;
- } in_aaaa;
- struct {
- uint16_t rdlen;
- const void *rdata;
- } other;
- } rr;
-};
-
-void unpack_init(struct unpack *, const char *, size_t);
-int unpack_header(struct unpack *, struct dns_header *);
-int unpack_rr(struct unpack *, struct dns_rr *);
-int unpack_query(struct unpack *, struct dns_query *);
-char *print_dname(const char *, char *, size_t);
-ssize_t dname_expand(const unsigned char *, size_t, size_t, size_t *,
- char *, size_t);
-
diff --git a/smtpd/util.c b/smtpd/util.c
deleted file mode 100644
index b2b1458c..00000000
--- a/smtpd/util.c
+++ /dev/null
@@ -1,870 +0,0 @@
-/* $OpenBSD: util.c,v 1.151 2020/02/24 23:54:28 millert Exp $ */
-
-/*
- * Copyright (c) 2000,2001 Markus Friedl. All rights reserved.
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <event.h>
-#include <fcntl.h>
-#include <fts.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <libgen.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <limits.h>
-#include <resolv.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-const char *log_in6addr(const struct in6_addr *);
-const char *log_sockaddr(struct sockaddr *);
-static int parse_mailname_file(char *, size_t);
-
-int tracing = 0;
-int foreground_log = 0;
-
-void *
-xmalloc(size_t size)
-{
- void *r;
-
- if ((r = malloc(size)) == NULL)
- fatal("malloc");
-
- return (r);
-}
-
-void *
-xcalloc(size_t nmemb, size_t size)
-{
- void *r;
-
- if ((r = calloc(nmemb, size)) == NULL)
- fatal("calloc");
-
- return (r);
-}
-
-char *
-xstrdup(const char *str)
-{
- char *r;
-
- if ((r = strdup(str)) == NULL)
- fatal("strdup");
-
- return (r);
-}
-
-void *
-xmemdup(const void *ptr, size_t size)
-{
- void *r;
-
- if ((r = malloc(size)) == NULL)
- fatal("malloc");
-
- memmove(r, ptr, size);
-
- return (r);
-}
-
-int
-xasprintf(char **ret, const char *format, ...)
-{
- int r;
- va_list ap;
-
- va_start(ap, format);
- r = vasprintf(ret, format, ap);
- va_end(ap);
- if (r == -1)
- fatal("vasprintf");
-
- return (r);
-}
-
-
-#if !defined(NO_IO)
-int
-io_xprintf(struct io *io, const char *fmt, ...)
-{
- va_list ap;
- int len;
-
- va_start(ap, fmt);
- len = io_vprintf(io, fmt, ap);
- va_end(ap);
- if (len == -1)
- fatal("io_xprintf(%p, %s, ...)", io, fmt);
-
- return len;
-}
-
-int
-io_xprint(struct io *io, const char *str)
-{
- int len;
-
- len = io_print(io, str);
- if (len == -1)
- fatal("io_xprint(%p, %s, ...)", io, str);
-
- return len;
-}
-#endif
-
-char *
-strip(char *s)
-{
- size_t l;
-
- while (isspace((unsigned char)*s))
- s++;
-
- for (l = strlen(s); l; l--) {
- if (!isspace((unsigned char)s[l-1]))
- break;
- s[l-1] = '\0';
- }
-
- return (s);
-}
-
-int
-bsnprintf(char *str, size_t size, const char *format, ...)
-{
- int ret;
- va_list ap;
-
- va_start(ap, format);
- ret = vsnprintf(str, size, format, ap);
- va_end(ap);
- if (ret < 0 || ret >= (int)size)
- return 0;
-
- return 1;
-}
-
-
-int
-ckdir(const char *path, mode_t mode, uid_t owner, gid_t group, int create)
-{
- char mode_str[12];
- int ret;
- struct stat sb;
-
- if (stat(path, &sb) == -1) {
- if (errno != ENOENT || create == 0) {
- log_warn("stat: %s", path);
- return (0);
- }
-
- /* chmod is deferred to avoid umask effect */
- if (mkdir(path, 0) == -1) {
- log_warn("mkdir: %s", path);
- return (0);
- }
-
- if (chown(path, owner, group) == -1) {
- log_warn("chown: %s", path);
- return (0);
- }
-
- if (chmod(path, mode) == -1) {
- log_warn("chmod: %s", path);
- return (0);
- }
-
- if (stat(path, &sb) == -1) {
- log_warn("stat: %s", path);
- return (0);
- }
- }
-
- ret = 1;
-
- /* check if it's a directory */
- if (!S_ISDIR(sb.st_mode)) {
- ret = 0;
- log_warnx("%s is not a directory", path);
- }
-
- /* check that it is owned by owner/group */
- if (sb.st_uid != owner) {
- ret = 0;
- log_warnx("%s is not owned by uid %d", path, owner);
- }
- if (sb.st_gid != group) {
- ret = 0;
- log_warnx("%s is not owned by gid %d", path, group);
- }
-
- /* check permission */
- if ((sb.st_mode & 07777) != mode) {
- ret = 0;
- strmode(mode, mode_str);
- mode_str[10] = '\0';
- log_warnx("%s must be %s (%o)", path, mode_str + 1, mode);
- }
-
- return ret;
-}
-
-int
-rmtree(char *path, int keepdir)
-{
- char *path_argv[2];
- FTS *fts;
- FTSENT *e;
- int ret, depth;
-
- path_argv[0] = path;
- path_argv[1] = NULL;
- ret = 0;
- depth = 0;
-
- fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
- if (fts == NULL) {
- log_warn("fts_open: %s", path);
- return (-1);
- }
-
- while ((e = fts_read(fts)) != NULL) {
- switch (e->fts_info) {
- case FTS_D:
- depth++;
- break;
- case FTS_DP:
- case FTS_DNR:
- depth--;
- if (keepdir && depth == 0)
- continue;
- if (rmdir(e->fts_path) == -1) {
- log_warn("rmdir: %s", e->fts_path);
- ret = -1;
- }
- break;
-
- case FTS_F:
- if (unlink(e->fts_path) == -1) {
- log_warn("unlink: %s", e->fts_path);
- ret = -1;
- }
- }
- }
-
- fts_close(fts);
-
- return (ret);
-}
-
-int
-mvpurge(char *from, char *to)
-{
- size_t n;
- int retry;
- const char *sep;
- char buf[PATH_MAX];
-
- if ((n = strlen(to)) == 0)
- fatalx("to is empty");
-
- sep = (to[n - 1] == '/') ? "" : "/";
- retry = 0;
-
-again:
- (void)snprintf(buf, sizeof buf, "%s%s%u", to, sep, arc4random());
- if (rename(from, buf) == -1) {
- /* ENOTDIR has actually 2 meanings, and incorrect input
- * could lead to an infinite loop. Consider that after
- * 20 tries something is hopelessly wrong.
- */
- if (errno == ENOTEMPTY || errno == EISDIR || errno == ENOTDIR) {
- if ((retry++) >= 20)
- return (-1);
- goto again;
- }
- return -1;
- }
-
- return 0;
-}
-
-
-int
-mktmpfile(void)
-{
- char path[PATH_MAX];
- int fd;
-
- if (!bsnprintf(path, sizeof(path), "%s/smtpd.XXXXXXXXXX",
- PATH_TEMPORARY)) {
- log_warn("snprintf");
- fatal("exiting");
- }
-
- if ((fd = mkstemp(path)) == -1) {
- log_warn("cannot create temporary file %s", path);
- fatal("exiting");
- }
- unlink(path);
- return (fd);
-}
-
-
-/* Close file, signifying temporary error condition (if any) to the caller. */
-int
-safe_fclose(FILE *fp)
-{
- if (ferror(fp)) {
- fclose(fp);
- return 0;
- }
- if (fflush(fp)) {
- fclose(fp);
- if (errno == ENOSPC)
- return 0;
- fatal("safe_fclose: fflush");
- }
- if (fsync(fileno(fp)))
- fatal("safe_fclose: fsync");
- if (fclose(fp))
- fatal("safe_fclose: fclose");
-
- return 1;
-}
-
-int
-hostname_match(const char *hostname, const char *pattern)
-{
- while (*pattern != '\0' && *hostname != '\0') {
- if (*pattern == '*') {
- while (*pattern == '*')
- pattern++;
- while (*hostname != '\0' &&
- tolower((unsigned char)*hostname) !=
- tolower((unsigned char)*pattern))
- hostname++;
- continue;
- }
-
- if (tolower((unsigned char)*pattern) !=
- tolower((unsigned char)*hostname))
- return 0;
- pattern++;
- hostname++;
- }
-
- return (*hostname == '\0' && *pattern == '\0');
-}
-
-int
-mailaddr_match(const struct mailaddr *maddr1, const struct mailaddr *maddr2)
-{
- struct mailaddr m1 = *maddr1;
- struct mailaddr m2 = *maddr2;
- char *p;
-
- /* catchall */
- if (m2.user[0] == '\0' && m2.domain[0] == '\0')
- return 1;
-
- if (m2.domain[0] && !hostname_match(m1.domain, m2.domain))
- return 0;
-
- if (m2.user[0]) {
- /* if address from table has a tag, we must respect it */
- if (strchr(m2.user, *env->sc_subaddressing_delim) == NULL) {
- /* otherwise, strip tag from session address if any */
- p = strchr(m1.user, *env->sc_subaddressing_delim);
- if (p)
- *p = '\0';
- }
- if (strcasecmp(m1.user, m2.user))
- return 0;
- }
- return 1;
-}
-
-int
-valid_localpart(const char *s)
-{
-#define IS_ATEXT(c) (isalnum((unsigned char)(c)) || strchr(MAILADDR_ALLOWED, (c)))
-nextatom:
- if (!IS_ATEXT(*s) || *s == '\0')
- return 0;
- while (*(++s) != '\0') {
- if (*s == '.')
- break;
- if (IS_ATEXT(*s))
- continue;
- return 0;
- }
- if (*s == '.') {
- s++;
- goto nextatom;
- }
- return 1;
-}
-
-int
-valid_domainpart(const char *s)
-{
- struct in_addr ina;
- struct in6_addr ina6;
- char *c, domain[SMTPD_MAXDOMAINPARTSIZE];
- const char *p;
- size_t dlen;
-
- if (*s == '[') {
- if (strncasecmp("[IPv6:", s, 6) == 0)
- p = s + 6;
- else
- p = s + 1;
-
- if (strlcpy(domain, p, sizeof domain) >= sizeof domain)
- return 0;
-
- c = strchr(domain, ']');
- if (!c || c[1] != '\0')
- return 0;
-
- *c = '\0';
-
- if (inet_pton(AF_INET6, domain, &ina6) == 1)
- return 1;
- if (inet_pton(AF_INET, domain, &ina) == 1)
- return 1;
-
- return 0;
- }
-
- if (*s == '\0')
- return 0;
-
- dlen = strlen(s);
- if (dlen >= sizeof domain)
- return 0;
-
- if (s[dlen - 1] == '.')
- return 0;
-
- return res_hnok(s);
-}
-
-#define LABELCHR(c) ((c) == '-' || (c) == '_' || isalpha((unsigned char)(c)) || isdigit((unsigned char)(c)))
-#define LABELMAX 63
-#define DNAMEMAX 253
-
-int
-valid_domainname(const char *str)
-{
- const char *label, *s;
-
- /*
- * Expect a sequence of dot-separated labels, possibly with a trailing
- * dot. The empty string is rejected, as well a single dot.
- */
- for (s = str; *s; s++) {
-
- /* Start of a new label. */
- label = s;
- while (LABELCHR(*s))
- s++;
-
- /* Must have at least one char and at most LABELMAX. */
- if (s == label || s - label > LABELMAX)
- return 0;
-
- /* If last label, stop here. */
- if (*s == '\0')
- break;
-
- /* Expect a dot as label separator or last char. */
- if (*s != '.')
- return 0;
- }
-
- /* Must have at leat one label and no more than DNAMEMAX chars. */
- if (s == str || s - str > DNAMEMAX)
- return 0;
-
- return 1;
-}
-
-int
-valid_smtp_response(const char *s)
-{
- if (strlen(s) < 5)
- return 0;
-
- if ((s[0] < '2' || s[0] > '5') ||
- (s[1] < '0' || s[1] > '9') ||
- (s[2] < '0' || s[2] > '9') ||
- (s[3] != ' '))
- return 0;
-
- return 1;
-}
-
-int
-secure_file(int fd, char *path, char *userdir, uid_t uid, int mayread)
-{
- char buf[PATH_MAX];
- char homedir[PATH_MAX];
- struct stat st;
- char *cp;
-
- if (realpath(path, buf) == NULL)
- return 0;
-
- if (realpath(userdir, homedir) == NULL)
- homedir[0] = '\0';
-
- /* Check the open file to avoid races. */
- if (fstat(fd, &st) == -1 ||
- !S_ISREG(st.st_mode) ||
- st.st_uid != uid ||
- (st.st_mode & (mayread ? 022 : 066)) != 0)
- return 0;
-
- /* For each component of the canonical path, walking upwards. */
- for (;;) {
- if ((cp = dirname(buf)) == NULL)
- return 0;
- (void)strlcpy(buf, cp, sizeof(buf));
-
- if (stat(buf, &st) == -1 ||
- (st.st_uid != 0 && st.st_uid != uid) ||
- (st.st_mode & 022) != 0)
- return 0;
-
- /* We can stop checking after reaching homedir level. */
- if (strcmp(homedir, buf) == 0)
- break;
-
- /*
- * dirname should always complete with a "/" path,
- * but we can be paranoid and check for "." too
- */
- if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
- break;
- }
-
- return 1;
-}
-
-void
-addargs(arglist *args, char *fmt, ...)
-{
- va_list ap;
- char *cp;
- uint nalloc;
- int r;
- char **tmp;
-
- va_start(ap, fmt);
- r = vasprintf(&cp, fmt, ap);
- va_end(ap);
- if (r == -1)
- fatal("addargs: argument too long");
-
- nalloc = args->nalloc;
- if (args->list == NULL) {
- nalloc = 32;
- args->num = 0;
- } else if (args->num+2 >= nalloc)
- nalloc *= 2;
-
- tmp = reallocarray(args->list, nalloc, sizeof(char *));
- if (tmp == NULL)
- fatal("addargs: reallocarray");
- args->list = tmp;
- args->nalloc = nalloc;
- args->list[args->num++] = cp;
- args->list[args->num] = NULL;
-}
-
-int
-lowercase(char *buf, const char *s, size_t len)
-{
- if (len == 0)
- return 0;
-
- if (strlcpy(buf, s, len) >= len)
- return 0;
-
- while (*buf != '\0') {
- *buf = tolower((unsigned char)*buf);
- buf++;
- }
-
- return 1;
-}
-
-int
-uppercase(char *buf, const char *s, size_t len)
-{
- if (len == 0)
- return 0;
-
- if (strlcpy(buf, s, len) >= len)
- return 0;
-
- while (*buf != '\0') {
- *buf = toupper((unsigned char)*buf);
- buf++;
- }
-
- return 1;
-}
-
-void
-xlowercase(char *buf, const char *s, size_t len)
-{
- if (len == 0)
- fatalx("lowercase: len == 0");
-
- if (!lowercase(buf, s, len))
- fatalx("lowercase: truncation");
-}
-
-uint64_t
-generate_uid(void)
-{
- static uint32_t id;
- static uint8_t inited;
- uint64_t uid;
-
- if (!inited) {
- id = arc4random();
- inited = 1;
- }
- while ((uid = ((uint64_t)(id++) << 32 | arc4random())) == 0)
- ;
-
- return (uid);
-}
-
-int
-session_socket_error(int fd)
-{
- int error;
- socklen_t len;
-
- len = sizeof(error);
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1)
- fatal("session_socket_error: getsockopt");
-
- return (error);
-}
-
-const char *
-parse_smtp_response(char *line, size_t len, char **msg, int *cont)
-{
- if (len >= LINE_MAX)
- return "line too long";
-
- if (len > 3) {
- if (msg)
- *msg = line + 4;
- if (cont)
- *cont = (line[3] == '-');
- } else if (len == 3) {
- if (msg)
- *msg = line + 3;
- if (cont)
- *cont = 0;
- } else
- return "line too short";
-
- /* validate reply code */
- if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) ||
- !isdigit((unsigned char)line[2]))
- return "reply code out of range";
-
- return NULL;
-}
-
-static int
-parse_mailname_file(char *hostname, size_t len)
-{
- FILE *fp;
- char *buf = NULL;
- size_t bufsz = 0;
- ssize_t buflen;
-
- if ((fp = fopen(MAILNAME_FILE, "r")) == NULL)
- return 1;
-
- if ((buflen = getline(&buf, &bufsz, fp)) == -1)
- goto error;
-
- if (buf[buflen - 1] == '\n')
- buf[buflen - 1] = '\0';
-
- if (strlcpy(hostname, buf, len) >= len) {
- fprintf(stderr, MAILNAME_FILE " entry too long");
- goto error;
- }
-
- return 0;
-error:
- fclose(fp);
- free(buf);
- return 1;
-}
-
-int
-getmailname(char *hostname, size_t len)
-{
- struct addrinfo hints, *res = NULL;
- int error;
-
- /* Try MAILNAME_FILE first */
- if (parse_mailname_file(hostname, len) == 0)
- return 0;
-
- /* Next, gethostname(3) */
- if (gethostname(hostname, len) == -1) {
- fprintf(stderr, "getmailname: gethostname() failed\n");
- return -1;
- }
-
- if (strchr(hostname, '.') != NULL)
- return 0;
-
- /* Canonicalize if domain part is missing */
- memset(&hints, 0, sizeof hints);
- hints.ai_family = PF_UNSPEC;
- hints.ai_flags = AI_CANONNAME;
- error = getaddrinfo(hostname, NULL, &hints, &res);
- if (error)
- return 0; /* Continue with non-canon hostname */
-
- if (strlcpy(hostname, res->ai_canonname, len) >= len) {
- fprintf(stderr, "hostname too long");
- freeaddrinfo(res);
- return -1;
- }
-
- freeaddrinfo(res);
- return 0;
-}
-
-int
-base64_encode(unsigned char const *src, size_t srclen,
- char *dest, size_t destsize)
-{
- return __b64_ntop(src, srclen, dest, destsize);
-}
-
-int
-base64_decode(char const *src, unsigned char *dest, size_t destsize)
-{
- return __b64_pton(src, dest, destsize);
-}
-
-int
-base64_encode_rfc3548(unsigned char const *src, size_t srclen,
- char *dest, size_t destsize)
-{
- size_t i;
- int ret;
-
- if ((ret = base64_encode(src, srclen, dest, destsize)) == -1)
- return -1;
-
- for (i = 0; i < destsize; ++i) {
- if (dest[i] == '/')
- dest[i] = '_';
- else if (dest[i] == '+')
- dest[i] = '-';
- }
-
- return ret;
-}
-
-void
-log_trace(int mask, const char *emsg, ...)
-{
- va_list ap;
-
- if (tracing & mask) {
- va_start(ap, emsg);
- vlog(LOG_DEBUG, emsg, ap);
- va_end(ap);
- }
-}
-
-void
-log_trace_verbose(int v)
-{
- tracing = v;
-
- /* Set debug logging in log.c */
- log_setverbose(v & TRACE_DEBUG ? 2 : foreground_log);
-}
-
-void
-xclosefrom(int lowfd)
-{
-#if defined HAVE_CLOSEFROM_INT
- if (closefrom(lowfd) == -1)
- err(1, "closefrom");
-#else
- closefrom(lowfd);
-#endif
-}
-
-void
-portable_freeaddrinfo(struct addrinfo *ai)
-{
- struct addrinfo *p;
-
- do {
- p = ai;
- ai = ai->ai_next;
- free(p->ai_canonname);
- free(p);
- } while (ai);
-}
diff --git a/smtpd/waitq.c b/smtpd/waitq.c
deleted file mode 100644
index 082a1e51..00000000
--- a/smtpd/waitq.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* $OpenBSD: waitq.c,v 1.6 2018/05/31 21:06:12 gilles 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 "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/uio.h>
-
-#include <imsg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-
-#include "smtpd.h"
-
-struct waiter {
- TAILQ_ENTRY(waiter) entry;
- void (*cb)(void *, void *, void *);
- void *arg;
-};
-
-struct waitq {
- SPLAY_ENTRY(waitq) entry;
- void *tag;
- TAILQ_HEAD(, waiter) waiters;
-};
-
-static int waitq_cmp(struct waitq *, struct waitq *);
-
-SPLAY_HEAD(waitqtree, waitq);
-SPLAY_PROTOTYPE(waitqtree, waitq, entry, waitq_cmp);
-
-static struct waitqtree waitqs = SPLAY_INITIALIZER(&waitqs);
-
-static int
-waitq_cmp(struct waitq *a, struct waitq *b)
-{
- if (a->tag < b->tag)
- return (-1);
- if (a->tag > b->tag)
- return (1);
- return (0);
-}
-
-SPLAY_GENERATE(waitqtree, waitq, entry, waitq_cmp);
-
-int
-waitq_wait(void *tag, void (*cb)(void *, void *, void *), void *arg)
-{
- struct waitq *wq, key;
- struct waiter *w;
-
- key.tag = tag;
- wq = SPLAY_FIND(waitqtree, &waitqs, &key);
- if (wq == NULL) {
- wq = xmalloc(sizeof *wq);
- wq->tag = tag;
- TAILQ_INIT(&wq->waiters);
- SPLAY_INSERT(waitqtree, &waitqs, wq);
- }
-
- w = xmalloc(sizeof *w);
- w->cb = cb;
- w->arg = arg;
- TAILQ_INSERT_TAIL(&wq->waiters, w, entry);
-
- return (w == TAILQ_FIRST(&wq->waiters));
-}
-
-void
-waitq_run(void *tag, void *result)
-{
- struct waitq *wq, key;
- struct waiter *w;
-
- key.tag = tag;
- wq = SPLAY_FIND(waitqtree, &waitqs, &key);
- SPLAY_REMOVE(waitqtree, &waitqs, wq);
-
- while ((w = TAILQ_FIRST(&wq->waiters))) {
- TAILQ_REMOVE(&wq->waiters, w, entry);
- w->cb(tag, w->arg, result);
- free(w);
- }
- free(wq);
-}