diff options
| author | Laurent Bercot <ska-skaware@skarnet.org> | 2026-02-07 14:08:53 +0000 |
|---|---|---|
| committer | Laurent Bercot <ska-skaware@skarnet.org> | 2026-02-07 14:08:53 +0000 |
| commit | de33be8ad62f2c3c2f7c4030b1ebb301a73e0872 (patch) | |
| tree | d97c4a015881ef52fee4236b07a82737f3e431c4 | |
| parent | 5ca4a0e5b65066f41af7c670e8200b0d0eba41dd (diff) | |
| download | smtpd-starttls-proxy-de33be8ad62f2c3c2f7c4030b1ebb301a73e0872.tar.gz | |
final batch of code, this should be complete!
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | doc/index.html | 6 | ||||
| -rw-r--r-- | doc/qmail-remote-io.html | 53 | ||||
| -rw-r--r-- | doc/qmail-remote.html | 172 | ||||
| -rw-r--r-- | doc/qmail-smtpc.html | 79 | ||||
| -rw-r--r-- | package/deps.mak | 8 | ||||
| -rw-r--r-- | package/modes | 1 | ||||
| -rw-r--r-- | package/targets.mak | 3 | ||||
| -rw-r--r-- | src/qmail-remote/deps-exe/qmail-remote-io | 2 | ||||
| -rw-r--r-- | src/qmail-remote/deps-lib/qmailr | 1 | ||||
| -rw-r--r-- | src/qmail-remote/dns.c | 57 | ||||
| -rw-r--r-- | src/qmail-remote/qmail-remote-io.c | 245 | ||||
| -rw-r--r-- | src/qmail-remote/qmail-remote.c | 262 | ||||
| -rw-r--r-- | src/qmail-remote/qmail-remote.h | 4 | ||||
| -rw-r--r-- | src/qmail-remote/qmailr.h | 35 | ||||
| -rw-r--r-- | src/qmail-remote/qmailr_error.c | 33 | ||||
| -rw-r--r-- | src/qmail-remote/qmailr_smtp.c | 53 | ||||
| -rw-r--r-- | src/qmail-remote/qmailr_tls.c | 2 | ||||
| -rw-r--r-- | src/qmail-remote/smtproutes.c | 42 |
19 files changed, 879 insertions, 183 deletions
@@ -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 — 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 ; } |
