Merge branch 'mdw/relayhosts'
authorMark Wooding <mdw@distorted.org.uk>
Tue, 14 Feb 2006 02:52:33 +0000 (02:52 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Tue, 14 Feb 2006 02:52:33 +0000 (02:52 +0000)
* mdw/relayhosts:
  qmail-smtpd: Read list of hosts allowed to relay from control/relayhosts

Conflicts:

qmail-control.9
qmail-showctl.c
qmail-smtpd.c

1  2 
qmail-control.9
qmail-showctl.c
qmail-smtpd.8
qmail-smtpd.c

diff --combined qmail-control.9
@@@ -23,7 -23,6 +23,7 @@@ i
  .IR badmailfrom ,
  .IR locals ,
  .IR percenthack ,
 +.IR qmqpservers ,
  .IR rcpthosts ,
  .IR smtproutes ,
  and
@@@ -47,7 -46,6 +47,7 @@@ control       default used b
  .I concurrencyremote  \fR20   \fRqmail-send
  .I defaultdomain      \fIme   \fRqmail-inject
  .I defaulthost        \fIme   \fRqmail-inject
 +.I databytes  \fR0    \fRqmail-smtpd
  .I doublebouncehost   \fIme   \fRqmail-send
  .I doublebounceto     \fRpostmaster   \fRqmail-send
  .I envnoathost        \fIme   \fRqmail-send
  .I idhost     \fIme   \fRqmail-inject
  .I localiphost        \fIme   \fRqmail-smtpd
  .I locals     \fIme   \fRqmail-send
 +.I morercpthosts      \fR(none)       \fRqmail-smtpd
  .I percenthack        \fR(none)       \fRqmail-send
  .I plusdomain \fIme   \fRqmail-inject
 +.I qmqpservers        \fR(none)       \fRqmail-qmqpc
  .I queuelifetime      \fR604800       \fRqmail-send
  .I rcpthosts  \fR(none)       \fRqmail-smtpd
 -.I recipientmap       \fR(none)       \fRqmail-send
+ .I relayhosts \fR(none)       \fRqmail-smtpd
  .I smtpgreeting       \fIme   \fRqmail-smtpd
  .I smtproutes \fR(none)       \fRqmail-remote
  .I timeoutconnect     \fR60   \fRqmail-remote
@@@ -71,7 -69,6 +72,7 @@@
  .RE
  .SH "SEE ALSO"
  qmail-inject(8),
 +qmail-qmqpc(8),
  qmail-remote(8),
  qmail-send(8),
  qmail-showctl(8),
diff --combined qmail-showctl.c
@@@ -1,5 -1,3 +1,5 @@@
 +#include <sys/types.h>
 +#include <sys/stat.h>
  #include "substdio.h"
  #include "subfd.h"
  #include "exit.h"
@@@ -9,12 -7,7 +9,12 @@@
  #include "constmap.h"
  #include "stralloc.h"
  #include "direntry.h"
 +#include "auto_uids.h"
  #include "auto_qmail.h"
 +#include "auto_break.h"
 +#include "auto_patrn.h"
 +#include "auto_spawn.h"
 +#include "auto_split.h"
  
  stralloc me = {0};
  int meok;
@@@ -100,7 -93,7 +100,7 @@@ char *pre
    }
  }
  
 -void do_lst(fn,def,pre,post)
 +int do_lst(fn,def,pre,post)
  char *fn;
  char *def;
  char *pre;
@@@ -117,7 -110,7 +117,7 @@@ char *post
        substdio_puts(subfdout,"(Default.) ");
        substdio_puts(subfdout,def);
        substdio_puts(subfdout,"\n");
 -      break;
 +      return 0;
      case 1:
        substdio_puts(subfdout,"\n");
        i = 0;
            substdio_puts(subfdout,"\n");
          i = j + 1;
        }
 -      break;
 +      return 1;
      default:
        substdio_puts(subfdout,"Oops! Trouble reading this file.\n");
 -      break;
 +      return -1;
    }
  }
  
