aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2026-02-07 14:08:53 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2026-02-07 14:08:53 +0000
commitde33be8ad62f2c3c2f7c4030b1ebb301a73e0872 (patch)
treed97c4a015881ef52fee4236b07a82737f3e431c4
parent5ca4a0e5b65066f41af7c670e8200b0d0eba41dd (diff)
downloadsmtpd-starttls-proxy-de33be8ad62f2c3c2f7c4030b1ebb301a73e0872.tar.gz
final batch of code, this should be complete!
-rw-r--r--.gitignore4
-rw-r--r--doc/index.html6
-rw-r--r--doc/qmail-remote-io.html53
-rw-r--r--doc/qmail-remote.html172
-rw-r--r--doc/qmail-smtpc.html79
-rw-r--r--package/deps.mak8
-rw-r--r--package/modes1
-rw-r--r--package/targets.mak3
-rw-r--r--src/qmail-remote/deps-exe/qmail-remote-io2
-rw-r--r--src/qmail-remote/deps-lib/qmailr1
-rw-r--r--src/qmail-remote/dns.c57
-rw-r--r--src/qmail-remote/qmail-remote-io.c245
-rw-r--r--src/qmail-remote/qmail-remote.c262
-rw-r--r--src/qmail-remote/qmail-remote.h4
-rw-r--r--src/qmail-remote/qmailr.h35
-rw-r--r--src/qmail-remote/qmailr_error.c33
-rw-r--r--src/qmail-remote/qmailr_smtp.c53
-rw-r--r--src/qmail-remote/qmailr_tls.c2
-rw-r--r--src/qmail-remote/smtproutes.c42
19 files changed, 879 insertions, 183 deletions
diff --git a/.gitignore b/.gitignore
index 1a3076b..653085b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,5 +6,5 @@
/config.mak
/src/include/smtpd-starttls-proxy/config.h
/smtpd-starttls-proxy-io
-/qmail-smtpc
-/qmail-smtpc-io
+/qmail-remote
+/qmail-remote-io
diff --git a/doc/index.html b/doc/index.html
index 8dfae68..3b350ea 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -93,6 +93,12 @@ the previous versions of smtpd-starttls-proxy and the current one. </li>
<li><a href="qmail-remote.html">The <tt>qmail-remote</tt> program</a></li>
</ul>
+<h3> Internal commands </h3>
+
+<ul>
+<li><a href="qmail-remote-io.html">The <tt>qmail-remote-io</tt> internal program</a></li>
+</ul>
+
<h2> Related resources </h2>
<ul>
diff --git a/doc/qmail-remote-io.html b/doc/qmail-remote-io.html
new file mode 100644
index 0000000..bdede26
--- /dev/null
+++ b/doc/qmail-remote-io.html
@@ -0,0 +1,53 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>smtpd-starttls-proxy: the qmail-remote-io internal program</title>
+ <meta name="Description" content="smtpd-starttls-proxy: the qmail-remote-io program" />
+ <meta name="Keywords" content="smtp client qmail qmail-remote" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">smtpd-starttls-proxy</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>qmail-remote-io</tt> internal program </h1>
+
+<p>
+<tt>qmail-remote-io</tt> is a part of the SMTP client implemented by
+<a href="qmail-remote.html">qmail-remote</a>. It manages the entirety
+of the SMTP transaction except <tt>EHLO</tt> and <tt>STARTTLS</tt>.
+</p>
+
+<p>
+<tt>qmail-remote-io</tt> is not meant to be invoked directly. It is
+meant to be executed into by <a href="qmail-remote.html">qmail-remote</a>.
+</p>
+
+<h2 id="details"> Internal details </h2>
+
+<ul>
+ <li> <a href="qmail-remote.html">qmail-remote</a> does the job of finding a
+suitable SMTP server and connecting to it, saying <tt>EHLO</tt> and, if the user
+has defined a <tt>control/trustanchors</tt> file and the server supports it,
+<tt>STARTTLS</tt>. </li>
+ <li> If the connection is not encrypted,
+<a href="qmail-remote.html">qmail-remote</a> execs <tt>qmail-remote-io</tt>
+immediately, and <tt>qmail-remote-io</tt> proceeds with all the rest of the
+SMTP transaction. </li>
+ <li> If the connection is encrypted,
+<a href="qmail-remote.html">qmail-remote</a> execs
+<tt><a href="//skarnet.org/software/s6-networking/s6-tlsc.html">s6-tlsc</a>
+qmail-remote-io</tt>, instead, so any further exchange with the SMTP server
+is protected by TLS. </li>
+ <li> <tt>qmail-remote-io</tt> does the same job in either case: it speaks
+plaintext SMTP. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/qmail-remote.html b/doc/qmail-remote.html
new file mode 100644
index 0000000..1cfc91a
--- /dev/null
+++ b/doc/qmail-remote.html
@@ -0,0 +1,172 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>smtpd-starttls-proxy: the qmail-remote program</title>
+ <meta name="Description" content="smtpd-starttls-proxy: the qmail-remote program" />
+ <meta name="Keywords" content="smtp client qmail qmail-remote" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">smtpd-starttls-proxy</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>qmail-remote</tt> program </h1>
+
+<p>
+<tt>qmail-remote</tt> is an SMTP client, meant to be used as a drop-in
+replacement to the
+<a href="http://qmail.org/man/man8/qmail-remote.html">qmail-remote</a>
+implementation in the original
+<a href="https://cr.yp.to/qmail.html">qmail</a> MTA, its
+<a href="https://netqmail.org/">netqmail</a> distribution, or its modern
+<a href="https://notqmail.org/">notqmail</a> successor.
+</p>
+
+<p>
+ It is not meant to be invoked directly by the user; it only makes sense
+in the context of a qmail, netqmail or notqmail installation.
+</p>
+
+<h2 id="interface"> Interface </h2>
+
+<p>
+ <tt>qmail-remote</tt> follows the exact same interface as the stock
+<a href="http://qmail.org/man/man8/qmail-remote.html">qmail-remote</a>.
+The binary can literally be dropped
+in <tt>/var/qmail/bin</tt>, replacing the previous binary by the same name.
+With certain qmail patches, it can also be used without overwriting anything,
+by running qmail with the QMAILREMOTE environment variable set to the path
+where this version of <tt>qmail-remote</tt> is installed.
+</p>
+
+<h2 id="differences"> Differences with other implementations of qmail-remote </h2>
+
+<p>
+ The main benefits of this <tt>qmail-remote</tt> implementations are the following:
+</p>
+
+<ul>
+ <li> If the underlying OS and <a href="//skarnet.org/software/skalibs/">skalibs</a>
+support IPv6, then this <tt>qmail-remote</tt> does as well, and uses IPv4 and IPv6
+addresses indiscriminately when connecting to an MX. </li>
+ <li> It speaks ESMTP and will use STARTTLS if the server supports it. </li>
+ <li> All its DNS resolutions are done in parallel, which eliminates some
+pathological cases where the original <tt>qmail-remote</tt> can hang around doing
+nothing for a <em>long</em> time. </li>
+</ul>
+
+<h2 id="control"> Control files </h2>
+
+<p>
+ <tt>qmail-remote</tt> uses the following control files in <tt>/var/qmail/control</tt>:
+<p>
+
+<dl>
+ <dt> <tt>me</tt>, <tt>helohost</tt>, <tt>smtproutes</tt>, <tt>timeoutconnect</tt>, <tt>timeoutremote</tt> </dt>
+ <dd> These files are used in the exact same way as in
+<a href="http://qmail.org/man/man8/qmail-remote.html">stock qmail-remote</a>. </dd>
+
+ <dt> <tt>timeoutdns</tt>
+ <dd> Number of seconds will wait for any given DNS resolution to succeed. Default:
+<strong>0</strong>, which means infinite (never time out on a resolution). </dd>
+
+ <dt> <tt>ipme</tt> </dt>
+ <dd> A list of the network IP addresses of the local machine, one per line. These can be
+IPv4 or IPv6, in textual format. These addresses are used to eliminate SMTP loops:
+<tt>qmail-remote</tt> will never connect to these addresses and never deliver mail
+to them. Note that the stock <tt>qmail-remote</tt> automatically detects the local
+IP addresses; this has several problems, one being OS portability, and another
+being that this autodetection is just impossible in IPv6 &mdash; so we
+delegate the listing of local IPs to the user here. Also, the best way to avoid
+SMTP loops remains making sure that your <tt>locals</tt> and <tt>virtualdomains</tt>
+files are correct and up-to-date, so that <tt>qmail-remote</tt> will never be
+invoked on a recipient that should be handled locally. </dd>
+
+ <dt> <tt>trustanchors</tt> </dt>
+ <dd> Contains the path to the certificates for known trust anchors for X.509
+certificate validation. If the path ends with a slash, like <tt>/etc/ssl/certs/</tt>,
+then it is interpreted as a directory containing hashes to the certificates. If
+it does not, like <tt>/etc/ssl/cert.pem</tt>, then it is interpreted as a big
+PEM file containing all the trust anchors. If the file is nonexistent or empty,
+or only contains a newline, then STARTTLS is not attempted. </dd>
+
+ <dt> <tt>clientcert</tt> </dt>
+ <dd> If this file exists and is nonempty, it must contain the path to a client
+certificate. This certificate will be sent to the server during a TLS negotiation.
+This is useful in certain setups requiring client authentication. </dd>
+
+ <dt> <tt>clientkey</tt> </dt>
+ <dd> This file must be used in conjunction with <tt>clientcert</tt>. It contains
+the path to the private key used to sign the client certificate. Note that access
+to the private key file should be as restricted as possible, but the <tt>qmailr</tt>
+user (or whatever user <tt>qmail-smtpc</tt> runs as) must be able to read it. </dd>
+
+ <dt> <tt>tlsstrictness</tt> </dt>
+ <dd> This file contains an integer representing the strictness level expected of
+TLS connections. The default is <strong>0</strong>, and means that an SMTP exchange
+will attempt to use STARTTLS if the <tt>trustanchors</tt> information is provided
+and the server advertises the capability, but will
+proceed in cleartext if the server does not advertise STARTTLS support, or if the
+STARTTLS command fails. <strong>1</strong> means that <tt>qmail-remote</tt> will
+attempt to find a server that supports STARTTLS in order to transmit its e-mail,
+but will fallback to cleartext if it cannot find any. <strong>2</strong> means that
+<tt>qmail-remote</tt> will flat out refuse to send e-mail to servers that do not
+support STARTTLS or fail to set it up. </dd>
+</dl>
+
+<h2 id="implementation"> Implementation notes </h2>
+
+<ul>
+ <li> At installation time, <tt>qmail-remote</tt> creates the
+<tt>/var/qmail/run</tt> directory, to host run-time information, and
+the <tt>/var/qmail/run/qmail-remote</tt> directory, writable by user
+<tt>qmailr</tt>. You can customize the qmail location and users at
+configure time. </li>
+ <li> At run-time, <tt>qmail-remote</tt> stores some information under
+<tt>/var/qmail/run/qmail-remote</tt>:
+ <ul>
+ <li> <tt>smtproutes.cdb</tt> is a cdb file containing the artificial
+SMTP routes, automatically compiled from <tt>/var/qmail/control/smtproutes</tt>
+whenever it changes. <tt>qmail-remote</tt> reads its route information
+from the cdb file. This is a more efficient mechanism than the original
+"constmap" one. </li>
+ <li> <tt>smtproutes.lock</tt>, a lock file used when reading and
+writing <tt>smtproutes.cdb</tt>. </li>
+ <li> <tt>tcpto6</tt>, a binary file hosting connection timeout information
+in a similar way to <tt>/var/qmail/queue/lock/tcpto</tt>, but for IPv6.
+<tt>qmail-remote</tt> reuses the same <tt>tcpto</tt> file as the original
+for IPv4 timeouts, and is still compatible with the
+<a href="http://qmail.org/man/man8/qmail-tcpto.html">qmail-tcpto</a>
+program; however, a new file was necessary for IPv6, and creating it
+under <tt>/var/qmail/queue/lock</tt> was tricky because the qmail queue
+has very precise permissions and it is better to leave it untouched.
+Since <tt>qmail-remote</tt> needed a new directory writable by <tt>qmailr</tt>
+to host its SMTP routes map anyway, storing the <tt>tcpto6</tt> files in the
+same place is a logical decision. </li>
+ </ul> </li>
+ <li> <tt>qmail-remote</tt> uses the
+<a href="https://skarnet.org/software/s6-dns/skadns/">skadns</a> library
+to perform DNS resolutions asynchronously. If you see a weird <tt>skadnsd</tt>
+process running as a child of <tt>qmail-remote</tt>, that's why. </li>
+ <li> The whole SMTP exchange can happen either over a TLS-encrypted connection
+after a STARTTLS command, or over a cleartext connection. To separate TLS
+management from the SMTP client and avoid duplication of code, the SMTP
+exchange is actually handled by a separate binary:
+<a href="qmail-remote-io.html">qmail-remote-io</a>. If your process list
+shows a <tt>qmail-remote-io</tt> process under <tt>qmail-rspawn</tt> instead
+of the regular <tt>qmail-remote</tt>, that is normal, it means that
+<tt>qmail-remote</tt> has found a suitable server and is transmitting its
+data. If there is a
+<a href="https://skarnet.org/software/s6-networking/s6-tlsc-io.html">s6-tlsc-io</a>
+process running as the child of <tt>qmail-remote-io</tt>, it means that the
+connection is happening under TLS. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/qmail-smtpc.html b/doc/qmail-smtpc.html
deleted file mode 100644
index 47ee375..0000000
--- a/doc/qmail-smtpc.html
+++ /dev/null
@@ -1,79 +0,0 @@
-<html>
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
- <meta http-equiv="Content-Language" content="en" />
- <title>smtpd-starttls-proxy: the qmail-smtpc program</title>
- <meta name="Description" content="smtpd-starttls-proxy: the qmail-smtpc program" />
- <meta name="Keywords" content="smtp client qmail qmail-remote" />
- <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
- </head>
-<body>
-
-<p>
-<a href="index.html">smtpd-starttls-proxy</a><br />
-<a href="//skarnet.org/software/">Software</a><br />
-<a href="//skarnet.org/">skarnet.org</a>
-</p>
-
-<h1> The <tt>qmail-smtpc</tt> program </h1>
-
-<p>
-<tt>qmail-smtpc</tt> is an SMTP client, meant to be used as a drop-in
-replacement to
-<a href="http://qmail.org/man/man8/qmail-remote.html">qmail-remote</a>.
-</p>
-
-<p>
- It is not meant to be invoked directly by the user; it only makes sense
-in the context of a qmail, netqmail or notqmail installation.
-</p>
-
-<h2> Interface </h2>
-
-<p>
- <tt>qmail-smtpc</tt> follows the exact same interface as
-<a href="http://qmail.org/man/man8/qmail-remote.html">qmail-remote</a>.
-The binary can literally be renamed <tt>qmail-remote</tt> then dropped
-in <tt>/var/qmail/bin</tt> in place of the stock <tt>qmail-remote</tt>
-program. With some patches, it can also be used by setting the
-QMAILREMOTE environment variable to the path where <tt>qmail-smtpc</tt>
-is installed.
-</p>
-
-<h2> Differences with qmail-remote </h2>
-
-<ul>
- <li> If the underlying OS and <a href="//skarnet.org/software/skalibs/">skalibs</a>
-support IPv6, then <tt>qmail-smtpc</tt> does as well, and use IPv4 and IPv6
-addresses indiscriminately when connecting to an MX. </li>
- <li> It speaks ESMTP and will use STARTTLS if the server supports it. </li>
-</ul>
-
-<h2> Control files </h2>
-
-<p>
- qmail-smtpc uses a few extra control files in <tt>/var/qmail/control</tt>:
-<p>
-
-<dl>
- <dt> <tt>trustanchors</tt> </dt>
- <dd> Contains the path to the certificates for known trust anchors for X.509
-certificate validation. If the path ends with a slash, like <tt>/etc/ssl/certs/</tt>,
-then it is interpreted as a directory containing hashes to the certificates. If
-it does not, like <tt>/etc/ssl/cert.pem</tt>, then it is interpreted as a big
-PEM file containing all the trust anchors. If the file is nonexistent or empty,
-or only contains a newline, then STARTTLS is not attempted. </dd>
- <dt> <tt>clientcert</tt> </dt>
- <dd> If this file exists and is nonempty, it must contain the path to a client
-certificate. This certificate will be sent to the server during a TLS negotiation.
-This is useful in certain setups requiring client authentication. </dd>
- <dt> <tt>clientkey</tt> </dt>
- <dd> This file must be used in conjunction with <tt>clientcert</tt>. It contains
-the path to the private key used to sign the client certificate. Note that access
-to the private key file should be as restricted as possible, but the <tt>qmailr</tt>
-user (or whatever user <tt>qmail-smtpc</tt> runs as) must be able to read it. </dd>
-</dl>
-
-</body>
-</html>
diff --git a/package/deps.mak b/package/deps.mak
index 1821dba..d759f38 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -4,9 +4,11 @@
src/qmail-remote/qmail-remote.h: src/qmail-remote/qmailr.h
src/qmail-remote/dns.o src/qmail-remote/dns.lo: src/qmail-remote/dns.c src/qmail-remote/qmail-remote.h src/qmail-remote/qmailr.h
+src/qmail-remote/qmail-remote-io.o src/qmail-remote/qmail-remote-io.lo: src/qmail-remote/qmail-remote-io.c src/qmail-remote/qmailr.h
src/qmail-remote/qmail-remote.o src/qmail-remote/qmail-remote.lo: src/qmail-remote/qmail-remote.c src/qmail-remote/qmail-remote.h src/qmail-remote/qmailr.h src/include/smtpd-starttls-proxy/config.h
src/qmail-remote/qmailr_control.o src/qmail-remote/qmailr_control.lo: src/qmail-remote/qmailr_control.c src/qmail-remote/qmailr.h
src/qmail-remote/qmailr_error.o src/qmail-remote/qmailr_error.lo: src/qmail-remote/qmailr_error.c src/include/smtpd-starttls-proxy/config.h
+src/qmail-remote/qmailr_smtp.o src/qmail-remote/qmailr_smtp.lo: src/qmail-remote/qmailr_smtp.c src/qmail-remote/qmailr.h
src/qmail-remote/qmailr_tcpto.o src/qmail-remote/qmailr_tcpto.lo: src/qmail-remote/qmailr_tcpto.c src/qmail-remote/qmailr.h src/include/smtpd-starttls-proxy/config.h
src/qmail-remote/qmailr_tls.o src/qmail-remote/qmailr_tls.lo: src/qmail-remote/qmailr_tls.c src/qmail-remote/qmailr.h src/include/smtpd-starttls-proxy/config.h
src/qmail-remote/qmailr_utils.o src/qmail-remote/qmailr_utils.lo: src/qmail-remote/qmailr_utils.c src/qmail-remote/qmailr.h
@@ -14,12 +16,14 @@ src/qmail-remote/smtproutes.o src/qmail-remote/smtproutes.lo: src/qmail-remote/s
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-remote/qmailr_control.o src/qmail-remote/qmailr_error.o src/qmail-remote/qmailr_tcpto.o src/qmail-remote/qmailr_tls.o src/qmail-remote/qmailr_utils.o
+libqmailr.a.xyzzy: src/qmail-remote/qmailr_control.o src/qmail-remote/qmailr_error.o src/qmail-remote/qmailr_smtp.o src/qmail-remote/qmailr_tcpto.o src/qmail-remote/qmailr_tls.o src/qmail-remote/qmailr_utils.o
else
-libqmailr.a.xyzzy:src/qmail-remote/qmailr_control.lo src/qmail-remote/qmailr_error.lo src/qmail-remote/qmailr_tcpto.lo src/qmail-remote/qmailr_tls.lo src/qmail-remote/qmailr_utils.lo
+libqmailr.a.xyzzy:src/qmail-remote/qmailr_control.lo src/qmail-remote/qmailr_error.lo src/qmail-remote/qmailr_smtp.lo src/qmail-remote/qmailr_tcpto.lo src/qmail-remote/qmailr_tls.lo src/qmail-remote/qmailr_utils.lo
endif
qmail-remote: EXTRA_LIBS :=
qmail-remote: src/qmail-remote/qmail-remote.o src/qmail-remote/dns.o src/qmail-remote/smtproutes.o libqmailr.a.xyzzy -lskadns -ls6dns -lskarnet
+qmail-remote-io: EXTRA_LIBS :=
+qmail-remote-io: src/qmail-remote/qmail-remote-io.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 := libqmailr.a.xyzzy
diff --git a/package/modes b/package/modes
index c005314..730999f 100644
--- a/package/modes
+++ b/package/modes
@@ -1,2 +1,3 @@
smtpd-starttls-proxy-io 0755
qmail-remote 0755
+qmail-remote-io 0755
diff --git a/package/targets.mak b/package/targets.mak
index 917dfad..7b116bc 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -1,6 +1,7 @@
BIN_TARGETS := \
smtpd-starttls-proxy-io \
-qmail-remote
+qmail-remote \
+qmail-remote-io
LIBEXEC_TARGETS :=
diff --git a/src/qmail-remote/deps-exe/qmail-remote-io b/src/qmail-remote/deps-exe/qmail-remote-io
new file mode 100644
index 0000000..7dcdb19
--- /dev/null
+++ b/src/qmail-remote/deps-exe/qmail-remote-io
@@ -0,0 +1,2 @@
+libqmailr.a.xyzzy
+-lskarnet
diff --git a/src/qmail-remote/deps-lib/qmailr b/src/qmail-remote/deps-lib/qmailr
index c1095ed..8ba0674 100644
--- a/src/qmail-remote/deps-lib/qmailr
+++ b/src/qmail-remote/deps-lib/qmailr
@@ -1,5 +1,6 @@
qmailr_control.o
qmailr_error.o
+qmailr_smtp.o
qmailr_tcpto.o
qmailr_tls.o
qmailr_utils.o
diff --git a/src/qmail-remote/dns.c b/src/qmail-remote/dns.c
index 4649fc7..fb01a0d 100644
--- a/src/qmail-remote/dns.c
+++ b/src/qmail-remote/dns.c
@@ -51,13 +51,13 @@ static unsigned int use_host_as_mx (skadns_t *a, char const *host, genalloc *mxi
mxipinfo info = MXIPINFO_ZERO ;
s6dns_domain_t q ;
if (!s6dns_domain_fromstring_noqualify_encode(&q, host, strlen(host)))
- qmailr_tempsys("Unable to DNS-encode host domain") ;
+ qmailr_tempusys("DNS-encode host domain") ;
if (!skadns_send_g(a, &info.id4, &q, S6DNS_T_A, deadline, deadline))
- qmailr_tempsys("Unable to send A DNS query") ;
+ qmailr_tempusys("send ", "A", " DNS query") ;
newreqs++ ;
#ifdef SKALIBS_IPV6_ENABLED
if (!skadns_send_g(a, &info.id6, &q, S6DNS_T_AAAA, deadline, deadline))
- qmailr_tempsys("Unable to send AAAA DNS query") ;
+ qmailr_tempusys("send ", "AAAA", " DNS query") ;
newreqs++ ;
#endif
if (!genalloc_catb(mxipinfo, mxip, &info, 1)) dienomem() ;
@@ -67,14 +67,14 @@ static unsigned int use_host_as_mx (skadns_t *a, char const *host, genalloc *mxi
/*
The point of this monster here is to do all the DNS resolutions in parallel,
to avoid compounding network latency. One of the many things that could never
- be done by patching qmail-remote.
+ be done by patching the original qmail-remote.
1 sender + n-1 recipients are given in eaddr.
- loop around CNAME until we get the canonical name, for the n eaddrs
- either lookup the MX for the host then find all the A and AAAAs of all the MXes,
or get the A and AAAAs of the host directly (if smtproutes)
- do not keep the As and AAAAs listed in ipme
- sort the set of IPs by MX preference
- When done, quote all the boxnames in eaddr.
+ When done, addrmangle (i.e. quote if needed) all the boxnames in eaddr.
Shove everything in storage and return the indices:
in eaddrpos for sender+recipients, in mxipind for the IPs to connect to.
@@ -83,19 +83,19 @@ static unsigned int use_host_as_mx (skadns_t *a, char const *host, genalloc *mxi
Also, fuck DNS.
*/
-void dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size_t *eaddrpos, genalloc *mxipind, stralloc *storage, unsigned int timeoutdns, char const *ipme4, unsigned int n4, char const *ipme6, unsigned int n6, uint32_t flags)
+unsigned int dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size_t *eaddrpos, genalloc *mxipind, stralloc *storage, unsigned int timeoutdns, char const *ipme4, unsigned int n4, char const *ipme6, unsigned int n6, uint32_t flags)
{
skadns_t a = SKADNS_ZERO ;
genalloc mxipi = GENALLOC_ZERO ; /* mxipinfo */
- tain deadline ;
unsigned int pending = 0 ;
- uint16_t mxn = 0 ;
+ unsigned int mxn = 0 ;
uint16_t mxid = UINT16_MAX ;
+ tain deadline ;
cnameinfo cnames[n] ;
tain_addsec_g(&deadline, timeoutdns) ;
if (!skadns_startf_g(&a, &deadline))
- qmailr_tempsys("Unable to start asynchronous DNS helper") ;
+ qmailr_tempusys("start asynchronous DNS helper") ;
for (unsigned int i = 0 ; i < n ; i++)
{
@@ -108,9 +108,9 @@ void dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size
cnames[i].atpos = at - eaddr[i] ;
if (!stralloc_catb(&cnames[i].sa, at+1, len)) dienomem() ;
if (!s6dns_domain_fromstring_noqualify_encode(&q, at+1, len))
- qmailr_tempsys("Unable to DNS-encode recipient domain") ;
+ qmailr_tempusys("DNS-encode recipient domain") ;
if (!skadns_send_g(&a, &cnames[i].id, &q, S6DNS_T_CNAME, &deadline, &deadline))
- qmailr_tempsys("Unable to send CNAME DNS query") ;
+ qmailr_tempusys("send ", "CNAME", " DNS query") ;
cnames[i].count = 1 ;
pending++ ;
}
@@ -126,9 +126,9 @@ void dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size
{
s6dns_domain_t q ;
if (!s6dns_domain_fromstring_noqualify_encode(&q, host, strlen(host)))
- qmailr_tempsys("Unable to DNS-encode host domain") ;
+ qmailr_tempusys("DNS-encode host domain") ;
if (!skadns_send_g(&a, &mxid, &q, S6DNS_T_MX, &deadline, &deadline))
- qmailr_tempsys("Unable to send MX DNS query") ;
+ qmailr_tempusys("send ", "MX", " DNS query") ;
pending++ ;
}
else
@@ -142,10 +142,10 @@ void dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size
uint16_t *ids ;
iopause_fd x = { .fd = skadns_fd(&a), .events = IOPAUSE_READ } ;
int r = iopause_g(&x, 1, &deadline) ;
- if (r == -1) qmailr_tempsys("Unable to iopause") ;
+ if (r == -1) qmailr_tempusys("iopause") ;
if (!r) qmailr_tempsys("Timed out waiting for DNS") ;
r = skadns_update(&a) ;
- if (r == -1) qmailr_tempsys("Unable to read DNS answers") ;
+ if (r == -1) qmailr_tempusys("read DNS answers") ;
ids = genalloc_s(uint16_t, &a.list) ;
for (size_t j = 0 ; j < genalloc_len(uint16_t, &a.list) ; j++)
{
@@ -161,8 +161,8 @@ void dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size
if (r == -1) qmailr_tempsys("DNS packet parsing error") ;
if (!r)
{
- if (errno == EBUSY || errno == EIO) qmailr_temp("Temporary DNS error while resolving MX") ;
- else qmailr_perm("DNS CNAME resolution error") ;
+ if (errno == EBUSY || errno == EIO) qmailr_temp("Temporary DNS error while resolving ", "MX") ;
+ else qmailr_perm("DNS ", "CNAME", " resolution error") ;
}
skadns_release(&a, ids[j]) ;
pending-- ;
@@ -178,11 +178,11 @@ void dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size
mxipinfo *p = genalloc_s(mxipinfo, &mxipi) + i ;
p->ip4 = p->ip6 = stralloc_zero ;
if (!skadns_send_g(&a, &p->id4, &mxs[i].exchange, S6DNS_T_A, &deadline, &deadline))
- qmailr_tempsys("Unable to send A DNS query") ;
+ qmailr_tempusys("send ", "A", " DNS query") ;
pending++ ;
#ifdef SKALIBS_IPV6_ENABLED
if (!skadns_send_g(&a, &p->id6, &mxs[i].exchange, S6DNS_T_AAAA, &deadline, &deadline))
- qmailr_tempsys("Unable to send AAAA DNS query") ;
+ qmailr_tempusys("send ", "AAAA", " DNS query") ;
pending++ ;
#endif
}
@@ -204,8 +204,8 @@ void dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size
if (r == -1) qmailr_tempsys("DNS packet parsing error") ;
if (!r)
{
- if (errno == EBUSY || errno == EIO) qmailr_temp("Temporary DNS error while resolving CNAME") ;
- else qmailr_perm("DNS CNAME resolution error") ;
+ if (errno == EBUSY || errno == EIO) qmailr_temp("Temporary DNS error while resolving ", "CNAME") ;
+ else qmailr_perm("DNS ", "CNAME", " resolution error") ;
}
skadns_release(&a, ids[j]) ;
pending-- ;
@@ -214,14 +214,14 @@ void dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size
s6dns_domain_t *domain = genalloc_s(s6dns_domain_t, &dlist.ds) ;
if (cnames[i].count++ >= 100) qmailr_temp("DNS CNAME loop") ;
if (!skadns_send_g(&a, &cnames[i].id, domain, S6DNS_T_CNAME, &deadline, &deadline))
- qmailr_tempsys("Unable to send CNAME DNS query") ;
+ qmailr_tempusys("send ", "CNAME", " DNS query") ;
pending++ ;
if (!stralloc_ready(&cnames[i].sa, 256)) dienomem() ;
s6dns_domain_decode(domain) ;
cnames[i].sa.len = s6dns_domain_tostring(cnames[i].sa.s, 256, domain) ;
genalloc_free(s6dns_domain_t, &dlist.ds) ;
}
- else cnames[i].id = UINT16_MAX ; /* that's the canonical host in cnames[i].sa */
+ else cnames[i].id = UINT16_MAX ; /* we have the canonical host in cnames[i].sa */
continue ;
}
@@ -235,8 +235,8 @@ void dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size
if (r == -1) qmailr_tempsys("DNS packet parsing error") ;
if (!r)
{
- if (errno == EBUSY || errno == EIO) qmailr_temp("Temporary DNS error while resolving A") ;
- else qmailr_perm("DNS A resolution error") ;
+ if (errno == EBUSY || errno == EIO) qmailr_temp("Temporary DNS error while resolving ", "A") ;
+ else qmailr_perm("DNS ", "A", " resolution error") ;
}
skadns_release(&a, ids[j]) ;
pending-- ;
@@ -259,8 +259,8 @@ void dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size
if (r == -1) qmailr_tempsys("DNS packet parsing error") ;
if (!r)
{
- if (errno == EBUSY || errno == EIO) qmailr_temp("Temporary DNS error while resolving AAAA") ;
- else qmailr_perm("DNS AAAA resolution error") ;
+ if (errno == EBUSY || errno == EIO) qmailr_temp("Temporary DNS error while resolving ", "AAAA") ;
+ else qmailr_perm("DNS ", "AAAA", " resolution error") ;
}
skadns_release(&a, ids[j]) ;
pending-- ;
@@ -279,7 +279,7 @@ void dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size
}
}
}
- skadns_end(&a) ; /* we done buddy */
+ skadns_end(&a) ; /* we done, buddy */
for (unsigned int i = 0 ; i < n ; i++)
{
@@ -312,4 +312,5 @@ void dns_stuff (char const *host, char const *const *eaddr, unsigned int n, size
#endif
}
genalloc_free(mxipinfo, &mxipi) ;
+ return mxn ;
}
diff --git a/src/qmail-remote/qmail-remote-io.c b/src/qmail-remote/qmail-remote-io.c
new file mode 100644
index 0000000..3f55f87
--- /dev/null
+++ b/src/qmail-remote/qmail-remote-io.c
@@ -0,0 +1,245 @@
+/* ISC license. */
+
+#include <unistd.h>
+
+#include <skalibs/gccattributes.h>
+#include <skalibs/types.h>
+#include <skalibs/buffer.h>
+#include <skalibs/gol.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+#include "qmailr.h"
+
+enum gola_e
+{
+ GOLA_TIMEOUT,
+ GOLA_FDR,
+ GOLA_FDW,
+ GOLA_N
+} ;
+
+static inline unsigned int qgol_argv (char const *const *argv, gol_bool const *b, unsigned int bn, gol_arg const *a, unsigned int an, uint64_t *br, char const **ar)
+{
+ int problem = 0 ;
+ int r = gol(argv, b, bn, a, an, br, ar, &problem) ;
+
+ if (r < 0)
+ {
+ if (problem > 0)
+ {
+ char s[2] = { argv[-r-1][problem], 0 } ;
+ qmailr_perm("qmail-remote-io: ", "unrecognized ", "short ", "option: ", s) ;
+ }
+ else if (!problem)
+ qmailr_perm("qmail-remote-io: ", "invalid ", "option: ", argv[-r-1]) ;
+ else if (problem == -1)
+ qmailr_perm("qmail-remote-io: ", "unrecognized ", "boolean ", "option: ", argv[-r-1]) ;
+ else
+ qmailr_perm("qmail-remote-io: ", "unrecognized ", "option with argument: ", argv[-r-1]) ;
+ }
+ else return r ;
+}
+
+static inline unsigned int qgol_main (int argc, char const *const *argv, gol_bool const *b, unsigned int bn, gol_arg const *a, unsigned int an, uint64_t *br, char const **ar)
+{
+ if (argc < 1 || argv[argc]) qmailr_perm("qmail-remote-io: ", "invalid argc/argv") ;
+ if (argc == 1) return 1 ;
+ return 1 + qgol_argv(argv + 1, b, bn, a, an, br, ar) ;
+}
+
+static unsigned int read_answer_options (buffer *in, unsigned int timeout, char *buf, size_t buflen, char const *fmtip, unsigned int flags)
+{
+ int r = qmailr_smtp_read_answer(in, buf, buflen, timeout) ;
+ if (r == -1) qmailr_tempsys("qmail-remote-io: ", "unable to ", "read SMTP answer from ", fmtip, flags & 1 ? " (Possible duplicate!)" : "") ;
+ if (!r) qmailr_tempsys("qmail-remote-io: ", fmtip, " closed the connection early", flags & 1 ? " (Possible duplicate!)" : "") ;
+ return r ;
+}
+
+static unsigned read_answer (buffer *in, unsigned int timeout, char *buf, size_t buflen, char const *fmtip)
+{
+ return read_answer_options(in, timeout, buf, buflen, fmtip, 0) ;
+}
+
+static void put (buffer *out, char const *s)
+{
+ if (buffer_puts(out, s) < 0)
+ qmailr_tempsys("qmail-remote-io: ", "unable to ", "queue SMTP command") ;
+}
+
+static void datachar (buffer *out, char c)
+{
+ if (buffer_put(out, &c, 1) < 1)
+ qmailr_tempsys("qmail-remote-io: ", "unable to ", "send character after DATA") ;
+}
+
+/*
+ small DFA for blast() to quote dots and newlines
+
+ 0 1 2 3
+st\ev EOF . \n other
+
+0 qp rp p
+START END INLINE START INLINE
+
+1 p rp p
+INLINE X INLINE START INLINE
+
+END=2 X=3
+
+0x10 q quote with .
+0x20 r quote with \r
+0x40 p print char
+
+*/
+
+static inline uint8_t cclass (char c)
+{
+ return c == '.' ? 1 : c == '\n' ? 2 : 3 ;
+}
+
+static void blast (buffer *out, unsigned int timeout)
+{
+ static uint8_t const table[2][4] =
+ {
+ { 0x02, 0x51, 0x60, 0x41 },
+ { 0x03, 0x41, 0x60, 0x41 }
+ } ;
+ uint8_t state = 0 ;
+ while (state < 2)
+ {
+ char c ;
+ uint8_t val ;
+ ssize_t r = buffer_get(buffer_0, &c, 1) ;
+ if (r == -1) qmailr_tempsys("qmail-remote-io: ", "unable to ", "read message") ;
+ val = r ? table[state][cclass(c)] : table[state][0] ;
+ state = val & 3 ;
+ if (val & 0x10) datachar(out, '.') ;
+ if (val & 0x20) datachar(out, '\r') ;
+ if (val & 0x40) datachar(out, c) ;
+ }
+ if (state > 2) qmailr_perm("qmail-remote-io: ", "SMTP cannot transfer messages with partial final lines") ;
+ if (buffer_putflush(out, ".\r\n", 3) < 0) qmailr_tempsys("qmail-remote-io: ", "unable to ", "finalize message data") ;
+}
+
+static void smtp_body (buffer *in, buffer *out, char const *fmtip, char const *sender, char const *const *recip, unsigned int n, unsigned int timeout) gccattr_noreturn ;
+static void smtp_body (buffer *in, buffer *out, char const *fmtip, char const *sender, char const *const *recip, unsigned int n, unsigned int timeout)
+{
+ tain deadline ;
+ unsigned int code ;
+ int flagbother = 0 ;
+ char buf[4096] ;
+
+ put(out, "MAIL FROM:<") ;
+ put(out, sender) ;
+ put(out, ">\r\n") ;
+ tain_addsec_g(&deadline, timeout) ;
+ if (!buffer_timed_flush_g(out, &deadline))
+ qmailr_tempsys("qmail-remote-io: ", "unable to ", "send command to ", fmtip) ;
+ code = read_answer(in, timeout, buf, 4096, fmtip) ;
+ if (code >= 500)
+ {
+ qmailr_smtp_quit(out, timeout) ;
+ qmailr_perm("qmail-remote-io: ", "connected to ", fmtip, " but sender was rejected") ;
+ }
+ else if (code >= 400)
+ {
+ qmailr_smtp_quit(out, timeout) ;
+ qmailr_temp("qmail-remote-io: ", "connected to ", fmtip, " but sender was rejected") ;
+ }
+
+ for (unsigned int i = 0 ; i < n ; i++)
+ {
+ put(out, "RCPT TO:<") ;
+ put(out, recip[i]) ;
+ put(out, ">\r\n") ;
+ tain_addsec_g(&deadline, timeout) ;
+ if (!buffer_timed_flush_g(out, &deadline))
+ qmailr_tempsys("qmail-remote-io: ", "unable to ", "send command to ", fmtip) ;
+ code = read_answer(in, timeout, buf, 4096, fmtip) ;
+ if (code >= 500)
+ {
+ qmailr_smtp_quit(out, timeout) ;
+ qmailr_die('h', fmtip, " does not like recipient.\n", "Remote host said: ", buf+4) ;
+ }
+ else if (code >= 400)
+ {
+ qmailr_smtp_quit(out, timeout) ;
+ qmailr_die('s', fmtip, " does not like recipient.\n", "Remote host said: ", buf+4) ;
+ }
+ else
+ {
+ buffer_put(buffer_1, "r", 2) ;
+ flagbother = 1 ;
+ }
+ }
+ if (!flagbother)
+ {
+ qmailr_smtp_quit(out, timeout) ;
+ qmailr_perm("Giving up on ", fmtip) ;
+ }
+
+ put(out, "DATA\r\n") ;
+ tain_addsec_g(&deadline, timeout) ;
+ if (!buffer_timed_flush_g(out, &deadline))
+ qmailr_tempsys("qmail-remote-io: ", "unable to ", "send command to ", fmtip) ;
+ code = read_answer(in, timeout, buf, 4096, fmtip) ;
+ if (code >= 500)
+ {
+ qmailr_smtp_quit(out, timeout) ;
+ qmailr_perm(fmtip, " failed on DATA command") ;
+ }
+ else if (code >= 400)
+ {
+ qmailr_smtp_quit(out, timeout) ;
+ qmailr_temp(fmtip, " failed on DATA command") ;
+ }
+
+ blast(out, timeout) ;
+ code = read_answer_options(in, timeout, buf, 4096, fmtip, 1) ;
+ if (code >= 500)
+ {
+ qmailr_smtp_quit(out, timeout) ;
+ qmailr_perm(fmtip, " failed after I sent the message") ;
+ }
+ else if (code >= 400)
+ {
+ qmailr_smtp_quit(out, timeout) ;
+ qmailr_temp(fmtip, " failed after I sent the message") ;
+ }
+
+ qmailr_smtp_quit(out, timeout) ;
+ qmailr_die('K', fmtip, " accepted message") ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ static gol_arg const rgola[] =
+ {
+ { .so = 't', .lo = "timeoutremote", .i = GOLA_TIMEOUT },
+ { .so = '6', .lo = "fdr", .i = GOLA_FDR },
+ { .so = '7', .lo = "fdw", .i = GOLA_FDW },
+ } ;
+ char const *wgola[GOLA_N] = { 0 } ;
+ unsigned int fdr = 6, fdw = 7 ;
+ unsigned int timeoutremote = 1200 ;
+ buffer in, out ;
+ char inbuf[1024] ;
+ char outbuf[BUFFER_OUTSIZE] ;
+ unsigned int golc = qgol_main(argc, argv, 0, 0, rgola, 3, 0, wgola) ;
+ argc -= golc ; argv += golc ;
+ if (argc < 3) qmailr_perm("qmail-remote-io: ", "too few arguments") ;
+
+ if (wgola[GOLA_TIMEOUT] && !uint0_scan(wgola[GOLA_TIMEOUT], &timeoutremote))
+ qmailr_perm("qmail-remote-io: ", "invalid timeoutremote") ;
+ if (wgola[GOLA_FDR] && !uint0_scan(wgola[GOLA_FDR], &fdr))
+ qmailr_perm("qmail-remote-io: ", "invalid fdr") ;
+ if (wgola[GOLA_FDW] && !uint0_scan(wgola[GOLA_FDW], &fdw))
+ qmailr_perm("qmail-remote-io: ", "invalid fdw") ;
+
+ buffer_init(&in, &buffer_read, fdr, inbuf, 1024) ;
+ buffer_init(&out, &buffer_write, fdw, outbuf, BUFFER_OUTSIZE) ;
+
+ tain_now_set_stopwatch_g() ;
+ smtp_body(&in, &out, argv[0], argv[1], argv + 2, argc - 2, timeoutremote) ;
+}
diff --git a/src/qmail-remote/qmail-remote.c b/src/qmail-remote/qmail-remote.c
index 870c797..2fa98ab 100644
--- a/src/qmail-remote/qmail-remote.c
+++ b/src/qmail-remote/qmail-remote.c
@@ -1,29 +1,197 @@
/* ISC license. */
#include <string.h>
+#include <strings.h>
#include <stdint.h>
#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <skalibs/types.h>
+#include <skalibs/env.h>
+#include <skalibs/exec.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/buffer.h>
#include <skalibs/cdb.h>
#include <skalibs/stralloc.h>
#include <skalibs/sig.h>
#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/socket.h>
#include <skalibs/ip46.h>
+#include <skalibs/unix-timed.h>
+#include <s6-networking/config.h>
#include <smtpd-starttls-proxy/config.h>
#include "qmailr.h"
#include "qmail-remote.h"
#define dieusage() qmailr_perm("qmail-remote was invoked improperly")
+static inline void exec_tls (int fd, char const *fmtip, unsigned int timeoutconnect, unsigned int timeoutremote, qmailr_tls const *qtls, size_t helopos, size_t const *eaddrpos, unsigned int n, char const *storage) gccattr_noreturn ;
+static inline void exec_tls (int fd, char const *fmtip, unsigned int timeoutconnect, unsigned int timeoutremote, qmailr_tls const *qtls, size_t helopos, size_t const *eaddrpos, unsigned int n, char const *storage)
+{
+ unsigned int m = 0 ;
+ char fmtfd[UINT_FMT] ;
+ char fmtt[UINT_FMT] ;
+ char fmtk[UINT_FMT] ;
+ char const *argv[20 + n] ;
+
+ if (!env_mexec("TLS_UID", 0) || !env_mexec("TLS_GID", 0)
+ || !env_mexec(qtls->flagtadir ? "CADIR" : "CAFILE", storage + qtls->tapos)) dienomem() ;
+ if (qtls->flagclientcert)
+ {
+ if (!env_mexec("CERTFILE", storage + qtls->certpos)
+ || !env_mexec("KEYFILE", storage + qtls->keypos)) dienomem() ;
+ }
+
+ {
+ int devnull = open_readb("/dev/null") ;
+ if (devnull >= 0)
+ {
+ if (devnull < 3) qmailr_temp("weird fd configuration") ;
+ fd_move(2, devnull) ;
+ }
+ }
+
+ fmtfd[uint_fmt(fmtfd, (unsigned int)fd)] = 0 ;
+ fmtt[uint_fmt(fmtt, timeoutremote)] = 0 ;
+ fmtk[uint_fmt(fmtk, timeoutconnect > UINT_MAX/1000 ? UINT_MAX : timeoutconnect * 1000)] = 0 ;
+
+ argv[m++] = S6_NETWORKING_EXTBINPREFIX "s6-tlsc" ;
+ argv[m++] = "-Sjzv0" ;
+ argv[m++] = "-K" ;
+ argv[m++] = fmtk ;
+ argv[m++] = "-6" ;
+ argv[m++] = fmtfd ;
+ argv[m++] = "-7" ;
+ argv[m++] = fmtfd ;
+ argv[m++] = "--" ;
+
+ argv[m++] = SMTPD_STARTTLS_PROXY_LIBEXECPREFIX "qmail-remote-io" ;
+ argv[m++] = "-t" ;
+ argv[m++] = fmtt ;
+ argv[m++] = "-6" ;
+ argv[m++] = fmtfd ;
+ argv[m++] = "-7" ;
+ argv[m++] = fmtfd ;
+ argv[m++] = "--" ;
+ argv[m++] = fmtip ;
+ argv[m++] = storage + helopos ;
+ for (unsigned int i = 0 ; i < n ; i++) argv[m++] = storage + eaddrpos[i] ;
+ argv[m++] = 0 ;
+ mexec(argv) ;
+ qmailr_tempusys("exec ", argv[0]) ;
+}
+
+static inline void exec_notls (int fd, char const *fmtip, unsigned int timeoutremote, size_t helopos, size_t const *eaddrpos, unsigned int n, char const *storage) gccattr_noreturn ;
+static inline void exec_notls (int fd, char const *fmtip, unsigned int timeoutremote, size_t helopos, size_t const *eaddrpos, unsigned int n, char const *storage)
+{
+ unsigned int m = 0 ;
+ char fmtfd[UINT_FMT] ;
+ char fmtt[UINT_FMT] ;
+ char const *argv[11 + n] ;
+
+ fmtfd[uint_fmt(fmtfd, (unsigned int)fd)] = 0 ;
+ fmtt[uint_fmt(fmtt, timeoutremote)] = 0 ;
+ argv[m++] = SMTPD_STARTTLS_PROXY_LIBEXECPREFIX "qmail-remote-io" ;
+ argv[m++] = "-t" ;
+ argv[m++] = fmtt ;
+ argv[m++] = "-6" ;
+ argv[m++] = fmtfd ;
+ argv[m++] = "-7" ;
+ argv[m++] = fmtfd ;
+ argv[m++] = "--" ;
+ argv[m++] = fmtip ;
+ argv[m++] = storage + helopos ;
+ for (unsigned int i = 0 ; i < n ; i++) argv[m++] = storage + eaddrpos[i] ;
+ argv[m++] = 0 ;
+ exec(argv) ;
+ qmailr_tempusys("exec ", argv[0]) ;
+}
+
+static int smtp_start (buffer *in, buffer *out, char const *helohost, unsigned int timeout, char const *fmtip)
+{
+ int hastls = 0 ;
+ tain deadline ;
+ char line[1024] ;
+
+ int r = qmailr_smtp_read_answer(in, line, 1024, timeout) ;
+ if (r == -1) qmailr_tempusys("read from ", fmtip) ;
+ if (!r) qmailr_temp("Connected to ", fmtip, " but connection died") ;
+ if (r != 220)
+ {
+ qmailr_smtp_quit(out, timeout) ;
+ qmailr_temp("Connected to ", fmtip, " but greeting failed") ;
+ }
+
+ buffer_putnoflush(out, "EHLO ", 5) ;
+ buffer_putsnoflush(out, helohost) ;
+ buffer_putnoflush(out, "\r\n", 2) ;
+
+ tain_addsec_g(&deadline, timeout) ;
+ if (!buffer_timed_flush_g(out, &deadline))
+ qmailr_tempusys("send ", "EHLO", " to ", fmtip) ;
+
+ tain_addsec_g(&deadline, timeout) ;
+ for (;;)
+ {
+ unsigned int code = 250 ;
+ int r = qmailr_smtp_read_line(in, line, 1024, &code, &deadline) ;
+ if (r == -1) qmailr_tempusys("read from ", fmtip) ;
+ if (!r) qmailr_temp("Connected to ", fmtip, " but connection died") ;
+ if (code != 250) qmailr_temp("Connected to ", fmtip, " but it speaks a weird protocol") ;
+ if (!strcasecmp(line + 4, "STARTTLS")) hastls = 1 ;
+ if (r == 1) break ;
+ }
+ return hastls ;
+}
+
+static void attempt_smtp (int fd, char const *ip, int is6, unsigned int timeoutconnect, unsigned int timeoutremote, qmailr_tls const *qtls, size_t helopos, size_t const *eaddrpos, unsigned int n, char const *storage)
+{
+ int hastls ;
+ char inbuf[2048] ;
+ char outbuf[2048] ;
+ char fmtip[IP6_FMT] ;
+ buffer in = BUFFER_INIT(&buffer_read, fd, inbuf, 2048) ;
+ buffer out = BUFFER_INIT(&buffer_write, fd, outbuf, 2048) ;
+ if (is6) fmtip[ip6_fmt(fmtip, ip)] = 0 ;
+ else fmtip[ip4_fmt(fmtip, ip)] = 0 ;
+
+ hastls = smtp_start(&in, &out, storage + helopos, timeoutremote, fmtip) ;
+ if (qtls->flagwanttls)
+ {
+ if (hastls)
+ {
+ int r ;
+ tain deadline ;
+ char line[1024] ;
+ buffer_putsnoflush(&out, "STARTTLS\r\n") ;
+ tain_addsec_g(&deadline, timeoutremote) ;
+ if (!buffer_timed_flush_g(&out, &deadline)) qmailr_tempusys("send ", "STARTTLS", " to ", fmtip) ;
+ r = qmailr_smtp_read_answer(&in, line, 1024, timeoutremote) ;
+ if (r == -1) qmailr_tempusys("read from ", fmtip) ;
+ else if (!r)
+ {
+ qmailr_smtp_quit(&out, timeoutremote) ;
+ qmailr_temp("Connected to ", fmtip, " but connection died") ;
+ }
+ else if (r == 220) exec_tls(fd, fmtip, timeoutconnect, timeoutremote, qtls, helopos, eaddrpos, n, storage) ;
+ if (qtls->strictness) return ;
+ }
+ else if (qtls->strictness >= 2) return ;
+ }
+ exec_notls(fd, fmtip, timeoutremote, helopos, eaddrpos, n, storage) ;
+}
+
int main (int argc, char const *const *argv)
{
stralloc storage = STRALLOC_ZERO ;
stralloc ipme4 = STRALLOC_ZERO ;
stralloc ipme6 = STRALLOC_ZERO ;
- qmailr_tls qt = QMAILR_TLS_ZERO ;
+ qmailr_tls qtls = QMAILR_TLS_ZERO ;
smtproutes routes = SMTPROUTES_ZERO ;
- unsigned int timeoutconnect = 60, timeoutremote = 1200 ;
+ unsigned int timeoutconnect = 60, timeoutremote = 1200, timeoutdns = 0 ;
char const *host ;
size_t mepos, helopos, hostpos = 0 ;
uint16_t port = 25 ;
@@ -31,8 +199,8 @@ int main (int argc, char const *const *argv)
if (argc-- < 4) dieusage() ;
argv++ ;
- if (chdir(SMTPD_STARTTLS_PROXY_QMAIL_HOME) == -1) qmailr_temp("Unable to chdir to " SMTPD_STARTTLS_PROXY_QMAIL_HOME) ;
- if (sig_altignore(SIGPIPE) == -1) qmailr_tempsys("Unable to ignore SIGPIPE") ;
+ if (chdir(SMTPD_STARTTLS_PROXY_QMAIL_HOME) == -1) qmailr_tempusys("chdir to ", SMTPD_STARTTLS_PROXY_QMAIL_HOME) ;
+ if (sig_altignore(SIGPIPE) == -1) qmailr_tempusys("ignore SIGPIPE") ;
host = *argv++ ; argc-- ;
tain_now_set_stopwatch_g() ;
@@ -40,27 +208,27 @@ int main (int argc, char const *const *argv)
/* init control */
r = qmailr_control_read("control/me", &storage, &mepos) ;
- if (r == -1) qmailr_tempsys("Unable to read control/me") ;
- else if (!r) qmailr_temp("Invalid control/me") ;
+ if (r == -1) qmailr_tempusys("read ", "control/me") ;
+ else if (!r) qmailr_temp("Invalid ", "control/me") ;
r = qmailr_control_read("control/helohost", &storage, &helopos) ;
- if (r == -1) qmailr_tempsys("Unable to read control/helohost") ;
+ if (r == -1) qmailr_tempusys("read ", "control/helohost") ;
else if (!r) helopos = mepos ;
r = qmailr_control_readint("control/timeoutconnect", &timeoutconnect, &storage) ;
- if (r == -1) qmailr_tempsys("Unable to read control/timeoutconnect") ;
+ if (r == -1) qmailr_tempusys("read ", "control/timeoutconnect") ;
r = qmailr_control_readint("control/timeoutremote", &timeoutremote, &storage) ;
- if (r == -1) qmailr_tempsys("Unable to read control/timeoutremote") ;
+ if (r == -1) qmailr_tempusys("read ", "control/timeoutremote") ;
+ r = qmailr_control_readint("control/timeoutdns", &timeoutdns, &storage) ;
+ if (r == -1) qmailr_tempusys("read ", "control/timeoutdns") ;
if (!qmailr_control_readiplist("control/ipme", &ipme4, &ipme6))
- qmailr_tempsys("Unable to read control/ipme") ;
- stralloc_shrink(&ipme4) ;
- stralloc_shrink(&ipme6) ;
+ qmailr_tempusys("read ", "control/ipme") ;
qsort(ipme4.s, ipme4.len >> 2, 4, &qmailr_memcmp4) ;
qsort(ipme6.s, ipme6.len >> 4, 16, &qmailr_memcmp16) ;
- if (!qmailr_tls_init(&qt, &storage))
- qmailr_tempsys("Unable to read TLS control files") ;
+ if (!qmailr_tls_init(&qtls, &storage))
+ qmailr_tempusys("read ", "TLS control files") ;
if (smtproutes_init(&routes))
{
@@ -76,11 +244,69 @@ int main (int argc, char const *const *argv)
{
genalloc mxipind = GENALLOC_ZERO ;
+ mxip const *mxs ;
size_t eaddrpos[argc] ;
- dns_stuff(hostpos ? storage.s + hostpos : host, argv, argc, eaddrpos, &mxipind, &storage, timeoutconnect, ipme4.s, ipme4.len >> 2, ipme6.s, ipme6.len >> 4, !hostpos) ;
-
- }
+ size_t ntot = 0 ;
+ unsigned int mxn = dns_stuff(hostpos ? storage.s + hostpos : host, argv, argc, eaddrpos, &mxipind, &storage, timeoutdns, ipme4.s, ipme4.len >> 2, ipme6.s, ipme6.len >> 4, !hostpos) ;
+ if (!mxn) qmailr_perm("No suitable MX found for remote host") ;
+ stralloc_free(&ipme4) ;
+ stralloc_free(&ipme6) ;
+ mxs = genalloc_s(mxip, &mxipind) ;
+ for (unsigned int i = 0 ; i < mxn ; i++) ntot += mxs[i].n4 + mxs[i].n6 ;
+ if (!ntot) qmailr_perm("No suitable IP addresses for the MX") ;
- _exit(0) ;
+ for (; qtls.flagwanttls && qtls.strictness == 1 ; qtls.flagwanttls = 0)
+ {
+ for (unsigned int i = 0 ; i < mxn ; i++)
+ {
+#ifdef SKALIBS_IPV6_ENABLED
+ for (unsigned int j = 0 ; j < mxs[i].n6 ; j++)
+ {
+ char const *ip = storage.s + mxs[i].pos6 + (j << 4) ;
+ tain deadline ;
+ int fd ;
+ if (qmailr_tcpto_match(ip, 1)) continue ;
+ fd = socket_tcp6() ;
+ if (fd == -1) qmailr_tempusys("create socket") ;
+ tain_addsec_g(&deadline, timeoutconnect) ;
+ if (!socket_deadlineconnstamp6_g(fd, ip, port, &deadline))
+ {
+ if (!qmailr_tcpto_update(ip, 1, errno == ETIMEDOUT))
+ qmailr_tempusys("update ", "tcpto6") ;
+ fd_close(fd) ;
+ continue ;
+ }
+ if (!qmailr_tcpto_update(ip, 1, 0))
+ qmailr_tempusys("update ", "tcpto6") ;
+ attempt_smtp(fd, ip, 1, timeoutconnect, timeoutremote, &qtls, helopos, eaddrpos, argc, storage.s) ;
+ fd_close(fd) ;
+ }
+#endif
+ for (unsigned int j = 0 ; j < mxs[i].n4 ; j++)
+ {
+ char const *ip = storage.s + mxs[i].pos4 + (j << 2) ;
+ tain deadline ;
+ int fd ;
+ if (qmailr_tcpto_match(ip, 0)) continue ;
+ fd = socket_tcp4() ;
+ if (fd == -1) qmailr_tempusys("create socket") ;
+ tain_addsec_g(&deadline, timeoutconnect) ;
+ if (!socket_deadlineconnstamp4_g(fd, ip, port, &deadline))
+ {
+ if (!qmailr_tcpto_update(ip, 0, errno == ETIMEDOUT))
+ qmailr_tempusys("update ", "tcpto") ;
+ fd_close(fd) ;
+ continue ;
+ }
+ if (!qmailr_tcpto_update(ip, 0, 0))
+ qmailr_tempusys("update ", "tcpto") ;
+ attempt_smtp(fd, ip, 0, timeoutconnect, timeoutremote, &qtls, helopos, eaddrpos, argc, storage.s) ;
+ fd_close(fd) ;
+ }
+ }
+ }
+ }
+ qmailr_tempusys("establish an SMTP connection") ;
+ _exit(101) ; /* not reached */
}
diff --git a/src/qmail-remote/qmail-remote.h b/src/qmail-remote/qmail-remote.h
index 8059344..afe4323 100644
--- a/src/qmail-remote/qmail-remote.h
+++ b/src/qmail-remote/qmail-remote.h
@@ -12,7 +12,7 @@
#include "qmailr.h"
-#define dienomem() qmailr_tempsys("Unable to grow stralloc")
+#define dienomem() qmailr_tempusys("stralloc_catb")
/* dns */
@@ -27,7 +27,7 @@ struct mxip_s
} ;
#define MXIP_ZERO { 0 }
-extern void dns_stuff (char const *, char const *const *, unsigned int, size_t *, genalloc *, stralloc *, unsigned int, char const *, unsigned int, char const *, unsigned int, uint32_t) ;
+extern unsigned int dns_stuff (char const *, char const *const *, unsigned int, size_t *, genalloc *, stralloc *, unsigned int, char const *, unsigned int, char const *, unsigned int, uint32_t) ;
/* smtproutes */
diff --git a/src/qmail-remote/qmailr.h b/src/qmail-remote/qmailr.h
index 08c4f41..451824a 100644
--- a/src/qmail-remote/qmailr.h
+++ b/src/qmail-remote/qmailr.h
@@ -7,23 +7,30 @@
#include <stdint.h>
#include <skalibs/gccattributes.h>
+#include <skalibs/buffer.h>
#include <skalibs/tai.h>
#include <skalibs/stralloc.h>
/* qmailr_error */
-extern void qmailr_diev (int, char const *const *, unsigned int) gccattr_noreturn ;
+extern void qmailr_warnv (char, char const *const *, unsigned int) gccattr_noreturn ;
+extern void qmailr_diev (char, 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)
+#define qmailr_array(...) ((char const *const[]){__VA_ARGS__})
+#define qmailr_dien(e, n, ...) qmailr_diev(e, qmailr_array(__VA_ARGS__), (n))
+#define qmailr_diensys(n, ...) qmailr_dievsys(qmailr_array(__VA_ARGS__), (n))
+
+#define qmailr_die(c, ...) qmailr_dien(c, sizeof(qmailr_array(__VA_ARGS__))/sizeof(char const *), __VA_ARGS__)
+#define qmailr_diesys(...) qmailr_diensys(sizeof(qmailr_array(__VA_ARGS__))/sizeof(char const *), __VA_ARGS__)
+
+#define qmailr_temp(...) qmailr_die('Z', __VA_ARGS__)
+#define qmailr_tempsys(...) qmailr_diesys(__VA_ARGS__)
+#define qmailr_perm(...) qmailr_die(1, __VA_ARGS__)
+
+#define qmailr_tempu(...) qmailr_die('D', "Unable to ", __VA_ARGS__)
+#define qmailr_tempusys(...) qmailr_diesys("Unable to ", __VA_ARGS__)
/* qmailr_utils */
@@ -46,6 +53,14 @@ extern int qmailr_control_readint (char const *file, unsigned int *, stralloc *)
extern int qmailr_control_readiplist (char const *, stralloc *, stralloc *) ;
+ /* qmailr_smtp */
+
+extern int qmailr_smtp_read_line (buffer *, char *, size_t, unsigned int *, tain const *) ;
+extern int qmailr_smtp_read_answer (buffer *, char *, size_t, unsigned int) ;
+extern int qmailr_smtp_start (buffer *, buffer *, char const *, unsigned int) ;
+extern void qmailr_smtp_quit (buffer *b, unsigned int) ;
+
+
/* qmailr_tls */
typedef struct qmailr_tls_s qmailr_tls, *qmailr_tls_ref ;
@@ -55,7 +70,7 @@ struct qmailr_tls_s
size_t certpos ;
size_t keypos ;
uint8_t strictness : 2 ;
- uint8_t flagtls : 1 ;
+ uint8_t flagwanttls : 1 ;
uint8_t flagtadir : 1 ;
uint8_t flagclientcert : 1 ;
} ;
diff --git a/src/qmail-remote/qmailr_error.c b/src/qmail-remote/qmailr_error.c
index fcdce54..0f500e2 100644
--- a/src/qmail-remote/qmailr_error.c
+++ b/src/qmail-remote/qmailr_error.c
@@ -8,31 +8,26 @@
#include <smtpd-starttls-proxy/config.h>
-void qmailr_diev (int permanent, char const *const *v, unsigned int n)
+void qmailr_warnv (char code, 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) ;
+ buffer_put(buffer_1, &code, 1) ;
+ while (n--) buffer_puts(buffer_1, *v++) ;
+ buffer_putflush(buffer_1, "\n", 2) ;
}
-void qmailr_dievsys (char const *const *v, unsigned int n)
+void qmailr_diev (char code, 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) ;
+ qmailr_warnv(code, v, n) ;
_exit(0) ;
}
-void qmailr_die (int permanent, char const *msg)
-{
- qmailr_diev(permanent, &msg, 1) ;
-}
-
-void qmailr_diesys (char const *msg)
+void qmailr_dievsys (char const *const *v, unsigned int n)
{
- qmailr_dievsys(&msg, 1) ;
+ char const *se = strerror(errno) ;
+ buffer_put(buffer_1, "Z", 1) ;
+ while (n--) buffer_puts(buffer_1, *v++) ;
+ buffer_put(buffer_1, ": ", 2) ;
+ buffer_puts(buffer_1, se) ;
+ buffer_putflush(buffer_1, "\n", 2) ;
+ _exit(0) ;
}
diff --git a/src/qmail-remote/qmailr_smtp.c b/src/qmail-remote/qmailr_smtp.c
new file mode 100644
index 0000000..5a778f4
--- /dev/null
+++ b/src/qmail-remote/qmailr_smtp.c
@@ -0,0 +1,53 @@
+/* ISC license. */
+
+#include <strings.h>
+#include <errno.h>
+
+#include <skalibs/types.h>
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+#include "qmailr.h"
+
+#include <skalibs/posixishard.h>
+
+int qmailr_smtp_read_line (buffer *in, char *line, size_t max, unsigned int *code, tain const *deadline)
+{
+ unsigned int c ;
+ size_t w = 0 ;
+ ssize_t r = timed_getlnmax_g(in, line, max, &w, '\n', deadline) ;
+ if (r <= 0) return r ;
+ if (w < 4) return (errno = EPROTO, -1) ;
+ line[--w] = 0 ;
+ if (line[w-1] == '\r') line[--w] = 0 ;
+ while (w >= 3 && line[w-1] == ' ') line[--w] = 0 ;
+ if (uint_scan(line, &c) != 3) return (errno = EPROTO, -1) ;
+ *code = c ;
+ return 1 + (line[3] == '-') ;
+}
+
+int qmailr_smtp_read_answer (buffer *in, char *line, size_t max, unsigned int timeout)
+{
+ unsigned int code = 1000 ;
+ tain deadline ;
+ tain_addsec_g(&deadline, timeout) ;
+ for (;;)
+ {
+ unsigned int c ;
+ int r = qmailr_smtp_read_line(in, line, max, &c, &deadline) ;
+ if (r <= 0) return r ;
+ if (code == 1000) code = c ;
+ else if (code != c) return (errno = EPROTO, -1) ;
+ if (r == 1) break ;
+ }
+ return code ;
+}
+
+void qmailr_smtp_quit (buffer *out, unsigned int timeout)
+{
+ tain deadline ;
+ buffer_puts(out, "QUIT\r\n") ;
+ tain_addsec_g(&deadline, timeout) ;
+ buffer_timed_flush_g(out, &deadline) ;
+}
diff --git a/src/qmail-remote/qmailr_tls.c b/src/qmail-remote/qmailr_tls.c
index 6e09a82..351401e 100644
--- a/src/qmail-remote/qmailr_tls.c
+++ b/src/qmail-remote/qmailr_tls.c
@@ -19,7 +19,7 @@ int qmailr_tls_init (qmailr_tls *qt, stralloc *sa)
if (r)
{
unsigned int strictness = 0 ;
- tmp.flagtls = 1 ;
+ tmp.flagwanttls = 1 ;
if (sa->s[sa->len - 2] == '/')
{
sa->s[--sa->len - 1] = 0 ;
diff --git a/src/qmail-remote/smtproutes.c b/src/qmail-remote/smtproutes.c
index 7467031..987961c 100644
--- a/src/qmail-remote/smtproutes.c
+++ b/src/qmail-remote/smtproutes.c
@@ -96,7 +96,7 @@ 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") ;
+ if (r == -1) qmailr_tempusys("read ", "control/smtproutes") ;
return r ? c : 0 ;
}
@@ -121,7 +121,7 @@ static inline void smtproutes_compile (int fdr, int fdw)
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") ;
+ if (!cdbmake_start(&cm, fdw)) qmailr_tempusys("cdbmake_start") ;
while (state < 0x0a)
{
@@ -130,36 +130,36 @@ static inline void smtproutes_compile (int fdr, int fdw)
state = val & 0x000f ;
if (val & 0x0100)
{
- if (!stralloc_catb(&sa, &c, 1)) qmailr_tempsys("Unable to grow stralloc") ;
+ if (!stralloc_catb(&sa, &c, 1)) dienomem() ;
}
if (val & 0x0200)
{
relaypos = sa.len + 1 ;
- if (!stralloc_catb(&sa, "\0\0\31", 3)) qmailr_tempsys("Unable to grow stralloc") ;
+ if (!stralloc_catb(&sa, "\0\0\31", 3)) dienomem() ;
}
if (val & 0x0400)
{
- if (!stralloc_0(&sa)) qmailr_tempsys("Unable to grow stralloc") ;
+ if (!stralloc_0(&sa)) dienomem() ;
relayend = sa.len ;
}
if (val & 0x0800)
{
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") ;
+ if (!stralloc_0(&sa)) dienomem() ;
+ if (!uint160_scan(sa.s + relayend, &port)) qmailr_temp("Invalid port in ", "control/smtproutes") ;
uint16_pack_big(sa.s + relaypos, port) ;
}
if (val & 0x0100)
{
if (relaypos > 1 || relayend > 2 + relaypos)
if (!cdbmake_add(&cm, sa.s, relaypos, sa.s + relaypos, relayend - relaypos))
- qmailr_tempsys("Unable to cdbmake_add") ;
+ qmailr_tempusys("cdbmake_add") ;
sa.len = 0 ;
}
}
- if (state != 0x0a) qmailr_temp("Syntax error in control/smtproutes") ;
+ if (state != 0x0a) qmailr_temp("Syntax error in ", "control/smtproutes") ;
stralloc_free(&sa) ;
- if (!cdbmake_finish(&cm)) qmailr_tempsys("Unable to cdbmake_finish") ;
+ if (!cdbmake_finish(&cm)) qmailr_tempusys("cdbmake_finish") ;
}
int smtproutes_init (smtproutes *routes)
@@ -169,17 +169,17 @@ int smtproutes_init (smtproutes *routes)
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") ;
+ if (fdl == -1) qmailr_tempusys("open ", "run/qmail-remote/smtproutes.lock") ;
+ if (fd_lock(fdl, 1, 0) == -1) qmailr_tempusys("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 (fstat(fdc, &stc) == -1) qmailr_tempusys("fstat ", "run/qmail-remote/smtproutes.cdb") ;
if (stat(txtfile, &str) == -1)
{
- if (errno != ENOENT) qmailr_tempsys("Unable to fstat control/smtproutes") ;
+ if (errno != ENOENT) qmailr_tempusys("fstat ", "control/smtproutes") ;
unlink_void(cdbfile) ;
fd_close(fdc) ;
goto zero ;
@@ -191,7 +191,7 @@ int smtproutes_init (smtproutes *routes)
int fdr = openc_read(txtfile) ;
if (fdr == -1)
{
- if (errno != ENOENT) qmailr_tempsys("Unable to open control/smtproutes") ;
+ if (errno != ENOENT) qmailr_tempusys("open ", "control/smtproutes") ;
goto zero ;
}
@@ -200,16 +200,16 @@ int smtproutes_init (smtproutes *routes)
memcpy(tmp, cdbfile, cdblen) ;
memcpy(tmp + cdblen, ":XXXXXX", 8) ;
fdc = mkstemp(tmp) ;
- if (fdc == -1) qmailr_tempsys("Unable to mkstemp") ;
+ if (fdc == -1) qmailr_tempusys("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") ;
+ if (lseek(fdc, 0, SEEK_SET) == -1) qmailr_tempusys("lseek") ;
+ if (fsync(fdc) == -1) qmailr_tempusys("fsync ", "run/qmail-remote/smtproutes.cdb") ;
fd_close(fdr) ;
if (rename(tmp, cdbfile) == -1) unlink_void(tmp) ;
}
useit:
- if (!cdb_init_fromfd(&routes->map, fdc)) qmailr_tempsys("Unable to mmap run/qmail-remote/smtproutes.cdb") ;
+ if (!cdb_init_fromfd(&routes->map, fdc)) qmailr_tempusys("mmap ", "run/qmail-remote/smtproutes.cdb") ;
fd_close(fdc) ;
fd_close(fdl) ;
return 1 ;
@@ -227,10 +227,10 @@ int smtproutes_match (smtproutes const *routes, char const *s, stralloc *sa, siz
if (r == -1) qmailr_temp("Invalid run/qmail-remote/smtproutes.cdb") ;
if (!r) return 0 ;
if (data.len < 3) return 0 ;
- if (data.s[data.len - 1]) qmailr_temp("Invalid run/qmail-remote/smtproutes.cdb") ;
+ if (data.s[data.len - 1]) qmailr_temp("Invalid ", "run/qmail-remote/smtproutes.cdb") ;
*pos = sa->len ;
uint16_unpack_big(data.s, port) ;
- if (!stralloc_catb(sa, data.s + 2, data.len - 2)) qmailr_tempsys("Unable to grow stralloc") ;
+ if (!stralloc_catb(sa, data.s + 2, data.len - 2)) dienomem() ;
return 1 ;
}