nightmaremail

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit efa1c86a8900037a0d48f193143ebb55a24e0018
parent 82ec9787f6d1067bbfc4b3bea59d8ed43c9e44ab
Author: Ellenor Bjornsdottir <ellenor@umbrellix.net>
Date:   Sun, 18 Sep 2022 19:58:01 +0000

a whole lotta nothing really

Diffstat:
Adoc/pictures/macro.mxps | 20++++++++++++++++++++
Adoc/pictures/macro.qmail-remote | 18++++++++++++++++++
Adoc/pictures/macro.tcpto | 12++++++++++++
Msrc/mxf-remote/NOTES.qmailr | 2+-
Asrc/mxf-remote/mxf-remote-connector.c | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mxf-remote/mxf-remote-qmtpc.c | 0
Asrc/mxf-remote/mxf-remote-smtpc.c | 0
Msrc/mxf-remote/qmail-remote.c | 112-------------------------------------------------------------------------------
Msrc/qmail-qmtpd.c | 19++++++++++---------
Msrc/qmail-remote.c | 34+++++++++++++++++-----------------
10 files changed, 220 insertions(+), 139 deletions(-)

diff --git a/doc/pictures/macro.mxps b/doc/pictures/macro.mxps @@ -0,0 +1,20 @@ + +# The NightmareMail Pictures » The Macro Picture » Mail eXchanger Protocol Switch + +(This document is prefaced with a newline so that it might be rendered correctly as Headered Markdown. It is regular Markdown, and further, any drawings within are conformed to a 70 character terminal, so that they might be rendered correctly doing `less` on an 80 character terminal.) + +"so, hang on... if you take MXPS in hex, the slice no. is the last digit (4 bits), and the preference level is the second to last digit (4 bits)?" ~ Melanie Bjornsdottir, to her friend who knows nothing about mail exchangers + +They say those that the gods would destroy, they first drive insane. (With apologies to the judge in 2013 ONCJ 160) With ready access to a Tcl interpreter with `[format]`, on this subject, it appears the gods are kind. We explore the specific implications of the distance numbers Daniel has chosen for the MXPS, which, by the way, is absolutely obsolete. In NightmareMail, we will support it, but on systems configured to use what we call SRVMAIL, we will try it only after failing _qmtp-relay._tcp and _smtp-relay._tcp. + +## Background + +The [**Mail Exchanger Protocol Switch**](http://cr.yp.to/im/mxps.html) is an Internet protocol proposal by Daniel Bernstein. In [the original version](http://cr.yp.to/im/mxps-old.txt), he elaborates on the slice design. There are sixteen slices counting from the distance of 12800 (decimal), numbered 0 through 15 (decimal). These slices appear to be based in a number starting zero to fifteen, and go up in strides of 16. When we enter the numbers into tclsh thus: +<code> +% puts stdout [format "%x %x %x %x" 12800 12816 12817 12834] +3200 3210 3211 3222 +</code> +(prio 0 slice 0, prio 1 slice 0, prio 1 slice 1, prio 2 slice 2) we see that this is effectively a bitmask. + +In the original proposal, 16 slices are defined, but only 2 are proposed to be used for a protocol. Slice 0 (dist & 000f = 0) is proposed to be used for classic SMTP, and slice 1 is proposed to be used for classic QMTP. In the new DJB proposal, the meaning of slice 1 is changed to classic QMTP, followed by classic SMTP. In this document, we propose that both slices should be changed to mean their secure-preferred versions, and slice 1 should be changed back to QMTPS-only, and we further propose that the MX record should be made obsolete in favour of SRV records, to be tried in the order of sending-side administrator preference. + diff --git a/doc/pictures/macro.qmail-remote b/doc/pictures/macro.qmail-remote @@ -0,0 +1,18 @@ + +# The NightmareMail Pictures » The Macro Picture » qmail-remote + +(This document is prefaced with a newline so that it might be rendered correctly as Headered Markdown. It is regular Markdown, and further, any drawings within are conformed to a 70 character terminal, so that they might be rendered correctly doing `less` on an 80 character terminal.) + +They say those that the gods would destroy, they first would drive insane. The Prophet, Daniel Bernstein, is merciful. (With apologies to the judge in 2013 ONCJ 160) In contrast to the interface he created, intending to be used only internally, for qmail-queue (where both stdin and stdout are input FDs), qmail-remote takes input *exclusively* on stdin. + +We can first visit the subject of the external interface to qmail-remote. This is documented well enough. Argv is thus, from zero (zero is ignored, and should be the binary name, especially in the event that one might like to produce a crunched qmail binary where argv0 is not ignored, or a multifunctional remote tool which also does not ignore argv0): +<code> +qmail-remote host sender [recipient ...]{1,} +</code> +One should not use getopt to parse the command line, lest, may the gods forbid it, you have a host, sender, or recipient whose name begins with the hyphen character. + +The input on stdin to qmail-remote is simple enough as well. It's the message. Events to read fd0 (substdio_get(&ssin)) only occur in the function void blast(), which is called only from the function void smtp(), which carries on an SMTP conversation, on a connection opened in main(). + +Lightning figured this out one year ago tomorrow as I write this (Sun Sep 18 2022 10:51:49 UTC). It seems like xe did not develop the model of how one would cut the program down to just mxf-smtpc. It seems in fact like qmail-remote can be very neatly cleft into the part making the connection, and the part that actually sends the message. + +However... what the hell is a tcpto? M. Bhangui tells me on the IRC that a tcpto is a host which times out when it is contacted. This throws a spanner in the idea of using a UCSPI. diff --git a/doc/pictures/macro.tcpto b/doc/pictures/macro.tcpto @@ -0,0 +1,12 @@ + +# The NightmareMail Pictures » The Macro Picture » the tcpto story + +(This document is prefaced with a newline so that it might be rendered correctly as Headered Markdown. It is regular Markdown, and further, any drawings within are conformed to a 70 character terminal, so that they might be rendered correctly doing `less` on an 80 character terminal.) + +They say those that the gods would destroy, they first drive insane. (With apologies to the judge in 2013 ONCJ 160) + +In this document, we attempt to comprehend the 'tcpto' system as it exists in qmail+Nightmare, and as it is used in the following source files: ```qmail-remote.c, qmail-rspawn.c, tcpto.c, tcpto_clean.c```. + +First, let's figure out what this is with tcpto_clean. This source file appears entirely fragmentary. It is mentioned twice in Makefile.legacy - once in its own right, and once as a part of qmail-rspawn. It would appear that that's the only functionality needed by qmail-rspawn as it relates to tcpto. + +Now we may move to tcpto itself. `tcpto.o` is mentioned in connection with qmail-remote, qmail-tcpto, and itself. diff --git a/src/mxf-remote/NOTES.qmailr b/src/mxf-remote/NOTES.qmailr @@ -1,4 +1,4 @@ -== MXF NOTES +## MXF NOTES qmail-remote.c: This whole thing has to be chunked out. (To be fair, it probably doesn't - but a lot needs to be gone.) diff --git a/src/mxf-remote/mxf-remote-connector.c b/src/mxf-remote/mxf-remote-connector.c @@ -0,0 +1,142 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include "sig.h" +#include "stralloc.h" +#include "substdio.h" +#include "subfd.h" +#include "scan.h" +#include "case.h" +#include "error.h" +#include "auto_qmail.h" +#include "control.h" +#include "dns.h" +#include "quote.h" +#include "ip.h" +#include "ipalloc.h" +#include "ipme.h" +#include "gen_alloc.h" +#include "gen_allocdefs.h" +#include "str.h" +#include "now.h" +#include "exit.h" +#include "constmap.h" +#include "noreturn.h" +#include "tcpto.h" +#include "readwrite.h" +#include "timeoutconn.h" +#include "timeoutread.h" +#include "timeoutwrite.h" + +void getcontrols() +{ + if (control_init() == -1) temp_control(); + if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control(); + if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1) + temp_control(); + if (control_rldef(&helohost,"control/helohost",1,NULL) != 1) + temp_control(); + switch(control_readfile(&routes,"control/smtproutes",0)) { + case -1: + temp_control(); + case 0: + if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); break; + case 1: + if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; + } +} + +int main(int argc, char **argv) +{ + static ipalloc ip = {0}; + int i; + unsigned long random; + char **recips; + unsigned long prefme; + char *relayhost; + + sig_pipeignore(); + if (argc < 4) perm_usage(); + if (chdir(auto_qmail) == -1) temp_chdir(); + getcontrols(); + + + if (!stralloc_copys(&host,argv[1])) temp_nomem(); + + relayhost = 0; + for (i = 0;i <= host.len;++i) + if ((i == 0) || (i == host.len) || (host.s[i] == '.')) + if ((relayhost = constmap(&maproutes,host.s + i,host.len - i))) + break; + if (relayhost && !*relayhost) relayhost = 0; + + if (relayhost) { + i = str_chr(relayhost,':'); + if (relayhost[i]) { + scan_ulong(relayhost + i + 1,&port); + relayhost[i] = 0; + } + if (!stralloc_copys(&host,relayhost)) temp_nomem(); + } + + + addrmangle(&sender,argv[2]); + + if (!saa_readyplus(&reciplist,0)) temp_nomem(); + if (ipme_init() != 1) temp_oserr(); + if (ip6me_init() != 1) temp_oserr(); + + recips = argv + 3; + while (*recips) { + if (!saa_readyplus(&reciplist,1)) temp_nomem(); + reciplist.sa[reciplist.len] = sauninit; + addrmangle(reciplist.sa + reciplist.len,*recips); + ++reciplist.len; + ++recips; + } + + + random = now() + (getpid() << 16); + switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random)) { + case DNS_MEM: temp_nomem(); + case DNS_SOFT: temp_dns(); + case DNS_HARD: perm_dns(); + case 1: + if (ip.len <= 0) temp_dns(); + } + + if (ip.len <= 0) perm_nomx(); + + prefme = 100000; + for (i = 0;i < ip.len;++i) + if (ipme_is(&ip.ix[i].ip)) + if (ip.ix[i].pref < prefme) + prefme = ip.ix[i].pref; + + if (relayhost) prefme = 300000; + + for (i = 0;i < ip.len;++i) + if (ip.ix[i].pref < prefme) + break; + + if (i >= ip.len) + perm_ambigmx(); + + for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme) { + if (tcpto(&ip.ix[i].ip)) continue; + + smtpfd = socket(AF_INET6,SOCK_STREAM,0); + if (smtpfd == -1) temp_oserr(); + + if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { + tcpto_err(&ip.ix[i].ip,0); + partner = ip.ix[i].ip; + smtp(); /* does not return */ + } + tcpto_err(&ip.ix[i].ip,errno == error_timeout); + close(smtpfd); + } + + temp_noconn(); +} diff --git a/src/mxf-remote/mxf-remote-qmtpc.c b/src/mxf-remote/mxf-remote-qmtpc.c diff --git a/src/mxf-remote/mxf-remote-smtpc.c b/src/mxf-remote/mxf-remote-smtpc.c diff --git a/src/mxf-remote/qmail-remote.c b/src/mxf-remote/qmail-remote.c @@ -324,115 +324,3 @@ void addrmangle(stralloc *saout, char *s) if (!stralloc_cat(saout,&canonhost)) temp_nomem(); } - -void getcontrols() -{ - if (control_init() == -1) temp_control(); - if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control(); - if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1) - temp_control(); - if (control_rldef(&helohost,"control/helohost",1,NULL) != 1) - temp_control(); - switch(control_readfile(&routes,"control/smtproutes",0)) { - case -1: - temp_control(); - case 0: - if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); break; - case 1: - if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; - } -} - -int main(int argc, char **argv) -{ - static ipalloc ip = {0}; - int i; - unsigned long random; - char **recips; - unsigned long prefme; - char *relayhost; - - sig_pipeignore(); - if (argc < 4) perm_usage(); - if (chdir(auto_qmail) == -1) temp_chdir(); - getcontrols(); - - - if (!stralloc_copys(&host,argv[1])) temp_nomem(); - - relayhost = 0; - for (i = 0;i <= host.len;++i) - if ((i == 0) || (i == host.len) || (host.s[i] == '.')) - if ((relayhost = constmap(&maproutes,host.s + i,host.len - i))) - break; - if (relayhost && !*relayhost) relayhost = 0; - - if (relayhost) { - i = str_chr(relayhost,':'); - if (relayhost[i]) { - scan_ulong(relayhost + i + 1,&port); - relayhost[i] = 0; - } - if (!stralloc_copys(&host,relayhost)) temp_nomem(); - } - - - addrmangle(&sender,argv[2]); - - if (!saa_readyplus(&reciplist,0)) temp_nomem(); - if (ipme_init() != 1) temp_oserr(); - if (ip6me_init() != 1) temp_oserr(); - - recips = argv + 3; - while (*recips) { - if (!saa_readyplus(&reciplist,1)) temp_nomem(); - reciplist.sa[reciplist.len] = sauninit; - addrmangle(reciplist.sa + reciplist.len,*recips); - ++reciplist.len; - ++recips; - } - - - random = now() + (getpid() << 16); - switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random)) { - case DNS_MEM: temp_nomem(); - case DNS_SOFT: temp_dns(); - case DNS_HARD: perm_dns(); - case 1: - if (ip.len <= 0) temp_dns(); - } - - if (ip.len <= 0) perm_nomx(); - - prefme = 100000; - for (i = 0;i < ip.len;++i) - if (ipme_is(&ip.ix[i].ip)) - if (ip.ix[i].pref < prefme) - prefme = ip.ix[i].pref; - - if (relayhost) prefme = 300000; - - for (i = 0;i < ip.len;++i) - if (ip.ix[i].pref < prefme) - break; - - if (i >= ip.len) - perm_ambigmx(); - - for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme) { - if (tcpto(&ip.ix[i].ip)) continue; - - smtpfd = socket(AF_INET,SOCK_STREAM,0); - if (smtpfd == -1) temp_oserr(); - - if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { - tcpto_err(&ip.ix[i].ip,0); - partner = ip.ix[i].ip; - smtp(); /* does not return */ - } - tcpto_err(&ip.ix[i].ip,errno == error_timeout); - close(smtpfd); - } - - temp_noconn(); -} diff --git a/src/qmail-qmtpd.c b/src/qmail-qmtpd.c @@ -168,7 +168,7 @@ main() bytestooverflow = 0; qmail_fail(&qq); } - while (len > 0) { /* XXX: could speed this up, obviously */ + while (len > 0) { /* XXX: could speed this up, if we wanted to use nbio */ substdio_get(&ssin,&ch,1); --len; qmail_put(&qq,&ch,1); @@ -247,10 +247,10 @@ main() if (!flagbother) qmail_fail(&qq); result = qmail_close(&qq); - if (!flagsenderok) result = "Dunacceptable sender (#5.1.7)"; - if (databytes) if (!bytestooverflow) result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)"; - if (realrcptto_deny()) result = "Dsorry, no mailbox here by that name. (#5.1.1)\r\n"; - + if (!flagsenderok) result = "DUnacceptable sender (#5.1.7)"; + if (databytes) if (!bytestooverflow) result = "DSorry, that message size exceeds my databytes limit. (#5.3.4)"; + if (realrcptto_deny()) result = "DSorry, no mailbox here by that name. (#5.1.1)\r\n"; + if (*result) len = str_len(result); else { @@ -260,25 +260,26 @@ main() len += fmt_ulong(buf2 + len,(unsigned long) now()); len += fmt_str(buf2 + len," qp "); len += fmt_ulong(buf2 + len,qp); + len += fmt_str(buf2 + len," - we'll give it our best shot!"); buf2[len] = 0; result = buf2; } - + len = fmt_ulong(buf,len); buf[len++] = ':'; len += fmt_str(buf + len,result); buf[len++] = ','; - + for (i = 0;i < failure.len;++i) switch(failure.s[i]) { case 0: substdio_put(&ssout,buf,len); break; case 'D': - substdio_puts(&ssout,"66:Dsorry, that domain isn't in my list of allowed rcpthosts (#5.7.1),"); + substdio_puts(&ssout,"67:DSorry, that domain isn't in my list of allowed rcpthosts. (#5.7.1),"); break; default: - substdio_puts(&ssout,"46:Dsorry, I can't handle that recipient (#5.1.3),"); + substdio_puts(&ssout,"47:DSorry, I can't handle that recipient. (#5.1.3),"); break; } diff --git a/src/qmail-remote.c b/src/qmail-remote.c @@ -320,22 +320,22 @@ int main(int argc, char **argv) char **recips; unsigned long prefme; char *relayhost; - + sig_pipeignore(); if (argc < 4) perm_usage(); if (chdir(auto_qmail) == -1) temp_chdir(); getcontrols(); - - + + if (!stralloc_copys(&host,argv[1])) temp_nomem(); - + relayhost = 0; for (i = 0;i <= host.len;++i) if ((i == 0) || (i == host.len) || (host.s[i] == '.')) if ((relayhost = constmap(&maproutes,host.s + i,host.len - i))) break; if (relayhost && !*relayhost) relayhost = 0; - + if (relayhost) { i = str_chr(relayhost,':'); if (relayhost[i]) { @@ -347,10 +347,10 @@ int main(int argc, char **argv) addrmangle(&sender,argv[2]); - + if (!saa_readyplus(&reciplist,0)) temp_nomem(); if (ipme_init() != 1) temp_oserr(); - + recips = argv + 3; while (*recips) { if (!saa_readyplus(&reciplist,1)) temp_nomem(); @@ -360,7 +360,7 @@ int main(int argc, char **argv) ++recips; } - + random = now() + (getpid() << 16); switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random)) { case DNS_MEM: temp_nomem(); @@ -369,30 +369,30 @@ int main(int argc, char **argv) case 1: if (ip.len <= 0) temp_dns(); } - + if (ip.len <= 0) perm_nomx(); - + prefme = 100000; for (i = 0;i < ip.len;++i) if (ipme_is(&ip.ix[i].ip)) if (ip.ix[i].pref < prefme) prefme = ip.ix[i].pref; - + if (relayhost) prefme = 300000; - + for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme) break; - + if (i >= ip.len) perm_ambigmx(); - + for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme) { if (tcpto(&ip.ix[i].ip)) continue; - + smtpfd = socket(AF_INET,SOCK_STREAM,0); if (smtpfd == -1) temp_oserr(); - + if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { tcpto_err(&ip.ix[i].ip,0); partner = ip.ix[i].ip; @@ -401,6 +401,6 @@ int main(int argc, char **argv) tcpto_err(&ip.ix[i].ip,errno == error_timeout); close(smtpfd); } - + temp_noconn(); }