@@@ -140,52 -133,10 +140,52 @@@ void main(
  {
    DIR *dir;
    direntry *d;
 +  struct stat stmrh;
 +  struct stat stmrhcdb;
  
 -  substdio_puts(subfdout,"The qmail control files are stored in ");
 +  substdio_puts(subfdout,"qmail home directory: ");
    substdio_puts(subfdout,auto_qmail);
 -  substdio_puts(subfdout,"/control.\n");
 +  substdio_puts(subfdout,".\n");
 +
 +  substdio_puts(subfdout,"user-ext delimiter: ");
 +  substdio_puts(subfdout,auto_break);
 +  substdio_puts(subfdout,".\n");
 +
 +  substdio_puts(subfdout,"paternalism (in decimal): ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_patrn));
 +  substdio_puts(subfdout,".\n");
 +
 +  substdio_puts(subfdout,"silent concurrency limit: ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_spawn));
 +  substdio_puts(subfdout,".\n");
 +
 +  substdio_puts(subfdout,"subdirectory split: ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_split));
 +  substdio_puts(subfdout,".\n");
 +
 +  substdio_puts(subfdout,"user ids: ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uida));
 +  substdio_puts(subfdout,", ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidd));
 +  substdio_puts(subfdout,", ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidl));
 +  substdio_puts(subfdout,", ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uido));
 +  substdio_puts(subfdout,", ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidp));
 +  substdio_puts(subfdout,", ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidq));
 +  substdio_puts(subfdout,", ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uidr));
 +  substdio_puts(subfdout,", ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_uids));
 +  substdio_puts(subfdout,".\n");
 +
 +  substdio_puts(subfdout,"group ids: ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_gidn));
 +  substdio_puts(subfdout,", ");
 +  substdio_put(subfdout,num,fmt_ulong(num,(unsigned long) auto_gidq));
 +  substdio_puts(subfdout,".\n");
  
    if (chdir(auto_qmail) == -1) {
      substdio_puts(subfdout,"Oops! Unable to chdir to ");
    do_str("bouncehost",1,"bouncehost","Bounce host name is ");
    do_int("concurrencylocal","10","Local concurrency is ","");
    do_int("concurrencyremote","20","Remote concurrency is ","");
 +  do_int("databytes","0","SMTP DATA limit is "," bytes");
    do_str("defaultdomain",1,"defaultdomain","Default domain name is ");
    do_str("defaulthost",1,"defaulthost","Default host name is ");
    do_str("doublebouncehost",1,"doublebouncehost","2B recipient host: ");
    do_str("me",0,"undefined! Uh-oh","My name is ");
    do_lst("percenthack","The percent hack is not allowed.","The percent hack is allowed for user%host@",".");
    do_str("plusdomain",1,"plusdomain","Plus domain name is ");
 +  do_lst("qmqpservers","No QMQP servers.","QMQP server: ",".");
    do_int("queuelifetime","604800","Message lifetime in the queue is "," seconds");
 -  do_lst("rcpthosts","SMTP clients may send messages to any recipient.","SMTP clients may send messages to recipients at ",".");
 -  do_lst("recipientmap","No redirections.","Redirection: ","");
 +
 +  if (do_lst("rcpthosts","SMTP clients may send messages to any recipient.","SMTP clients may send messages to recipients at ","."))
 +    do_lst("morercpthosts","No effect.","SMTP clients may send messages to recipients at ",".");
 +  else
 +    do_lst("morercpthosts","No rcpthosts; morercpthosts is irrelevant.","No rcpthosts; doesn't matter that morercpthosts has ",".");
 +  /* XXX: check morercpthosts.cdb contents */
 +  substdio_puts(subfdout,"\nmorercpthosts.cdb: ");
 +  if (stat("morercpthosts",&stmrh) == -1)
 +    if (stat("morercpthosts.cdb",&stmrhcdb) == -1)
 +      substdio_puts(subfdout,"(Default.) No effect.\n");
 +    else
 +      substdio_puts(subfdout,"Oops! morercpthosts.cdb exists but morercpthosts doesn't.\n");
 +  else
 +    if (stat("morercpthosts.cdb",&stmrhcdb) == -1)
 +      substdio_puts(subfdout,"Oops! morercpthosts exists but morercpthosts.cdb doesn't.\n");
 +    else
 +      if (stmrh.st_mtime > stmrhcdb.st_mtime)
 +        substdio_puts(subfdout,"Oops! morercpthosts.cdb is older than morercpthosts.\n");
 +      else
 +        substdio_puts(subfdout,"Modified recently enough; hopefully up to date.\n");
 +
+   do_lst("relayhosts","No relayhosts","Relay host: ","");
    do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 ");
    do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ","");
    do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds");
      if (str_equal(d->d_name,"bouncehost")) continue;
      if (str_equal(d->d_name,"concurrencylocal")) continue;
      if (str_equal(d->d_name,"concurrencyremote")) continue;
 +    if (str_equal(d->d_name,"databytes")) continue;
      if (str_equal(d->d_name,"defaultdomain")) continue;
      if (str_equal(d->d_name,"defaulthost")) continue;
      if (str_equal(d->d_name,"doublebouncehost")) continue;
      if (str_equal(d->d_name,"localiphost")) continue;
      if (str_equal(d->d_name,"locals")) continue;
      if (str_equal(d->d_name,"me")) continue;
 +    if (str_equal(d->d_name,"morercpthosts")) continue;
 +    if (str_equal(d->d_name,"morercpthosts.cdb")) continue;
      if (str_equal(d->d_name,"percenthack")) continue;
      if (str_equal(d->d_name,"plusdomain")) continue;
 +    if (str_equal(d->d_name,"qmqpservers")) continue;
      if (str_equal(d->d_name,"queuelifetime")) continue;
      if (str_equal(d->d_name,"rcpthosts")) continue;
 -    if (str_equal(d->d_name,"recipientmap")) continue;
+     if (str_equal(d->d_name,"relayhosts")) continue;
      if (str_equal(d->d_name,"smtpgreeting")) continue;
      if (str_equal(d->d_name,"smtproutes")) continue;
      if (str_equal(d->d_name,"timeoutconnect")) continue;
diff --combined qmail-smtpd.8
@@@ -28,9 -28,6 +28,9 @@@ supports ESMTP, including the 8BITMIME 
  .B qmail-smtpd
  converts the SMTP newline convention into the UNIX newline convention
  by converting CR LF into LF.
 +It returns a temporary error and drops the connection on bare LFs;
 +see
 +.BR http://pobox.com/~djb/docs/smtplf.html .
  
  .B qmail-smtpd
  accepts messages that contain long lines or non-ASCII characters,
@@@ -50,33 -47,6 +50,33 @@@ may be of the for
  meaning every address at
  .IR host .
  .TP 5
 +.I databytes
 +Maximum number of bytes allowed in a message,
 +or 0 for no limit.
 +Default: 0.
 +If a message exceeds this limit,
 +.B qmail-smtpd
 +returns a permanent error code to the client;
 +in contrast, if
 +the disk is full or
 +.B qmail-smtpd
 +hits a resource limit,
 +.B qmail-smtpd
 +returns a temporary error code.
 +
 +.I databytes
 +counts bytes as stored on disk, not as transmitted through the network.
 +It does not count the
 +.B qmail-smtpd
 +Received line, the
 +.B qmail-queue
 +Received line, or the envelope.
 +
 +If the environment variable
 +.B DATABYTES
 +is set, it overrides
 +.IR databytes .
 +.TP 5
  .I localiphost
  Replacement host name for local IP addresses.
  Default:
@@@ -97,29 -67,6 +97,29 @@@ wit
  This is done before
  .IR rcpthosts .
  .TP 5
 +.I morercpthosts
 +Extra allowed RCPT domains.
 +If
 +.I rcpthosts
 +and
 +.I morercpthosts
 +both exist,
 +.I morercpthosts
 +is effectively appended to
 +.IR rcpthosts .
 +
 +You must run
 +.B qmail-newmrh
 +whenever
 +.I morercpthosts
 +changes.
 +
 +Rule of thumb for large sites:
 +Put your 50 most commonly used domains into
 +.IR rcpthosts ,
 +and the rest into
 +.IR morercpthosts .
 +.TP 5
  .I rcpthosts
  Allowed RCPT domains.
  If
@@@ -128,18 -75,11 +128,11 @@@ is supplied
  .B qmail-smtpd
  will reject
  any envelope recipient address with a domain not listed in
- .IR rcpthosts .
- Exception:
- If the environment variable
- .B RELAYCLIENT
- is set,
- .B qmail-smtpd
- will ignore
- .IR rcpthosts ,
- and will append the value of
- .B RELAYCLIENT
- to each incoming recipient address.
+ .I rcpthosts
+ unless the sending host is a designated relay client (see the
+ description of the
+ .I relayhosts
+ file beow).
  
  .I rcpthosts
  may include wildcards:
  Envelope recipient addresses without @ signs are
  always allowed through.
  .TP 5
+ .I relayhosts
+ Allowed relay clients.  Each line is a host-suffix pair, separated by a
+ colon.  If the client's hostname matches one of the hostnames in the
+ file, that client is permitted to send mail to any host (i.e., to use us
+ as a relay), and the corresponding suffix is appended to all recipient
+ addresses generated by the client.
+ .I relayhosts
+ may include wildcards:
+ .EX
+    heaven.af.mil:
+    .heaven.af.mil:
+    hell.irs.gov:.irs.virtdomain
+ .EE
+ For historical reasons, the
+ .B RELAYCLIENT
+ environment variable overrides this table.  If
+ .B RELAYCLIENT
+ is set, it has the same effect as there being a matching entry in the
+ .I relayhosts
+ file, using the value of
+ .B RELAYCLIENT
+ as the suffix.
+ .TP 5
  .I smtpgreeting
  SMTP greeting message.
  Default:
@@@ -174,6 -140,5 +193,6 @@@ tcp-env(1)
  tcp-environ(5),
  qmail-control(5),
  qmail-inject(8),
 +qmail-newmrh(8),
  qmail-queue(8),
  qmail-remote(8)
diff --combined qmail-smtpd.c
@@@ -1,5 -1,6 +1,5 @@@
  #include "sig.h"
  #include "readwrite.h"
 -#include "getln.h"
  #include "stralloc.h"
  #include "substdio.h"
  #include "alloc.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 "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 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};
 -int relayhostsok = 0;
 -stralloc relayhosts = {0};
 -struct constmap maprelayhosts;
 -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_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;
@@@ -82,340 -75,398 +82,365 @@@ 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");
 - 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;
 -   }
 - }
 - 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;
  
 -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(&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; }
 - out("250 ok\r\n");
 - if (!stralloc_cats(&rcptto,"T")) outofmem();
 - if (!stralloc_cats(&rcptto,addr.s)) outofmem();
 - if (!stralloc_0(&rcptto)) outofmem(); }
 +  else
 +    if (!addrallowed()) { err_nogateway(); return; }
 +  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();
 -  }
 - switch (control_readfile(&relayhosts, "control/relayhosts", 0)) {
 -   case -1:
 -     die();
 -   case 1:
 -     relayhostsok = 1;
 -     if (!constmap_init(&maprelayhosts, relayhosts.s, relayhosts.len, 1))
 -       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();
  }