nightmaremail

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

qmail-qmtpd.c (7729B)


      1 #include "stralloc.h"
      2 #include "substdio.h"
      3 #include "qmail.h"
      4 #include "now.h"
      5 #include "str.h"
      6 #include "fmt.h"
      7 #include "env.h"
      8 #include "noreturn.h"
      9 #include "scan.h"
     10 #include "sig.h"
     11 #include "rcpthosts.h"
     12 #include "realrcptto.h"
     13 #include "auto_qmail.h"
     14 #include "readwrite.h"
     15 #include "control.h"
     16 #include "received.h"
     17 #include "exit.h"
     18 #include "netstrings.h"
     19 
     20 #define _BADPROTOCOL "29:Z Bad protocol! Crashing now.,"
     21 
     22 void _noreturn_ badproto();
     23 void _noreturn_ resources() { _exit(111); }
     24 void die_nomem() { resources(); }
     25 void die_control() { resources(); }
     26 void die_cdb() { resources(); }
     27 void die_sys() { resources(); }
     28 void die_wtf() { resources(); } // this should never, EVER be called
     29 
     30 // XXX: should be in realrcptto.h
     31 
     32 ssize_t safewrite(int fd, const void *buf, size_t len)
     33 {
     34   ssize_t r;
     35   r = write(fd,buf,len);
     36   if (r == 0 || r == -1) _exit(0);
     37   return r;
     38 }
     39 
     40 char ssoutbuf[256];
     41 substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof(ssoutbuf));
     42 
     43 ssize_t saferead(int fd, void *buf, size_t len)
     44 {
     45   ssize_t r;
     46   substdio_flush(&ssout);
     47   r = read(fd,buf,len);
     48   if (r == 0 || r == -1) _exit(0);
     49   return r;
     50 }
     51 
     52 char ssinbuf[512];
     53 substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof(ssinbuf));
     54 
     55 unsigned long long getlen()
     56 {
     57   unsigned long long len = 0;
     58 #if 0
     59   char ch;
     60   for (;;) {
     61     substdio_get(&ssin,&ch,1);
     62     if (ch == ':') return len;
     63     if (len > 200000000) resources();
     64     len = 10 * len + (ch - '0'); // This depends upon a quirk of ASCII. ch - 0 will be 9 for a value of '9'.
     65 	// This isn't robust!
     66   }
     67 #else
     68   switch (ns_getlength(&ssin,&len)) {
     69     case NS_SUCCESS: return len; break;
     70     case NS_TOOMUCH: resources(); break;
     71     case NS_PROTOCOL: badproto(); break;
     72     case NS_INVAL: die_wtf(); break;
     73     case NS_BIOERR: die_wtf(); break;
     74   }
     75   // we won't reach here.
     76   return len;
     77 #endif
     78 }
     79 
     80 void _noreturn_ badproto() { write(1, _BADPROTOCOL, strlen(_BADPROTOCOL)); _exit(100); }
     81 void getcomma()
     82 {
     83 #if 0
     84   char ch;
     85   substdio_get(&ssin,&ch,1);
     86   if (ch != ',') badproto();
     87 #else
     88   switch (ns_getcomma(&ssin)) {
     89     case NS_SUCCESS: break;
     90     case NS_PROTOCOL: badproto(); break;
     91   }
     92 #endif
     93 }
     94 
     95 unsigned int databytes = 0;
     96 unsigned int bytestooverflow = 0;
     97 struct qmail qq;
     98 
     99 char buf[1000];
    100 char buf2[100];
    101 
    102 char *remotehost;
    103 char *remoteinfo;
    104 char *remoteip;
    105 char *local;
    106 
    107 stralloc failure = {0};
    108 
    109 char *relayclient;
    110 int relayclientlen;
    111 
    112 int
    113 main()
    114 {
    115   char ch;
    116   int i;
    117   unsigned long biglen;
    118   unsigned long len;
    119   int flagdos;
    120   int flagsenderok;
    121   int flagbother;
    122   unsigned long qp;
    123   char *result;
    124   char *x;
    125   unsigned long u;
    126 
    127   sig_pipeignore();
    128   sig_alarmcatch(resources);
    129   alarm(3600);
    130 
    131   if (chdir(auto_qmail) == -1) resources();
    132 
    133   if (control_init() == -1) resources();
    134   if (rcpthosts_init() == -1) resources();
    135   relayclient = env_get("RELAYCLIENT");
    136   relayclientlen = relayclient ? str_len(relayclient) : 0;
    137 
    138   realrcptto_init();
    139 
    140   if (control_readint(&databytes,"control/databytes") == -1) resources();
    141   x = env_get("DATABYTES");
    142   if (x) { scan_ulong(x,&u); databytes = u; }
    143   if (!(databytes + 1)) --databytes;
    144  
    145   remotehost = env_get("TCPREMOTEHOST");
    146   if (!remotehost) remotehost = "unknown";
    147   remoteinfo = env_get("TCPREMOTEINFO");
    148   remoteip = env_get("TCPREMOTEIP");
    149   if (!remoteip) remoteip = "unknown";
    150   local = env_get("TCPLOCALHOST");
    151   if (!local) local = env_get("TCPLOCALIP");
    152   if (!local) local = "unknown";
    153 
    154   for (;;) {
    155     realrcptto_start();
    156     if (!stralloc_copys(&failure,"")) resources();
    157     flagsenderok = 1;
    158 
    159     len = getlen();
    160     if (len == 0) {
    161       badproto();
    162     }
    163 
    164     if (databytes) bytestooverflow = databytes + 1;
    165     if (qmail_open(&qq) == -1) resources();
    166     qp = qmail_qp(&qq);
    167 
    168     substdio_get(&ssin,&ch,1);
    169     --len;
    170     if (ch == 10) flagdos = 0;
    171     else if (ch == 13) flagdos = 1;
    172     else badproto();
    173 
    174     received(&qq,"QMTP",local,remoteip,remotehost,remoteinfo,NULL);
    175 
    176     /* XXX: check for loops? only if len is big? */
    177 
    178     if (flagdos)
    179       while (len > 0) {
    180         substdio_get(&ssin,&ch,1);
    181         --len;
    182         while ((ch == 13) && len) {
    183           substdio_get(&ssin,&ch,1);
    184           --len;
    185           if (ch == 10) break;
    186           if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qq);
    187           qmail_put(&qq,"\015",1);
    188         }
    189         if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qq);
    190         qmail_put(&qq,&ch,1);
    191       }
    192     else {
    193       if (databytes)
    194         if (len > databytes) {
    195           bytestooverflow = 0;
    196           qmail_fail(&qq);
    197         }
    198       while (len > 0) { /* XXX: could speed this up, if we wanted to use nbio */
    199         substdio_get(&ssin,&ch,1);
    200         --len;
    201         qmail_put(&qq,&ch,1);
    202       }
    203     }
    204     getcomma();
    205 
    206     len = getlen();
    207 
    208     if (len >= 1000) {
    209       buf[0] = 0;
    210       flagsenderok = 0;
    211       for (i = 0;i < len;++i)
    212         substdio_get(&ssin,&ch,1);
    213     }
    214     else {
    215       for (i = 0;i < len;++i) {
    216         substdio_get(&ssin,buf + i,1);
    217         if (!buf[i]) flagsenderok = 0;
    218       }
    219       buf[len] = 0;
    220     }
    221     getcomma();
    222 
    223     flagbother = 0;
    224     qmail_from(&qq,buf);
    225     if (!flagsenderok) qmail_fail(&qq);
    226 
    227     biglen = getlen();
    228     while (biglen > 0) {
    229       if (!stralloc_append(&failure,"")) resources();
    230 
    231       len = 0;
    232       for (;;) {
    233         if (!biglen) badproto();
    234         substdio_get(&ssin,&ch,1);
    235         --biglen;
    236         if (ch == ':') break;
    237         if (len > 200000000) resources();
    238         len = 10 * len + (ch - '0');
    239       }
    240       if (len >= biglen) badproto();
    241       if (len + relayclientlen >= 1000) {
    242         failure.s[failure.len - 1] = 'L';
    243         for (i = 0;i < len;++i)
    244           substdio_get(&ssin,&ch,1);
    245       }
    246       else {
    247         for (i = 0;i < len;++i) {
    248           substdio_get(&ssin,buf + i,1);
    249           if (!buf[i]) failure.s[failure.len - 1] = 'N';
    250         }
    251         buf[len] = 0;
    252 
    253         if (relayclient)
    254           str_copy(buf + len,relayclient);
    255         else
    256           switch(rcpthosts(buf,len)) {
    257             case -1: resources();
    258             case 0: failure.s[failure.len - 1] = 'D';
    259           }
    260 
    261         if (!failure.s[failure.len - 1])
    262           if (!realrcptto(buf))
    263             failure.s[failure.len - 1] = 'D';
    264 
    265         if (!failure.s[failure.len - 1]) {
    266           qmail_to(&qq,buf);
    267           flagbother = 1;
    268         }
    269       }
    270       getcomma();
    271       biglen -= (len + 1);
    272     }
    273     getcomma();
    274 
    275     if (!flagbother) qmail_fail(&qq);
    276     result = qmail_close(&qq);
    277     if (!flagsenderok) result = "D5.1.7 Unacceptable sender";
    278     if (databytes) if (!bytestooverflow) result = "D5.3.4 Sorry, that message size exceeds my databytes limit.";
    279     if (realrcptto_deny()) result = "D5.1.1 Sorry, no mailbox here by that name.\r\n";
    280 
    281     if (*result)
    282       len = str_len(result);
    283     else {
    284       /* success! */
    285       len = 0;
    286       len += fmt_str(buf2 + len,"KAccepted responsibility for delivery at time ");
    287       len += fmt_ulong(buf2 + len,(unsigned long) now());
    288       len += fmt_str(buf2 + len," queue process ");
    289       len += fmt_ulong(buf2 + len,qp);
    290       len += fmt_str(buf2 + len," - we'll do our best!");
    291       buf2[len] = 0;
    292       result = buf2;
    293     }
    294 
    295     len = fmt_ulong(buf,len);
    296     buf[len++] = ':';
    297     len += fmt_str(buf + len,result);
    298     buf[len++] = ',';
    299 
    300     for (i = 0;i < failure.len;++i)
    301       switch(failure.s[i]) {
    302         case 0:
    303           substdio_put(&ssout,buf,len);
    304           break;
    305         case 'D':
    306           substdio_puts(&ssout,"64:D5.7.1 Sorry, that domain isn't in my list of allowed rcpthosts.,");
    307           break;
    308         default:
    309           substdio_puts(&ssout,"44:D5.1.3 Sorry, I can't handle that recipient.,");
    310           break;
    311       }
    312 
    313     /* ssout will be flushed when we read from the network again */
    314   }
    315 }