chiark / gitweb /
Merge branches 'idx/verh' and 'idx/qmqpc'
[qmail] / qmail-qmtpd.c
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 "sig.h"
9 #include "rcpthosts.h"
10 #include "auto_qmail.h"
11 #include "readwrite.h"
12 #include "control.h"
13 #include "received.h"
14
15 void badproto() { _exit(100); }
16 void resources() { _exit(111); }
17
18 int safewrite(fd,buf,len) int fd; char *buf; int len;
19 {
20   int r;
21   r = write(fd,buf,len);
22   if (r <= 0) _exit(0);
23   return r;
24 }
25
26 char ssoutbuf[256];
27 substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
28
29 int saferead(fd,buf,len) int fd; char *buf; int len;
30 {
31   int r;
32   substdio_flush(&ssout);
33   r = read(fd,buf,len);
34   if (r <= 0) _exit(0);
35   return r;
36 }
37
38 char ssinbuf[512];
39 substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
40
41 unsigned long getlen()
42 {
43   unsigned long len = 0;
44   char ch;
45   for (;;) {
46     substdio_get(&ssin,&ch,1);
47     if (ch == ':') return len;
48     if (len > 200000000) resources();
49     len = 10 * len + (ch - '0');
50   }
51 }
52
53 void getcomma()
54 {
55   char ch;
56   substdio_get(&ssin,&ch,1);
57   if (ch != ',') badproto();
58 }
59
60 unsigned int databytes = 0;
61 unsigned int bytestooverflow = 0;
62 struct qmail qq;
63
64 char buf[1000];
65 char buf2[100];
66
67 char *remotehost;
68 char *remoteinfo;
69 char *remoteip;
70 char *local;
71
72 stralloc failure = {0};
73
74 char *relayclient;
75 int relayclientlen;
76
77 main()
78 {
79   char ch;
80   int i;
81   unsigned long biglen;
82   unsigned long len;
83   int flagdos;
84   int flagsenderok;
85   int flagbother;
86   unsigned long qp;
87   char *result;
88   char *x;
89   unsigned long u;
90  
91   sig_pipeignore();
92   sig_alarmcatch(resources);
93   alarm(3600);
94  
95   if (chdir(auto_qmail) == -1) resources();
96  
97   if (control_init() == -1) resources();
98   if (rcpthosts_init() == -1) resources();
99   relayclient = env_get("RELAYCLIENT");
100   relayclientlen = relayclient ? str_len(relayclient) : 0;
101  
102   if (control_readint(&databytes,"control/databytes") == -1) resources();
103   x = env_get("DATABYTES");
104   if (x) { scan_ulong(x,&u); databytes = u; }
105   if (!(databytes + 1)) --databytes;
106  
107   remotehost = env_get("TCPREMOTEHOST");
108   if (!remotehost) remotehost = "unknown";
109   remoteinfo = env_get("TCPREMOTEINFO");
110   remoteip = env_get("TCPREMOTEIP");
111   if (!remoteip) remoteip = "unknown";
112   local = env_get("TCPLOCALHOST");
113   if (!local) local = env_get("TCPLOCALIP");
114   if (!local) local = "unknown";
115  
116   for (;;) {
117     if (!stralloc_copys(&failure,"")) resources();
118     flagsenderok = 1;
119  
120     len = getlen();
121     if (len == 0) badproto();
122  
123     if (databytes) bytestooverflow = databytes + 1;
124     if (qmail_open(&qq) == -1) resources();
125     qp = qmail_qp(&qq);
126  
127     substdio_get(&ssin,&ch,1);
128     --len;
129     if (ch == 10) flagdos = 0;
130     else if (ch == 13) flagdos = 1;
131     else badproto();
132  
133     received(&qq,"QMTP",local,remoteip,remotehost,remoteinfo,(char *) 0);
134  
135     /* XXX: check for loops? only if len is big? */
136  
137     if (flagdos)
138       while (len > 0) {
139         substdio_get(&ssin,&ch,1);
140         --len;
141         while ((ch == 13) && len) {
142           substdio_get(&ssin,&ch,1);
143           --len;
144           if (ch == 10) break;
145           if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qq);
146           qmail_put(&qq,"\015",1);
147         }
148         if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qq);
149         qmail_put(&qq,&ch,1);
150       }
151     else {
152       if (databytes)
153         if (len > databytes) {
154           bytestooverflow = 0;
155           qmail_fail(&qq);
156         }
157       while (len > 0) { /* XXX: could speed this up, obviously */
158         substdio_get(&ssin,&ch,1);
159         --len;
160         qmail_put(&qq,&ch,1);
161       }
162     }
163     getcomma();
164  
165     len = getlen();
166  
167     if (len >= 1000) {
168       buf[0] = 0;
169       flagsenderok = 0;
170       for (i = 0;i < len;++i)
171         substdio_get(&ssin,&ch,1);
172     }
173     else {
174       for (i = 0;i < len;++i) {
175         substdio_get(&ssin,buf + i,1);
176         if (!buf[i]) flagsenderok = 0;
177       }
178       buf[len] = 0;
179     }
180     getcomma();
181  
182     flagbother = 0;
183     qmail_from(&qq,buf);
184     if (!flagsenderok) qmail_fail(&qq);
185  
186     biglen = getlen();
187     while (biglen > 0) {
188       if (!stralloc_append(&failure,"")) resources();
189  
190       len = 0;
191       for (;;) {
192         if (!biglen) badproto();
193         substdio_get(&ssin,&ch,1);
194         --biglen;
195         if (ch == ':') break;
196         if (len > 200000000) resources();
197         len = 10 * len + (ch - '0');
198       }
199       if (len >= biglen) badproto();
200       if (len + relayclientlen >= 1000) {
201         failure.s[failure.len - 1] = 'L';
202         for (i = 0;i < len;++i)
203           substdio_get(&ssin,&ch,1);
204       }
205       else {
206         for (i = 0;i < len;++i) {
207           substdio_get(&ssin,buf + i,1);
208           if (!buf[i]) failure.s[failure.len - 1] = 'N';
209         }
210         buf[len] = 0;
211  
212         if (relayclient)
213           str_copy(buf + len,relayclient);
214         else
215           switch(rcpthosts(buf,len)) {
216             case -1: resources();
217             case 0: failure.s[failure.len - 1] = 'D';
218           }
219  
220         if (!failure.s[failure.len - 1]) {
221           qmail_to(&qq,buf);
222           flagbother = 1;
223         }
224       }
225       getcomma();
226       biglen -= (len + 1);
227     }
228     getcomma();
229  
230     if (!flagbother) qmail_fail(&qq);
231     result = qmail_close(&qq);
232     if (!flagsenderok) result = "Dunacceptable sender (#5.1.7)";
233     if (databytes) if (!bytestooverflow) result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)";
234  
235     if (*result)
236       len = str_len(result);
237     else {
238       /* success! */
239       len = 0;
240       len += fmt_str(buf2 + len,"Kok ");
241       len += fmt_ulong(buf2 + len,(unsigned long) now());
242       len += fmt_str(buf2 + len," qp ");
243       len += fmt_ulong(buf2 + len,qp);
244       buf2[len] = 0;
245       result = buf2;
246     }
247       
248     len = fmt_ulong(buf,len);
249     buf[len++] = ':';
250     len += fmt_str(buf + len,result);
251     buf[len++] = ',';
252  
253     for (i = 0;i < failure.len;++i)
254       switch(failure.s[i]) {
255         case 0:
256           substdio_put(&ssout,buf,len);
257           break;
258         case 'D':
259           substdio_puts(&ssout,"66:Dsorry, that domain isn't in my list of allowed rcpthosts (#5.7.1),");
260           break;
261         default:
262           substdio_puts(&ssout,"46:Dsorry, I can't handle that recipient (#5.1.3),");
263           break;
264       }
265  
266     /* ssout will be flushed when we read from the network again */
267   }
268 }