commit cf169fee8126a148a834165db265aad4172371d0
parent e441c8b4efcb6d8dc77e98e2f99299d5efb96023
Author: Ellenor Bjornsdottir <ellenor@umbrellix.net>
Date: Wed, 19 Oct 2022 07:53:43 +0000
so the smtpd finally works... you have to flush every time you send something
Diffstat:
6 files changed, 614 insertions(+), 582 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -382,3 +382,5 @@ qmail-send.service
*.obj
src/mxf-remote/mxf-remote
src/netstrings.o
+src/nmail-smtpd.o
+nmail-smtpd
diff --git a/Makefile.legacy b/Makefile.legacy
@@ -1467,6 +1467,21 @@ src/case.a src/stralloc.a src/error.a
socket.lib` src/dns.o `cat dns.lib` src/auto_usera.o src/auto_break.o src/realrcptto.o \
src/case.a src/stralloc.a src/error.a
+nmail-smtpd: \
+load src/nmail-smtpd.o src/rcpthosts.o src/commands.o src/timeoutread.o src/ndelay.a \
+src/timeoutwrite.o src/ip.o src/ipme.o src/ipalloc.o src/control.o src/constmap.o src/received.o \
+src/date822fmt.o src/qmail.o src/fmt_ulong.o src/cdb.a src/fd.a src/wait.a src/datetime.a src/getln.a \
+src/open.a src/sig.a src/case.a src/env.a src/stralloc.a src/substdio.a src/error.a src/str.a \
+src/fs.a src/auto_qmail.o socket.lib src/dns.o dns.lib src/auto_usera.o src/auto_break.o src/realrcptto.o \
+src/case.a src/stralloc.a src/error.a
+ ./load nmail-smtpd src/rcpthosts.o src/commands.o src/timeoutread.o src/ndelay.a \
+ src/timeoutwrite.o src/ip.o src/ipme.o src/ipalloc.o src/control.o src/constmap.o \
+ src/received.o src/date822fmt.o src/qmail.o src/fmt_ulong.o src/cdb.a src/fd.a src/wait.a \
+ src/datetime.a src/getln.a src/open.a src/sig.a src/case.a src/env.a src/stralloc.a \
+ src/substdio.a src/error.a src/str.a src/fs.a src/auto_qmail.o `cat \
+ socket.lib` src/dns.o `cat dns.lib` src/auto_usera.o src/auto_break.o src/realrcptto.o \
+ src/case.a src/stralloc.a src/error.a
+
doc/man/qmail-smtpd.0: \
doc/man/qmail-smtpd.8
@@ -1478,6 +1493,14 @@ include/substdio.h include/str.h include/fmt.h include/scan.h include/byte.h inc
include/exit.h include/rcpthosts.h include/timeoutread.h include/timeoutwrite.h include/commands.h include/dns.h
./compile src/qmail-smtpd.c
+src/nmail-smtpd.o: \
+compile src/nmail-smtpd.c include/sig.h include/readwrite.h include/stralloc.h include/gen_alloc.h \
+include/substdio.h include/alloc.h include/auto_qmail.h include/control.h include/received.h include/constmap.h \
+include/error.h include/ipme.h include/ip.h include/ipalloc.h include/ip.h include/gen_alloc.h include/ip.h include/qmail.h \
+include/substdio.h include/str.h include/fmt.h include/scan.h include/byte.h include/case.h include/env.h include/now.h include/datetime.h \
+include/exit.h include/rcpthosts.h include/timeoutread.h include/timeoutwrite.h include/commands.h include/dns.h
+ ./compile src/nmail-smtpd.c
+
qmail-start: \
load src/qmail-start.o src/prot.o src/fd.a src/ids.a src/substdio.a src/error.a src/str.a
./load qmail-start src/prot.o src/fd.a src/ids.a src/substdio.a src/error.a src/str.a
diff --git a/TARGETS b/TARGETS
@@ -377,3 +377,5 @@ setup
include/qtmp.h
qmail-send.service
src/netstrings.o
+src/nmail-smtpd.o
+nmail-smtpd
diff --git a/conf-cc b/conf-cc
@@ -1,3 +1,3 @@
-cc -g -ggdb3 -O3 -Iinclude/
+cc -g -ggdb3 -O3 -Wall -Iinclude/
This will be used to compile .c files.
diff --git a/src/nmail-smtpd.c b/src/nmail-smtpd.c
@@ -0,0 +1,586 @@
+#include "sig.h"
+#include "ndelay.h"
+#include "readwrite.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "alloc.h"
+#include "auto_qmail.h"
+#include "control.h"
+#include "datetime.h"
+#include "received.h"
+#include "constmap.h"
+#include "error.h"
+#include "ipme.h"
+#include "ip.h"
+#include "qmail.h"
+#include "str.h"
+#include "fmt.h"
+#include "scan.h"
+#include "byte.h"
+#include "case.h"
+#include "env.h"
+#include "now.h"
+#include "exit.h"
+#include "rcpthosts.h"
+#include "realrcptto.h"
+#include "timeoutread.h"
+#include "timeoutwrite.h"
+#include "commands.h"
+#include "dns.h"
+#include "fd.h"
+
+#define MAXHOPS 100
+unsigned int databytes = 0;
+int timeout = 1200;
+
+GEN_SAFE_TIMEOUTWRITE(safewrite,timeout,fd,_exit(10))
+
+char ssoutbuf[512];
+substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof(ssoutbuf));
+
+void flush() { substdio_flush(&ssout); }
+void out(s) char *s; { substdio_puts(&ssout,s); }
+
+void die_read() { _exit(1); }
+void die_alarm() { out("451 4.4.2 Timeout\r\n"); flush(); _exit(2); }
+void die_nomem() { out("421 4.3.0 Out of memory\r\n"); flush(); _exit(3); }
+void die_control() { out("421 4.3.0 Unable to read controls\r\n"); flush(); _exit(4); }
+void die_ipme() { out("421 4.3.0 Unable to figure out my IP addresses\r\n"); flush(); _exit(5); }
+void die_ip6me() { out("421 4.3.0 Unable to figure out my IPv6 addresses\r\n"); flush(); _exit(6); }
+void straynewline() { out("451 You sent a stray newline. See https://cr.yp.to/docs/smtplf.html.\r\n"); flush(); _exit(1); }
+
+char ssebuf[1024];
+substdio sse = SUBSTDIO_FDBUF(write,2,ssebuf,sizeof(ssebuf));
+void err(char *s) { substdio_putsflush(&sse,s); }
+
+void err_bmf() { out("553 5.7.1 Sorry, your envelope sender is in my badmailfrom list.\r\n"); }
+void err_nogateway() { out("553 5.7.1 Sorry, that domain isn't in my list of allowed rcpthosts\r\n"); }
+void err_unimpl(char *arg) { out("502 5.5.1 Unimplemented\r\n"); }
+void err_syntax() { out("555 5.5.4 Syntax error\r\n"); }
+void die_wtf(int d, char *arg) { out("421 4.3.2 inconsistent state: "); out(arg); out(" - shutting down!\r\n"); flush();
+err("421 4.3.2 inconsistent state: "); err(arg); err(" - shutting down!\r\n"); _exit(d); }
+void err_wantmail() { out("503 5.5.1 You must execute MAIL first\r\n"); }
+void err_wantrcpt() { out("503 5.5.1 You must execute RCPT first\r\n"); }
+void err_notlshere() { out("454 4.7.6 STARTTLS is not available at this site.\r\n"); }
+void err_alreadytlshere() { out("454 5.7.6 STARTTLS is already active on this connection.\r\n"); }
+void die_cdb() { out("421 4.3.0 Unable to read cdb user database\r\n"); flush(); _exit(7); }
+void die_sys() { out("421 4.3.0 Unable to read system user database\r\n"); flush(); _exit(8); }
+void err_noop(char *arg) { out("250 As requested, no operation performed.\r\n"); }
+void err_vrfy(char *arg) { out("252 Send something and we'll give it a shot.\r\n"); }
+void err_qqt() { out("451 4.3.0 Temporarily unavailable: cannot inject message into queue.\r\n"); }
+void die_dnsbl(char *arg)
+{
+ out("421 Your IP is currently blacklisted. If available at this site, auth first. ("); out(arg); out(")\r\n");
+ flush();
+ _exit(9);
+}
+
+stralloc greeting = {0};
+int starttlsready = 0, sslctlfd = -1, sslrfd = -1, sslwfd = -1, isstarttls = 0, isesmtp = 0;
+
+void smtp_greet(code) char *code;
+{
+ substdio_puts(&ssout,code);
+ substdio_put(&ssout,greeting.s,greeting.len);
+}
+void smtp_help(char *arg)
+{
+ out("214-NightmareMail home page: <https://umbrellix.net./software/nightmaremail/index.html>\r\n");
+ out("214-NightmareMail is based on notqmail. notqmail home page: <https://notqmail.org>\r\n");
+ out("214-We accept the following commands: HELO, EHLO, (SEND/SOML/SAML/MAIL) FROM:<, RCPT TO:<, DATA, RSET"); if (starttlsready) out(", STARTTLS"); out("\r\n");
+ out("214 Note that SMTP is not a user interface. It should only be used thus to verify correct operation of the mail system.\r\n");
+}
+void smtp_quit(char *arg)
+{
+ smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
+}
+
+char *remoteip;
+char *remotehost;
+char *remoteinfo;
+char *local;
+char *relayclient;
+char *dnsblskip;
+
+stralloc helohost = {0};
+char *fakehelo; /* pointer into helohost, or 0 */
+
+void dohelo(char *arg) {
+ if (!stralloc_copys(&helohost,arg)) die_nomem();
+ if (!stralloc_0(&helohost)) die_nomem();
+ fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;
+}
+
+int liphostok = 0;
+stralloc liphost = {0};
+int bmfok = 0;
+stralloc bmf = {0};
+struct constmap mapbmf;
+
+void setuptls()
+{
+ char *x;
+ unsigned long u;
+
+ x = env_get("SSLCTLFD");
+ if (x) { scan_ulong(x,&u); sslctlfd = u; } else return;
+ x = env_get("SSLREADFD");
+ if (x) { scan_ulong(x,&u); sslrfd = u; } else return;
+ x = env_get("SSLWRITEFD");
+ if (x) { scan_ulong(x,&u); sslwfd = u; } else return;
+ ndelay_on(sslctlfd);
+ //ndelay_on(sslrfd);
+ //ndelay_on(sslwfd);
+ starttlsready = 1; // if reached, then we can do starttls
+ return;
+}
+
+void setup()
+{
+ char *x;
+ unsigned long u;
+
+ if (control_init() == -1) die_control();
+ if (control_rldef(&greeting,"control/smtpgreeting",1,NULL) != 1)
+ die_control();
+ liphostok = control_rldef(&liphost,"control/localiphost",1,NULL);
+ if (liphostok == -1) die_control();
+ if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
+ if (timeout <= 0) timeout = 1;
+
+ if (rcpthosts_init() == -1) die_control();
+
+ bmfok = control_readfile(&bmf,"control/badmailfrom",0);
+ if (bmfok == -1) die_control();
+ if (bmfok)
+ if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
+
+ // manually merged from prj's realrcptto patch
+ realrcptto_init();
+
+ if (control_readint(&databytes,"control/databytes") == -1) die_control();
+ x = env_get("DATABYTES");
+ if (x) { scan_ulong(x,&u); databytes = u; }
+ if (!(databytes + 1)) --databytes;
+ setuptls();
+
+ remoteip = env_get("TCPREMOTEIP");
+ if (!remoteip) remoteip = "unknown";
+ local = env_get("TCPLOCALHOST");
+ if (!local) local = env_get("TCPLOCALIP");
+ if (!local) local = "unknown";
+ remotehost = env_get("TCPREMOTEHOST");
+ if (!remotehost) remotehost = "unknown";
+ remoteinfo = env_get("TCPREMOTEINFO");
+ relayclient = env_get("RELAYCLIENT");
+ dnsblskip = env_get("DNSBLSKIP");
+ dohelo(remotehost);
+}
+
+extern void realrcptto_init();
+extern void realrcptto_start();
+extern int realrcptto();
+extern int realrcptto_deny();
+
+stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
+
+int addrparse(char *arg)
+{
+ int i;
+ char ch;
+ char terminator;
+ struct ip_address ip;
+ int flagesc;
+ int flagquoted;
+
+ terminator = '>';
+ i = str_chr(arg,'<');
+ if (arg[i])
+ arg += i + 1;
+ else { /* partner should go read rfc 821 */
+ terminator = ' ';
+ arg += str_chr(arg,':');
+ if (*arg == ':') ++arg;
+ while (*arg == ' ') ++arg;
+ }
+
+ /* strip source route */
+ if (*arg == '@') while (*arg) if (*arg++ == ':') break;
+
+ if (!stralloc_copys(&addr,"")) die_nomem();
+ flagesc = 0;
+ flagquoted = 0;
+ for (i = 0;(ch = arg[i]);++i) { /* copy arg to addr, stripping quotes */
+ if (flagesc) {
+ if (!stralloc_append(&addr,&ch)) die_nomem();
+ flagesc = 0;
+ }
+ else {
+ if (!flagquoted && (ch == terminator)) break;
+ switch(ch) {
+ case '\\': flagesc = 1; break;
+ case '"': flagquoted = !flagquoted; break;
+ default: if (!stralloc_append(&addr,&ch)) die_nomem();
+ }
+ }
+ }
+ /* could check for termination failure here, but why bother? */
+ if (!stralloc_append(&addr,"")) die_nomem();
+
+ if (liphostok) {
+ i = byte_rchr(addr.s,addr.len,'@');
+ if (i < addr.len) /* if not, partner should go read rfc 821 */
+ if (addr.s[i + 1] == '[')
+ if (!addr.s[i + 1 + ip_scanbracket(addr.s + i + 1,&ip)])
+ if (ipme_is(&ip)) {
+ addr.len = i + 1;
+ if (!stralloc_cat(&addr,&liphost)) die_nomem();
+ if (!stralloc_0(&addr)) die_nomem();
+ }
+ }
+
+ if (addr.len > 900) return 0;
+ return 1;
+}
+
+int bmfcheck()
+{
+ int j;
+ if (!bmfok) return 0;
+ if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1;
+ j = byte_rchr(addr.s,addr.len,'@');
+ if (j < addr.len)
+ if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1;
+ return 0;
+}
+
+int addrallowed()
+{
+ int r;
+ r = rcpthosts(addr.s,str_len(addr.s));
+ if (r == -1) die_control();
+ return r;
+}
+
+int flagdnsbl = 0;
+stralloc dnsblhost = {0};
+
+int dnsblcheck()
+{
+ char *ch;
+ static stralloc dnsblbyte = {0};
+ static stralloc dnsblrev = {0};
+ static ipalloc dnsblip = {0};
+ static ip6alloc dnsblip6 = {0};
+ static stralloc dnsbllist = {0};
+
+ ch = remoteip;
+ if(control_readfile(&dnsbllist,"control/dnsbllist",0) != 1) return 0;
+
+ if (!stralloc_copys(&dnsblrev,"")) return 0;
+ for (;;) {
+ if (!stralloc_copys(&dnsblbyte,"")) return 0;
+ while (ch[0] && (ch[0] != '.')) {
+ if (!stralloc_append(&dnsblbyte,ch)) return 0;
+ ch++;
+ }
+ if (!stralloc_append(&dnsblbyte,".")) return 0;
+ if (!stralloc_cat(&dnsblbyte,&dnsblrev)) return 0;
+ if (!stralloc_copy(&dnsblrev,&dnsblbyte)) return 0;
+
+ if (!ch[0]) break;
+ ch++;
+ }
+
+ flagdnsbl = 1;
+ ch = dnsbllist.s;
+ while (ch < (dnsbllist.s + dnsbllist.len)) {
+ if (!stralloc_copy(&dnsblhost,&dnsblrev)) return 0;
+ if (!stralloc_cats(&dnsblhost,ch)) return 0;
+ if (!stralloc_0(&dnsblhost)) return 0;
+
+ if (!dns_ip(&dnsblip,&dnsblhost)) return 1;
+ while (*ch++);
+ }
+
+ return 0;
+}
+
+int seenmail = 0;
+int flagbarf; /* defined if seenmail */
+stralloc mailfrom = {0};
+stralloc rcptto = {0};
+
+ssize_t saferead(int fd, void *buf, size_t len)
+{
+ ssize_t r;
+ r = timeoutread(timeout,fd,buf,len);
+ if (r == -1) if (errno == error_timeout) die_alarm();
+ if (r == 0 || r == -1) die_read();
+ return r;
+}
+
+char ssinbuf[1024];
+substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof(ssinbuf));
+
+
+void smtp_starttls(char *arg)
+{
+ char fdou[1920]; // compare skarnet s-s-proxy: OUTSIZE 1920. Me thinks I cribbed too much.
+ ssize_t hsread = 0, hsr = 0;
+ if (str_len(arg) != 0) { err_syntax(); return; }
+ if (!starttlsready) { err_notlshere(); return; }
+ if (timeoutwrite(1, sslctlfd, "Y", 1) != 1) { die_wtf(errno, "could not cut over to TLS in one second"); }
+ out("220 2.7.0 Ready to start TLS\r\n");
+ substdio_flush(&ssout);
+ for (;;) {
+ hsread += (hsr = timeoutread(1, sslctlfd, fdou, 1920));
+ if (hsr < 0) { if (errno != EAGAIN) die_wtf(errno, "could not cut over to TLS in 1 second"); }
+ if (!hsr) break;
+ isstarttls = 1; // just use the global variable (TODO: make TL when npthread)
+ }
+ if (!isstarttls) { _exit(10); }
+ // At this point, we are starttls. We must now cut sslwfd to fd 1 and sslrfd to fd 0.
+ ssout.fd = sslwfd;
+ ssin.fd = sslrfd;
+ substdio_flush(&ssout);
+ substdio_flush(&sse);
+}
+void smtp_helo(char *arg)
+{
+ smtp_greet("250 "); out("\r\n");
+ seenmail = 0; dohelo(arg);
+}
+void smtp_ehlo(char *arg)
+{
+ isesmtp = 1;
+ smtp_greet("250-"); out("\r\n");
+ out("250-PIPELINING\r\n");
+ out("250-ENHANCEDSTATUSCODES\r\n");
+ // TODO: add startTLS cutover support
+ if (starttlsready && !isstarttls) {
+ out("250-8BITMIME\r\n");
+ out("250 STARTTLS\r\n");
+ } else out("250 8BITMIME\r\n");
+ seenmail = 0; dohelo(arg);
+}
+void smtp_rset(char *arg)
+{
+ seenmail = 0;
+ out("250 Envelope flushed\r\n");
+}
+void smtp_mail(char *arg)
+{
+ if (!addrparse(arg)) { err_syntax(); return; }
+ flagbarf = bmfcheck();
+ seenmail = 1;
+ if (!stralloc_copys(&rcptto,"")) die_nomem();
+ if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
+ if (!stralloc_0(&mailfrom)) die_nomem();
+ realrcptto_start();
+ out("250 Go on...\r\n");
+}
+void smtp_rcpt(char *arg) {
+ if (!seenmail) { err_wantmail(); return; }
+ if (!addrparse(arg)) { err_syntax(); return; }
+ if (flagbarf) { err_bmf(); return; }
+ if (relayclient) {
+ --addr.len;
+ if (!stralloc_cats(&addr,relayclient)) die_nomem();
+ if (!stralloc_0(&addr)) die_nomem();
+ }
+ else
+ if (!addrallowed()) { err_nogateway(); return; }
+ if (!realrcptto(addr.s)) {
+ out("550 5.1.1 That user has elected not to receive emails.\r\n");
+ return;
+ }
+ if (!(relayclient || dnsblskip || flagdnsbl))
+ if (dnsblcheck()) die_dnsbl(dnsblhost.s);
+ if (!stralloc_cats(&rcptto,"T")) die_nomem();
+ if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
+ if (!stralloc_0(&rcptto)) die_nomem();
+ out("250 Go on...\r\n");
+}
+
+struct qmail qqt;
+unsigned int bytestooverflow = 0;
+
+void put(ch)
+char *ch;
+{
+ if (bytestooverflow)
+ if (!--bytestooverflow)
+ qmail_fail(&qqt);
+ qmail_put(&qqt,ch,1);
+}
+
+void blast(hops)
+int *hops;
+{
+ char ch;
+ int state;
+ int flaginheader;
+ int pos; /* number of bytes since most recent \n, if fih */
+ int flagmaybex; /* 1 if this line might match RECEIVED, if fih */
+ int flagmaybey; /* 1 if this line might match \r\n, if fih */
+ int flagmaybez; /* 1 if this line might match DELIVERED, if fih */
+
+ state = 1;
+ *hops = 0;
+ flaginheader = 1;
+ pos = 0; flagmaybex = flagmaybey = flagmaybez = 1;
+ for (;;) {
+ /* This isn't very optimized -- Amelia B */
+ substdio_get(&ssin,&ch,1);
+ if (flaginheader) {
+ if (pos < 9) { // something about method and madness?
+ if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0;
+ if (flagmaybez) if (pos == 8) ++*hops;
+ if (pos < 8)
+ if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0;
+ if (flagmaybex) if (pos == 7) ++*hops;
+ if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;
+ if (flagmaybey) if (pos == 1) flaginheader = 0;
+ ++pos;
+ }
+ if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
+ }
+ switch(state) {
+ case 0:
+ if (ch == '\n') straynewline();
+ if (ch == '\r') { state = 4; continue; }
+ break;
+ case 1: /* \r\n */
+ if (ch == '\n') straynewline();
+ if (ch == '.') { state = 2; continue; }
+ if (ch == '\r') { state = 4; continue; }
+ state = 0;
+ break;
+ case 2: /* \r\n + . */
+ if (ch == '\n') straynewline();
+ if (ch == '\r') { state = 3; continue; }
+ state = 0;
+ break;
+ case 3: /* \r\n + .\r */
+ if (ch == '\n') return;
+ put(".");
+ put("\r");
+ if (ch == '\r') { state = 4; continue; }
+ state = 0;
+ break;
+ case 4: /* + \r */
+ if (ch == '\n') { state = 1; break; }
+ if (ch != '\r') { put("\r"); state = 0; }
+ }
+ put(&ch);
+ }
+}
+
+char accept_buf[FMT_ULONG];
+void acceptmessage(qp) unsigned long qp;
+{
+ datetime_sec when;
+ when = now();
+ out("250 Accepted responsibility at time ");
+ accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0;
+ out(accept_buf);
+ out(" queue process ");
+ accept_buf[fmt_ulong(accept_buf,qp)] = 0;
+ out(accept_buf);
+ out(" - we'll do our best!\r\n");
+ flush();
+}
+
+/*
+ unsigned long alen, plen, len;
+
+ if (databytes && ((alen = str_len(arg)) != 0)) {
+ // We now accept the optional argument to DATA which is a ulong which must < databytes + 1.
+ plen = scan_ulong(arg, &len);
+ if (alen != plen) { err_syntax(); return; } // crap at the end of the argument
+ if (len > databytes) { out("552 5.3.4 This message is too big for me (rapid rejection).\r\n"); return; }
+ }
+ */
+void smtp_data(char *arg) {
+ int hops;
+ unsigned long qp;
+ char *qqx;
+ char *esmtps;
+
+ if (!seenmail) { err_wantmail(); return; }
+ if (!rcptto.len) { err_wantrcpt(); return; }
+ if (realrcptto_deny()) { out("554 5.1.1 That user has elected not to receive emails.\r\n"); return; }
+ seenmail = 0;
+ if (databytes) bytestooverflow = databytes + 1;
+ if (qmail_open(&qqt) == -1) { err_qqt(); return; }
+ qp = qmail_qp(&qqt);
+ out("354 go ahead\r\n");
+ substdio_flush(&ssout);
+
+ switch ((isesmtp & 1)+((isstarttls & 1) << 1)+((relayclient != NULL) << 2)) {
+ case 7: esmtps = "ESMTPSA";
+ break;
+ case 6: esmtps = "SMTPSA"; // Compliant clients should never do this, but it's not pathological.
+ break;
+ case 5: esmtps = "ESMTPA";
+ break;
+ case 4: esmtps = "SMTPA"; // Clients, at all, will never do this, as we don't process SMTP AUTH.
+ break;
+ case 3: esmtps = "ESMTPS";
+ break;
+ case 2: esmtps = "SMTPS"; // Compliant clients should never do this, but it's not pathological.
+ break;
+ case 1: esmtps = "ESMTP";
+ break;
+ default: esmtps = "SMTP";
+ }
+ received(&qqt,esmtps,local,remoteip,remotehost,remoteinfo,fakehelo);
+ blast(&hops);
+ hops = (hops >= MAXHOPS);
+ if (hops) qmail_fail(&qqt);
+ qmail_from(&qqt,mailfrom.s);
+ qmail_put(&qqt,rcptto.s,rcptto.len);
+
+ qqx = qmail_close(&qqt);
+ if (!*qqx) { acceptmessage(qp); return; }
+ if (hops) { out("554 5.4.6 This message has gone through too many SMTP, QMTP or other mail protocol servers; it may be looping. It ends with me.\r\n"); return; }
+ if (databytes) if (!bytestooverflow) { out("552 5.3.4 This message is too big for me.\r\n"); return; }
+ if (*qqx == 'D') out("554 "); else out("451 ");
+ out(qqx + 1);
+ out("\r\n");
+}
+
+struct commands smtpcommands[] = {
+ { "rcpt", smtp_rcpt, flush }
+, { "mail", smtp_mail, flush }
+, { "send", smtp_mail, flush } // not recommended
+, { "soml", smtp_mail, flush } // not recommended
+, { "saml", smtp_mail, flush } // not recommended
+, { "data", smtp_data, flush }
+, { "quit", smtp_quit, flush }
+, { "helo", smtp_helo, flush }
+, { "ehlo", smtp_ehlo, flush }
+, { "rset", smtp_rset, flush }
+, { "help", smtp_help, flush }
+, { "starttls", smtp_starttls, flush }
+, { "noop", err_noop, flush }
+, { "vrfy", err_vrfy, flush }
+, { 0, err_unimpl, flush }
+} ;
+
+int main(void)
+{
+ //ndelay_on(0);
+ //ndelay_on(1);
+ sig_pipeignore();
+ if (chdir(auto_qmail) == -1) die_control();
+ setup();
+ if (ipme_init() != 1) die_ipme();
+ if (ip6me_init() != 1) die_ip6me();
+ smtp_greet("220 ");
+ out(" ESMTP\r\n");
+ substdio_flush(&ssout);
+ if (commands(&ssin,&smtpcommands) == 0) die_read();
+ die_nomem();
+}
diff --git a/src/qmail-notsmtpd.c b/src/qmail-notsmtpd.c
@@ -1,581 +0,0 @@
-#include "sig.h"
-#include "ndelay.h"
-#include "readwrite.h"
-#include "stralloc.h"
-#include "substdio.h"
-#include "alloc.h"
-#include "auto_qmail.h"
-#include "control.h"
-#include "datetime.h"
-#include "received.h"
-#include "constmap.h"
-#include "error.h"
-#include "ipme.h"
-#include "ip.h"
-#include "qmail.h"
-#include "str.h"
-#include "fmt.h"
-#include "scan.h"
-#include "byte.h"
-#include "case.h"
-#include "env.h"
-#include "now.h"
-#include "exit.h"
-#include "rcpthosts.h"
-#include "realrcptto.h"
-#include "timeoutread.h"
-#include "timeoutwrite.h"
-#include "commands.h"
-#include "dns.h"
-#include "fd.h"
-
-#define MAXHOPS 100
-unsigned int databytes = 0;
-int timeout = 1200;
-
-GEN_SAFE_TIMEOUTWRITE(safewrite,timeout,fd,_exit(10))
-
-char ssoutbuf[512];
-substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof(ssoutbuf));
-
-void flush() { substdio_flush(&ssout); }
-void out(s) char *s; { substdio_puts(&ssout,s); }
-
-void die_read() { _exit(1); }
-void die_alarm() { out("451 4.4.2 Timeout\r\n"); flush(); _exit(2); }
-void die_nomem() { out("421 4.3.0 Out of memory\r\n"); flush(); _exit(3); }
-void die_control() { out("421 4.3.0 Unable to read controls\r\n"); flush(); _exit(4); }
-void die_ipme() { out("421 4.3.0 Unable to figure out my IP addresses\r\n"); flush(); _exit(5); }
-void die_ip6me() { out("421 4.3.0 Unable to figure out my IPv6 addresses\r\n"); flush(); _exit(6); }
-void straynewline() { out("451 You sent a stray newline. See https://cr.yp.to/docs/smtplf.html.\r\n"); flush(); _exit(1); }
-
-ssize_t saferead(int fd, void *buf, size_t len)
-{
- ssize_t r;
- r = timeoutread(timeout,fd,buf,len);
- if (r == -1) if (errno == error_timeout) die_alarm();
- if (r == 0 || r == -1) die_read();
- return r;
-}
-
-char ssinbuf[1024];
-substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof(ssinbuf));
-
-char ssebuf[1024];
-substdio sse = SUBSTDIO_FDBUF(write,2,ssebuf,sizeof(ssebuf));
-void err(char *s) { substdio_putsflush(&sse,s); }
-
-void err_bmf() { out("553 5.7.1 Sorry, your envelope sender is in my badmailfrom list.\r\n"); }
-void err_nogateway() { out("553 5.7.1 Sorry, that domain isn't in my list of allowed rcpthosts\r\n"); }
-void err_unimpl(char *arg) { out("502 5.5.1 Unimplemented\r\n"); }
-void err_syntax() { out("555 5.5.4 Syntax error\r\n"); }
-void die_wtf(int d, char *arg) { out("421 4.3.2 inconsistent state: "); out(arg); out(" - shutting down!\r\n"); flush();
-err("421 4.3.2 inconsistent state: "); err(arg); err(" - shutting down!\r\n"); _exit(d); }
-void err_wantmail() { out("503 5.5.1 You must execute MAIL first\r\n"); }
-void err_wantrcpt() { out("503 5.5.1 You must execute RCPT first\r\n"); }
-void err_notlshere() { out("454 4.7.6 STARTTLS is not available at this site.\r\n"); }
-void err_alreadytlshere() { out("454 5.7.6 STARTTLS is already active on this connection.\r\n"); }
-void die_cdb() { out("421 4.3.0 Unable to read cdb user database\r\n"); flush(); _exit(7); }
-void die_sys() { out("421 4.3.0 Unable to read system user database\r\n"); flush(); _exit(8); }
-void err_noop(char *arg) { out("250 As requested, no operation performed.\r\n"); }
-void err_vrfy(char *arg) { out("252 Send something and we'll give it a shot.\r\n"); }
-void err_qqt() { out("451 4.3.0 Temporarily unavailable: cannot inject message into queue.\r\n"); }
-void die_dnsbl(char *arg)
-{
- out("421 Your IP is currently blacklisted. If available at this site, auth first. ("); out(arg); out(")\r\n");
- flush();
- _exit(9);
-}
-
-stralloc greeting = {0};
-int starttlsready = 0, sslctlfd = -1, sslrfd = -1, sslwfd = -1, isstarttls = 0, isesmtp = 0;
-
-void smtp_greet(code) char *code;
-{
- substdio_puts(&ssout,code);
- substdio_put(&ssout,greeting.s,greeting.len);
-}
-void smtp_help(char *arg)
-{
- out("214-NightmareMail home page: <https://umbrellix.net./software/nightmaremail/index.html>\r\n");
- out("214-NightmareMail is based on notqmail. notqmail home page: <https://notqmail.org>\r\n");
- out("214-We accept the following commands: HELO, EHLO, (SEND/SOML/SAML/MAIL) FROM:<, RCPT TO:<, DATA, RSET"); if (starttlsready) out(", STARTTLS"); out("\r\n");
- out("214 Note that SMTP is not a user interface. It should only be used thus to verify correct operation of the mail system.\r\n");
-}
-void smtp_quit(char *arg)
-{
- smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
-}
-
-char *remoteip;
-char *remotehost;
-char *remoteinfo;
-char *local;
-char *relayclient;
-char *dnsblskip;
-
-stralloc helohost = {0};
-char *fakehelo; /* pointer into helohost, or 0 */
-
-void dohelo(char *arg) {
- if (!stralloc_copys(&helohost,arg)) die_nomem();
- if (!stralloc_0(&helohost)) die_nomem();
- fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;
-}
-
-int liphostok = 0;
-stralloc liphost = {0};
-int bmfok = 0;
-stralloc bmf = {0};
-struct constmap mapbmf;
-
-void setuptls()
-{
- char *x;
- unsigned long u;
-
- x = env_get("SSLCTLFD");
- if (x) { scan_ulong(x,&u); sslctlfd = u; } else return;
- x = env_get("SSLREADFD");
- if (x) { scan_ulong(x,&u); sslrfd = u; } else return;
- x = env_get("SSLWRITEFD");
- if (x) { scan_ulong(x,&u); sslwfd = u; } else return;
- ndelay_on(sslctlfd);
- ndelay_on(sslrfd);
- ndelay_on(sslwfd);
- starttlsready = 1; // if reached, then we can do starttls
- return;
-}
-
-void setup()
-{
- char *x;
- unsigned long u;
-
- if (control_init() == -1) die_control();
- if (control_rldef(&greeting,"control/smtpgreeting",1,NULL) != 1)
- die_control();
- liphostok = control_rldef(&liphost,"control/localiphost",1,NULL);
- if (liphostok == -1) die_control();
- if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
- if (timeout <= 0) timeout = 120;
-
- if (rcpthosts_init() == -1) die_control();
-
- bmfok = control_readfile(&bmf,"control/badmailfrom",0);
- if (bmfok == -1) die_control();
- if (bmfok)
- if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
-
- // manually merged from prj's realrcptto patch
- realrcptto_init();
-
- if (control_readint(&databytes,"control/databytes") == -1) die_control();
- x = env_get("DATABYTES");
- if (x) { scan_ulong(x,&u); databytes = u; }
- if (!(databytes + 1)) --databytes;
- setuptls();
-
- remoteip = env_get("TCPREMOTEIP");
- if (!remoteip) remoteip = "unknown";
- local = env_get("TCPLOCALHOST");
- if (!local) local = env_get("TCPLOCALIP");
- if (!local) local = "unknown";
- remotehost = env_get("TCPREMOTEHOST");
- if (!remotehost) remotehost = "unknown";
- remoteinfo = env_get("TCPREMOTEINFO");
- relayclient = env_get("RELAYCLIENT");
- dnsblskip = env_get("DNSBLSKIP");
- dohelo(remotehost);
-}
-
-extern void realrcptto_init();
-extern void realrcptto_start();
-extern int realrcptto();
-extern int realrcptto_deny();
-
-stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
-
-int addrparse(char *arg)
-{
- int i;
- char ch;
- char terminator;
- struct ip_address ip;
- int flagesc;
- int flagquoted;
-
- terminator = '>';
- i = str_chr(arg,'<');
- if (arg[i])
- arg += i + 1;
- else { /* partner should go read rfc 821 */
- terminator = ' ';
- arg += str_chr(arg,':');
- if (*arg == ':') ++arg;
- while (*arg == ' ') ++arg;
- }
-
- /* strip source route */
- if (*arg == '@') while (*arg) if (*arg++ == ':') break;
-
- if (!stralloc_copys(&addr,"")) die_nomem();
- flagesc = 0;
- flagquoted = 0;
- for (i = 0;(ch = arg[i]);++i) { /* copy arg to addr, stripping quotes */
- if (flagesc) {
- if (!stralloc_append(&addr,&ch)) die_nomem();
- flagesc = 0;
- }
- else {
- if (!flagquoted && (ch == terminator)) break;
- switch(ch) {
- case '\\': flagesc = 1; break;
- case '"': flagquoted = !flagquoted; break;
- default: if (!stralloc_append(&addr,&ch)) die_nomem();
- }
- }
- }
- /* could check for termination failure here, but why bother? */
- if (!stralloc_append(&addr,"")) die_nomem();
-
- if (liphostok) {
- i = byte_rchr(addr.s,addr.len,'@');
- if (i < addr.len) /* if not, partner should go read rfc 821 */
- if (addr.s[i + 1] == '[')
- if (!addr.s[i + 1 + ip_scanbracket(addr.s + i + 1,&ip)])
- if (ipme_is(&ip)) {
- addr.len = i + 1;
- if (!stralloc_cat(&addr,&liphost)) die_nomem();
- if (!stralloc_0(&addr)) die_nomem();
- }
- }
-
- if (addr.len > 900) return 0;
- return 1;
-}
-
-int bmfcheck()
-{
- int j;
- if (!bmfok) return 0;
- if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1;
- j = byte_rchr(addr.s,addr.len,'@');
- if (j < addr.len)
- if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1;
- return 0;
-}
-
-int addrallowed()
-{
- int r;
- r = rcpthosts(addr.s,str_len(addr.s));
- if (r == -1) die_control();
- return r;
-}
-
-int flagdnsbl = 0;
-stralloc dnsblhost = {0};
-
-int dnsblcheck()
-{
- char *ch;
- static stralloc dnsblbyte = {0};
- static stralloc dnsblrev = {0};
- static ipalloc dnsblip = {0};
- static ip6alloc dnsblip6 = {0};
- static stralloc dnsbllist = {0};
-
- ch = remoteip;
- if(control_readfile(&dnsbllist,"control/dnsbllist",0) != 1) return 0;
-
- if (!stralloc_copys(&dnsblrev,"")) return 0;
- for (;;) {
- if (!stralloc_copys(&dnsblbyte,"")) return 0;
- while (ch[0] && (ch[0] != '.')) {
- if (!stralloc_append(&dnsblbyte,ch)) return 0;
- ch++;
- }
- if (!stralloc_append(&dnsblbyte,".")) return 0;
- if (!stralloc_cat(&dnsblbyte,&dnsblrev)) return 0;
- if (!stralloc_copy(&dnsblrev,&dnsblbyte)) return 0;
-
- if (!ch[0]) break;
- ch++;
- }
-
- flagdnsbl = 1;
- ch = dnsbllist.s;
- while (ch < (dnsbllist.s + dnsbllist.len)) {
- if (!stralloc_copy(&dnsblhost,&dnsblrev)) return 0;
- if (!stralloc_cats(&dnsblhost,ch)) return 0;
- if (!stralloc_0(&dnsblhost)) return 0;
-
- if (!dns_ip(&dnsblip,&dnsblhost)) return 1;
- while (*ch++);
- }
-
- return 0;
-}
-
-int seenmail = 0;
-int flagbarf; /* defined if seenmail */
-stralloc mailfrom = {0};
-stralloc rcptto = {0};
-
-void smtp_starttls(char *arg)
-{
- char fdou[1920]; // compare skarnet s-s-proxy: OUTSIZE 1920. Me thinks I cribbed too much.
- ssize_t hsread = 0, hsr = 0;
- if (str_len(arg) != 0) { err_syntax(); return; }
- if (!starttlsready) { err_notlshere(); return; }
- out("220 2.7.0 Ready to start TLS\r\n");
- substdio_flush(&ssout);
- if (timeoutwrite(1, sslctlfd, "Y", 1) != 1) { die_wtf(errno, "could not cut over to TLS in one second"); }
- for (;;) {
- hsread += (hsr = timeoutread(1, sslctlfd, fdou, 1920));
- if (hsr < 0) { if (errno != EAGAIN) die_wtf(errno, "could not cut over to TLS in 1 second"); }
- if (!hsr) break;
- isstarttls = 1; // just use the global variable (TODO: make TL when npthread)
- }
- if (!isstarttls) { _exit(10); }
- // At this point, we are starttls. We must now cut sslwfd to fd 1 and sslrfd to fd 0.
- ssout.fd = sslwfd;
- ssin.fd = sslrfd;
- substdio_flush(&ssout);
- substdio_flush(&sse);
-}
-void smtp_helo(char *arg)
-{
- smtp_greet("250 "); out("\r\n");
- seenmail = 0; dohelo(arg);
-}
-void smtp_ehlo(char *arg)
-{
- isesmtp = 1;
- smtp_greet("250-"); out("\r\n");
- out("250-PIPELINING\r\n");
- out("250-ENHANCEDSTATUSCODES\r\n");
- // TODO: add startTLS cutover support
- if (starttlsready && !isstarttls) {
- out("250-8BITMIME\r\n");
- out("250 STARTTLS\r\n");
- } else out("250 8BITMIME\r\n");
- seenmail = 0; dohelo(arg);
-}
-void smtp_rset(char *arg)
-{
- seenmail = 0;
- out("250 Envelope flushed\r\n");
-}
-void smtp_mail(char *arg)
-{
- if (!addrparse(arg)) { err_syntax(); return; }
- flagbarf = bmfcheck();
- seenmail = 1;
- if (!stralloc_copys(&rcptto,"")) die_nomem();
- if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
- if (!stralloc_0(&mailfrom)) die_nomem();
- realrcptto_start();
- out("250 Go on...\r\n");
-}
-void smtp_rcpt(char *arg) {
- if (!seenmail) { err_wantmail(); return; }
- if (!addrparse(arg)) { err_syntax(); return; }
- if (flagbarf) { err_bmf(); return; }
- if (relayclient) {
- --addr.len;
- if (!stralloc_cats(&addr,relayclient)) die_nomem();
- if (!stralloc_0(&addr)) die_nomem();
- }
- else
- if (!addrallowed()) { err_nogateway(); return; }
- if (!realrcptto(addr.s)) {
- out("550 5.1.1 That user has elected not to receive emails.\r\n");
- return;
- }
- if (!(relayclient || dnsblskip || flagdnsbl))
- if (dnsblcheck()) die_dnsbl(dnsblhost.s);
- if (!stralloc_cats(&rcptto,"T")) die_nomem();
- if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
- if (!stralloc_0(&rcptto)) die_nomem();
- out("250 Go on...\r\n");
-}
-
-struct qmail qqt;
-unsigned int bytestooverflow = 0;
-
-void put(ch)
-char *ch;
-{
- if (bytestooverflow)
- if (!--bytestooverflow)
- qmail_fail(&qqt);
- qmail_put(&qqt,ch,1);
-}
-
-void blast(hops)
-int *hops;
-{
- char ch;
- int state;
- int flaginheader;
- int pos; /* number of bytes since most recent \n, if fih */
- int flagmaybex; /* 1 if this line might match RECEIVED, if fih */
- int flagmaybey; /* 1 if this line might match \r\n, if fih */
- int flagmaybez; /* 1 if this line might match DELIVERED, if fih */
-
- state = 1;
- *hops = 0;
- flaginheader = 1;
- pos = 0; flagmaybex = flagmaybey = flagmaybez = 1;
- for (;;) {
- /* This isn't very optimized -- Amelia B */
- substdio_get(&ssin,&ch,1);
- if (flaginheader) {
- if (pos < 9) { // something about method and madness?
- if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0;
- if (flagmaybez) if (pos == 8) ++*hops;
- if (pos < 8)
- if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0;
- if (flagmaybex) if (pos == 7) ++*hops;
- if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;
- if (flagmaybey) if (pos == 1) flaginheader = 0;
- ++pos;
- }
- if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
- }
- switch(state) {
- case 0:
- if (ch == '\n') straynewline();
- if (ch == '\r') { state = 4; continue; }
- break;
- case 1: /* \r\n */
- if (ch == '\n') straynewline();
- if (ch == '.') { state = 2; continue; }
- if (ch == '\r') { state = 4; continue; }
- state = 0;
- break;
- case 2: /* \r\n + . */
- if (ch == '\n') straynewline();
- if (ch == '\r') { state = 3; continue; }
- state = 0;
- break;
- case 3: /* \r\n + .\r */
- if (ch == '\n') return;
- put(".");
- put("\r");
- if (ch == '\r') { state = 4; continue; }
- state = 0;
- break;
- case 4: /* + \r */
- if (ch == '\n') { state = 1; break; }
- if (ch != '\r') { put("\r"); state = 0; }
- }
- put(&ch);
- }
-}
-
-char accept_buf[FMT_ULONG];
-void acceptmessage(qp) unsigned long qp;
-{
- datetime_sec when;
- when = now();
- out("250 Accepted responsibility at time ");
- accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0;
- out(accept_buf);
- out(" queue process ");
- accept_buf[fmt_ulong(accept_buf,qp)] = 0;
- out(accept_buf);
- out(" - we'll do our best!\r\n");
- flush();
-}
-
-/*
- unsigned long alen, plen, len;
-
- if (databytes && ((alen = str_len(arg)) != 0)) {
- // We now accept the optional argument to DATA which is a ulong which must < databytes + 1.
- plen = scan_ulong(arg, &len);
- if (alen != plen) { err_syntax(); return; } // crap at the end of the argument
- if (len > databytes) { out("552 5.3.4 This message is too big for me (rapid rejection).\r\n"); return; }
- }
- */
-void smtp_data(char *arg) {
- int hops;
- unsigned long qp;
- char *qqx;
- char *esmtps;
-
- if (!seenmail) { err_wantmail(); return; }
- if (!rcptto.len) { err_wantrcpt(); return; }
- if (realrcptto_deny()) { out("554 5.1.1 That user has elected not to receive emails.\r\n"); return; }
- seenmail = 0;
- if (databytes) bytestooverflow = databytes + 1;
- if (qmail_open(&qqt) == -1) { err_qqt(); return; }
- qp = qmail_qp(&qqt);
- out("354 go ahead\r\n");
-
- switch ((isesmtp & 1)+((isstarttls & 1) << 1)+((relayclient != NULL) << 2)) {
- case 7: esmtps = "ESMTPSA";
- break;
- case 6: esmtps = "SMTPSA"; // Compliant clients should never do this, but it's not pathological.
- break;
- case 5: esmtps = "ESMTPA";
- break;
- case 4: esmtps = "SMTPA"; // Clients, at all, will never do this, as we don't process SMTP AUTH.
- break;
- case 3: esmtps = "ESMTPS";
- break;
- case 2: esmtps = "SMTPS"; // Compliant clients should never do this, but it's not pathological.
- break;
- case 1: esmtps = "ESMTP";
- break;
- default: esmtps = "SMTP";
- }
- received(&qqt,esmtps,local,remoteip,remotehost,remoteinfo,fakehelo);
- blast(&hops);
- hops = (hops >= MAXHOPS);
- if (hops) qmail_fail(&qqt);
- qmail_from(&qqt,mailfrom.s);
- qmail_put(&qqt,rcptto.s,rcptto.len);
-
- qqx = qmail_close(&qqt);
- if (!*qqx) { acceptmessage(qp); return; }
- if (hops) { out("554 5.4.6 This message has gone through too many SMTP, QMTP or other mail protocol servers; it may be looping. It ends with me.\r\n"); return; }
- if (databytes) if (!bytestooverflow) { out("552 5.3.4 This message is too big for me.\r\n"); return; }
- if (*qqx == 'D') out("554 "); else out("451 ");
- out(qqx + 1);
- out("\r\n");
-}
-
-struct commands smtpcommands[] = {
- { "rcpt", smtp_rcpt, 0 }
-, { "mail", smtp_mail, 0 }
-, { "send", smtp_mail, 0 } // not recommended
-, { "soml", smtp_mail, 0 } // not recommended
-, { "saml", smtp_mail, 0 } // not recommended
-, { "data", smtp_data, flush }
-, { "quit", smtp_quit, flush }
-, { "helo", smtp_helo, flush }
-, { "ehlo", smtp_ehlo, flush }
-, { "rset", smtp_rset, 0 }
-, { "help", smtp_help, flush }
-, { "starttls", smtp_starttls, flush }
-, { "noop", err_noop, flush }
-, { "vrfy", err_vrfy, flush }
-, { 0, err_unimpl, flush }
-} ;
-
-int main(void)
-{
- sig_pipeignore();
- if (chdir(auto_qmail) == -1) die_control();
- setup();
- if (ipme_init() != 1) die_ipme();
- if (ip6me_init() != 1) die_ip6me();
- smtp_greet("220 ");
- out(" ESMTP\r\n");
- if (commands(&ssin,&smtpcommands) == 0) die_read();
- die_nomem();
-}