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 }