s/qmail 4.3.20
Next generation secure email transport
Loading...
Searching...
No Matches
qmail-qmtpd.c
Go to the documentation of this file.
1#include <unistd.h>
2#include "stralloc.h"
3#include "buffer.h"
4#include "qmail.h"
5#include "now.h"
6#include "str.h"
7#include "fmt.h"
8#include "env.h"
9#include "sig.h"
10#include "case.h"
11#include "exit.h"
12#include "scan.h"
13#include "rcpthosts.h"
14#include "auto_qmail.h"
15#include "control.h"
16#include "received.h"
17#include "ip.h"
18#include "byte.h"
19#include "timeout.h"
20#include "error.h"
21#include "logmsg.h"
22
23#define WHO "qmail-qmtpd"
24
25#define PORT_QMTPS "6209" // string compare
26#define QMTP_SIZE 200000000 // 23 MB transfer limit
27#define QMTP_TIMEOUT 3600 // 1 hour
28
33void badproto() { logmsg(WHO,100,ERROR,"client does not talk QMTP"); }
34void resources() { logmsg(WHO,111,ERROR,"out of resources"); }
35
36ssize_t safewrite(int fd,char *buf,int len)
37{
38 int r;
39 r = timeoutwrite(timeout,fd,buf,len);
40 if (r <= 0) _exit(1);
41 return r;
42}
43
45buffer bo = BUFFER_INIT(safewrite,1,outbuf,sizeof(outbuf));
46
47ssize_t saferead(int fd,char *buf,int len)
48{
49 int r;
50 buffer_flush(&bo);
51 r = timeoutread(timeout,fd,buf,len);
52 if (r == -1) if (errno == ETIMEDOUT) { buffer_flush(&bo); _exit(1); }
53 if (r <= 0) _exit(0);
54 return r;
55}
56
58buffer bi = BUFFER_INIT(saferead,0,inbuf,sizeof(inbuf));
59
60unsigned long getlen()
61{
62 unsigned long len = 0;
63 char ch;
64 for (;;) {
65 buffer_get(&bi,&ch,1);
66 if (ch == ':') return len;
67 if (ch < '0' || ch > '9') resources();
68 if (len > QMTP_SIZE) resources();
69 len = 10 * len + (ch - '0');
70 }
71}
72
74{
75 char ch;
76 buffer_get(&bi,&ch,1);
77 if (ch != ',') badproto();
78}
79
80unsigned long databytes = 0;
81unsigned int bytestooverflow = 0;
82struct qmail qq;
83
84char buf[BUFSIZE_LINE]; // sender/recipient buffer
85char buf2[BUFFER_SMALL]; // results buffer
86
91char *local;
92char strnum[FMT_ULONG];
93
94stralloc failure = {0};
95stralloc protocol = {0};
96stralloc tlsinfo = {0};
97
100
103char *cipher;
106char *clientdn = 0;
107char *clientcn = 0;
109
110int seentls = 0;
111
113{
114 tlsversion = env_get("SSL_PROTOCOL");
115 if (!tlsversion) return 0;
116 seentls = 1;
117
118 cipher = env_get("SSL_CIPHER");
119 if (!cipher) cipher = "unknown";
120 cipherperm = env_get("SSL_CIPHER_ALGKEYSIZE");
121 if (!cipherperm) cipherperm = "unknown";
122 cipherused = env_get("SSL_CIPHER_USEKEYSIZE");
123 if (!cipherused) cipherused = "unknown";
124 clientdn = env_get("SSL_CLIENT_S_DN");
125 if (clientdn) seentls = 2;
126
128 if (!stralloc_cats(&tlsinfo,": ")) resources();
129 if (!stralloc_cats(&tlsinfo,cipher)) resources();
130 if (!stralloc_cats(&tlsinfo," [")) resources();
131 if (!stralloc_cats(&tlsinfo,cipherused)) resources();
132 if (!stralloc_cats(&tlsinfo,"/")) resources();
133 if (!stralloc_cats(&tlsinfo,cipherperm)) resources();
134 if (!stralloc_cats(&tlsinfo,"]")) resources();
135 if (clientdn) {
136 if (!stralloc_cats(&tlsinfo,"\n DN=")) resources();
137 if (!stralloc_cats(&tlsinfo,clientdn)) resources();
138 }
139 if (!stralloc_0(&tlsinfo)) resources();
140
141 if (!stralloc_append(&protocol,"S")) resources();
142
143 if (seentls == 2) {
144 clientcn = env_get("SSL_CLIENT_S_DN_CN");
146 dnemail = env_get("SSL_CLIENT_S_DN_Email");
147 if (!dnemail) dnemail = "unknown";
148 if (!stralloc_append(&protocol,"A")) resources();
149 relayclient = "";
150 }
151 return 1;
152}
153
154int main()
155{
156 char ch;
157 int i;
158 unsigned long biglen;
159 unsigned long len;
160 int flagdos;
161 int flagsenderok;
162 int flagbother;
163 unsigned long qp;
164 char *result;
165 char *x;
166 unsigned long u;
167
168 sig_pipeignore();
169 sig_alarmcatch(resources);
170 alarm(QMTP_TIMEOUT);
171
172 if (chdir(auto_qmail) == -1) resources();
173
174 if (control_init() == -1) resources();
175 if (rcpthosts_init() == -1) resources();
176
177 if (control_readint(&databytes,"control/databytes") == -1) resources();
178 x = env_get("DATABYTES");
179 if (x) { scan_ulong(x,&u); databytes = u; }
180 if (!(databytes + 1)) --databytes;
181
182 relayclient = env_get("RELAYCLIENT");
183 remotehost = env_get("TCP6REMOTEHOST");
184 if (!remotehost) remotehost = env_get("TCPREMOTEHOST");
185 if (!remotehost) remotehost = "unknown";
186 remoteinfo = env_get("TCP6REMOTEINFO");
187 if (!remoteinfo) remoteinfo = env_get("TCPREMOTEINFO");
188 remoteip = env_get("TCP6REMOTEIP");
189 if (!remoteip) remoteip = env_get("TCPREMOTEIP");
190 if (remoteip && byte_equal(remoteip,7,V4MAPPREFIX)) remoteip = remoteip + 7;
191 if (!remoteip) remoteip = "unknown";
192 local = env_get("TCP6LOCALHOST");
193 if (!local) local = env_get("TCPLOCALHOST");
194 if (!local) local = env_get("TCP6LOCALIP");
195 if (!local) local = env_get("TCPLOCALIP");
196 if (!local) local = "unknown";
197 localport = env_get("TCP6LOCALPORT");
198 if (!localport) localport = env_get("TCPLOCALPORT");
199 if (!localport) localport = "0";
200
201 if (!stralloc_copys(&protocol,"QMTP")) resources();
202 if (!case_diffs(localport,PORT_QMTPS))
203 if (!modssl_info()) resources();
204
205 if (relayclient)
206 relayclientlen = str_len(relayclient);
207
208 for (;;) { // https://cr.yp.to/proto/qmtp.txt
209 if (!stralloc_copys(&failure,"")) resources();
210 flagsenderok = 1;
211
212 len = getlen(); // package to read
213 if (len == 0) badproto();
214
216 if (qmail_open(&qq) == -1) resources();
217 qp = qmail_qp(&qq);
218
219 buffer_get(&bi,&ch,1);
220 --len;
221 if (ch == 10) flagdos = 0;
222 else if (ch == 13) flagdos = 1;
223 else badproto();
224
225 /* no fakehelo, no spfinfo */
226
227 received(&qq,protocol.s,local,remoteip,remotehost,remoteinfo,(char *) 0,tlsinfo.s,(char *) 0,(char *) 0);
228
229 /* XXX: check for loops? only if len is big? - message */
230
231 if (flagdos)
232 while (len > 0) {
233 buffer_get(&bi,&ch,1);
234 --len;
235 while ((ch == 13) && len) {
236 buffer_get(&bi,&ch,1);
237 --len;
238 if (ch == 10) break;
240 qmail_put(&qq,"\015",1);
241 }
243 qmail_put(&qq,&ch,1);
244 }
245 else {
246 if (databytes)
247 if (len > databytes) {
248 bytestooverflow = 0;
249 qmail_fail(&qq);
250 }
251
252 while (len > 0) { /* XXX: could speed this up, obviously */
253 buffer_get(&bi,&ch,1);
254 --len;
255 qmail_put(&qq,&ch,1);
256 }
257 }
258
259 getcomma();
260
261 len = getlen(); // QMTP sender
262
263 if (len >= BUFSIZE_LINE) {
264 buf[0] = 0;
265 flagsenderok = 0;
266 for (i = 0; i < len; ++i)
267 buffer_get(&bi,&ch,1);
268 }
269 else {
270 for (i = 0; i < len; ++i) {
271 buffer_get(&bi,buf + i,1);
272 if (!buf[i]) flagsenderok = 0;
273 }
274 buf[len] = 0;
275 }
276 getcomma();
277
278 flagbother = 0;
279 qmail_from(&qq,buf);
280 if (!flagsenderok) qmail_fail(&qq);
281
282 biglen = getlen(); // QMTP recipients
283 while (biglen > 0) {
284 if (!stralloc_append(&failure,"")) resources();
285
286 len = 0;
287 for (;;) {
288 if (!biglen) badproto();
289 buffer_get(&bi,&ch,1);
290 --biglen;
291 if (ch == ':') break;
292 if (ch < '0' || ch > '9') resources();
293 if (len > QMTP_SIZE) resources();
294 len = 10 * len + (ch - '0');
295 }
296 if (len >= biglen) badproto();
297 if (len + relayclientlen >= BUFSIZE_LINE) {
298 failure.s[failure.len - 1] = 'L';
299 for (i = 0; i < len; ++i)
300 buffer_get(&bi,&ch,1);
301 }
302 else {
303 for (i = 0; i < len; ++i) {
304 buffer_get(&bi,buf + i,1);
305 if (!buf[i]) failure.s[failure.len - 1] = 'N';
306 }
307 buf[len] = 0;
308
309 if (relayclientlen)
310 str_copy(buf + len,relayclient);
311 if (!relayclient)
312 switch (rcpthosts(buf,len)) {
313 case -1: resources();
314 case 0: failure.s[failure.len - 1] = 'D';
315 }
316
317 if (!failure.s[failure.len - 1]) {
318 qmail_to(&qq,buf);
319 flagbother = 1;
320 }
321 }
322 getcomma();
323 biglen -= (len + 1);
324 }
325 getcomma();
326
327 if (!flagbother) qmail_fail(&qq);
328 result = qmail_close(&qq);
329 if (!flagsenderok) result = "D Unacceptable sender (#5.1.7)";
330 if (databytes) if (!bytestooverflow) result = "D Sorry, that message size exceeds my databytes limit (#5.3.4)";
331
332 if (*result)
333 len = str_len(result);
334 else {
335 /* success! */
336 len = 0;
337 len += fmt_str(buf2 + len,"K Ok ");
338 len += fmt_ulong(buf2 + len,(unsigned long) now());
339 len += fmt_str(buf2 + len," qp ");
340 len += fmt_ulong(buf2 + len,qp);
341 buf2[len] = 0;
342 result = buf2;
343 }
344
345 len = fmt_ulong(buf,len); // Definition of netstring:= "NN:stringlen(NN),"
346 buf[len++] = ':';
347 len += fmt_str(buf + len,result);
348 buf[len++] = ',';
349
350 for (i = 0; i < failure.len; ++i)
351 switch (failure.s[i]) {
352 case 0:
353 buffer_put(&bo,buf,len);
354 break;
355 case 'D':
356 buffer_puts(&bo,"67:D Sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1),");
357 break;
358 default:
359 buffer_puts(&bo,"47:D Sorry, I can't handle that recipient (#5.1.3),");
360 break;
361 }
362
363 /* bo will be flushed when we read from the network again */
364
365 buffer_flush(&bo);
366 strnum[fmt_ulong(strnum,getpid())] = 0;
367 if (relayclient) logmsg(WHO,0,0,B("pid ",strnum, " relayclient done."));
368 else logmsg(WHO,0,0,B("pid ",strnum, " done."));
369 }
370}
char auto_qmail[]
int control_readint(unsigned long *i, char *fn)
Definition: control.c:72
int control_init(void)
Definition: control.c:33
int stralloc_copys(stralloc *, char const *)
void _exit(int)
char qp[FMT_ULONG]
Definition: fastforward.c:53
datetime_sec now()
Definition: now.c:5
int fd
int seentls
Definition: qmail-qmtpd.c:110
stralloc tlsinfo
Definition: qmail-qmtpd.c:96
char * dnemail
Definition: qmail-qmtpd.c:108
ssize_t saferead(int fd, char *buf, int len)
Definition: qmail-qmtpd.c:47
int modssl_info()
Definition: qmail-qmtpd.c:112
char * cipherused
Definition: qmail-qmtpd.c:105
char * local
Definition: qmail-qmtpd.c:91
#define PORT_QMTPS
Definition: qmail-qmtpd.c:25
stralloc failure
Definition: qmail-qmtpd.c:94
#define QMTP_TIMEOUT
Definition: qmail-qmtpd.c:27
char * clientdn
Definition: qmail-qmtpd.c:106
char buf2[BUFFER_SMALL]
Definition: qmail-qmtpd.c:85
char * cipher
Definition: qmail-qmtpd.c:103
char * clientcn
Definition: qmail-qmtpd.c:107
void badproto()
Definition: qmail-qmtpd.c:33
char strnum[FMT_ULONG]
Definition: qmail-qmtpd.c:92
unsigned int bytestooverflow
Definition: qmail-qmtpd.c:81
int timeout
Definition: qmail-qmtpd.c:32
unsigned long databytes
Definition: qmail-qmtpd.c:80
void getcomma()
Definition: qmail-qmtpd.c:73
stralloc protocol
Definition: qmail-qmtpd.c:95
char * relayclient
Definition: qmail-qmtpd.c:98
char * cipherperm
Definition: qmail-qmtpd.c:104
char * remoteip
Definition: qmail-qmtpd.c:89
char * tlsversion
Definition: qmail-qmtpd.c:102
char buf[BUFSIZE_LINE]
Definition: qmail-qmtpd.c:84
char * ucspitls
Definition: qmail-qmtpd.c:101
char outbuf[BUFSIZE_LINE]
Definition: qmail-qmtpd.c:44
char * localport
Definition: qmail-qmtpd.c:90
int relayclientlen
Definition: qmail-qmtpd.c:99
buffer bi
Definition: qmail-qmtpd.c:58
ssize_t safewrite(int fd, char *buf, int len)
Definition: qmail-qmtpd.c:36
void resources()
Definition: qmail-qmtpd.c:34
char * remotehost
Definition: qmail-qmtpd.c:87
buffer bo
Definition: qmail-qmtpd.c:45
#define WHO
Definition: qmail-qmtpd.c:23
int main()
Definition: qmail-qmtpd.c:154
char * remoteinfo
Definition: qmail-qmtpd.c:88
char inbuf[BUFSIZE_LINE]
Definition: qmail-qmtpd.c:57
unsigned long getlen()
Definition: qmail-qmtpd.c:60
struct qmail qq
Definition: qmail-qmtpd.c:82
#define QMTP_SIZE
Definition: qmail-qmtpd.c:26
char * received
Definition: qmail-queue.c:69
void qmail_to(struct qmail *, char *)
Definition: qmail.c:83
#define BUFSIZE_LINE
Definition: qmail.h:8
void qmail_from(struct qmail *, char *)
Definition: qmail.c:73
void qmail_put(struct qmail *, char *, int)
Definition: qmail.c:63
char * qmail_close(struct qmail *)
Definition: qmail.c:90
unsigned long qmail_qp(struct qmail *)
Definition: qmail.c:53
int qmail_open(struct qmail *)
Definition: qmail.c:21
void qmail_fail(struct qmail *)
Definition: qmail.c:58
int rcpthosts(char *, int)
Definition: rcpthosts.c:35
int rcpthosts_init(void)
Definition: rcpthosts.c:22
Definition: qmail.h:14