X-Git-Url: https://git.distorted.org.uk/~mdw/qmail/blobdiff_plain/2117e02ec495fdfd6e96b39778b701a5bcff8aa5..HEAD:/qmail-smtpd.c diff --git a/qmail-smtpd.c b/qmail-smtpd.c index 50cfc0a..88f4ea6 100644 --- a/qmail-smtpd.c +++ b/qmail-smtpd.c @@ -1,6 +1,5 @@ #include "sig.h" #include "readwrite.h" -#include "getln.h" #include "stralloc.h" #include "substdio.h" #include "alloc.h" @@ -14,57 +13,71 @@ #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 "addrcheck.h" +#include "timeoutread.h" +#include "timeoutwrite.h" +#include "commands.h" #define MAXHOPS 100 +unsigned int databytes = 0; int timeout = 1200; -char ssoutbuf[512]; -substdio ssout = SUBSTDIO_FDBUF(write,1,ssoutbuf,sizeof(ssoutbuf)); - -void die() { substdio_flush(&ssout); _exit(1); } -void flush() { if (substdio_flush(&ssout) == -1) _exit(1); } -void out(s) char *s; { if (substdio_puts(&ssout,s) == -1) die(); } - -int timeoutread(fd,buf,n) int fd; char *buf; int n; +int safewrite(fd,buf,len) int fd; char *buf; int len; { - int r; int saveerrno; - flush(); - alarm(timeout); - r = read(fd,buf,n); saveerrno = errno; - alarm(0); - errno = saveerrno; return r; + int r; + r = timeoutwrite(timeout,fd,buf,len); + if (r <= 0) _exit(1); + return r; } -char ssinbuf[1024]; -substdio ssin = SUBSTDIO_FDBUF(timeoutread,0,ssinbuf,sizeof(ssinbuf)); +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 outofmem() { out("421 out of memory (#4.3.0)\r\n"); die(); } -void sigalrm() { out("451 timeout (#4.4.2)\r\n"); die(); } +void die_read() { _exit(1); } +void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); } +void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); } +void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } +void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } +void die_badaddr() { out("553 too many bad recipients: sulking (#5.5.1)\r\n"); flush(); _exit(1); } +void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } -struct qmail qqt; -stralloc greeting = {0}; -int liphostok = 0; -stralloc liphost = {0}; -int rhok = 0; -stralloc rcpthosts = {0}; -struct constmap maprcpthosts; -int bmfok = 0; -stralloc bmf = {0}; -struct constmap mapbmf; -int flagbarf; /* defined if seenmail */ +void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } +void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } +void err_badaddr() { out("550 unknown mailbox (#5.1.1)\r\n"); } +void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); } +void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } +void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } +void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); } +void err_noop() { out("250 ok\r\n"); } +void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } +void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } -stralloc helohost = {0}; -stralloc mailfrom = {0}; -stralloc rcptto = {0}; -int seenmail = 0; -stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ +stralloc greeting = {0}; + +void smtp_greet(code) char *code; +{ + substdio_puts(&ssout,code); + substdio_put(&ssout,greeting.s,greeting.len); +} +void smtp_help() +{ + out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n"); +} +void smtp_quit() +{ + smtp_greet("221 "); out("\r\n"); flush(); _exit(0); +} char *remoteip; char *remotehost; @@ -72,378 +85,393 @@ char *remoteinfo; char *local; char *relayclient; -void dohelo(arg) char *arg; -{ - if (!stralloc_copys(&helohost,arg)) outofmem(); - if (!stralloc_0(&helohost)) outofmem(); -} +stralloc helohost = {0}; +char *fakehelo; /* pointer into helohost, or 0 */ -void getenvs() -{ - 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"); - dohelo(remotehost); +void dohelo(arg) char *arg; { + if (!stralloc_copys(&helohost,arg)) die_nomem(); + if (!stralloc_0(&helohost)) die_nomem(); + fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; } -void straynewline() -{ - out("451 \ -Put ,E=\\r\\n at the end of Mether, Mtcp, or Msmtp in sendmail.cf \ -if you are using Solaris 2.5 (fixed in 2.5.1). \ -I cannot accept messages with stray newlines. \ -Many SMTP servers will time out waiting for \\r\\n.\\r\\n.\ -\r\n"); - die(); -} +int liphostok = 0; +stralloc liphost = {0}; +int relayhostsok = 0; +stralloc relayhosts = {0}; +struct constmap maprelayhosts; +int bmfok = 0; +stralloc bmf = {0}; +struct constmap mapbmf; +static int ac_slow = 5; +static int ac_limit = 50; +static int ac_delay = 2; +static int ac_count = 0; +static int ac_fd = -1; -void blast(ssfrom,hops) -substdio *ssfrom; -int *hops; +void setup() { - 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 (;;) - { - if (substdio_get(ssfrom,&ch,1) <= 0) die(); - if (flaginheader) - { - if (pos < 9) - { - 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; - qmail_put(&qqt,".\r",2); - if (ch == '\r') { state = 4; continue; } - state = 0; - break; - case 4: /* + \r */ - if (ch == '\n') { state = 1; break; } - if (ch != '\r') { qmail_put(&qqt,"\r",1); state = 0; } + char *x; + unsigned long u; + + if (control_init() == -1) die_control(); + if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) + die_control(); + liphostok = control_rldef(&liphost,"control/localiphost",1,(char *) 0); + 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(); + + switch (control_readfile(&relayhosts, "control/relayhosts", 0)) { + case -1: + die_control(); + case 1: + relayhostsok = 1; + if (!constmap_init(&maprelayhosts, relayhosts.s, relayhosts.len, 1)) + die_nomem(); + } + + if (control_readint(&ac_slow, "control/addrcheck-slow") == -1 || + control_readint(&ac_slow, "control/addrcheck-limit") == -1 || + control_readint(&ac_slow, "control/addrcheck-delay") == -1) + die_control(); + + if ((ac_fd = open_read("control/addrcheck.cdb")) < 0 && errno != error_noent) + die_control(); + + 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; + + 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"); + if (!relayclient && relayhostsok) { + int j; + int l = str_len(remotehost); + relayclient = constmap(&maprelayhosts, remotehost, l); + if (!relayclient) for (j = 0; j < l; ++j) { + if (remotehost[j] == '.' && + (relayclient = constmap(&maprelayhosts, + remotehost + j, + l - j)) != 0) + break; } - qmail_put(&qqt,&ch,1); } + dohelo(remotehost); } + +stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ + int addrparse(arg) char *arg; { - int i; - char ch; - struct ip_address ip; - int flagesc; - int flagquoted; - - arg += str_chr(arg,'<'); - if (*arg != '<') return 0; - ++arg; - - /* strip source route */ - if (*arg == '@') while (*arg) if (*arg++ == ':') break; - - if (!*arg) return 0; - if (!stralloc_copys(&addr,"")) outofmem(); - flagesc = 0; - flagquoted = 0; - for (i = 0;ch = arg[i];++i) /* copy arg to addr, stripping quotes */ - { - if (flagesc) - { if (!stralloc_append(&addr,&ch)) outofmem(); flagesc = 0; } - else - { - if (!flagquoted && (ch == '>')) break; - switch(ch) - { - case '\\': flagesc = 1; break; - case '"': flagquoted = !flagquoted; break; - default: if (!stralloc_append(&addr,&ch)) outofmem(); + 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(); } } } - if (!ch) return 0; - if (!stralloc_append(&addr,"")) outofmem(); - ++i; - while (arg[i]) - { - if (!case_diffs(arg + i," BODY=8BITMIME")) i += 14; - else if (!case_diffs(arg + i," BODY=7BIT")) i += 10; - else return 0; - } - - 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)) outofmem(); - if (!stralloc_0(&addr)) outofmem(); + /* 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(); } } - return 1; + if (addr.len > 900) return 0; + return 1; } -int addrallowed() +int bmfcheck() { - int j; - if (!rhok) return 1; - j = byte_rchr(addr.s,addr.len,'@'); - if (j >= addr.len) return 1; /* can be taken care of by envnoathost */ - if (constmap(&maprcpthosts,addr.s + j + 1,addr.len - j - 2)) return 1; - for (;j < addr.len;++j) - if (addr.s[j] == '.') - if (constmap(&maprcpthosts,addr.s + j,addr.len - j - 1)) return 1; - return 0; + 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; } -void bmfcheck() +int addrallowed() { - int j; - flagbarf = 0; - if (!bmfok) return; - if (constmap(&mapbmf,addr.s,addr.len - 1)) { flagbarf = 1; return; } - j = byte_rchr(addr.s,addr.len,'@'); - if (j < addr.len) - if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) flagbarf = 1; + int r; + r = rcpthosts(addr.s,str_len(addr.s)); + if (r == -1) die_control(); + return r; } -void smtp_greet(code) char *code; { - if (substdio_puts(&ssout,code) == -1) die(); - if (substdio_put(&ssout,greeting.s,greeting.len) == -1) die(); } -void smtp_quit() { smtp_greet("221 "); out("\r\n"); die(); } -void smtp_help() { out("214-qmail home page: http://pobox.com/~djb/qmail.html\r\n214 send comments to qmail@pobox.com\r\n"); } -void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } -void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } -void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } -void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); } -void err_seenmail() { out("503 one MAIL per message (#5.5.1)\r\n"); } -void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } -void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); } -void err_noop() { out("250 ok\r\n"); } -void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } -void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } -void smtp_helo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); - seenmail = 0; - dohelo(arg ? arg : ""); } -void smtp_rset() { - seenmail = 0; - out("250 flushed\r\n"); } -void smtp_mail(arg) char *arg; { - if (seenmail) { err_seenmail(); return; } - if (!arg) { err_syntax(); return; } - if (!addrparse(arg)) { err_syntax(); return; } - bmfcheck(); - seenmail = 1; out("250 ok\r\n"); - if (!stralloc_copys(&rcptto,"")) outofmem(); - if (!stralloc_copys(&mailfrom,addr.s)) outofmem(); - if (!stralloc_0(&mailfrom)) outofmem(); } + +int seenmail = 0; +int flagbarf; /* defined if seenmail */ +stralloc mailfrom = {0}; +stralloc rcptto = {0}; + +void smtp_helo(arg) char *arg; +{ + smtp_greet("250 "); out("\r\n"); + seenmail = 0; dohelo(arg); +} +void smtp_ehlo(arg) char *arg; +{ + smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + seenmail = 0; dohelo(arg); +} +void smtp_rset() +{ + seenmail = 0; + out("250 flushed\r\n"); +} +void smtp_mail(arg) 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(); + out("250 ok\r\n"); +} void smtp_rcpt(arg) char *arg; { - if (!seenmail) { err_wantmail(); return; } - if (!arg) { err_syntax(); return; } - if (!addrparse(arg)) { err_syntax(); return; } - if (flagbarf) { err_bmf(); return; } - if (relayclient) - { - --addr.len; - if (!stralloc_cats(&addr,relayclient)) outofmem(); - if (!stralloc_0(&addr)) outofmem(); + 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 (ac_fd != -1) { + int rc; + if (addrcheck(ac_fd, addr.s, mailfrom.s, &rc) < 0) { + if (errno == error_nomem) + die_nomem(); + else + die_control(); + } + if (!rc) { + ac_count++; + if (ac_limit && ac_count >= ac_limit) die_badaddr(); + if (ac_delay && ac_count >= ac_slow) sleep(ac_delay); + err_badaddr(); + return; + } } - else - if (!addrallowed()) { err_nogateway(); return; } - out("250 ok\r\n"); - if (!stralloc_cats(&rcptto,"T")) outofmem(); - if (!stralloc_cats(&rcptto,addr.s)) outofmem(); - if (!stralloc_0(&rcptto)) outofmem(); } + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + out("250 ok\r\n"); +} -char accept_buf[FMT_ULONG]; -void acceptmessage(qp) unsigned long qp; + +int saferead(fd,buf,len) int fd; char *buf; int len; { - datetime_sec when; - when = now(); - out("250 ok "); - accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0; - out(accept_buf); - out(" qp "); - accept_buf[fmt_ulong(accept_buf,qp)] = 0; - out(accept_buf); - out("\r\n"); + int r; + flush(); + r = timeoutread(timeout,fd,buf,len); + if (r == -1) if (errno == error_timeout) die_alarm(); + if (r <= 0) die_read(); + return r; } -void smtp_data() { - int hops; int r; unsigned long qp; - if (!seenmail) { err_wantmail(); return; } - if (!rcptto.len) { err_wantrcpt(); return; } - seenmail = 0; - if (qmail_open(&qqt) == -1) { err_qqt(); return; } - qp = qmail_qp(&qqt); - out("354 go ahead\r\n"); - - received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,case_diffs(remotehost,helohost.s) ? helohost.s : 0); - blast(&ssin,&hops); - hops = (hops >= MAXHOPS); - if (hops) qmail_fail(&qqt); - qmail_from(&qqt,mailfrom.s); - qmail_put(&qqt,rcptto.s,rcptto.len); - - r = qmail_close(&qqt); - if (!r) { acceptmessage(qp); return; } - if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } - switch(r) - { - case QMAIL_TOOLONG: out("554 address too long (#5.1.3)\r\n"); return; - case QMAIL_SYS: out("451 qq system error (#4.3.0)\r\n"); return; - case QMAIL_READ: out("451 qq read error (#4.3.0)\r\n"); return; - case QMAIL_WRITE: out("451 qq write error or disk full (#4.3.0)\r\n"); return; - case QMAIL_NOMEM: out("451 qq out of memory (#4.3.0)\r\n"); return; - case QMAIL_EXECSOFT: out("451 could not exec qq (#4.3.0)\r\n"); return; - case QMAIL_TIMEOUT: out("451 qq timeout (#4.3.0)\r\n"); return; - case QMAIL_WAITPID: out("451 qq waitpid surprise (#4.3.0)\r\n"); return; - case QMAIL_CRASHED: out("451 qq crashed (#4.3.0)\r\n"); return; - case QMAIL_USAGE: out("451 qq usage surprise (#4.3.0)\r\n"); return; - default: out("451 qq internal bug (#4.3.0)\r\n"); return; - } +char ssinbuf[1024]; +substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf); + +struct qmail qqt; +unsigned int bytestooverflow = 0; + +void put(ch) +char *ch; +{ + if (bytestooverflow) + if (!--bytestooverflow) + qmail_fail(&qqt); + qmail_put(&qqt,ch,1); } -static struct { void (*fun)(); char *text; int flagflush; } smtpcmd[] = { - { smtp_rcpt, "rcpt", 0 } -, { smtp_mail, "mail", 0 } -, { smtp_data, "data", 1 } -, { smtp_quit, "quit", 1 } -, { smtp_helo, "helo", 1 } -, { smtp_helo, "ehlo", 1 } -, { smtp_rset, "rset", 0 } -, { smtp_help, "help", 1 } -, { err_noop, "noop", 1 } -, { err_vrfy, "vrfy", 1 } -, { 0, 0, 0 } -}; - -void doit(cmd) -char *cmd; +void blast(hops) +int *hops; { - int i; - int j; - char ch; - - for (i = 0;smtpcmd[i].fun;++i) - { - for (j = 0;ch = smtpcmd[i].text[j];++j) - if ((cmd[j] != ch) && (cmd[j] != ch - 32)) - break; - if (!ch) - if (!cmd[j] || (cmd[j] == ' ')) - { - while (cmd[j] == ' ') ++j; - if (!cmd[j]) - smtpcmd[i].fun((char *) 0); - else - smtpcmd[i].fun(cmd + j); - if (smtpcmd[i].flagflush) flush(); - return; + 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 (;;) { + substdio_get(&ssin,&ch,1); + if (flaginheader) { + if (pos < 9) { + 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); } - err_unimpl(); - flush(); } -void getcontrols() +char accept_buf[FMT_ULONG]; +void acceptmessage(qp) unsigned long qp; { - if (control_init() == -1) die(); - if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) die(); - switch(control_rldef(&liphost,"control/localiphost",1,(char *) 0)) - { case -1: die(); case 1: liphostok = 1; } - if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die(); - if (timeout <= 0) timeout = 1; - switch(control_readfile(&rcpthosts,"control/rcpthosts",0)) - { - case -1: die(); - case 1: - rhok = 1; - if (!constmap_init(&maprcpthosts,rcpthosts.s,rcpthosts.len,0)) die(); - } - switch(control_readfile(&bmf,"control/badmailfrom",0)) - { - case -1: die(); - case 1: - bmfok = 1; - if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die(); - } + datetime_sec when; + when = now(); + out("250 ok "); + accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0; + out(accept_buf); + out(" qp "); + accept_buf[fmt_ulong(accept_buf,qp)] = 0; + out(accept_buf); + out("\r\n"); +} + +void smtp_data() { + int hops; + unsigned long qp; + char *qqx; + + if (!seenmail) { err_wantmail(); return; } + if (!rcptto.len) { err_wantrcpt(); 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"); + + received(&qqt,"SMTP",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 too many hops, this message is looping (#5.4.6)\r\n"); return; } + if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\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 } +, { "data", smtp_data, flush } +, { "quit", smtp_quit, flush } +, { "helo", smtp_helo, flush } +, { "ehlo", smtp_ehlo, flush } +, { "rset", smtp_rset, 0 } +, { "help", smtp_help, flush } +, { "noop", err_noop, flush } +, { "vrfy", err_vrfy, flush } +, { 0, err_unimpl, flush } +} ; + void main() { - static stralloc cmd = {0}; - int match; - - sig_alarmcatch(sigalrm); - sig_pipeignore(); - - if (chdir(auto_qmail) == -1) die(); - getcontrols(); - getenvs(); - - if (ipme_init() != 1) die(); - - smtp_greet("220 "); - out(" ESMTP\r\n"); - - for (;;) - { - /* XXX: recipient can contain quoted lf. aargh. */ - if (getln(&ssin,&cmd,&match,'\n') == -1) die(); - if (!match) die(); - if (cmd.len == 0) die(); - if (cmd.s[--cmd.len] != '\n') die(); - if ((cmd.len > 0) && (cmd.s[cmd.len - 1] == '\r')) --cmd.len; - cmd.s[cmd.len++] = 0; - doit(cmd.s); - } + sig_pipeignore(); + if (chdir(auto_qmail) == -1) die_control(); + setup(); + if (ipme_init() != 1) die_ipme(); + smtp_greet("220 "); + out(" ESMTP\r\n"); + if (commands(&ssin,&smtpcommands) == 0) die_read(); + die_nomem(); }