aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--COPYING2
-rw-r--r--INSTALL7
-rw-r--r--Makefile4
-rw-r--r--NEWS6
-rw-r--r--README1
-rwxr-xr-xconfigure14
-rw-r--r--doc/index.html16
-rw-r--r--doc/upgrade.html11
-rw-r--r--package/configure-snippets/configure_case_lines3
-rw-r--r--package/configure-snippets/configure_expand_dirs2
-rw-r--r--package/configure-snippets/configure_generate_configh2
-rw-r--r--package/configure-snippets/configure_generate_make2
-rw-r--r--package/configure-snippets/configure_help_options2
-rw-r--r--package/configure-snippets/configure_init_vars3
-rw-r--r--package/deps.mak13
-rw-r--r--package/info2
-rw-r--r--package/modes1
-rw-r--r--package/targets.mak17
-rw-r--r--src/qmail-smtpc/deps-exe/qmail-smtpc3
-rw-r--r--src/qmail-smtpc/deps-lib/qmailr3
-rw-r--r--src/qmail-smtpc/qmail-smtpc.c11
-rw-r--r--src/qmail-smtpc/qmail-smtpc.h9
-rw-r--r--src/qmail-smtpc/qmailr.h27
-rw-r--r--src/qmail-smtpc/qmailr_error.c38
-rw-r--r--src/qmail-smtpc/qmailr_tcpto.c167
-rw-r--r--src/qmail-smtpc/smtproutes.c212
27 files changed, 556 insertions, 28 deletions
diff --git a/.gitignore b/.gitignore
index 48eb5a0..6277dc2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,9 @@
/*.pc
*.o
-*.a
+*.a.xyzzy
*.lo
-*.so
-*.so.*
+*.so.xyzzy
/config.mak
/src/include/smtpd-starttls-proxy/config.h
/smtpd-starttls-proxy-io
+/qmail-smtpc
diff --git a/COPYING b/COPYING
index 4acd7bb..615a298 100644
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
-Copyright (c) 2021-2025 Laurent Bercot <ska-skaware@skarnet.org>
+Copyright (c) 2021-2026 Laurent Bercot <ska-skaware@skarnet.org>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/INSTALL b/INSTALL
index 281c9ac..15c2da3 100644
--- a/INSTALL
+++ b/INSTALL
@@ -6,10 +6,9 @@ Build Instructions
- A POSIX-compliant C development environment
- GNU make version 3.81 or later
- - skalibs version 2.14.5.0 or later: https://skarnet.org/software/skalibs/
- - s6 version 2.13.3.0 or later: https://skarnet.org/software/s6/
- - (optional but recommended): s6-networking version 2.7.2.0 or later:
- https://skarnet.org/software/s6-networking/
+ - skalibs version 2.14.5.1 or later: https://skarnet.org/software/skalibs/
+ - s6 version 2.14.0.1 or later: https://skarnet.org/software/s6/
+ - s6-networking version 2.7.2.1 or later: https://skarnet.org/software/s6-networking/
This software will run on any operating system that implements
POSIX.1-2024, available at:
diff --git a/Makefile b/Makefile
index a8af4b4..4562eb1 100644
--- a/Makefile
+++ b/Makefile
@@ -24,6 +24,7 @@ PC_TARGETS :=
LIB_DEFS :=
BIN_SYMLINKS :=
TEST_BINS :=
+EXTRA_INSTALL :=
-include config.mak
include package/targets.mak
@@ -42,6 +43,7 @@ endif
lib$(2).pc:
exec env \
+ prefix="$(prefix)" \
library="$(2)" \
includedir="$(includedir)" \
dynlibdir="$(dynlibdir)" \
@@ -123,7 +125,7 @@ ifneq ($(strip $(ALL_BINS)$(SHARED_LIBS)),)
exec $(STRIP) -R .note -R .comment $(ALL_BINS) $(SHARED_LIBS)
endif
-install: install-dynlib install-libexec install-bin install-symlinks install-lib install-include install-pkgconfig
+install: install-dynlib install-libexec install-bin install-symlinks install-lib install-include install-pkgconfig $(EXTRA_INSTALL)
install-dynlib: $(SHARED_LIBS:lib%.$(SHLIB_EXT).xyzzy=$(DESTDIR)$(dynlibdir)/lib%.$(SHLIB_EXT))
install-libexec: $(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%)
install-bin: $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%)
diff --git a/NEWS b/NEWS
index 6cc99dd..382766c 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,11 @@
Changelog for smtpd-starttls-proxy.
+In 0.1.0.0
+----------
+
+ - New binary: qmail-remote.
+
+
In 0.0.2.1
----------
diff --git a/README b/README
index 2564a70..0fc4021 100644
--- a/README
+++ b/README
@@ -22,4 +22,3 @@ STARTTLS on behalf of the server.
Please use the <skaware at list.skarnet.org> mailing-list for
questions about smtpd-starttls-proxy.
-
diff --git a/configure b/configure
index 3bfa226..0640b19 100755
--- a/configure
+++ b/configure
@@ -48,6 +48,8 @@ Optional features:
--enable-pkgconfig Build and install .pc files for pkg-config [disabled]
--enable-slashpackage[=ROOT] assume /package installation at ROOT [disabled]
--enable-absolute-paths hardcode absolute BINDIR/foobar paths in binaries [disabled]
+ --with-qmail-home=DIR qmail installation directory [/var/qmail]
+ --with-qmailr-ids=uid:gid qmail-remote user:group [qmailr:qmail]
EOF
exit 0
@@ -168,7 +170,8 @@ deplibpath=''
vpaths=''
vpathd=''
build=
-
+qmailhome=/var/qmail
+qmailrids=qmailr:qmail
for arg ; do
case "$arg" in
@@ -206,7 +209,8 @@ for arg ; do
--disable-slashpackage) sproot= ; slashpackage=false ;;
--enable-absolute-paths|--enable-absolute-paths=yes) abspath=true ;;
--disable-absolute-paths|--enable-absolute-paths=no) abspath=false ;;
-
+ --with-qmail-home=*) qmailhome=${arg#*=} ;;
+ --with-qmailr-ids=*) qmailrids=${arg#*=} ;;
--enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;;
--enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;;
--host=*|--target=*) target=${arg#*=} ;;
@@ -235,7 +239,7 @@ fi
# Expand installation directories
stripdir prefix
-for i in exec_prefix dynlibdir libexecdir bindir libdir includedir sysconfdir pkgconfdir sysdeps sproot ; do
+for i in exec_prefix dynlibdir libexecdir bindir libdir includedir sysconfdir pkgconfdir sysdeps sproot qmailhome ; do
eval tmp=\${$i}
eval $i=$tmp
stripdir $i
@@ -512,6 +516,8 @@ if $allpic ; then
else
echo "STATIC_LIBS_ARE_PIC :="
fi
+echo "QMAIL_HOME := $qmailhome"
+echo "QMAILR_IDS := $qmailrids"
exec 1>&3 3>&-
echo " ... done."
@@ -544,6 +550,8 @@ else
fi
echo "#define ${package_macro_name}_LIBEXECPREFIX \"$libexecdir/\""
echo "#define ${package_macro_name}_SYSCONFPREFIX \"$sysconfdir/\""
+echo "#undef ${package_macro_name}_QMAIL_HOME"
+echo "#define ${package_macro_name}_QMAIL_HOME \"$qmailhome\""
echo
echo "#endif"
diff --git a/doc/index.html b/doc/index.html
index 89ef1d8..fc355d7 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -34,16 +34,13 @@ inetd-like mail servers that do not already support it.
<li> A POSIX-compliant system with a standard C development environment </li>
<li> GNU make, version 3.81 or later </li>
<li> <a href="//skarnet.org/software/skalibs/">skalibs</a> version
-2.14.5.0 or later. It's a build-time requirement. It's also a run-time
+2.14.5.1 or later. It's a build-time requirement. It's also a run-time
requirement if you link against the shared version of the skalibs
library. </li>
<li> <a href="//skarnet.org/software/s6/">s6</a> version
-2.13.3.0 or later. It's a build-time and run-time requirement. </li>
- <li> Recommended at run-time: <a href="//skarnet.org/software/s6-networking/">s6-networking</a> version
-2.7.2.0 or later. It's not a requirement, but you won't be able
-to do much with smtpd-starttls-proxy if you don't have a suitable
-UCSPI-TLS server tool, and <a href="//skarnet.org/software/s6-networking/">s6-networking</a>
-provides a simple one. </li>
+2.14.0.1 or later. It's a build-time and run-time requirement. </li>
+ <li> <a href="//skarnet.org/software/s6-networking/">s6-networking</a> version
+2.7.2.1 or later. It's a build-time and run-time requirement. </li>
</ul>
<h3> Licensing </h3>
@@ -57,8 +54,8 @@ provides a simple one. </li>
<ul>
<li> The current released version of smtpd-starttls-proxy is
-<a href="smtpd-starttls-proxy-0.0.2.1.tar.gz">0.0.2.1</a>.
-You can access its checksum <a href="smtpd-starttls-proxy-0.0.2.1.tar.gz.sha256">here</a>. </li>
+<a href="smtpd-starttls-proxy-0.1.0.0.tar.gz">0.1.0.0</a>.
+You can access its checksum <a href="smtpd-starttls-proxy-0.1.0.0.tar.gz.sha256">here</a>. </li>
<li> You can checkout a copy of the
<a href="//git.skarnet.org/cgi-bin/cgit.cgi/smtpd-starttls-proxy/">smtpd-starttls-proxy
git repository</a>:
@@ -90,6 +87,7 @@ the previous versions of smtpd-starttls-proxy and the current one. </li>
<ul>
<li><a href="smtpd-starttls-proxy-io.html">The <tt>smtpd-starttls-proxy-io</tt> program</a></li>
+<li><a href="qmail-remote.html">The <tt>qmail-remote</tt> program</a></li>
</ul>
<h2> Related resources </h2>
diff --git a/doc/upgrade.html b/doc/upgrade.html
index b07e8d7..7aa6237 100644
--- a/doc/upgrade.html
+++ b/doc/upgrade.html
@@ -18,6 +18,17 @@
<h1> What has changed in smtpd-starttls-proxy </h1>
+<h2> in 0.1.0.0 </h2>
+
+<ul>
+ <li> <a href="//skarnet.org/software/skalibs/">skalibs</a>
+dependency bumped to 2.14.5.1 </li>
+ <li> <a href="//skarnet.org/software/s6/">s6</a>
+dependency bumped to 2.14.0.1 </li>
+ <li> <a href="//skarnet.org/software/s6-networking/">s6-networking</a>
+dependency bumped to 2.7.2.1 and made mandatory. </li>
+</ul>
+
<h2> in 0.0.2.1 </h2>
<ul>
diff --git a/package/configure-snippets/configure_case_lines b/package/configure-snippets/configure_case_lines
index 8b13789..d0af7a0 100644
--- a/package/configure-snippets/configure_case_lines
+++ b/package/configure-snippets/configure_case_lines
@@ -1 +1,2 @@
-
+ --with-qmail-home=*) qmailhome=${arg#*=} ;;
+ --with-qmailr-ids=*) qmailrids=${arg#*=} ;;
diff --git a/package/configure-snippets/configure_expand_dirs b/package/configure-snippets/configure_expand_dirs
index 8b13789..f9a8b46 100644
--- a/package/configure-snippets/configure_expand_dirs
+++ b/package/configure-snippets/configure_expand_dirs
@@ -1 +1 @@
-
+qmailhome
diff --git a/package/configure-snippets/configure_generate_configh b/package/configure-snippets/configure_generate_configh
index 8b13789..d9bcacf 100644
--- a/package/configure-snippets/configure_generate_configh
+++ b/package/configure-snippets/configure_generate_configh
@@ -1 +1,3 @@
+echo "#undef ${package_macro_name}_QMAIL_HOME"
+echo "#define ${package_macro_name}_QMAIL_HOME \"$qmailhome\""
diff --git a/package/configure-snippets/configure_generate_make b/package/configure-snippets/configure_generate_make
index 8b13789..54fa00c 100644
--- a/package/configure-snippets/configure_generate_make
+++ b/package/configure-snippets/configure_generate_make
@@ -1 +1,3 @@
+echo "QMAIL_HOME := $qmailhome"
+echo "QMAILR_IDS := $qmailrids"
diff --git a/package/configure-snippets/configure_help_options b/package/configure-snippets/configure_help_options
index 8b13789..b439bc5 100644
--- a/package/configure-snippets/configure_help_options
+++ b/package/configure-snippets/configure_help_options
@@ -1 +1,3 @@
+ --with-qmail-home=DIR qmail installation directory [/var/qmail]
+ --with-qmailr-ids=uid:gid qmail-remote user:group [qmailr:qmail]
diff --git a/package/configure-snippets/configure_init_vars b/package/configure-snippets/configure_init_vars
index 8b13789..a30b387 100644
--- a/package/configure-snippets/configure_init_vars
+++ b/package/configure-snippets/configure_init_vars
@@ -1 +1,2 @@
-
+qmailhome=/var/qmail
+qmailrids=qmailr:qmail
diff --git a/package/deps.mak b/package/deps.mak
index 343c0fc..f17b9c0 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -2,8 +2,19 @@
# This file has been generated by tools/gen-deps.sh
#
+src/qmail-smtpc/qmail-smtpc.o src/qmail-smtpc/qmail-smtpc.lo: src/qmail-smtpc/qmail-smtpc.c src/qmail-smtpc/qmailr.h src/include/smtpd-starttls-proxy/config.h
+src/qmail-smtpc/qmailr_error.o src/qmail-smtpc/qmailr_error.lo: src/qmail-smtpc/qmailr_error.c src/include/smtpd-starttls-proxy/config.h
+src/qmail-smtpc/qmailr_tcpto.o src/qmail-smtpc/qmailr_tcpto.lo: src/qmail-smtpc/qmailr_tcpto.c src/qmail-smtpc/qmailr.h src/include/smtpd-starttls-proxy/config.h
+src/qmail-smtpc/smtproutes.o src/qmail-smtpc/smtproutes.lo: src/qmail-smtpc/smtproutes.c src/qmail-smtpc/qmailr.h src/include/smtpd-starttls-proxy/config.h
src/smtpd-starttls-proxy/smtpd-starttls-proxy-io.o src/smtpd-starttls-proxy/smtpd-starttls-proxy-io.lo: src/smtpd-starttls-proxy/smtpd-starttls-proxy-io.c
+ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),)
+libqmailr.a.xyzzy: src/qmail-smtpc/qmailr_error.o src/qmail-smtpc/qmailr_tcpto.o
+else
+libqmailr.a.xyzzy:src/qmail-smtpc/qmailr_error.lo src/qmail-smtpc/qmailr_tcpto.lo
+endif
+qmail-smtpc: EXTRA_LIBS :=
+qmail-smtpc: src/qmail-smtpc/qmail-smtpc.o src/qmail-smtpc/smtproutes.o libqmailr.a.xyzzy -lskarnet
smtpd-starttls-proxy-io: EXTRA_LIBS := ${SOCKET_LIB} ${SYSCLOCK_LIB}
smtpd-starttls-proxy-io: src/smtpd-starttls-proxy/smtpd-starttls-proxy-io.o -lskarnet
-INTERNAL_LIBS :=
+INTERNAL_LIBS := libqmailr.a.xyzzy
diff --git a/package/info b/package/info
index c93dc0c..8c539c9 100644
--- a/package/info
+++ b/package/info
@@ -1,4 +1,4 @@
package=smtpd-starttls-proxy
-version=0.0.2.1
+version=0.1.0.0
category=mail
package_macro_name=SMTPD_STARTTLS_PROXY
diff --git a/package/modes b/package/modes
index 07461a5..e22534f 100644
--- a/package/modes
+++ b/package/modes
@@ -1 +1,2 @@
smtpd-starttls-proxy-io 0755
+qmail-smtpc 0755
diff --git a/package/targets.mak b/package/targets.mak
index 9faea17..c4707f9 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -1,5 +1,20 @@
BIN_TARGETS := \
-smtpd-starttls-proxy-io
+smtpd-starttls-proxy-io \
+qmail-smtpc
LIBEXEC_TARGETS :=
+EXTRA_INSTALL += install-qmailr
+
+QMAILR_UID := $(firstword $(subst :, ,$(QMAILR_IDS)))
+QMAILR_GID := $(lastword $(subst :, ,$(QMAILR_IDS)))
+
+install-qmailr:
+ mkdir -p -- $(QMAIL_HOME)/run/qmail-remote
+ chgrp -- $(QMAILR_GID) $(QMAIL_HOME)/run
+ chmod 02750 $(QMAIL_HOME)/run
+ chown -- $(QMAILR_IDS) $(QMAIL_HOME)/run/qmail-remote
+ chmod 02700 $(QMAIL_HOME)/run/qmail-remote
+ touch -- $(QMAIL_HOME)/run/qmail-remote/tcpto6
+ chown -- $(QMAILR_IDS) $(QMAIL_HOME)/run/qmail-remote
+ chmod 0640 $(QMAIL_HOME)/run/qmail-remote
diff --git a/src/qmail-smtpc/deps-exe/qmail-smtpc b/src/qmail-smtpc/deps-exe/qmail-smtpc
new file mode 100644
index 0000000..98605d9
--- /dev/null
+++ b/src/qmail-smtpc/deps-exe/qmail-smtpc
@@ -0,0 +1,3 @@
+smtproutes.o
+libqmailr.a.xyzzy
+-lskarnet
diff --git a/src/qmail-smtpc/deps-lib/qmailr b/src/qmail-smtpc/deps-lib/qmailr
new file mode 100644
index 0000000..294fde8
--- /dev/null
+++ b/src/qmail-smtpc/deps-lib/qmailr
@@ -0,0 +1,3 @@
+qmailr_error.o
+qmailr_tcpto.o
+-lskarnet
diff --git a/src/qmail-smtpc/qmail-smtpc.c b/src/qmail-smtpc/qmail-smtpc.c
new file mode 100644
index 0000000..b6a7c47
--- /dev/null
+++ b/src/qmail-smtpc/qmail-smtpc.c
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#include <unistd.h>
+
+#include <smtpd-starttls-proxy/config.h>
+#include "qmailr.h"
+
+int main (int argc, char const *const *argv)
+{
+ _exit(0) ;
+}
diff --git a/src/qmail-smtpc/qmail-smtpc.h b/src/qmail-smtpc/qmail-smtpc.h
new file mode 100644
index 0000000..7c178bb
--- /dev/null
+++ b/src/qmail-smtpc/qmail-smtpc.h
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#include <skalibs/cdb.h>
+
+
+/* smtproutes */
+
+extern int smtproutes_init (cdb *) ;
+
diff --git a/src/qmail-smtpc/qmailr.h b/src/qmail-smtpc/qmailr.h
new file mode 100644
index 0000000..6ee9bfc
--- /dev/null
+++ b/src/qmail-smtpc/qmailr.h
@@ -0,0 +1,27 @@
+/* ISC license. */
+
+#include <stdint.h>
+
+#include <skalibs/gccattributes.h>
+#include <skalibs/tai.h>
+
+/* qmailr_error */
+
+extern void qmailr_diev (int, char const *const *, unsigned int) gccattr_noreturn ;
+extern void qmailr_dievsys (char const *const *, unsigned int) gccattr_noreturn ;
+extern void qmailr_die (int, char const *) gccattr_noreturn ;
+extern void qmailr_diesys (char const *) gccattr_noreturn ;
+
+#define qmailr_temp(s) qmailr_die(0, (s))
+#define qmailr_tempv(v, n) qmailr_diev(0, (v), n)
+#define qmailr_tempsys(s) qmailr_diesys(s)
+#define qmailr_tempvsys(v, n) qmailr_dievsys(v, n)
+#define qmailr_perm(s) qmailr_die(1, (s))
+#define qmailr_permv(v, n) qmailr_diev(1, (v), n)
+
+
+/* qmailr_tcpto */
+
+extern int qmailr_tcpto_match (char const *, int) ;
+extern int qmailr_tcpto_update (char const *, int, int) ;
+
diff --git a/src/qmail-smtpc/qmailr_error.c b/src/qmail-smtpc/qmailr_error.c
new file mode 100644
index 0000000..fcdce54
--- /dev/null
+++ b/src/qmail-smtpc/qmailr_error.c
@@ -0,0 +1,38 @@
+/* ISC license. */
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <skalibs/buffer.h>
+
+#include <smtpd-starttls-proxy/config.h>
+
+void qmailr_diev (int permanent, char const *const *v, unsigned int n)
+{
+ buffer_put(buffer_1small, permanent ? "D" : "Z", 1) ;
+ while (n--) buffer_puts(buffer_1small, *v++) ;
+ buffer_putflush(buffer_1small, "\n", 2) ;
+ _exit(0) ;
+}
+
+void qmailr_dievsys (char const *const *v, unsigned int n)
+{
+ char const *se = strerror(errno) ;
+ buffer_put(buffer_1small, "Z", 1) ;
+ while (n--) buffer_puts(buffer_1small, *v++) ;
+ buffer_put(buffer_1small, ": ", 2) ;
+ buffer_puts(buffer_1small, se) ;
+ buffer_putflush(buffer_1small, "\n", 2) ;
+ _exit(0) ;
+}
+
+void qmailr_die (int permanent, char const *msg)
+{
+ qmailr_diev(permanent, &msg, 1) ;
+}
+
+void qmailr_diesys (char const *msg)
+{
+ qmailr_dievsys(&msg, 1) ;
+}
diff --git a/src/qmail-smtpc/qmailr_tcpto.c b/src/qmail-smtpc/qmailr_tcpto.c
new file mode 100644
index 0000000..fc8841b
--- /dev/null
+++ b/src/qmail-smtpc/qmailr_tcpto.c
@@ -0,0 +1,167 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <skalibs/uint32.h>
+#include <skalibs/uint64.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/cdb.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+
+#include <smtpd-starttls-proxy/config.h>
+#include "qmailr.h"
+
+#include <skalibs/posixishard.h>
+
+
+/*
+ tcpto implementation, should be compatible with qmail-tcpto.
+ Assumes the 4 unused bytes at the end of a record are there to
+ accommodate 64-bit time_t. Which we use. But qmail-tcpto does
+ not, so you should patch that before 2038.
+ Has ipv6 support, storing v6 records in a different file.
+ Unlike qmail's tcpto, we assume the records are sorted by IP.
+ This makes it easy to look for a record with bsearch. We
+ keep records sorted with every modification, and we aggressively
+ cut empty ones from the file.
+ For the match function, not sure what is faster between mmapping and
+ simple reading. Currently we mmap to save private/dirty RAM, but
+ that holds the lock longer; it should be ok because we switched to
+ a shared lock for this (unsure why djb didn't).
+*/
+
+static int memcmp4 (void const *a, void const *b)
+{
+ return memcmp(a, b, 4) ;
+}
+
+static int memcmp16 (void const *a, void const *b)
+{
+ return memcmp(a, b, 16) ;
+}
+
+int qmailr_tcpto_match (char const *ip, int is6)
+{
+ char const *file = is6 ? SMTPD_STARTTLS_PROXY_QMAIL_HOME "/run/qmail-remote/tcpto6" : SMTPD_STARTTLS_PROXY_QMAIL_HOME "/queue/lock/tcpto" ;
+ uint32_t iplen = is6 ? 16 : 4 ;
+ uint32_t width = iplen + 12 ;
+ int r = 0 ;
+ char const *p ;
+ cdb c ; /* XXX: not a cdb, we're just using the mmap wrapper */
+ int fd = openc_read(file) ;
+
+ if (fd == -1) return -1 ;
+ if (fd_lock(fd, 0, 0) == -1) goto err ;
+ if (!cdb_init_fromfd(&c, fd)) goto err ;
+ if (c.size % width) goto errproto ;
+ p = bsearch(ip, c.map, c.size / width, width, is6 ? &memcmp16 : &memcmp4) ;
+ if (p)
+ {
+ if (p[iplen] >= 2)
+ {
+ tai when ;
+ uint64_t x ;
+ uint64_unpack(p + iplen + 4, &x) ;
+ tai_u64(&when, x) ;
+ tai_sub(&when, tain_secp(&STAMP), &when) ;
+ r = tai_sec(&when) < ((60 + (getpid() & 31)) << 6) ; /* don't ask me, ask djb */
+ }
+ }
+ cdb_free(&c) ;
+ fd_close(fd) ;
+ return r ;
+
+ errproto:
+ errno = EPROTO ;
+ err:
+ fd_close(fd) ;
+ return -1 ;
+}
+
+int qmailr_tcpto_update (char const *ip, int is6, int problem)
+{
+ char const *file = is6 ? SMTPD_STARTTLS_PROXY_QMAIL_HOME "/run/qmail-remote/tcpto6" : SMTPD_STARTTLS_PROXY_QMAIL_HOME "/queue/lock/tcpto" ;
+ uint32_t iplen = is6 ? 16 : 4 ;
+ uint32_t width = iplen + 12 ;
+ uint32_t n ;
+ char *p = 0 ;
+ struct stat st ;
+ int fdr ;
+ int fdw = openc_create(file) ;
+
+ if (fdw == -1) return 0 ;
+ if (fd_lock(fdw, 1, 0) == -1) goto err ;
+ fdr = openc_read(file) ;
+ if (fdr == -1) goto err ;
+ if (fstat(fdr, &st) == -1) goto err0 ;
+ if (st.st_size % width) goto errproto ;
+ n = st.st_size / width ;
+
+ {
+ char buf[(n+1) * width] ; /* relax, it won't bite */
+ if (n)
+ {
+ if (allread(fdr, buf, st.st_size) < st.st_size) goto err0 ;
+ memset(buf + st.st_size, 0, width) ;
+ p = bsearch(ip, buf, n, width, is6 ? &memcmp16 : &memcmp4) ;
+ if (p)
+ {
+ if (problem)
+ {
+ tai when ;
+ uint64_t x ;
+ uint64_unpack(p + iplen + 4, &x) ;
+ tai_u64(&when, x) ;
+ tai_sub(&when, tain_secp(&STAMP), &when) ;
+ if (tai_sec(&when) < 120) p = 0 ;
+ else
+ {
+ if (++p[iplen] > 10) p[iplen] = 10 ;
+ x = tai_sec(tain_secp(&STAMP)) - TAI_MAGIC ;
+ uint64_pack(p + iplen + 4, x) ;
+ }
+ }
+ else p[iplen] = 0 ;
+ }
+ }
+ else if (problem)
+ {
+ uint64_t x = tai_sec(tain_secp(&STAMP)) - TAI_MAGIC ;
+ p = buf + n++ * width ;
+ memcpy(p, ip, iplen) ;
+ p[iplen] = 1 ;
+ memset(p + iplen + 1, 0, 3) ;
+ uint64_pack(p + iplen + 4, x) ;
+ }
+ fd_close(fdr) ;
+
+ if (p)
+ {
+ for (uint32_t i = 0 ; i < n ; i++)
+ if (!buf[i * width + iplen])
+ memcpy(buf + i * width, buf + --n * width, width) ;
+ if (n)
+ {
+ qsort(buf, n, width, is6 ? &memcmp16 : &memcmp4) ;
+ if (allwrite(fdw, buf, n * width) < n * width) goto err ;
+ }
+ if (ftruncate(fdw, n * width) == -1) goto err ;
+ }
+ }
+
+ fd_close(fdw) ;
+ return 1 ;
+
+ errproto:
+ errno = EPROTO ;
+ err0:
+ fd_close(fdr) ;
+ err:
+ fd_close(fdw) ;
+ return 0 ;
+}
diff --git a/src/qmail-smtpc/smtproutes.c b/src/qmail-smtpc/smtproutes.c
new file mode 100644
index 0000000..08829c9
--- /dev/null
+++ b/src/qmail-smtpc/smtproutes.c
@@ -0,0 +1,212 @@
+/* ISC license. */
+
+#include <skalibs/bsdsnowflake.h>
+
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <skalibs/stat.h>
+#include <skalibs/posixplz.h>
+#include <skalibs/uint16.h>
+#include <skalibs/buffer.h>
+#include <skalibs/cdb.h>
+#include <skalibs/cdbmake.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbtime.h>
+#include <skalibs/djbunix.h>
+
+#include <smtpd-starttls-proxy/config.h>
+#include "qmailr.h"
+
+
+/*
+ qmail-remote uses a "constmap" for smtproutes, which is
+ basically a cdb in RAM. Every instance of qmail-remote
+ parses control/smtproutes to make the constmap.
+ We replace it with a real cdb, stored in the filesystem.
+ It saves CPU (N-1 instances of qmail-remote use the cdb
+ directly) and RAM (the cdb is read-only and shared).
+ The cdb is updated whenever control/smtproutes is newer.
+ We have to lock around the test to avoid several
+ concurrent compilations; the lock feels a bit too big,
+ the crit section can probably be made smaller, but the
+ current behaviour is safe and avoids retry heuristics.
+*/
+
+/*
+ Key to the control/smtproutes parser:
+
+ 0 1 2 3 4 5
+st\ev EOF # \n : 0-9 other
+
+0 h n n
+START END COMMENT START RELAY HOST HOST
+
+1
+COMMENT END COMMENT START COMMENT COMMENT COMMENT
+
+2 n h n n
+HOST X HOST X RELAY HOST HOST
+
+3 ra n ra r n n
+RELAY END RELAY START PORT RELAY RELAY
+
+4 pa pa n
+PORT END X START X PORT X
+
+END=5, X=6
+
+0x08 n push character
+0x10 h compute host length
+0x20 r compute relay length
+0x40 p compute port
+0x80 a add route entry
+*/
+
+static inline uint8_t cclass (char c)
+{
+ switch (c)
+ {
+ case 0 : return 0 ;
+ case '#' : return 1 ;
+ case '\n' : return 2 ;
+ case ':' : return 3 ;
+ case '0' :
+ case '1' :
+ case '2' :
+ case '3' :
+ case '4' :
+ case '5' :
+ case '6' :
+ case '7' :
+ case '8' :
+ case '9' : return 4 ;
+ default : break ;
+ }
+ return 5 ;
+}
+
+static inline char getnext (buffer *b)
+{
+ char c ;
+ ssize_t r = buffer_get(b, &c, 1) ;
+ if (r == -1) qmailr_tempsys("unable to read from control/smtproutes") ;
+ return r ? c : 0 ;
+}
+
+static inline void smtproutes_compile (int fdr, int fdw)
+{
+ static uint8_t const table[5][6] =
+ {
+ { 0x05, 0x01, 0x00, 0x13, 0x0a, 0x0a },
+ { 0x05, 0x01, 0x00, 0x01, 0x01, 0x01 },
+ { 0x06, 0x0a, 0x06, 0x13, 0x0a, 0x0a },
+ { 0xa5, 0x0b, 0xa0, 0x24, 0x0b, 0x0b },
+ { 0xc5, 0x06, 0xc0, 0x06, 0x0c, 0x06 }
+ } ;
+ cdbmaker cm = CDBMAKER_ZERO ;
+ stralloc sa = STRALLOC_ZERO ;
+ char buf[2048] ;
+ buffer b = BUFFER_INIT(&buffer_read, fdr, buf, 2048) ;
+ uint32_t relaypos = 0, relayend = 0 ;
+ uint8_t state = 0 ;
+ if (!cdbmake_start(&cm, fdw)) qmailr_tempsys("Unable to cdbmake_start") ;
+
+ while (state < 5)
+ {
+ char c = getnext(&b) ;
+ uint8_t val = table[state][cclass(c)] ;
+ state = val & 0x07 ;
+ if (val & 0x08)
+ {
+ if (!stralloc_catb(&sa, &c, 1)) qmailr_tempsys("Unable to grow stralloc") ;
+ }
+ if (val & 0x10)
+ {
+ relaypos = sa.len + 1 ;
+ if (!stralloc_catb(&sa, "\0\0\31", 3)) qmailr_tempsys("Unable to grow stralloc") ;
+ }
+ if (val & 0x20)
+ {
+ if (!stralloc_0(&sa)) qmailr_tempsys("Unable to grow stralloc") ;
+ relayend = sa.len ;
+ }
+ if (val & 0x40)
+ {
+ uint16_t port ;
+ if (!stralloc_0(&sa)) qmailr_tempsys("Unable to grow stralloc") ;
+ if (!uint160_scan(sa.s + relayend, &port)) qmailr_temp("Invalid port in control/smtproutes") ;
+ uint16_pack_big(sa.s + relaypos, port) ;
+ }
+ if (val & 0x80)
+ {
+ if (!cdbmake_add(&cm, sa.s, relaypos, sa.s + relaypos, relayend - relaypos))
+ qmailr_tempsys("Unable to cdbmake_add") ;
+ sa.len = 0 ;
+ }
+ }
+ if (state != 5) qmailr_temp("Syntax error in control/smtproutes") ;
+ stralloc_free(&sa) ;
+ if (!cdbmake_finish(&cm)) qmailr_tempsys("Unable to cdbmake_finish") ;
+}
+
+int smtproutes_init (cdb *c)
+{
+ static char const *cdbfile = SMTPD_STARTTLS_PROXY_QMAIL_HOME "/run/qmail-remote/smtproutes.cdb" ;
+ static char const *lckfile = SMTPD_STARTTLS_PROXY_QMAIL_HOME "/run/qmail-remote/smtproutes.lock" ;
+ static char const *txtfile = SMTPD_STARTTLS_PROXY_QMAIL_HOME "/control/smtproutes" ;
+ static size_t const cdblen = sizeof(cdbfile) - 1 ;
+ int fdl = openc_create(lckfile) ;
+ if (fdl == -1) qmailr_tempsys("Unable to open run/qmail-remote/smtproutes.lock") ;
+ if (fd_lock(fdl, 1, 0) == -1) qmailr_tempsys("Unable to lock run/qmail-remote/smtproutes.lock") ;
+
+ int fdc = openc_read(cdbfile) ;
+ if (fdc >= 0)
+ {
+ struct stat stc, str ;
+ if (fstat(fdc, &stc) == -1) qmailr_tempsys("Unable to fstat run/qmail-remote/smtproutes.cdb") ;
+ if (stat(txtfile, &str) == -1)
+ {
+ if (errno != ENOENT) qmailr_tempsys("Unable to fstat control/smtproutes") ;
+ unlink_void(cdbfile) ;
+ fd_close(fdc) ;
+ goto zero ;
+ }
+ if (timespec_cmp(&stc.st_mtim, &str.st_mtim) > 0) goto useit ;
+ fd_close(fdc) ;
+ }
+
+ int fdr = openc_read(txtfile) ;
+ if (fdr == -1)
+ {
+ if (errno != ENOENT) qmailr_tempsys("Unable to open control/smtproutes") ;
+ goto zero ;
+ }
+
+ {
+ char tmp[cdblen + 8] ;
+ memcpy(tmp, cdbfile, cdblen) ;
+ memcpy(tmp + cdblen, ":XXXXXX", 8) ;
+ fdc = mkstemp(tmp) ;
+ if (fdc == -1) qmailr_tempsys("Unable to mkstemp") ;
+ smtproutes_compile(fdr, fdc) ;
+ if (lseek(fdc, 0, SEEK_SET) == -1) qmailr_tempsys("Unable to lseek") ;
+ if (fsync(fdc) == -1) qmailr_tempsys("Unable to fsync run/qmail-remote/smtproutes.cdb") ;
+ fd_close(fdr) ;
+ if (rename(tmp, cdbfile) == -1) unlink_void(tmp) ;
+ }
+
+ useit:
+ if (!cdb_init_fromfd(c, fdc)) qmailr_tempsys("Unable to mmap run/qmail-remote/smtproutes.cdb") ;
+ fd_close(fdc) ;
+ fd_close(fdl) ;
+ return 1 ;
+
+ zero:
+ fd_close(fdl) ;
+ errno = 0 ;
+ return 0 ;
+}