commit d939c95346f048faea3bee63ee6139fbfb68e081
parent c179147792c96a289edd536c76a7ff88d1ef6c2a
Author: Lightning <lightning@chatspeed.net>
Date: Wed, 31 Dec 2025 10:11:07 +0000
Readme updates, make it make sense Molnija
Diffstat:
13 files changed, 963 insertions(+), 64 deletions(-)
diff --git a/conf-cc b/conf-cc
@@ -1,3 +1,3 @@
-cc -g -ggdb3 -O0 -Iinclude/ -Iinclude/fmxs -DUSING_SKALIBS -I/package/prog/skalibs/include
+cc -g -ggdb3 -O0 -Iinclude/ -Iinclude/fmxs
This will be used to compile .c files.
diff --git a/conf-groups b/conf-groups
@@ -1,5 +1,5 @@
-qmail
-nofiles
+nmmail
+nmnofi
These are the qmail groups. The second group should not have access to
any files, but it must be usable for processes; this requirement
diff --git a/conf-ld b/conf-ld
@@ -1,3 +1,3 @@
-cc /package/prog/skalibs/library/libskarnet.a
+cc
This will be used to link .o files into an executable.
diff --git a/conf-users b/conf-users
@@ -1,11 +1,11 @@
-alias
-qmaild
-qmaill
+nmalias
+nmaild
+nmaill
root
-qmailp
-qmailq
-qmailr
-qmails
+nmailp
+nmailq
+nmailr
+nmails
The qmail system is heavily partitioned for security; it does almost
nothing as root.
diff --git a/doc/README.mxf b/doc/README.mxf
@@ -23,10 +23,8 @@ Nightmare Mail is also known as MxF, as it'll be included in New Vision,
the working codename for the Falsix operating system, and is our default
Mail eXchanger Service. (Mail eXchanger for Falsix)
-* = Void where prohibited by law. Bitcoin is used as the payment method.
- If the value of 1/6 of my savings account is higher than USD50, you will
- receive the higher amount for successfully triggering a security guarantee
- payment. Local holes experienced by sealed servers will receive 1/2 the
+* = Void where prohibited by law.
+ Local holes experienced by sealed servers will receive 1/2 the
guaranteed amount. Shell server users are backed by the full guarantee.
Only the first reporter of a specific bug may get the guaranteed amount.
@@ -59,13 +57,21 @@ We plan on adding, in the future:
the venerable Laurent Bercot from skarnet.org, uses GNU Make too.
GNU-free build systems will be part of the New Vision project.
- [ ] mxf-lmtpc - QMQP client, but speaking LMTP instead of QMQP.
- In the modern day, many sites use local delivery solutions from
- Dovecot or other IMAP servers, which also support Maildir. These
- LDAs as they're called often speak LMTP, which is a protocol used
- for intra-site email delivery similar to QMQP. Easing the transition
- away from Postfix for sites like this - sites like Umbrellix - is
- a high priority of NightmareMail.
+ [ ] mxf-lmtpc - LMTP delivery channel.
+ LMTP is a local delivery protocol commonly supported by mail
+ servers, used to deliver to mailbox servers like Dovecot.
+
+ mxf-lmtpc aims to add support for these as an optional delivery
+ channel.
+
+ [ ] mxf-lmtpd - LMTP server, to use NightmareLists with other MTAs.
+ LMTP is a local delivery protocol commonly supported by various
+ mailbox servers. Nightmare Mail includes its own mailbox server
+ which can deliver to mboxes, Maildirs or programmes like Nightmare
+ Lists. I can see many reasons why someone would want to use
+ the Nightmare local delivery agent as a channel in Postfix. While
+ this is already possible, having more ways to do the same thing is
+ better.
[ ] QMQP over SSH.
QMQP is an intrasite protocol like LMTP. It doesn't use transport
diff --git a/include/alloc.h b/include/alloc.h
@@ -8,7 +8,7 @@
//#define alloc(x) malloc(x)
-// secure alloc(): zero bytes before use.
+// secureish alloc(): zero bytes before use.
// prevents heartbleed. Hilda
static inline void *alloc(size_t x)
{
diff --git a/include/netstrings.h b/include/netstrings.h
@@ -45,6 +45,7 @@
#define NS_PROTOCOL -2
#define NS_TOOMUCH -3
#define NS_INVAL -4
+#define NS_BYTES -5
#define NS_SUCCESS 0
/* signed char ns_getlength. Returns information as to its success
@@ -55,9 +56,9 @@ signed char ns_getlength (_BUFFER *bio, unsigned long long *length);
/* signed char ns_getcomma. Returns information as to whether the
* next byte is an ASCII comma.
- * Retval: Success if so, protocol if not.
+ * Retval: Success if so, protocol if not (or if ran out of bytes).
*/
-signed char ns_getcomma (_BUFFER *bio);
+signed char ns_getcomma (_BUFFER *bio, unsigned long long *bytesleft);
/* signed char ns_put. Returns information as to its success in
* pushing the bytes buffer, of length length, onto the buffered I/O
diff --git a/src/netstrings.c b/src/netstrings.c
@@ -6,10 +6,12 @@ signed char ns_getlength (_BUFFER *bio, unsigned long long *length)
{
char ch;
+ write(2, "getting length: ", 16);
if (length == NULL) return NS_INVAL;
*length = 0;
for (;;) {
_BUFFER_GET(bio, &ch, 1);
+ write(2, &ch, 1);
if (ch == ':') return NS_SUCCESS; // *length is already set correctly
if (ch < '0') return NS_PROTOCOL;
if (ch > '9') return NS_PROTOCOL; // ':', which is > '9', is already covered
@@ -19,10 +21,13 @@ signed char ns_getlength (_BUFFER *bio, unsigned long long *length)
}
}
-signed char ns_getcomma (_BUFFER *bio)
+signed char ns_getcomma (_BUFFER *bio, unsigned long long *bytesleft)
{
// This can effectively be verbatim from qmtpd.
char ch;
+ if (bytesleft != NULL) {
+ if ((*bytesleft) - 1 == 0) return NS_BYTES;
+ }
_BUFFER_GET(bio, &ch, 1);
return (ch == ',')? NS_SUCCESS : NS_PROTOCOL;
}
diff --git a/src/nmail-qmqpd.c b/src/nmail-qmqpd.c
@@ -0,0 +1,259 @@
+#include <unistd.h>
+#include <stdio.h>
+//#define dprintf(x, ...)
+#include "noreturn.h"
+#include "auto_qmail.h"
+#include "qmail.h"
+#include "received.h"
+#include "sig.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "now.h"
+#include "fmt.h"
+#include "byte.h"
+#include "env.h"
+#include "str.h"
+#include "netstrings.h"
+
+
+ssize_t safewrite(int fd, const void *buf, size_t len)
+{
+ ssize_t r;
+ r = write(fd,buf,len);
+ if (r == 0 || r == -1) _exit(0);
+ return r;
+}
+ssize_t saferead(int fd, void *buf, size_t len)
+{
+ ssize_t r;
+ r = read(fd,buf,len);
+ if (r == 0 || r == -1) _exit(0);
+ return r;
+}
+
+char buf[1000];
+char strnum[FMT_ULONG];
+
+char ssinbuf[512];
+substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof(ssinbuf));
+char ssoutbuf[256];
+substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof(ssoutbuf));
+
+void _noreturn_ resources() {
+ char *result = "DOut of resources";
+
+ substdio_put(&ssout,strnum,fmt_ulong(strnum,(unsigned long) str_len(result)));
+ substdio_puts(&ssout,":");
+ substdio_puts(&ssout,result);
+ substdio_puts(&ssout,",");
+ substdio_flush(&ssout);
+_exit(111); }
+void _noreturn_ badproto() {
+ char *result = "DBad protocol";
+
+ substdio_put(&ssout,strnum,fmt_ulong(strnum,(unsigned long) str_len(result)));
+ substdio_puts(&ssout,":");
+ substdio_puts(&ssout,result);
+ substdio_puts(&ssout,",");
+ substdio_flush(&ssout);
+_exit(100); }
+void _noreturn_ bytesout() {
+ char *result = "DBytes Out";
+
+ substdio_put(&ssout,strnum,fmt_ulong(strnum,(unsigned long) str_len(result)));
+ substdio_puts(&ssout,":");
+ substdio_puts(&ssout,result);
+ substdio_puts(&ssout,",");
+ substdio_flush(&ssout);
+_exit(100); }
+void _noreturn_ badcomma() {
+ char *result = "DBad comma";
+
+ substdio_put(&ssout,strnum,fmt_ulong(strnum,(unsigned long) str_len(result)));
+ substdio_puts(&ssout,":");
+ substdio_puts(&ssout,result);
+ substdio_puts(&ssout,",");
+ substdio_flush(&ssout);
+_exit(100); }
+void _noreturn_ die_wtf() {
+ char *result = "DWTF?";
+
+ substdio_put(&ssout,strnum,fmt_ulong(strnum,(unsigned long) str_len(result)));
+ substdio_puts(&ssout,":");
+ substdio_puts(&ssout,result);
+ substdio_puts(&ssout,",");
+ substdio_flush(&ssout);
+_exit(100); }
+
+unsigned long long bytesleft = 100;
+
+void getbyte(ch)
+char *ch;
+{
+ if (!bytesleft--) bytesout();
+ substdio_get(&ssin,ch,1);
+}
+
+int getlen_times_called = 0;
+
+unsigned long long getlen()
+{
+ unsigned long long len = 0;
+ dprintf(2, "getlen has been called %d times\n", getlen_times_called++);
+ switch (ns_getlength(&ssin,&len)) {
+ case NS_SUCCESS: return len; break;
+ case NS_TOOMUCH: resources(); break;
+ case NS_PROTOCOL: badproto(); break;
+ case NS_INVAL: die_wtf(); break;
+ case NS_BIOERR: die_wtf(); break;
+ }
+ // we won't reach here.
+ return len;
+#if 0
+ char ch;
+
+ for (;;) {
+ getbyte(&ch);
+ if (ch == ':') return len;
+ if (len > 200000000) resources();
+ len = 10 * len + (ch - '0');
+ }
+#endif
+}
+
+void getcomma()
+{
+ switch (ns_getcomma(&ssin,&bytesleft)) {
+ case NS_SUCCESS: break;
+ case NS_BYTES: bytesout(); break;
+ case NS_PROTOCOL: badcomma(); break;
+ }
+
+#if 0
+ char ch;
+ getbyte(&ch);
+ if (ch != ',') _exit(100);
+#endif
+}
+
+struct qmail qq;
+
+void identify()
+{
+ char *remotehost;
+ char *remoteinfo;
+ char *remoteip;
+ char *local;
+
+ remotehost = env_get("TCPREMOTEHOST");
+ if (!remotehost) remotehost = "unknown";
+ remoteinfo = env_get("TCPREMOTEINFO");
+ remoteip = env_get("TCPREMOTEIP");
+ if (!remoteip) remoteip = "unknown";
+ local = env_get("TCPLOCALHOST");
+ if (!local) local = env_get("TCPLOCALIP");
+ if (!local) local = "unknown";
+
+ received(&qq,"QMQP",local,remoteip,remotehost,remoteinfo,NULL);
+}
+
+int getbuf()
+{
+ unsigned long long len;
+ int i;
+
+ len = getlen();
+ if (len >= 1000) {
+ for (i = 0;i < len;++i) getbyte(buf);
+ getcomma();
+ buf[0] = 0;
+ return 0;
+ }
+
+ for (i = 0;i < len;++i) getbyte(buf + i);
+ getcomma();
+ buf[len] = 0;
+ return byte_chr(buf,len,'\0') == len;
+}
+
+int flagok = 1;
+
+int
+main()
+{
+ char *result;
+ unsigned long long qp;
+ unsigned long long len, gb;
+ char ch;
+
+ sig_pipeignore();
+ sig_alarmcatch(resources);
+ alarm(3600);
+ dprintf(2, "%s\n", "stopwatch set: die in 3600 s");
+
+ bytesleft = getlen();
+
+ len = getlen();
+ dprintf(2, "bytesleft %llu len %llu\n", bytesleft, len);
+
+ if (chdir(auto_qmail) == -1) resources();
+ if (qmail_open(&qq) == -1) resources();
+ qp = qmail_qp(&qq);
+ identify();
+
+ while (len > 0) { /* XXX: could speed this up */
+ getbyte(&ch);
+ --len;
+ qmail_put(&qq,&ch,1);
+ }
+ getcomma();
+
+ dprintf(2, "pre-from bytesleft %llu len %llu\n", bytesleft, len);
+
+ if (getbuf()) {
+ dprintf(2, "from bytesleft %llu len %llu\n", bytesleft, len);
+ qmail_from(&qq,buf);
+ } else {
+ qmail_from(&qq,"");
+ qmail_fail(&qq);
+ flagok = 0;
+ dprintf(2, "dying due to failed getbuf for from\n");
+ }
+
+ while (bytesleft)
+ if (getbuf()) {
+ dprintf(2, "to bytesleft %llu len %llu\n", bytesleft, len);
+ qmail_to(&qq,buf);
+ } else {
+ dprintf(2, "to bytesleft %llu len %llu\n", bytesleft, len);
+ dprintf(2, "dying due to failed getbuf for to\n");
+ qmail_fail(&qq);
+ flagok = 0;
+ }
+
+ bytesleft = 1;
+ getcomma();
+ dprintf(2, "bytesleft %llu len %llu\n", bytesleft, len);
+
+ result = qmail_close(&qq);
+
+ if (!*result) {
+ len = fmt_str(buf,"Kok ");
+ len += fmt_ulong(buf + len,(unsigned long) now());
+ len += fmt_str(buf + len," qp ");
+ len += fmt_ulong(buf + len,qp);
+ buf[len] = 0;
+ result = buf;
+ }
+
+ if (!flagok)
+ result = "Dsorry, I can't accept addresses like that (#5.1.3)";
+
+ substdio_put(&ssout,strnum,fmt_ulong(strnum,(unsigned long) str_len(result)));
+ substdio_puts(&ssout,":");
+ substdio_puts(&ssout,result);
+ substdio_puts(&ssout,",");
+ substdio_flush(&ssout);
+ _exit(0);
+}
diff --git a/src/nmail-remote-smtpc.c b/src/nmail-remote-smtpc.c
@@ -0,0 +1,653 @@
+#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 "bufmissing.h" /* to reset ringbuffers */
+#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" /* should use typeallocs de suitcase */
+#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
+/* ? Sort errors into netw and application */
+#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 = STRALLOC_ZERO;
+
+stralloc helohost = {0};
+stralloc routes = {0};
+struct constmap maproutes;
+stralloc host = {0};
+stralloc sender = {0};
+int pedanticsmtp = 1;
+
+saa reciplist = {0};
+/* These defines make it easier to sort for future wreckage into app and net subprograms. */
+#define _err_app
+#define _err_net
+#define _call_app
+#define _call_net
+
+/* XXX: should be an ip46full-like object
+ * XXX: the future is here! ip_address IS an ip46full-like object! */
+_call_app 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_ _err_app temp_nomem() {
+ out("Z4.3.0 Out of memory.\n");
+ zerodie();
+}
+void _noreturn_ _err_net _err_app temp_oserr()
+{
+ out("Z4.3.0 System resources temporarily unavailable.\n");
+ zerodie();
+}
+void _noreturn_ _err_net temp_noconn()
+{
+ out("Z4.4.1 Sorry, I wasn't able to establish an SMTP connection.\n");
+ zerodie();
+}
+void _noreturn_ _err_app temp_read() {
+ out("Z4.3.0 Unable to read message.\n");
+ zerodie();
+}
+void _noreturn_ _err_net temp_dnscanon()
+{
+ out("Z4.4.3 CNAME lookup failed temporarily.\n");
+ zerodie();
+}
+void _noreturn_ _err_net temp_dns()
+{
+ out("Z4.1.2 Sorry, I couldn't find any host by that name.\n");
+ zerodie();
+}
+void _noreturn_ _err_app temp_chdir()
+{
+ out("Z4.3.0 Unable to switch to home directory.\n");
+ zerodie();
+}
+void _noreturn_ _err_app temp_control()
+{
+ out("Z4.3.0 Unable to read control files.\n");
+ zerodie();
+}
+void _noreturn_ _err_app perm_partialline()
+{
+ out("D5.6.2 SMTP cannot transfer messages with partial final lines.\n");
+ zerodie();
+}
+void _noreturn_ _err_app _err_net perm_usage()
+{
+ out("D5.3.5 I (qmail-remote) was invoked improperly.\n");
+ zerodie();
+}
+void _noreturn_ _err_net perm_dns()
+{
+ out("D5.1.2 Sorry, I couldn't find any host named ");
+ outsafe(&host);
+ out(".\n");
+ zerodie();
+}
+void _noreturn_ _err_net perm_nomx()
+{
+ out("D5.4.4 Sorry, I couldn't find a mail exchanger or IP address.\n");
+ zerodie();
+}
+void _noreturn_ _err_net perm_ambigmx()
+{
+ out("D5.4.6 Sorry. Although I'm listed as a best-preference MX, A or AAAA for that host,\n"
+ "it isn't in my control/locals file, so I don't treat it as local.\n");
+ zerodie();
+}
+
+int flagcritical = 0;
+
+void
+outhost()
+{
+ char x[IPFMT];
+ if (substdio_put(subfdoutsmall, x, ip_fmt(x, &partner)) == -1)
+ _exit(0);
+}
+
+void _noreturn_
+dropped()
+{
+ out("Z4.4.2 Connected to ");
+ outhost();
+ out(" but connection died.");
+ if (flagcritical)
+ out(" Possible duplicate!");
+ out("\n");
+ zerodie();
+}
+
+int timeoutconnect = 60;
+int smtpfdin;
+int smtpfdou;
+int timeout = 1200;
+
+/* this is an utter crock. what the fuck is a timeoutread??? */
+GEN_SAFE_TIMEOUTREAD(saferead, timeout, smtpfdin, dropped())
+GEN_SAFE_TIMEOUTWRITE(safewrite, timeout, smtpfdou, dropped())
+
+char inbuf[1024];
+substdio ssin = SUBSTDIO_FDBUF(read, 0, inbuf, sizeof(inbuf));
+
+// smtpc version is UCSPI-TCP.
+char smtptobuf[1024];
+substdio smtpto = SUBSTDIO_FDBUF(safewrite, 6, smtptobuf, sizeof(smtptobuf));
+char smtpfrombuf[128];
+substdio smtpfrom = SUBSTDIO_FDBUF(saferead, 7, 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 = 0;
+ unsigned long code = 0;
+
+ if (!stralloc_copys(&smtptext, ""))
+ temp_nomem();
+
+ get(&ch);
+ if (ch < '0' || ch > '9') return 599;
+ code = ch - '0';
+ get(&ch);
+ if (ch < '0' || ch > '9') return 599;
+ code = code * 10 + (ch - '0');
+ get(&ch);
+ if (ch < '0' || ch > '9') return 599;
+ 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;
+}
+
+/* smtpcodetext
+ * globals: smtptext, both directly and indirectly
+ * inputs: a stralloc to scribble on
+ * outputs: a code
+ * side-effects: reads an SMTP response from the server,
+ */
+unsigned long
+smtpcodetext(stralloc *sa)
+{
+ /* We assume we get a stralloc we can scribble on. Hildie */
+ unsigned char ch = 0;
+ unsigned long code = 0;
+
+ if (!stralloc_copys(&smtptext, ""))
+ temp_nomem();
+
+ if (sa != NULL) if (!stralloc_copys(sa, ""))
+ temp_nomem();
+
+ if (sa != NULL) stralloc_readyplus(sa, 256); /* It doesn't hurt to have a little extra space eh? Hildie */
+ get(&ch);
+ if (ch < '0' || ch > '9') return 599;
+ code = ch - '0';
+ get(&ch);
+ if (ch < '0' || ch > '9') return 599;
+ code = code * 10 + (ch - '0');
+ get(&ch);
+ if (ch < '0' || ch > '9') return 599;
+ code = code * 10 + (ch - '0');
+ for (;;) {
+ get(&ch);
+ if (ch != '-') /* If it is a dash, we keep iterating until we get */
+ break;
+ while (ch != '\n') /* XXX - wouldn't strip \r Hildie */ {
+ if (sa != NULL) stralloc_cat(sa, &ch, 1);
+ get(&ch);
+ }
+ /* .. wouldn't ch be \n at this stage? Hildie */
+ if (ch == '\n') stralloc_cat(sa, &ch, 1);
+ 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 _call_app
+quit(char *prepend,
+ char *append)
+{
+ substdio_putsflush(&smtpto, "QUIT\r\n");
+ /* waiting for remote side is just too ridiculous
+ * do it anyway if we are pedanticsmtp (the default) */
+ out(prepend);
+ outhost();
+ out(append);
+ out(".\n");
+ outsmtptext();
+ // if (pedanticsmtp) smtpcode();
+ /* Discard; &smtptext will just be freed when we die */
+ zerodie(); /* the qmail-remote-net program should cut if the qmail-remote-smtpc program dies zero (success or permfail) */
+}
+
+void _call_app
+blastsmtp()
+{
+ 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); /* SMTP quoting crock. Only needed for DATA, not BDAT. Hildie */
+ 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); /* Accepts both \r\n... */
+ } else
+ break;
+ }
+ substdio_put(&smtpto, &ch, 1);
+ r = substdio_get(&ssin, &ch, 1);
+ if (r == 0)
+ perm_partialline(); /* Daniel, this is a crock. remote should fastforward the file to the end, check it ends appropriately, and if not, fail fast. */
+ if (r == -1)
+ temp_read();
+ }
+ substdio_put(&smtpto, "\r\n", 2); /* ... and \n in the queue. Why shouldn't we just store \r\n in the queue, as it's required by SMTP anyway and would allow us to give an accurate size description for SIZE= and BDAT=? */
+ }
+
+ flagcritical = 1;
+ substdio_put(&smtpto, ".\r\n", 3);
+ substdio_flush(&smtpto);
+}
+
+stralloc recip = {0};
+int esmtp = 1;
+
+void
+smtp()
+{
+ unsigned long code;
+ int flagbother;
+ int i;
+
+ //XXX commented example(straight BER flow) is RFC non-compliant.
+ // if (smtpcode() != 220) quit("ZConnected to ", " but greeting failed");
+ //Use theory of codes instead - any 2xx code is fine
+ code = smtpcode();
+ if (code < 200 || code > 299) {
+ substdio_putsflush(&smtpto, "QUIT\r\n");
+ close(smtpfdin);
+ close(smtpfdou); /* Already closed? Doesn't matter. Hildie */
+ BUF_HEAD(&smtpfrom) = BUF_TAIL(&smtpfrom); /* clear the buffer - re qmail-get-141331@list.cr.yp.to */
+ return;
+ }
+
+ substdio_puts(&smtpto, "EHLO ");
+ substdio_put(&smtpto, helohost.s, helohost.len);
+ substdio_puts(&smtpto, "\r\n");
+ substdio_flush(&smtpto);
+ code = smtpcode();
+ esmtp = 1;
+ if (code < 250 || code > 259) {
+ esmtp = 0;
+ /* Old far side doesn't understand EHLO. That's fine, we don't use any ESMTP extensions anyway. */
+ substdio_puts(&smtpto, "HELO ");
+ substdio_put(&smtpto, helohost.s, helohost.len);
+ substdio_puts(&smtpto, "\r\n");
+ substdio_flush(&smtpto);
+ code = smtpcode();
+ if (code < 250 || code > 259)
+ quit("ZConnected to ", " but my name was rejected, both for EHLO and HELO. I do not expect to be able to deliver mail here at this time.");
+ }
+
+ 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 deferred");
+
+ flagbother = 0;
+ /* strange: qmail-remote is capable of multiple recipients, but
+ * is only ever fed just one. éh? Lightning */
+ 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 ", " because remote server ");
+
+ 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");
+
+ blastsmtp();
+ 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");
+ if (esmtp)
+ quit("K", " accepted message with ESMTP");
+ else quit("K", " accepted message with standard SMTP");
+}
+
+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();
+}
+
+void _call_app
+getappcontrols()
+{
+ if (control_readint(&pedanticsmtp, "control/pedanticsmtp") == -1) _call_app
+ pedanticsmtp = 1; // default to pedantic
+ if (control_readint(&timeout, "control/timeoutremote") == -1) _call_app
+ temp_control();
+ if (control_rldef(&helohost, "control/helohost", 1, NULL) != 1) _call_app
+ temp_control();
+}
+
+void _call_net
+getnetcontrols()
+{
+ if (control_readint(&timeoutconnect, "control/timeoutconnect") == -1) _call_net
+ temp_control();
+ switch (control_readfile(&routes, "control/smtproutes", 0)) { _call_net /* would be looked up by */
+ 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;
+ }
+}
+
+void _call_app _call_net
+getcontrols()
+{
+ if (control_init() == -1)
+ temp_control();
+ getappcontrols();
+ //getnetcontrols();
+}
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ _call_net static ipalloc ip = {0};
+ _call_net unsigned long prefme;
+ _call_net char *relayhost;
+ _call_app unsigned long random;
+ _call_app char **recips;
+
+ sig_pipeignore();
+
+ if (argc < 4)
+ perm_usage();
+ if (chdir(auto_qmail) == -1)
+ temp_chdir();
+
+ getcontrols();
+
+ if (!stralloc_copys(&host, argv[1]))
+ temp_nomem();
+
+ _call_net // {
+
+ 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();
+ }
+ // }
+
+ _call_app addrmangle(&sender, argv[2]);
+
+ _call_app if (!saa_readyplus(&reciplist, 0))
+ temp_nomem();
+
+ _call_net if (ipme_init() != 1)
+ temp_oserr();
+
+ _call_app recips = argv + 3;
+ _call_app 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);
+ _call_net 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();
+ }
+
+ _call_net // {
+ 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(); */ // All of this is already handled for smtpc.
+
+ _call_net for (i = 0; i < ip.len; ++i)
+ if (ip.ix[i].pref < prefme) {
+ if (tcpto(&ip.ix[i].ip))
+ continue;
+
+ smtpfdin = socket(AF_INET, SOCK_STREAM, 0);
+ if (smtpfdin == -1)
+ temp_oserr();
+ smtpfdou = smtpfdin;
+
+ if (timeoutconn(smtpfdin, &ip.ix[i].ip, (unsigned int)port, timeoutconnect) == 0) {
+ tcpto_err(&ip.ix[i].ip, 0);
+ partner = ip.ix[i].ip;
+ _call_app smtp(); /* does not return, unless greeting
+ * wrong
+ * smtp() would be the entry point of a qmail-remote-app*/
+ }
+ tcpto_err(&ip.ix[i].ip, errno == error_timeout);
+ close(smtpfdin);
+ close(smtpfdou);
+ }
+
+ temp_noconn();
+}
diff --git a/src/qmail-qmqpd.c b/src/qmail-qmqpd.c
@@ -1,4 +1,4 @@
-#include "noreturn.h"
+#include <unistd.h>
#include "auto_qmail.h"
#include "qmail.h"
#include "received.h"
@@ -11,11 +11,9 @@
#include "byte.h"
#include "env.h"
#include "str.h"
-#include "netstrings.h"
-void _noreturn_ resources() { _exit(111); }
-void _noreturn_ badproto() { _exit(100); }
-void _noreturn_ die_wtf() { _exit(100); }
+void resources() { _exit(111); }
+void badproto() { _exit(100); }
ssize_t safewrite(int fd, const void *buf, size_t len)
{
@@ -37,7 +35,7 @@ substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof(ssinbuf));
char ssoutbuf[256];
substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof(ssoutbuf));
-unsigned long long bytesleft = 100;
+unsigned long bytesleft = 100;
void getbyte(ch)
char *ch;
@@ -46,42 +44,25 @@ char *ch;
substdio_get(&ssin,ch,1);
}
-unsigned long long getlen()
+unsigned long getlen()
{
- unsigned long long len = 0;
- switch (ns_getlength(&ssin,&len)) {
- case NS_SUCCESS: return len; break;
- case NS_TOOMUCH: resources(); break;
- case NS_PROTOCOL: badproto(); break;
- case NS_INVAL: die_wtf(); break;
- case NS_BIOERR: die_wtf(); break;
- }
- // we won't reach here.
- return len;
-#if 0
+ unsigned long len = 0;
char ch;
for (;;) {
getbyte(&ch);
if (ch == ':') return len;
if (len > 200000000) resources();
+ if (ch < '0' || ch > '9') badproto();
len = 10 * len + (ch - '0');
}
-#endif
}
void getcomma()
{
- switch (ns_getcomma(&ssin)) {
- case NS_SUCCESS: break;
- case NS_PROTOCOL: badproto(); break;
- }
-
-#if 0
char ch;
getbyte(&ch);
if (ch != ',') _exit(100);
-#endif
}
struct qmail qq;
@@ -110,7 +91,7 @@ char strnum[FMT_ULONG];
int getbuf()
{
- unsigned long long len;
+ unsigned long len;
int i;
len = getlen();
@@ -133,8 +114,8 @@ int
main()
{
char *result;
- unsigned long long qp;
- unsigned long long len;
+ unsigned long qp;
+ unsigned long len;
char ch;
sig_pipeignore();
diff --git a/src/qmail-qmtpd.c b/src/qmail-qmtpd.c
@@ -85,7 +85,7 @@ void getcomma()
substdio_get(&ssin,&ch,1);
if (ch != ',') badproto();
#else
- switch (ns_getcomma(&ssin)) {
+ switch (ns_getcomma(&ssin, NULL)) {
case NS_SUCCESS: break;
case NS_PROTOCOL: badproto(); break;
}
diff --git a/src/qmail-rspawn.c b/src/qmail-rspawn.c
@@ -11,9 +11,7 @@
#include "auto_users.h"
#include "env.h"
-void initialize(argc,argv)
-int argc;
-char **argv;
+void initialize(int argc, char **argv)
{
auto_uidq = inituid(auto_userq);
tcpto_clean();
@@ -21,11 +19,7 @@ char **argv;
int truncreport = 0;
-void report(ss,wstat,s,len)
-substdio *ss;
-int wstat;
-char *s;
-int len;
+void report(substdio *ss, int wstat, char *s, int len)
{
int j;
int k;