nightmaremail

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

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:
Dsrc/mxf-remote/NOTES.qmailr | 4----
Asrc/mxf-remote/errs.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mxf-remote/errs.h | 21+++++++++++++++++++++
Asrc/mxf-remote/ga_foreach.h | 2++
Dsrc/mxf-remote/mxf-remote-connector.c | 266-------------------------------------------------------------------------------
Dsrc/mxf-remote/mxf-remote-smtpc.c | 0
Asrc/mxf-remote/mxf-remote.c | 267+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/mxf-remote/mxf-remote-qmtpc.c -> src/mxf-remote/mxf-remote.do | 0
Asrc/mxf-remote/mxf-remote.h | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mxf-remote/mxf-remote.h.sav | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mxf-remote/protomapdef | 3+++
Dsrc/mxf-remote/qmail-remote.c | 326-------------------------------------------------------------------------------
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(); -}