nightmaremail

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

commit e441c8b4efcb6d8dc77e98e2f99299d5efb96023
parent 6ce6f52bba210de65c6ad058b61a5b1c4129af75
Author: Ellenor Bjornsdottir <ellenor@umbrellix.net>
Date:   Wed, 19 Oct 2022 05:20:40 +0000

i broke the smtpd

Diffstat:
MMakefile.legacy | 46+++++++++++++++++++++++-----------------------
Mconf-cc | 2+-
Mconf-ld | 2+-
Mdoc/man/qmail-local.8 | 117++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Ainclude/bufmissing.h | 26++++++++++++++++++++++++++
Asrc/qmail-notsmtpd.c | 581+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/qmail-qmtpd.c | 16++++++++--------
Msrc/qmail-send.c | 14++++++++++----
Msrc/qmail.c | 55+++++++++++++++++++++++++++++++++++--------------------
9 files changed, 747 insertions(+), 112 deletions(-)

diff --git a/Makefile.legacy b/Makefile.legacy @@ -348,9 +348,9 @@ make-compile warn-auto.sh condredirect: \ load src/condredirect.o src/qmail.o src/strerr.a src/fd.a src/sig.a src/wait.a src/env.a \ -src/substdio.a src/error.a src/str.a src/fs.a src/auto_qmail.o +src/substdio.a src/error.a src/str.a src/stralloc.a src/fs.a src/auto_qmail.o src/fmt_ulong.o ./load condredirect src/qmail.o src/strerr.a src/fd.a src/sig.a src/wait.a \ - src/env.a src/substdio.a src/error.a src/str.a src/fs.a src/auto_qmail.o + src/env.a src/substdio.a src/stralloc.a src/error.a src/str.a src/fs.a src/auto_qmail.o src/fmt_ulong.o doc/man/condredirect.0: \ doc/man/condredirect.1 @@ -562,10 +562,10 @@ doc/man/forgeries.0: \ doc/man/forgeries.7 forward: \ -load src/forward.o src/qmail.o src/strerr.a src/fd.a src/wait.a src/sig.a src/env.a \ -src/substdio.a src/error.a src/str.a src/fs.a src/auto_qmail.o - ./load forward src/qmail.o src/strerr.a src/fd.a src/wait.a src/sig.a \ - src/env.a src/substdio.a src/error.a src/str.a src/fs.a src/auto_qmail.o +load src/forward.o src/qmail.o src/stralloc.a src/strerr.a src/fd.a src/wait.a src/sig.a src/env.a \ +src/substdio.a src/error.a src/str.a src/fs.a src/auto_qmail.o src/fmt_ulong.o + ./load forward src/qmail.o src/stralloc.a src/strerr.a src/fd.a src/wait.a src/sig.a \ + src/env.a src/substdio.a src/error.a src/str.a src/fs.a src/auto_qmail.o src/fmt_ulong.o doc/man/forward.0: \ doc/man/forward.1 @@ -1071,12 +1071,12 @@ qmail-inject: \ load src/qmail-inject.o src/headerbody.o src/hfield.o src/newfield.o src/quote.o \ src/control.o src/date822fmt.o src/constmap.o src/qmail.o src/case.a src/fd.a src/wait.a src/open.a \ src/getln.a src/sig.a src/getopt.a src/datetime.a src/token822.o src/env.a src/stralloc.a \ -src/substdio.a src/error.a src/str.a src/fs.a src/auto_qmail.o +src/substdio.a src/error.a src/str.a src/fs.a src/auto_qmail.o src/fmt_ulong.o ./load qmail-inject src/headerbody.o src/hfield.o src/newfield.o \ src/quote.o src/control.o src/date822fmt.o src/constmap.o src/qmail.o \ src/case.a src/fd.a src/wait.a src/open.a src/getln.a src/sig.a src/getopt.a src/datetime.a \ src/token822.o src/env.a src/stralloc.a src/substdio.a src/error.a \ - src/str.a src/fs.a src/auto_qmail.o + src/str.a src/fs.a src/auto_qmail.o src/fmt_ulong.o doc/man/qmail-inject.0: \ doc/man/qmail-inject.8 @@ -1101,11 +1101,11 @@ doc/man/qmail-limits.9 conf-qmail conf-break conf-spawn > doc/man/qmail-limits.7 qmail-local: \ -load src/qmail-local.o src/qmail.o src/quote.o src/gfrom.o src/myctime.o \ +load src/qmail-local.o src/qmail.o src/fmt_ulong.o src/quote.o src/gfrom.o src/myctime.o \ src/slurpclose.o src/case.a src/getln.a src/getopt.a src/sig.a src/open.a src/lock.a src/fd.a \ src/wait.a src/env.a src/stralloc.a src/strerr.a src/substdio.a src/error.a src/str.a \ src/fs.a src/datetime.a src/auto_qmail.o src/auto_patrn.o socket.lib - ./load qmail-local src/qmail.o src/quote.o src/gfrom.o src/myctime.o \ + ./load qmail-local src/qmail.o src/fmt_ulong.o src/quote.o src/gfrom.o src/myctime.o \ src/slurpclose.o src/case.a src/getln.a src/getopt.a src/sig.a src/open.a \ src/lock.a src/fd.a src/wait.a src/env.a src/stralloc.a src/strerr.a \ src/substdio.a src/error.a src/str.a src/fs.a src/datetime.a src/auto_qmail.o \ @@ -1272,10 +1272,10 @@ include/timeoutread.h include/timeoutwrite.h include/auto_qmail.h include/contro ./compile src/qmail-qmqpc.c qmail-qmqpd: \ -load src/qmail-qmqpd.o src/received.o src/date822fmt.o src/qmail.o src/auto_qmail.o \ +load src/qmail-qmqpd.o src/received.o src/date822fmt.o src/qmail.o src/fmt_ulong.o src/auto_qmail.o \ src/netstrings.o src/env.a src/substdio.a src/sig.a src/error.a src/wait.a src/fd.a \ src/str.a src/datetime.a src/fs.a - ./load qmail-qmqpd src/received.o src/date822fmt.o src/qmail.o src/netstrings.o \ + ./load qmail-qmqpd src/received.o src/date822fmt.o src/qmail.o src/netstrings.o src/fmt_ulong.o \ src/auto_qmail.o src/env.a src/substdio.a src/sig.a src/error.a src/wait.a src/fd.a \ src/str.a src/datetime.a src/fs.a @@ -1289,12 +1289,12 @@ include/sig.h include/substdio.h include/readwrite.h include/exit.h include/now. qmail-qmtpd: \ load src/qmail-qmtpd.o src/rcpthosts.o src/control.o src/constmap.o src/received.o \ -src/netstrings.o src/date822fmt.o src/qmail.o src/cdb.a src/fd.a src/wait.a src/datetime.a \ +src/netstrings.o src/date822fmt.o src/qmail.o src/fmt_ulong.o src/cdb.a src/fd.a src/wait.a src/datetime.a \ src/open.a src/getln.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 src/auto_usera.o src/auto_break.o \ src/realrcptto.o src/case.a src/stralloc.a src/error.a ./load qmail-qmtpd src/rcpthosts.o src/control.o src/constmap.o src/netstrings.o \ - src/received.o src/date822fmt.o src/qmail.o src/cdb.a src/fd.a src/wait.a \ + 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/open.a src/getln.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 \ src/auto_usera.o src/auto_break.o src/realrcptto.o src/case.a src/stralloc.a src/error.a @@ -1398,13 +1398,13 @@ include/tcpto.h include/spawn.h qmail-send: \ load src/qmail-send.o src/qsutil.o src/control.o src/constmap.o src/newfield.o src/prioq.o \ -src/trigger.o src/fmtqfn.o src/quote.o src/readsubdir.o src/qmail.o src/date822fmt.o \ +src/trigger.o src/fmtqfn.o src/quote.o src/readsubdir.o src/qmail.o src/fmt_ulong.o src/date822fmt.o \ src/datetime.a src/case.a src/ndelay.a src/getln.a src/wait.a src/fd.a src/sig.a src/open.a \ src/lock.a src/stralloc.a src/substdio.a src/error.a src/str.a src/fs.a src/auto_qmail.o \ src/auto_split.o src/env.a ./load qmail-send src/qsutil.o src/control.o src/constmap.o src/newfield.o \ src/prioq.o src/trigger.o src/fmtqfn.o src/quote.o src/readsubdir.o \ - src/qmail.o src/date822fmt.o src/datetime.a src/case.a src/ndelay.a src/getln.a \ + src/qmail.o src/fmt_ulong.o src/date822fmt.o src/datetime.a src/case.a src/ndelay.a src/getln.a \ src/wait.a src/fd.a src/sig.a src/open.a src/lock.a src/stralloc.a \ src/substdio.a src/error.a src/str.a src/fs.a src/auto_qmail.o src/auto_split.o src/env.a @@ -1453,15 +1453,15 @@ include/auto_spawn.h include/auto_split.h ./compile src/qmail-showctl.c qmail-smtpd: \ -load src/qmail-smtpd.o src/rcpthosts.o src/commands.o src/timeoutread.o \ +load src/qmail-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/cdb.a src/fd.a src/wait.a src/datetime.a src/getln.a \ +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 qmail-smtpd src/rcpthosts.o src/commands.o src/timeoutread.o \ + ./load qmail-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/cdb.a src/fd.a src/wait.a \ + 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 \ @@ -1549,8 +1549,8 @@ doc/man/qmail.0: \ doc/man/qmail.7 src/qmail.o: \ -compile src/qmail.c include/substdio.h include/readwrite.h include/wait.h include/exit.h include/fork.h include/fd.h \ -include/qmail.h include/auto_qmail.h include/env.h +compile src/qmail.c include/substdio.h include/readwrite.h include/wait.h include/exit.h include/fork.h include/fd.h include/stralloc.h \ +include/qmail.h include/auto_qmail.h include/env.h include/fmt.h ./compile src/qmail.c qreceipt: \ @@ -1559,7 +1559,7 @@ src/getln.a src/fd.a src/wait.a src/sig.a src/env.a src/stralloc.a src/substdio. src/str.a src/auto_qmail.o ./load qreceipt src/headerbody.o src/hfield.o src/quote.o src/token822.o \ src/qmail.o src/getln.a src/fd.a src/wait.a src/sig.a src/env.a src/stralloc.a \ - src/substdio.a src/error.a src/str.a src/auto_qmail.o + src/substdio.a src/error.a src/str.a src/auto_qmail.o src/fmt_ulong.o doc/man/qreceipt.0: \ doc/man/qreceipt.1 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 -O3 -Iinclude/ This will be used to compile .c files. 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/doc/man/qmail-local.8 b/doc/man/qmail-local.8 @@ -1,99 +1,106 @@ -.TH qmail-local 8 -.SH NAME +.Dt qmail-local 8 +.Dd Oct 19, 2022 +.Sh NAME qmail-local \- deliver or forward a mail message -.SH SYNOPSIS -.B qmail-local -[ -.B \-nN -] -.I user -.I homedir -.I local -.I dash -.I ext -.I domain -.I sender -.I defaultdelivery -.SH DESCRIPTION -.B qmail-local +.Sh SYNOPSIS +.Nm qmail-local +.Op Fl nN +.Ar user +.Ar homedir +.Ar local +.Ar dash +.Ar ext +.Ar domain +.Ar sender +.Ar defaultdelivery +.Sh DESCRIPTION +.Nm reads a mail message and delivers it to -.I user +.Ar user by the procedure described in -.BR dot-qmail(5) . - +.Xr dot-qmail 5 +\fR. The message's envelope recipient is .IR local@domain . -.B qmail-local +.Nm records -.I local@domain +.Ar local@domain in a new -.B Delivered-To +\fBDelivered-To\fR header field. If exactly the same -.B Delivered-To: \fIlocal@domain +\fBDelivered-To: \fIlocal@domain\fR already appears in the header, -.B qmail-local +.Nm bounces the message, to prevent mail forwarding loops. The message's envelope sender is -.IR sender . -.B qmail-local +.Ar sender +\fR. +.Nm records -.I sender +.Ar sender in a new -.B Return-Path +\fBReturn-Path\fR header field. -.I homedir +.Ar homedir is the user's home directory. It must be an absolute directory name. -.I dash +.Ar dash and -.I ext +.Ar ext identify the -.B .qmail\fIdashext +\fB.qmail\fIdashext\fR file used by -.BR qmail-local ; +.Nm qmail-local +; see -.BR dot-qmail(5) . +.Xr dot-qmail 5 Normally -.I dash +.Ar dash is either empty or a lone hyphen. If it is empty, -.B qmail-local +.Nm treats a nonexistent -.B .qmail\fIext +\fB.qmail\fIext\fR the same way as an empty -.BR .qmail\fIext : +\fB.qmail\fIext\fR: namely, following the delivery instructions in -.IR defaultdelivery . +\fIdefaultdelivery\fR. The standard input for -.B qmail-local +.Nm must be a seekable file, so that -.B qmail-local +.Nm can read it more than once. -.SH "OPTIONS" -.TP -.B \-n +.Sh "OPTIONS" +.Bl -tag -width "-w size" +.It -n Instead of reading and delivering the message, print a description of the delivery instructions. -.TP -.B \-N +.It -N (Default.) Read and deliver the message. -.SH "EXIT CODES" +.It -T +(Not implemented yet.) Used by SMTPDs. Requires the target user to have a home directory that is executable by the SMTPD user, and for the relevant .qmail file to be readable by qmail-local. Exit 99 if delivery WILL succeed, exit 0 if delivery MAY succeed, exit 111 if delivery will temporarily fail, exit with any other status if delivery will permanently fail. Output is not to be relied upon, but must be possible; pass the first line through to the sender and the rest to some logging mechanism. +.El +.Sh "EXIT CODES" 0 if the delivery is completely successful; nonzero if any delivery instruction failed. Exit code 111 indicates temporary failure. -.SH "SEE ALSO" -dot-qmail(5), -envelopes(5), -qmail-command(8), -qmail-queue(8), -qmail-send(8), -qmail-lspawn(8) +.Sh "SEE ALSO" +.Bl -tag -width "-w size" +.It Xr dot-qmail 5 +.It Xr envelopes 5 +.It Xr qmail-command 8 +.It Xr qmail-queue 8 +.It Xr qmail-send 8 +.It Xr qmail-lspawn 8 +.It Lk http://www.courier-mta.org/dot-courier.html dot-courier(5) +to compare and contrast with the methodology used by the Courier MTA for its .qmail file support. In the coming years, Nightmare Mail's developers intend to conform Nightmare Mail to Courier's .qmail file interface. +.El diff --git a/include/bufmissing.h b/include/bufmissing.h @@ -0,0 +1,26 @@ +#ifndef BUFMISSING_H +#define BUFMISSING_H + +#ifndef SKALIBS_CBUFFER_H +// classic substdio buffer +#define BUF_HEAD(b) ((b)->p) +#define BUF_TAIL(b) ((b)->n) +#define BUF_SIZ(b) ((b)->a) +#define BUF_PTR(b) ((b)->x) +#else +// ska buffer - is this its last resort? +#define BUF_HEAD(b) ((b)->c.p) +#define BUF_TAIL(b) ((b)->c.n) +#define BUF_SIZ(b) ((b)->c.a) +#define BUF_PTR(b) ((b)->c.x) +#endif + +#define buffer_fileno(s) ((s)->fd) + +#define buffer_PEEK(s) ( BUF_PTR(s) + BUF_TAIL(s) ) +#define buffer_SEEK(s,len) ( ( BUF_HEAD(s) -= (len) ) , ( BUF_TAIL(s) += (len) ) ) +// Query - why the redundancy? We have buffer_PEEK here and buffer_peek over in bufi.c +// also: why are we using an fd? Why not use potentially any thunk? If we use any thunk, +// we can target items that are not file descriptors. + +#endif // BUFMISSING_H diff --git a/src/qmail-notsmtpd.c b/src/qmail-notsmtpd.c @@ -0,0 +1,581 @@ +#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(); +} diff --git a/src/qmail-qmtpd.c b/src/qmail-qmtpd.c @@ -274,20 +274,20 @@ main() if (!flagbother) qmail_fail(&qq); result = qmail_close(&qq); - if (!flagsenderok) result = "DUnacceptable sender (#5.1.7)"; - if (databytes) if (!bytestooverflow) result = "DSorry, that message size exceeds my databytes limit. (#5.3.4)"; - if (realrcptto_deny()) result = "DSorry, no mailbox here by that name. (#5.1.1)\r\n"; + if (!flagsenderok) result = "D5.1.7 Unacceptable sender"; + if (databytes) if (!bytestooverflow) result = "D5.3.4 Sorry, that message size exceeds my databytes limit."; + if (realrcptto_deny()) result = "D5.1.1 Sorry, no mailbox here by that name.\r\n"; if (*result) len = str_len(result); else { /* success! */ len = 0; - len += fmt_str(buf2 + len,"Kok "); + len += fmt_str(buf2 + len,"KAccepted responsibility for delivery at time "); len += fmt_ulong(buf2 + len,(unsigned long) now()); - len += fmt_str(buf2 + len," qp "); + len += fmt_str(buf2 + len," queue process "); len += fmt_ulong(buf2 + len,qp); - len += fmt_str(buf2 + len," - we'll give it our best shot!"); + len += fmt_str(buf2 + len," - we'll do our best!"); buf2[len] = 0; result = buf2; } @@ -303,10 +303,10 @@ main() substdio_put(&ssout,buf,len); break; case 'D': - substdio_puts(&ssout,"67:DSorry, that domain isn't in my list of allowed rcpthosts. (#5.7.1),"); + substdio_puts(&ssout,"64:D5.7.1 Sorry, that domain isn't in my list of allowed rcpthosts.,"); break; default: - substdio_puts(&ssout,"47:DSorry, I can't handle that recipient. (#5.1.3),"); + substdio_puts(&ssout,"44:D5.1.3 Sorry, I can't handle that recipient.,"); break; } diff --git a/src/qmail-send.c b/src/qmail-send.c @@ -659,6 +659,7 @@ unsigned long id; static stralloc quoted = {0}; datetime_sec birth; unsigned long qp; + char *qclose; if (!getinfo(&sender,&birth,id)) return 0; /* XXX: print warning */ @@ -750,8 +751,11 @@ I tried to deliver a bounce message to this address, but the bounce bounced!\n\ qmail_from(&qqt,bouncesender); qmail_to(&qqt,bouncerecip); - if (*qmail_close(&qqt)) - { log1("warning: trouble injecting bounce message, will try later\n"); return 0; } + if (*(qclose = qmail_close(&qqt))) + { log1("warning: trouble injecting bounce message, will try later: "); + log1(qclose); + log1("\n"); + return 0; } strnum2[fmt_ulong(strnum2,id)] = 0; qslog2("bounce msg ",strnum2); @@ -1441,7 +1445,8 @@ fd_set *rfds; /* this file is too long ---------------------------------------------- MAIN */ -int getcontrols() { if (control_init() == -1) return 0; +int getcontrols() { + if (control_init() == -1) return 0; if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0; if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0; if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0; @@ -1467,7 +1472,8 @@ int getcontrols() { if (control_init() == -1) return 0; case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break; case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break; } - return 1; } + return 1; +} stralloc newlocals = {0}; stralloc newvdoms = {0}; diff --git a/src/qmail.c b/src/qmail.c @@ -1,6 +1,7 @@ #include "qmail.h" #include "substdio.h" +//#include "stralloc.h" #include "readwrite.h" #include "wait.h" #include "exit.h" @@ -8,6 +9,7 @@ #include "fd.h" #include "auto_qmail.h" #include "env.h" +#include "fmt.h" static char *binqqargs[2] = { 0, 0 } ; @@ -111,6 +113,7 @@ struct qmail *qq; int exitcode; static char errstr[256]; size_t errlen; + char excode[10]; qmail_put(qq,"",1); if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1; @@ -119,40 +122,52 @@ struct qmail *qq; close(qq->fderr); if (wait_pid(&wstat,qq->pid) != qq->pid) - return "Zqq waitpid surprise (#4.3.0)"; + return "Z4.3.0 qq waitpid surprise"; if (wait_crashed(wstat)) - return "Zqq crashed (#4.3.0)"; + return "Z4.3.0 qq crashed"; exitcode = wait_exitcode(wstat); switch(exitcode) { case 115: /* compatibility */ - case 11: return "Denvelope address too long for qq (#5.1.3)"; - case 31: return "Dmail server permanently rejected message (#5.3.0)"; - case 51: return "Zqq out of memory (#4.3.0)"; - case 52: return "Zqq timeout (#4.3.0)"; - case 53: return "Zqq write error or disk full (#4.3.0)"; + case 11: return "D5.1.3 envelope address too long for qq"; + case 31: return "D5.3.0 mail server permanently rejected message"; + case 51: return "Z4.3.0 qq out of memory"; + case 52: return "Z4.3.0 qq timeout"; + case 53: return "Z4.3.0 qq write error or disk full"; case 0: if (!qq->flagerr) return ""; /* fall through */ - case 54: return "Zqq read error (#4.3.0)"; - case 55: return "Zqq unable to read configuration (#4.3.0)"; - case 56: return "Zqq trouble making network connection (#4.3.0)"; - case 61: return "Zqq trouble in home directory (#4.3.0)"; + case 54: return "Z4.3.0 qq read error"; + case 55: return "Z4.3.0 qq unable to read configuration"; + case 56: return "Z4.3.0 qq trouble making network connection"; + case 61: return "Z4.3.0 qq trouble in home directory"; case 63: case 64: case 65: case 66: - case 62: return "Zqq trouble creating files in queue (#4.3.0)"; - case 71: return "Zmail server temporarily rejected message (#4.3.0)"; - case 72: return "Zconnection to mail server timed out (#4.4.1)"; - case 73: return "Zconnection to mail server rejected (#4.4.1)"; - case 74: return "Zcommunication with mail server failed (#4.4.2)"; + case 62: return "Z4.3.0 qq trouble creating files in queue"; + case 71: return "Z4.3.0 mail server temporarily rejected message"; + case 72: return "Z4.4.1 connection to mail server timed out"; + case 73: return "Z4.4.1 connection to mail server rejected"; + case 74: return "Z4.4.2 communication with mail server failed"; case 91: /* fall through */ - case 81: return "Zqq internal bug (#4.3.0)"; - case 120: return "Zunable to exec qq (#4.3.0)"; + case 81: return "Z4.3.0 qq internal bug"; + case 120: return "Z4.3.0 unable to exec qq"; default: if (exitcode == 82 && errlen > 2) return errstr; if ((exitcode >= 11) && (exitcode <= 40)) - return "Dqq permanent problem (#5.3.0)"; - return "Zqq temporary problem (#4.3.0)"; + return "D5.3.0 qq permanent problem"; +#if 0 // change this to zero when done please + if (!stralloc_copys(&exitstr, "Z4.3.0 qq temporary problem: exited with code ")) return "Z4.3.0 temporary problem + memory allocation failure"; + excode[fmt_ulong(excode, exitcode)] = 0; + if (!stralloc_cats(&exitstr, excode)) return "Z4.3.0 temporary problem + memory allocation failure"; + if (errlen > 2) { + if (!stralloc_cats(&exitstr, " and errstr of")) return "Z4.3.0 temporary problem + memory allocation failure"; + if (!stralloc_cats(&exitstr, errstr)) return "Z4.3.0 temporary problem + memory allocation failure"; + } + stralloc_0(&exitstr); + return exitstr.s; // dangling pointer +#else + return "Z4.3.0 qq temporary problem"; +#endif } }