commit 27a8117b249633808e6a5d049f29714342f368ee
parent 9e99181f7f34b859b62efb9c44fff0daa27ff253
Author: Ellenor Malik <ellenor@umbrellix.net>
Date: Tue, 27 Sep 2022 13:04:52 +0000
it's not complete yet, don't try it !
Diffstat:
12 files changed, 611 insertions(+), 596 deletions(-)
diff --git a/src/mxf-remote/NOTES.qmailr b/src/mxf-remote/NOTES.qmailr
@@ -1,4 +0,0 @@
-## 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/errs.c b/src/mxf-remote/errs.c
@@ -0,0 +1,86 @@
+#include "mxf-remote.h"
+#include <stdlib.h>
+
+extern stralloc host;
+//extern buffer errbufsmall;
+#define _HSC {
+
+void out(char *s) _HSC
+ if (buffer_puts(errbufsmall,s) == -1) _exit(0);
+}
+void zero() _HSC
+ if (buffer_put(errbufsmall,"\0",1) == -1) _exit(0);
+}
+void _noreturn_ zerodie() _HSC
+ zero();
+ buffer_flush(errbufsmall);
+ _exit(0);
+}
+void outsafe(stralloc *sa) _HSC
+ int i; char ch;
+ for (i = 0;i < sa->len;++i) {
+ ch = sa->s[i];
+ if (ch < 33) ch = '?';
+ if (ch > 126) ch = '?';
+ if (buffer_put(errbufsmall,&ch,1) == -1) _exit(0);
+ }
+}
+
+void _noreturn_ temp_nomem() _HSC
+ out("ZOut of memory. (#4.3.0)\n");
+ zerodie();
+}
+void _noreturn_ temp_oserr() _HSC
+ out("ZSystem resources temporarily unavailable. (#4.3.0)\n");
+ zerodie();
+}
+void _noreturn_ temp_noconn() _HSC
+ out("ZSorry, I wasn't able to establish a connection to the target mail exchanger. (#4.4.1)\n");
+ zerodie();
+}
+void _noreturn_ temp_read() _HSC
+ out("ZUnable to read message. (#4.3.0)\n");
+ zerodie();
+}
+void _noreturn_ temp_dnscanon() _HSC
+ out("ZCNAME lookup failed temporarily. (#4.4.3)\n");
+ zerodie();
+}
+void _noreturn_ temp_dns() _HSC
+ out("ZSorry, I couldn't find any host by that name. (#4.1.2)\n");
+ zerodie();
+}
+void _noreturn_ temp_chdir() _HSC
+ out("ZUnable to switch to home directory. (#4.3.0)\n");
+ zerodie();
+}
+void _noreturn_ temp_control() _HSC
+ out("ZMissing or malformed control files. (#4.3.0)\n");
+ zerodie();
+}
+// The next one belongs in -smtpc only, but is retained here.
+void _noreturn_ perm_partialline() _HSC
+ out("DSMTP cannot transfer messages with partial final lines. (#5.6.2)\n");
+ zerodie();
+}
+void _noreturn_ perm_usage() _HSC
+ out("DI (");
+ out(&progname);
+ out(") was invoked improperly. If this occurs in mxf-remote-qmtpc or mxf-remote-smtpc, that means there is a bug in mxf-remote - contact the developers to report this! (#5.3.5)\n");
+ zerodie();
+}
+void _noreturn_ perm_dns() _HSC
+ out("DSorry, I couldn't find any host named ");
+ outsafe(&host);
+ out(". (#5.1.2)\n");
+ zerodie();
+}
+void _noreturn_ perm_nomx() _HSC
+ out("DSorry, I couldn't find a mail exchanger, SRV _smtp._tcp or IP address. (#5.4.4)\n");
+ zerodie();
+}
+void _noreturn_ perm_ambigmx() _HSC
+ out("DSorry. Although I'm listed as a best-preference MX or A for that host,\n" // this is legal nowadays, so we do it fbo highlighters
+ "it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
+ zerodie();
+}
diff --git a/src/mxf-remote/errs.h b/src/mxf-remote/errs.h
@@ -0,0 +1,21 @@
+#include <skalibs/stralloc.h>
+
+#define _HSC ;
+
+void out(char *s) _HSC
+void zero() _HSC
+void _noreturn_ zerodie() _HSC
+void outsafe(stralloc *sa) _HSC
+void _noreturn_ temp_nomem() _HSC
+void _noreturn_ temp_oserr() _HSC
+void _noreturn_ temp_noconn() _HSC
+void _noreturn_ temp_read() _HSC
+void _noreturn_ temp_dnscanon() _HSC
+void _noreturn_ temp_dns() _HSC
+void _noreturn_ temp_chdir() _HSC
+void _noreturn_ temp_control() _HSC
+void _noreturn_ perm_partialline() _HSC
+void _noreturn_ perm_usage() _HSC
+void _noreturn_ perm_dns() _HSC
+void _noreturn_ perm_nomx() _HSC
+void _noreturn_ perm_ambigmx() _HSC
diff --git a/src/mxf-remote/ga_foreach.h b/src/mxf-remote/ga_foreach.h
@@ -0,0 +1,2 @@
+#include <skalibs/genalloc.h>
+#define ga_foreach(type, m, idx) for (idx = 0; (idx * sizeof(type)) < (m)->len; ++idx)
diff --git a/src/mxf-remote/mxf-remote-connector.c b/src/mxf-remote/mxf-remote-connector.c
@@ -1,266 +0,0 @@
-#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"
-
-#define HUGESMTPTEXT 5000
-
-#define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */
-unsigned long port = PORT_SMTP;
-
-/* mxfnotes
- * How much of this is really the remit of
- */
-GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
-GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,10,saa_readyplus)
-static stralloc sauninit = {0};
-
-stralloc helohost = {0};
-stralloc routes = {0};
-struct constmap maproutes;
-stralloc host = {0};
-stralloc sender = {0};
-
-saa reciplist = {0};
-
-struct ip_address partner;
-
-void out(char *s)
-{
- if (substdio_puts(subfdoutsmall,s) == -1) _exit(0);
-}
-void zero() {
- if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0);
-}
-void _noreturn_ zerodie() {
- zero(); substdio_flush(subfdoutsmall); _exit(0);
-}
-void outsafe(stralloc *sa) {
- int i;
- char ch;
- for (i = 0;i < sa->len;++i) {
- ch = sa->s[i];
- if (ch < 33) ch = '?';
- if (ch > 126) ch = '?';
- if (substdio_put(subfdoutsmall,&ch,1) == -1) _exit(0);
- }
-}
-
-void _noreturn_ temp_nomem() {
- out("ZOut of memory. (#4.3.0)\n"); zerodie();
-}
-void _noreturn_ temp_oserr() {
- out("ZSystem resources temporarily unavailable. (#4.3.0)\n"); zerodie();
-}
-void _noreturn_ temp_noconn() {
- out("ZSorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n");
- zerodie();
-}
-void _noreturn_ temp_read() {
- out("ZUnable to read message. (#4.3.0)\n");
- zerodie();
-}
-void _noreturn_ temp_dnscanon() {
- out("ZCNAME lookup failed temporarily. (#4.4.3)\n");
- zerodie();
-}
-void _noreturn_ temp_dns() {
- out("ZSorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie();
-}
-void _noreturn_ temp_chdir() {
- out("ZUnable to switch to home directory. (#4.3.0)\n"); zerodie();
-}
-void _noreturn_ temp_control() {
- out("ZUnable to read control files. (#4.3.0)\n"); zerodie();
-}
-void _noreturn_ perm_partialline() {
- out("DSMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); zerodie();
-}
-void _noreturn_ perm_usage() {
- out("DI (qmail-remote) was invoked improperly. (#5.3.5)\n"); zerodie();
-}
-void _noreturn_ perm_dns() {
- out("DSorry, I couldn't find any host named ");
- outsafe(&host);
- out(". (#5.1.2)\n"); zerodie();
-}
-void _noreturn_ perm_nomx() {
- out("DSorry, I couldn't find a mail exchanger or IP address. (#5.4.4)\n");
- zerodie();
-}
-void _noreturn_ perm_ambigmx() {
- out("DSorry. Although I'm listed as a best-preference MX or A for that host,\n\
- it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
- zerodie();
-}
-
-void outhost()
-{
- char x[IPFMT];
- if (substdio_put(subfdoutsmall,x,ip_fmt(x,&partner)) == -1) _exit(0);
-}
-
-int flagcritical = 0;
-
-void _noreturn_ dropped() {
- out("ZConnected to ");
- outhost();
- out(" but connection died. ");
- if (flagcritical) out("Possible duplicate! ");
- out("(#4.4.2)\n");
- zerodie();
-}
-
-int timeoutconnect = 60;
-int smtpfd;
-int timeout = 1200;
-
-GEN_SAFE_TIMEOUTREAD(saferead,timeout,smtpfd,dropped())
-GEN_SAFE_TIMEOUTWRITE(safewrite,timeout,smtpfd,dropped())
-
-char inbuf[1024];
-substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
-char smtptobuf[1024];
-substdio smtpto = SUBSTDIO_FDBUF(safewrite,-1,smtptobuf,sizeof(smtptobuf));
-char smtpfrombuf[128];
-substdio smtpfrom = SUBSTDIO_FDBUF(saferead,-1,smtpfrombuf,sizeof(smtpfrombuf));
-
-stralloc smtptext = {0};
-
-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-smtpc.c b/src/mxf-remote/mxf-remote-smtpc.c
diff --git a/src/mxf-remote/mxf-remote.c b/src/mxf-remote/mxf-remote.c
@@ -0,0 +1,267 @@
+/* src/mxf-remote/mxf-remote.c
+ * requires skalibs, s6-networking and s6-dns; if not, you must
+ * use regular qmail-remote with the attendant loss of
+ * functionality with qmtp, tls and ipv6.
+ */
+
+#include "mxf-remote.h"
+#include "errs.h"
+//#include <s6-dns/skadns.h>
+
+tain deadline, stamp;
+//protocol_t protocols[SLICE_LAST];
+genalloc protocols = GENALLOC_ZERO; // genalloc_*(protocol_t
+genalloc maproutes = GENALLOC_ZERO; // genalloc_*(constmap
+char errbuf[512];
+buffer buffer_2_ = BUFFER_INIT(&buffer_write, 2, errbuf, 512);
+char errbufsmall[256];
+skadns_t dnsres = SKADNS_ZERO;
+//char namechosen[513]; // That's more bytes than we will ever need.
+//char progname[61] = "mxf-remote";
+//stralloc sa = STRALLOC_ZERO; // ?
+int timeoutconnect = 60; int mtpfd;
+stralloc host = STRALLOC_ZERO, sender = STRALLOC_ZERO, protocolsraw = STRALLOC_ZERO;
+//genalloc recips = GENALLOC_ZERO; // will be filled with strallocs
+//struct constmap maproutes[SLICE_LAST];
+char PROTOMAPTEXT[] = "qmtps 6209 3 Y N mxf-remote-qmtpc\n" \
+ "qmtp 209 1 N N mxf-remote-qmtpc\n" \
+ "smtp 25 0 N Y mxf-remote-smtpc\n";
+protocol_t slicemap[MAXSLICES]; // on solaris this'll be about 18kB. No idea if it'll be used.
+
+int protocols_init (genalloc *protos, char *protomaptext, size_t length)
+{
+ size_t i = 0;
+ char field = 0; /* there are six fields, numbered zero to five
+ * their identities are:
+ * protoname, port, protoslice, cantls, musttls, executable-filename
+ */
+ size_t lpos = 0, fpos = 0; // record the position we are at in the line
+ char comment = 0; //true if line is comment
+
+ for (i = 0; i < MAXSLICES; i++) {
+ slicemap[i] = {"", 0, -1, 0, 0, ""};
+ }
+ if (length == 0) {
+ protomaptext = PROTOMAPTEXT;
+ length = strlen(PROTOMAPTEXT); // that string has no funny business with nulls so we can do this
+ }
+ protocol_t proto;
+ memset(&proto, 0, sizeof(protocol_t));
+ proto.slice = 1;
+
+ for {i = lpos = fpos = 0; i < length; ++i) {
+ switch (protomaptext[i]) {
+/* case '\r': // activate this when we have our own control_readfile; until then it's no use
+ continue; */ // probably DOS; tolerate bad input. If the user is really using a CR in a filename or w/e, it'll be eaten up by a \ doing runahead.
+ case '\n': // still needed by the default protomap
+ case 0:
+ switch (fpos) {
+ case 0: continue; // it was an empty line
+ default : break;
+ }
+ lpos = 0;
+ field = 0;
+ comment = 0;
+ getroutes(&proto);
+ if (proto.srvservice[0] != 0) if (!genalloc_catb(protocol_t, protocols, &proto, 1)) return 0;
+ if (proto.slice > -1 && proto.slice < 16) memcpy(&(slicemap[proto.slice]), &proto, sizeof(protocol_t));
+ memset(&proto, 0, sizeof(protocol_t));
+ proto.slice = 1;
+ break;
+ case ' ':
+ switch (comment) {
+ case 1: continue;
+ default : break;
+ }
+ switch (field) {
+ case 5:
+ break;
+ case 0:
+ switch (fpos) {
+ case 0:
+ ++comment
+ break;
+ default :
+ proto.srvservice[fpos] = 0; // nullcap the srvservice
+ fpos = 0;
+ ++field;
+ }
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ fpos = 0;
+ ++field;
+ break;
+ }
+ break;
+ case '\\':
+ switch (comment) {
+ case 1: continue;
+ default : break;
+ }
+ // The synopsis of \ is: Take next character verbatim into current field, no matter what it is.
+ switch (field) {
+ case 5:
+ if (fpos > PATH_MAX) temp_control(); // malformed
+ proto.app[fpos] = protomaptext[i];
+ break;
+ case 0:
+ proto.srvservice[fpos] = protomaptext[++i];
+ case 1: // It is ignorable in fields 2-5.
+ case 2:
+ case 3:
+ case 4:
+ if (i >= length) temp_control(); // malformed
+ if (protomaptext[i] == 0) temp_control(); // malformed
+ ++lpos;
+ break;
+ }
+ break;
+ default :
+ switch (comment) {
+ case 1: continue;
+ default : break;
+ }
+ switch (field) {
+ case 0: // protoname
+ if (fpos < MAXSRVLEN) proto.srvservice[fpos] = protomaptext[i];
+ break;
+ case 5: // protoprog (app)
+ if (fpos < PATH_MAX) proto.app[fpos] = protomaptext[i];
+ break;
+ case 1: // defport
+ if (protomaptext[i] < '0') temp_control();
+ if (protomaptext[i] > '9') temp_control();
+ proto.defport = (proto.defport * 10) + (protomaptext[i] - '0');
+ break;
+ case 2: // MXPS slice, DECIMAL
+ if (fpos = 0 && !(protomaptext[i] == '-')) proto.slice = 0; // reset slice to 0 as it is -1
+ if (proto.slice != -1) { // a slice of -1 means we are not using a slice and this is a SRV only protocol
+ switch (protomaptext[i]) {
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9':
+ proto.defport = (proto.defport * 10) + (protomaptext[i] - '0');
+ default : temp_control();
+ }
+ }
+ break;
+ case 3: // is TLS
+ switch (fpos) {
+ case 0: // we only care about char 1
+ switch (protomaptext[i]) {
+ case '0': case 'N': case 'n': case 'F':
+ case 'f':
+ proto.tls = 0;
+ break;
+ case '1': case 'Y': case 'y': case 'T':
+ case 't':
+ proto.tls = 1;
+ default :
+ // in default of a valid choice, we have no choice but to blow up.
+ temp_control();
+ }
+ default : break;
+ }
+ break;
+ case 4: // can TLS (irrelevant if is TLS)
+ switch (fpos + proto.tls) {
+ case 0: // we only care about char 1
+ switch (protomaptext[i]) {
+ case '0': case 'N': case 'n': case 'F':
+ case 'f':
+ proto.starttls = 0;
+ break;
+ case '1': case 'Y': case 'y': case 'T':
+ case 't':
+ proto.starttls = 1;
+ default :
+ // in default of a valid choice, we have no choice but to blow up.
+ temp_control();
+ }
+ default : break;
+ }
+ break;
+ }
+ }
+ ++lpos; ++fpos; // if comment notreached; lpos and fpos don't run if we are in a comment.
+ // lpos and fpos only increase 1 for every 2 characters if every first character is a \
+ }
+ // the music is over. we're at the end of the file, on a partial line. hopefully app is at least an app we can try.
+ if (proto.app[0] != 0) if (!genalloc_catb(protocol_t, &proto, 1)) return 0;
+ return 1;
+
+}
+
+// imported from qmail-remote
+// inputs: controlfiles from the filesystem
+// outputs: none
+// SE: reads control files
+
+void getcontrols()
+{
+ if (control_init() == -1) temp_control();
+ //if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control();
+ // The client application is responsible for this.
+ if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1)
+ temp_control();
+ if (control_rldef(&helohost,"control/helohost",1,NULL) != 1)
+ temp_control();
+ switch(control_readfile(&protocolsraw,"control/protocols",0)) {
+ case -1:
+ temp_control();
+ case 0:
+ if (!protocols_init(&(protocols),"",0)) temp_nomem(); break;
+ case 1:
+ if (!protocols_init(&(protocols),protocolsraw.s,protocolsraw.len)) temp_nomem(); break;
+ }
+}
+
+void getroutes (protocol_t *protocol)
+{
+ stralloc ctrl = STRALLOC_ZERO, routes = STRALLOC_ZERO;
+ stralloc_copys(&ctrl, "control/");
+ stralloc_cats(&ctrl, protocol->srvservice);
+ stralloc_cats(&ctrl, "routes");
+ stralloc_0(&ctrl);
+ switch(control_readfile(&routes,ctrl.s,0)) {
+ case -1: temp_control();
+ case 0:
+ if (!constmap_init(&(protocol->maproutes),"",0,1)) temp_nomem(); break;
+ case 1:
+ if (!constmap_init(&(protocol->maproutes),routes.s,routes.len,1)) temp_nomem(); break;
+ }
+ stralloc_free(&ctrl);
+}
+
+// so we need to deduce the hostname to feed to tcpclient, then we need to launch tcpclient (s/qmtpc), wait on its exit, and maybe try again with a different hostname
+// we could also, if we wanted, be tcpclient ourselves, and just launch things under ucspitlsclient as relevant
+
+int main (int argcount, char **args)
+{
+ char **rrecips, *relayhost;
+ int i;
+ PROG = "mxf-remote";
+
+ tain_now(&stamp);
+ tain_addsecond_deadline(&deadline, &stamp, 2); // eh, the manpages at skarnet's website said to do this so I will
+ if (!skadns_startf(&dnsres, &deadline, &stamp)) {
+ buffer_puts(buffer_2, "ZDNS resolution startup timeout\n");
+ buffer_flush(buffer_2);
+ exit(111);
+ }
+
+ sig_ignore(SIGPIPE);
+ if (argcount < 4) perm_usage();
+ if (chdir(auto_qmail) == -1) temp_chdir();
+ if (!stralloc_copys(&host,args[1])) temp_nomem();
+
+ getcontrols();
+
+ /* TODO: actually send the letter */
+
+ skadns_end(&dnsres);
+}
+#endif
diff --git a/src/mxf-remote/mxf-remote-qmtpc.c b/src/mxf-remote/mxf-remote.do
diff --git a/src/mxf-remote/mxf-remote.h b/src/mxf-remote/mxf-remote.h
@@ -0,0 +1,127 @@
+#ifndef MXF_REMOTE_H
+#define MXF_REMOTE_H
+
+#include "ga_foreach.h"
+#include "controls.h"
+// Needs control.o from NMMail
+#include "netstrings.h"
+// Needs netstrings.o from NMMail
+#include "auto_qmail.h"
+// Needs auto-qmail from NMMail
+#include "constmap.h"
+// Needs constmap from NMMail
+#include "ipme.h"
+// Needs IPMe from NMMail
+#ifndef USING_SKALIBS
+#error "You must use skalibs, s6-networking and s6-dns for Nightmare Remote."
+#else
+#include <stdlib.h>
+#include <signal.h>
+#include <limits.h>
+#include <skalibs/tai.h>
+#include <skalibs/types.h>
+#include <skalibs/iopause.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/sig.h>
+#include <skalibs/selfpipe.h>
+//#include <skalibs/strerr2.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/socket.h>
+#include <skalibs/ip46.h>
+#include <s6-dns/s6dns.h>
+#include <s6-dns/skadns.h>
+
+
+#ifndef UCSPITLSC
+#define UCSPITLSC "/package/net/s6-networking/command/s6-ucspitlsc"
+#endif
+
+#ifndef TLSC
+#define TLSC "/package/net/s6-networking/command/s6-tlsc"
+#endif
+
+/* we are tcpclient, so we don't need this
+#ifndef TCPCLIENT
+#define TCPCLIENT "/package/net/s6-networking/command/s6-tcpclient"
+#endif
+ */
+
+// expected to be in PATH and to be ucspi client tools
+/* already configured elsewhere
+#ifndef QMTP_CLIENT
+#define QMTP_CLIENT "mxf-remote-qmtpc"
+#endif
+
+#ifndef SMTP_CLIENT
+#define SMTP_CLIENT "mxf-remote-smtpc"
+#endif
+ */
+#define DIE_RESOURCES 111
+#define DIE_BADPROTO 100
+
+// Only use these on verified MXPS distances!
+// xmxps_prio will never be used, it is just an example
+//#define _xmxps_prio(distance) (((distance - MXPS_MAGIC_NO) & 0xfff0) >> 4)
+#define _mxps_prio(distance) ((distance & 0x00f0) >> 4)
+#define _mxps_slice(distance) (distance & 0x000f)
+#define _is_mxps(distance) ((distance + 1 > MXPS_MAGIC_NO) && (distance - 1 < MXPS_END))
+
+#define MXPS_MAGIC_NO 0x3200
+#define MXPS_END 0x32ff
+/*
+#define SRV_TCP "tcp"
+#define SRV_QMTP "qmtp"
+#define SRV_QMTPS "qmtps"
+#define SRV_SMTP "smtp"
+#define SRV_SMTPS "smtps" // officially deregistered by IANA; continued here
+#define SLICE_LAST 0x3f
+#define SLICE_SRV_SMTP 0x010 // all +16 from the kosher slices
+#define SLICE_SRV_QMTP 0x011
+#define SLICE_SRV_SMTPS 0x012
+#define SLICE_SRV_QMTPS 0x013
+#define SLICE_SMTP 0x00
+#define SLICE_QMTP 0x01
+#define SLICE_SMTPS 0x02 // we propose the use of 2 and 3 for -s versions of protocols.
+#define SLICE_QMTPS 0x03
+ */
+#define NOMXPS_SLICE 0x0
+#define MAXSLICES 16 // there will never be more than 16 slices. advancement in internet mail is likely to use SRV records
+
+// defaults, in case not a SRV record
+/*
+#define SMTP_PORT 25
+#define SMTPS_PORT 465 // Should be all but extinct; most SMTPS servers support STARTTLS
+#define QMTP_PORT 209
+#define QMTPS_PORT 6209 // Not extinct at all; QMTP has no STARTTLS support and will never grow it.
+*/
+#define FALSE 0
+#define TRUE 1
+#define MAXSRVLEN 64
+
+typedef struct {
+ char srvservice[MAXSRVLEN]; // by happy coincidence we can also use this as the prefix for control/<service>routes filenames
+ uint16_t defport; // default port
+ signed char slice; // plain mx = 0
+ unsigned int tls:1; // TLSC
+ unsigned int starttls:1; // UCSPITLSC
+ char app[PATH_MAX];
+ constmap maproutes; // control/<service>routes
+} protocol_t;
+
+typedef struct {
+ uint16_t port;
+ uint16_t weight; // 0 for MX, A/AAAA, and SLIP; as published for SRV
+ char name[256]; // may be a hostname or an IP; will not contain ports or priorities.
+ //unsigned int isv6:1;
+} mxresult_t; // stores processed results; SRV hostnames and ports, MX hostnames and ports, IPs (A/AAAA legacy, string literal IPs)
+
+typedef struct {
+ uint32_t prio; // lower is to try sooner
+ genalloc mxresults; // &(mxresults.s) is of type mxresult_t
+} mxprio_t;
+
+//extern protocol_t protocols; // 16 slices, plus the 4 SRV pseudo slices plus unknown more
+//extern buffer errbuf, errbufsmall;
+extern char const *PROG;
+
+#endif
diff --git a/src/mxf-remote/mxf-remote.h.sav b/src/mxf-remote/mxf-remote.h.sav
@@ -0,0 +1,105 @@
+#ifndef MXF_REMOTE_H
+#define MXF_REMOTE_H
+
+#include "controls.h"
+// Needs control.o from qmail
+#ifndef USING_SKALIBS
+#error "You must use skalibs, s6-networking and s6-dns for Nightmare Remote."
+#else
+#include <limits.h>
+#include <skalibs/ip46.h>
+#include <skalibs/tai.h>
+#include <skalibs/types.h>
+#include <skalibs/iopause.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/socket.h>
+#include <s6-dns/s6dns.h>
+#include <s6-dns/skadns.h>
+
+
+#ifndef UCSPITLSC
+#define UCSPITLSC "/package/net/s6-networking/command/s6-ucspitlsc"
+#endif
+
+#ifndef TLSC
+#define TLSC "/package/net/s6-networking/command/s6-tlsc"
+#endif
+
+#ifndef TCPCLIENT
+#define TCPCLIENT "/package/net/s6-networking/command/s6-tcpclient"
+#endif
+
+// expected to be in PATH and to be ucspi client tools
+#ifndef QMTP_CLIENT
+#define QMTP_CLIENT "mxf-remote-qmtpc"
+#endif
+
+#ifndef SMTP_CLIENT
+#define SMTP_CLIENT "mxf-remote-smtpc"
+#endif
+
+#define DIE_RESOURCES 111
+#define DIE_BADPROTO 100
+//#define DIE_
+
+// Only use these on verified MXPS distances!
+// xmxps_prio will never be used, it is just an example
+//#define _xmxps_prio(distance) (((distance - MXPS_MAGIC_NO) & 0xfff0) >> 4)
+#define _mxps_prio(distance) ((distance & 0x00f0) >> 4)
+#define _mxps_slice(distance) (distance & 0x000f)
+#define _is_mxps(distance) ((distance + 1 > MXPS_MAGIC_NO) && (distance - 1 < MXPS_END))
+
+#define MXPS_MAGIC_NO 0x3200
+#define MXPS_END 0x32ff
+#define SRV_TCP "tcp"
+#define SRV_RQMTP "qmtp-relay"
+#define SRV_QMTP "qmtp"
+#define SRV_RQMTPS "qmtps-relay"
+#define SRV_QMTPS "qmtps"
+#define SRV_SMTP "smtp"
+#define SRV_RSMTP "smtp-relay"
+#define SRV_SMTPS "smtps" // officially deregistered by IANA; continued here
+#define SRV_RSMTPS "smtps-relay"
+#define SLICE_SRV_SMTP 0x010 // all +32 from the kosher slices
+#define SLICE_SRV_QMTP 0x011
+#define SLICE_SRV_SMTPS 0x012
+#define SLICE_SRV_QMTPS 0x013
+#define SLICE_SMTP 0x00
+#define SLICE_QMTP 0x01
+#define SLICE_SMTPS 0x02 // we propose the use of 2 and 3 for -s versions of protocols.
+#define SLICE_QMTPS 0x03
+#define NOMXPS_SLICE 0x0
+
+// defaults, in case not a SRV record
+#define SMTP_PORT 25
+#define SMTPS_PORT 465 // Should be all but extinct; most SMTPS servers support STARTTLS
+#define QMTP_PORT 209
+#define QMTPS_PORT 6209 // Not extinct at all; QMTP has no STARTTLS support and will never grow it.
+
+#define FALSE 0
+#define TRUE 1
+
+typedef struct {
+ //unsigned int slice:4; // plain mx = 0
+ char srvservice[64];
+ char tool[PATH_MAX];
+ unsigned int starttls:1; // UCSPITLSC
+ unsigned int tls:1; // TLSC
+} protocol_t;
+
+typedef struct {
+ uint16_t port;
+ uint16_t weight; // 0 for MX and A/AAAA; as published for SRV
+ char name[256]; // may be a hostname or an IP; will not contain ports or priorities.
+ //unsigned int isv6:1;
+} mxresult_t; // stores processed results; SRV hostnames and ports, MX hostnames and ports, IPs (A/AAAA legacy)
+
+typedef struct {
+ uint32_t prio; // lower is to try sooner; we want a 32 bit value to apply slice-based offsets ((ffff-slice)<<16; try higher slice numbers sooner)
+ genalloc mxresults; // should be of type mxresult_t
+} mxprio_t;
+
+extern protocol_t protocols[32]; // 16 slices, plus the 4 SRV pseudo slices plus unknown more
+
+#endif
diff --git a/src/mxf-remote/protomapdef b/src/mxf-remote/protomapdef
@@ -0,0 +1,3 @@
+qmtps 6209 3 N Y mxf-remote-qmtpc
+qmtp 209 1 N N mxf-remote-qmtpc
+smtp 25 0 N Y mxf-remote-smtpc
diff --git a/src/mxf-remote/qmail-remote.c b/src/mxf-remote/qmail-remote.c
@@ -1,326 +0,0 @@
-#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"
-
-#define HUGESMTPTEXT 5000
-
-#define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */
-unsigned long port = PORT_SMTP;
-
-GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
-GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,10,saa_readyplus)
-static stralloc sauninit = {0};
-
-stralloc helohost = {0};
-stralloc routes = {0};
-struct constmap maproutes;
-stralloc host = {0};
-stralloc sender = {0};
-
-saa reciplist = {0};
-
-struct ip_address partner;
-
-void out(char *s)
-{
- if (substdio_puts(subfdoutsmall,s) == -1) _exit(0);
-}
-void zero() {
- if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0);
-}
-void _noreturn_ zerodie() {
- zero(); substdio_flush(subfdoutsmall); _exit(0);
-}
-void outsafe(stralloc *sa) {
- int i;
- char ch;
- for (i = 0;i < sa->len;++i) {
- ch = sa->s[i];
- if (ch < 33) ch = '?';
- if (ch > 126) ch = '?';
- if (substdio_put(subfdoutsmall,&ch,1) == -1) _exit(0);
- }
-}
-
-void _noreturn_ temp_nomem() {
- out("ZOut of memory. (#4.3.0)\n"); zerodie();
-}
-void _noreturn_ temp_oserr() {
- out("ZSystem resources temporarily unavailable. (#4.3.0)\n"); zerodie();
-}
-void _noreturn_ temp_noconn() {
- out("ZSorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n");
- zerodie();
-}
-void _noreturn_ temp_read() {
- out("ZUnable to read message. (#4.3.0)\n");
- zerodie();
-}
-void _noreturn_ temp_dnscanon() {
- out("ZCNAME lookup failed temporarily. (#4.4.3)\n");
- zerodie();
-}
-void _noreturn_ temp_dns() {
- out("ZSorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie();
-}
-void _noreturn_ temp_chdir() {
- out("ZUnable to switch to home directory. (#4.3.0)\n"); zerodie();
-}
-void _noreturn_ temp_control() {
- out("ZUnable to read control files. (#4.3.0)\n"); zerodie();
-}
-void _noreturn_ perm_partialline() {
- out("DSMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); zerodie();
-}
-void _noreturn_ perm_usage() {
- out("DI (qmail-remote) was invoked improperly. (#5.3.5)\n"); zerodie();
-}
-void _noreturn_ perm_dns() {
- out("DSorry, I couldn't find any host named ");
- outsafe(&host);
- out(". (#5.1.2)\n"); zerodie();
-}
-void _noreturn_ perm_nomx() {
- out("DSorry, I couldn't find a mail exchanger or IP address. (#5.4.4)\n");
- zerodie();
-}
-void _noreturn_ perm_ambigmx() {
- out("DSorry. Although I'm listed as a best-preference MX or A for that host,\n\
- it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
- zerodie();
-}
-
-void outhost()
-{
- char x[IPFMT];
- if (substdio_put(subfdoutsmall,x,ip_fmt(x,&partner)) == -1) _exit(0);
-}
-
-int flagcritical = 0;
-
-void _noreturn_ dropped() {
- out("ZConnected to ");
- outhost();
- out(" but connection died. ");
- if (flagcritical) out("Possible duplicate! ");
- out("(#4.4.2)\n");
- zerodie();
-}
-
-int timeoutconnect = 60;
-int smtpfd;
-int timeout = 1200;
-
-GEN_SAFE_TIMEOUTREAD(saferead,timeout,smtpfd,dropped())
-GEN_SAFE_TIMEOUTWRITE(safewrite,timeout,smtpfd,dropped())
-
-char inbuf[1024];
-substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
-char smtptobuf[1024];
-substdio smtpto = SUBSTDIO_FDBUF(safewrite,-1,smtptobuf,sizeof(smtptobuf));
-char smtpfrombuf[128];
-substdio smtpfrom = SUBSTDIO_FDBUF(saferead,-1,smtpfrombuf,sizeof(smtpfrombuf));
-
-stralloc smtptext = {0};
-
-static void get(unsigned char *uc)
-{
- char *ch = (char *)uc;
- substdio_get(&smtpfrom,ch,1);
- if (*ch != '\r')
- if (smtptext.len < HUGESMTPTEXT)
- if (!stralloc_append(&smtptext,ch)) temp_nomem();
-}
-
-unsigned long smtpcode()
-{
- unsigned char ch;
- unsigned long code;
-
- if (!stralloc_copys(&smtptext,"")) temp_nomem();
-
- get(&ch); code = ch - '0';
- get(&ch); code = code * 10 + (ch - '0');
- get(&ch); code = code * 10 + (ch - '0');
- for (;;) {
- get(&ch);
- if (ch != '-') break;
- while (ch != '\n') get(&ch);
- get(&ch);
- get(&ch);
- get(&ch);
- }
- while (ch != '\n') get(&ch);
-
- return code;
-}
-
-void outsmtptext()
-{
- int i;
- if (smtptext.s) if (smtptext.len) {
- out("Remote host said: ");
- for (i = 0;i < smtptext.len;++i)
- if (!smtptext.s[i]) smtptext.s[i] = '?';
- if (substdio_put(subfdoutsmall,smtptext.s,smtptext.len) == -1) _exit(0);
- smtptext.len = 0;
- }
-}
-
-void quit(prepend,append)
-char *prepend;
-char *append;
-{
- substdio_putsflush(&smtpto,"QUIT\r\n");
- /* waiting for remote side is just too ridiculous */
- out(prepend);
- outhost();
- out(append);
- out(".\n");
- outsmtptext();
- zerodie();
-}
-
-void blast()
-{
- int r;
- char ch;
-
- for (;;) {
- r = substdio_get(&ssin,&ch,1);
- if (r == 0) break;
- if (r == -1) temp_read();
- if (ch == '.')
- substdio_put(&smtpto,".",1);
- while (ch != '\n') {
- if (ch == '\r') {
- r = substdio_get(&ssin, &ch, 1);
- if (r == 0)
- break;
- if (r == -1) temp_read();
- if (ch != '\n') {
- substdio_put(&smtpto, "\r\n", 2);
- } else
- break;
- }
- substdio_put(&smtpto,&ch,1);
- r = substdio_get(&ssin,&ch,1);
- if (r == 0) perm_partialline();
- if (r == -1) temp_read();
- }
- substdio_put(&smtpto,"\r\n",2);
- }
-
- flagcritical = 1;
- substdio_put(&smtpto,".\r\n",3);
- substdio_flush(&smtpto);
-}
-
-stralloc recip = {0};
-
-void smtp()
-{
- unsigned long code;
- int flagbother;
- int i;
-
- if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
-
- substdio_puts(&smtpto,"HELO ");
- substdio_put(&smtpto,helohost.s,helohost.len);
- substdio_puts(&smtpto,"\r\n");
- substdio_flush(&smtpto);
- if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
-
- substdio_puts(&smtpto,"MAIL FROM:<");
- substdio_put(&smtpto,sender.s,sender.len);
- substdio_puts(&smtpto,">\r\n");
- substdio_flush(&smtpto);
- code = smtpcode();
- if (code >= 500) quit("DConnected to "," but sender was rejected");
- if (code >= 400) quit("ZConnected to "," but sender was rejected");
-
- flagbother = 0;
- for (i = 0;i < reciplist.len;++i) {
- substdio_puts(&smtpto,"RCPT TO:<");
- substdio_put(&smtpto,reciplist.sa[i].s,reciplist.sa[i].len);
- substdio_puts(&smtpto,">\r\n");
- substdio_flush(&smtpto);
- code = smtpcode();
- if (code >= 500) {
- out("h"); outhost(); out(" does not like recipient.\n");
- outsmtptext(); zero();
- }
- else if (code >= 400) {
- out("s"); outhost(); out(" does not like recipient.\n");
- outsmtptext(); zero();
- }
- else {
- out("r"); zero();
- flagbother = 1;
- }
- }
- if (!flagbother) quit("DGiving up on ","");
-
- substdio_putsflush(&smtpto,"DATA\r\n");
- code = smtpcode();
- if (code >= 500) quit("D"," failed on DATA command");
- if (code >= 400) quit("Z"," failed on DATA command");
-
- blast();
- code = smtpcode();
- flagcritical = 0;
- if (code >= 500) quit("D"," failed after I sent the message");
- if (code >= 400) quit("Z"," failed after I sent the message");
- quit("K"," accepted message");
-}
-
-stralloc canonhost = {0};
-stralloc canonbox = {0};
-
-void addrmangle(stralloc *saout, char *s)
-{
- int j;
-
- j = str_rchr(s,'@');
- if (!s[j]) {
- if (!stralloc_copys(saout,s)) temp_nomem();
- return;
- }
- if (!stralloc_copys(&canonbox,s)) temp_nomem();
- canonbox.len = j;
- /* box has to be quoted */
- if (!quote(saout,&canonbox)) temp_nomem();
- if (!stralloc_cats(saout,"@")) temp_nomem();
-
- if (!stralloc_copys(&canonhost,s + j + 1)) temp_nomem();
-
- if (!stralloc_cat(saout,&canonhost)) temp_nomem();
-}