Mercurial > hg > nginx-mail
annotate src/mail/ngx_mail_smtp_handler.c @ 436:9b19e26b2660
Mail: smtp pipelining support.
Basically, this does the following two changes (and corresponding
modifications of related code):
1. Does not reset session buffer unless it's reached it's end, and always
wait for LF to terminate command (even if we detected invalid command).
2. Record command name as the first argument to make it available for
handlers (since now we can't assume that command starts from s->buffer->start).
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Thu, 11 Sep 2008 15:26:25 +0400 |
parents | e2df123bbbe2 |
children | d67e93e97b4a |
rev | line source |
---|---|
336 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_event.h> | |
10 #include <ngx_mail.h> | |
11 #include <ngx_mail_smtp_module.h> | |
12 | |
13 | |
366 | 14 static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx); |
15 static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx); | |
16 static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c); | |
336 | 17 static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev); |
18 static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, | |
19 ngx_connection_t *c); | |
20 | |
21 static ngx_int_t ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c); | |
22 static ngx_int_t ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c); | |
23 static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c); | |
24 static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s, | |
25 ngx_connection_t *c); | |
409 | 26 static ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c); |
27 static ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c); | |
336 | 28 |
29 static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s, | |
30 ngx_connection_t *c, char *err); | |
31 static void ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, | |
32 ngx_connection_t *c, char *err); | |
33 | |
34 | |
35 static u_char smtp_ok[] = "250 2.0.0 OK" CRLF; | |
36 static u_char smtp_bye[] = "221 2.0.0 Bye" CRLF; | |
358 | 37 static u_char smtp_starttls[] = "220 2.0.0 Start TLS" CRLF; |
336 | 38 static u_char smtp_next[] = "334 " CRLF; |
39 static u_char smtp_username[] = "334 VXNlcm5hbWU6" CRLF; | |
40 static u_char smtp_password[] = "334 UGFzc3dvcmQ6" CRLF; | |
41 static u_char smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF; | |
42 static u_char smtp_invalid_pipelining[] = | |
43 "503 5.5.0 Improper use of SMTP command pipelining" CRLF; | |
44 static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF; | |
45 static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF; | |
409 | 46 static u_char smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF; |
336 | 47 |
48 | |
366 | 49 static ngx_str_t smtp_unavailable = ngx_string("[UNAVAILABLE]"); |
50 static ngx_str_t smtp_tempunavail = ngx_string("[TEMPUNAVAIL]"); | |
51 | |
52 | |
336 | 53 void |
54 ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c) | |
55 { | |
366 | 56 struct sockaddr_in *sin; |
57 ngx_resolver_ctx_t *ctx; | |
58 ngx_mail_core_srv_conf_t *cscf; | |
59 | |
60 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
61 | |
62 if (cscf->resolver == NULL) { | |
63 s->host = smtp_unavailable; | |
64 ngx_mail_smtp_greeting(s, c); | |
65 return; | |
66 } | |
67 | |
68 c->log->action = "in resolving client address"; | |
69 | |
70 ctx = ngx_resolve_start(cscf->resolver, NULL); | |
71 if (ctx == NULL) { | |
72 ngx_mail_close_connection(c); | |
73 return; | |
74 } | |
75 | |
76 /* AF_INET only */ | |
77 | |
78 sin = (struct sockaddr_in *) c->sockaddr; | |
79 | |
80 ctx->addr = sin->sin_addr.s_addr; | |
81 ctx->handler = ngx_mail_smtp_resolve_addr_handler; | |
82 ctx->data = s; | |
83 ctx->timeout = cscf->resolver_timeout; | |
84 | |
85 if (ngx_resolve_addr(ctx) != NGX_OK) { | |
86 ngx_mail_close_connection(c); | |
87 } | |
88 } | |
89 | |
90 | |
91 static void | |
92 ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx) | |
93 { | |
94 ngx_connection_t *c; | |
95 ngx_mail_session_t *s; | |
96 ngx_mail_core_srv_conf_t *cscf; | |
97 | |
98 s = ctx->data; | |
99 c = s->connection; | |
100 | |
101 if (ctx->state) { | |
102 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
103 "%V could not be resolved (%i: %s)", | |
104 &c->addr_text, ctx->state, | |
105 ngx_resolver_strerror(ctx->state)); | |
106 | |
107 if (ctx->state == NGX_RESOLVE_NXDOMAIN) { | |
108 s->host = smtp_unavailable; | |
109 | |
110 } else { | |
111 s->host = smtp_tempunavail; | |
112 } | |
113 | |
114 ngx_resolve_addr_done(ctx); | |
115 | |
116 ngx_mail_smtp_greeting(s, s->connection); | |
117 | |
118 return; | |
119 } | |
120 | |
121 c->log->action = "in resolving client hostname"; | |
122 | |
123 s->host.data = ngx_pstrdup(c->pool, &ctx->name); | |
124 if (s->host.data == NULL) { | |
125 ngx_resolve_addr_done(ctx); | |
126 ngx_mail_close_connection(c); | |
127 return; | |
128 } | |
129 | |
130 s->host.len = ctx->name.len; | |
131 | |
132 ngx_resolve_addr_done(ctx); | |
133 | |
134 ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, | |
135 "address resolved: %V", &s->host); | |
136 | |
137 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
138 | |
139 ctx = ngx_resolve_start(cscf->resolver, NULL); | |
140 if (ctx == NULL) { | |
141 ngx_mail_close_connection(c); | |
142 return; | |
143 } | |
144 | |
145 ctx->name = s->host; | |
146 ctx->type = NGX_RESOLVE_A; | |
147 ctx->handler = ngx_mail_smtp_resolve_name_handler; | |
148 ctx->data = s; | |
149 ctx->timeout = cscf->resolver_timeout; | |
150 | |
151 if (ngx_resolve_name(ctx) != NGX_OK) { | |
152 ngx_mail_close_connection(c); | |
153 } | |
154 } | |
155 | |
156 | |
157 static void | |
158 ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx) | |
159 { | |
160 in_addr_t addr; | |
161 ngx_uint_t i; | |
162 ngx_connection_t *c; | |
163 struct sockaddr_in *sin; | |
164 ngx_mail_session_t *s; | |
165 | |
166 s = ctx->data; | |
167 c = s->connection; | |
168 | |
169 if (ctx->state) { | |
170 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
171 "%V could not be resolved (%i: %s)", | |
172 &ctx->name, ctx->state, | |
173 ngx_resolver_strerror(ctx->state)); | |
174 | |
175 if (ctx->state == NGX_RESOLVE_NXDOMAIN) { | |
176 s->host = smtp_unavailable; | |
177 | |
178 } else { | |
179 s->host = smtp_tempunavail; | |
180 } | |
181 | |
182 } else { | |
183 | |
184 /* AF_INET only */ | |
185 | |
186 sin = (struct sockaddr_in *) c->sockaddr; | |
187 | |
188 for (i = 0; i < ctx->naddrs; i++) { | |
189 | |
190 addr = ctx->addrs[i]; | |
191 | |
192 ngx_log_debug4(NGX_LOG_DEBUG_MAIL, c->log, 0, | |
193 "name was resolved to %ud.%ud.%ud.%ud", | |
194 (ntohl(addr) >> 24) & 0xff, | |
195 (ntohl(addr) >> 16) & 0xff, | |
196 (ntohl(addr) >> 8) & 0xff, | |
197 ntohl(addr) & 0xff); | |
198 | |
199 if (addr == sin->sin_addr.s_addr) { | |
200 goto found; | |
201 } | |
202 } | |
203 | |
204 s->host = smtp_unavailable; | |
205 } | |
206 | |
207 found: | |
208 | |
209 ngx_resolve_name_done(ctx); | |
210 | |
211 ngx_mail_smtp_greeting(s, c); | |
212 } | |
213 | |
214 | |
215 static void | |
216 ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c) | |
217 { | |
336 | 218 ngx_msec_t timeout; |
219 ngx_mail_core_srv_conf_t *cscf; | |
220 ngx_mail_smtp_srv_conf_t *sscf; | |
221 | |
366 | 222 ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, |
223 "smtp greeting for \"%V\"", &s->host); | |
224 | |
336 | 225 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); |
226 sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); | |
227 | |
228 timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout; | |
229 ngx_add_timer(c->read, timeout); | |
230 | |
231 if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { | |
232 ngx_mail_close_connection(c); | |
233 } | |
234 | |
235 if (sscf->greeting_delay) { | |
236 c->read->handler = ngx_mail_smtp_invalid_pipelining; | |
237 return; | |
238 } | |
239 | |
240 c->read->handler = ngx_mail_smtp_init_protocol; | |
241 | |
242 s->out = sscf->greeting; | |
243 | |
244 ngx_mail_send(c->write); | |
245 } | |
246 | |
247 | |
248 static void | |
249 ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev) | |
250 { | |
251 ngx_connection_t *c; | |
252 ngx_mail_session_t *s; | |
253 ngx_mail_core_srv_conf_t *cscf; | |
254 ngx_mail_smtp_srv_conf_t *sscf; | |
255 | |
256 c = rev->data; | |
257 s = c->data; | |
258 | |
259 c->log->action = "in delay pipelining state"; | |
260 | |
261 if (rev->timedout) { | |
262 | |
263 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "delay greeting"); | |
264 | |
265 rev->timedout = 0; | |
266 | |
267 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
268 | |
269 c->read->handler = ngx_mail_smtp_init_protocol; | |
270 | |
271 ngx_add_timer(c->read, cscf->timeout); | |
272 | |
273 if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { | |
274 ngx_mail_close_connection(c); | |
275 return; | |
276 } | |
277 | |
278 sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); | |
279 | |
280 s->out = sscf->greeting; | |
281 | |
282 } else { | |
283 | |
284 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "invalid pipelining"); | |
285 | |
286 if (s->buffer == NULL) { | |
287 if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) { | |
288 return; | |
289 } | |
290 } | |
291 | |
292 if (ngx_mail_smtp_discard_command(s, c, | |
293 "client was rejected before greeting: \"%V\"") | |
294 != NGX_OK) | |
295 { | |
296 return; | |
297 } | |
298 | |
299 s->out.len = sizeof(smtp_invalid_pipelining) - 1; | |
300 s->out.data = smtp_invalid_pipelining; | |
435
e2df123bbbe2
Mail: close session on smtp_greeting_delay violation.
Maxim Dounin <mdounin@mdounin.ru>
parents:
410
diff
changeset
|
301 s->quit = 1; |
336 | 302 } |
303 | |
304 ngx_mail_send(c->write); | |
305 } | |
306 | |
307 | |
308 void | |
309 ngx_mail_smtp_init_protocol(ngx_event_t *rev) | |
310 { | |
311 ngx_connection_t *c; | |
312 ngx_mail_session_t *s; | |
313 | |
314 c = rev->data; | |
315 | |
316 c->log->action = "in auth state"; | |
317 | |
318 if (rev->timedout) { | |
319 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); | |
320 c->timedout = 1; | |
321 ngx_mail_close_connection(c); | |
322 return; | |
323 } | |
324 | |
325 s = c->data; | |
326 | |
327 if (s->buffer == NULL) { | |
328 if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) { | |
329 return; | |
330 } | |
331 } | |
332 | |
333 s->mail_state = ngx_smtp_start; | |
334 c->read->handler = ngx_mail_smtp_auth_state; | |
335 | |
336 ngx_mail_smtp_auth_state(rev); | |
337 } | |
338 | |
339 | |
340 static ngx_int_t | |
341 ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c) | |
342 { | |
343 ngx_mail_smtp_srv_conf_t *sscf; | |
344 | |
345 if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) { | |
346 ngx_mail_session_internal_server_error(s); | |
347 return NGX_ERROR; | |
348 } | |
349 | |
350 sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); | |
351 | |
352 s->buffer = ngx_create_temp_buf(c->pool, sscf->client_buffer_size); | |
353 if (s->buffer == NULL) { | |
354 ngx_mail_session_internal_server_error(s); | |
355 return NGX_ERROR; | |
356 } | |
357 | |
358 return NGX_OK; | |
359 } | |
360 | |
361 | |
362 void | |
363 ngx_mail_smtp_auth_state(ngx_event_t *rev) | |
364 { | |
365 ngx_int_t rc; | |
366 ngx_connection_t *c; | |
367 ngx_mail_session_t *s; | |
368 | |
369 c = rev->data; | |
370 s = c->data; | |
371 | |
372 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state"); | |
373 | |
374 if (rev->timedout) { | |
375 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); | |
376 c->timedout = 1; | |
377 ngx_mail_close_connection(c); | |
378 return; | |
379 } | |
380 | |
381 if (s->out.len) { | |
382 ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy"); | |
383 s->blocked = 1; | |
384 return; | |
385 } | |
386 | |
387 s->blocked = 0; | |
388 | |
389 rc = ngx_mail_read_command(s, c); | |
390 | |
391 if (rc == NGX_AGAIN || rc == NGX_ERROR) { | |
392 return; | |
393 } | |
394 | |
395 s->out.len = sizeof(smtp_ok) - 1; | |
396 s->out.data = smtp_ok; | |
397 | |
398 if (rc == NGX_OK) { | |
399 switch (s->mail_state) { | |
400 | |
401 case ngx_smtp_start: | |
402 | |
403 switch (s->command) { | |
404 | |
405 case NGX_SMTP_HELO: | |
406 case NGX_SMTP_EHLO: | |
407 rc = ngx_mail_smtp_helo(s, c); | |
408 break; | |
409 | |
410 case NGX_SMTP_AUTH: | |
411 rc = ngx_mail_smtp_auth(s, c); | |
412 break; | |
413 | |
414 case NGX_SMTP_QUIT: | |
415 s->quit = 1; | |
416 s->out.len = sizeof(smtp_bye) - 1; | |
417 s->out.data = smtp_bye; | |
418 break; | |
419 | |
420 case NGX_SMTP_MAIL: | |
421 rc = ngx_mail_smtp_mail(s, c); | |
422 break; | |
423 | |
409 | 424 case NGX_SMTP_RCPT: |
425 rc = ngx_mail_smtp_rcpt(s, c); | |
426 break; | |
427 | |
428 case NGX_SMTP_RSET: | |
429 rc = ngx_mail_smtp_rset(s, c); | |
430 break; | |
431 | |
336 | 432 case NGX_SMTP_NOOP: |
433 break; | |
434 | |
435 case NGX_SMTP_STARTTLS: | |
436 rc = ngx_mail_smtp_starttls(s, c); | |
358 | 437 s->out.len = sizeof(smtp_starttls) - 1; |
438 s->out.data = smtp_starttls; | |
336 | 439 break; |
440 | |
441 default: | |
442 rc = NGX_MAIL_PARSE_INVALID_COMMAND; | |
443 break; | |
444 } | |
445 | |
446 break; | |
447 | |
448 case ngx_smtp_auth_login_username: | |
449 rc = ngx_mail_auth_login_username(s, c); | |
450 | |
451 s->out.len = sizeof(smtp_password) - 1; | |
452 s->out.data = smtp_password; | |
453 s->mail_state = ngx_smtp_auth_login_password; | |
454 break; | |
455 | |
456 case ngx_smtp_auth_login_password: | |
457 rc = ngx_mail_auth_login_password(s, c); | |
458 break; | |
459 | |
460 case ngx_smtp_auth_plain: | |
461 rc = ngx_mail_auth_plain(s, c, 0); | |
462 break; | |
463 | |
464 case ngx_smtp_auth_cram_md5: | |
465 rc = ngx_mail_auth_cram_md5(s, c); | |
466 break; | |
467 } | |
468 } | |
469 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
470 if (s->buffer->pos < s->buffer->last) { |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
471 s->blocked = 1; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
472 } |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
473 |
336 | 474 switch (rc) { |
475 | |
476 case NGX_DONE: | |
477 ngx_mail_auth(s, c); | |
478 return; | |
479 | |
480 case NGX_ERROR: | |
481 ngx_mail_session_internal_server_error(s); | |
482 return; | |
483 | |
484 case NGX_MAIL_PARSE_INVALID_COMMAND: | |
485 s->mail_state = ngx_smtp_start; | |
486 s->state = 0; | |
487 | |
488 s->out.len = sizeof(smtp_invalid_command) - 1; | |
489 s->out.data = smtp_invalid_command; | |
490 | |
491 /* fall through */ | |
492 | |
493 case NGX_OK: | |
494 s->args.nelts = 0; | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
495 |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
496 if (s->buffer->pos == s->buffer->last) { |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
497 s->buffer->pos = s->buffer->start; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
498 s->buffer->last = s->buffer->start; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
499 } |
336 | 500 |
501 if (s->state) { | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
502 s->arg_start = s->buffer->pos; |
336 | 503 } |
504 | |
505 ngx_mail_send(c->write); | |
506 } | |
507 } | |
508 | |
509 | |
510 static ngx_int_t | |
511 ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c) | |
512 { | |
513 ngx_str_t *arg; | |
514 ngx_mail_smtp_srv_conf_t *sscf; | |
515 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
516 if (s->args.nelts != 2) { |
336 | 517 s->out.len = sizeof(smtp_invalid_argument) - 1; |
518 s->out.data = smtp_invalid_argument; | |
519 s->state = 0; | |
520 return NGX_OK; | |
521 } | |
522 | |
523 arg = s->args.elts; | |
524 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
525 s->smtp_helo.len = arg[1].len; |
336 | 526 |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
527 s->smtp_helo.data = ngx_pnalloc(c->pool, arg[1].len); |
336 | 528 if (s->smtp_helo.data == NULL) { |
529 return NGX_ERROR; | |
530 } | |
531 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
532 ngx_memcpy(s->smtp_helo.data, arg[1].data, arg[1].len); |
336 | 533 |
409 | 534 s->smtp_from.len = 0; |
535 s->smtp_from.data = NULL; | |
536 s->smtp_to.len = 0; | |
537 s->smtp_to.data = NULL; | |
538 | |
336 | 539 sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); |
540 | |
541 if (s->command == NGX_SMTP_HELO) { | |
542 s->out = sscf->server_name; | |
543 | |
544 } else { | |
545 s->esmtp = 1; | |
546 | |
547 #if (NGX_MAIL_SSL) | |
548 | |
549 if (c->ssl == NULL) { | |
356 | 550 ngx_mail_ssl_conf_t *sslcf; |
551 | |
336 | 552 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); |
553 | |
554 if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { | |
555 s->out = sscf->starttls_capability; | |
556 return NGX_OK; | |
557 } | |
558 | |
559 if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { | |
560 s->out = sscf->starttls_only_capability; | |
561 return NGX_OK; | |
562 } | |
563 } | |
564 #endif | |
565 | |
566 s->out = sscf->capability; | |
567 } | |
568 | |
569 return NGX_OK; | |
570 } | |
571 | |
572 | |
573 static ngx_int_t | |
574 ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c) | |
575 { | |
576 ngx_int_t rc; | |
577 ngx_mail_core_srv_conf_t *cscf; | |
578 ngx_mail_smtp_srv_conf_t *sscf; | |
579 | |
580 #if (NGX_MAIL_SSL) | |
581 if (ngx_mail_starttls_only(s, c)) { | |
582 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
583 } | |
584 #endif | |
585 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
586 if (s->args.nelts < 2) { |
336 | 587 s->out.len = sizeof(smtp_invalid_argument) - 1; |
588 s->out.data = smtp_invalid_argument; | |
589 s->state = 0; | |
590 return NGX_OK; | |
591 } | |
592 | |
593 rc = ngx_mail_auth_parse(s, c); | |
594 | |
595 switch (rc) { | |
596 | |
597 case NGX_MAIL_AUTH_LOGIN: | |
598 | |
599 s->out.len = sizeof(smtp_username) - 1; | |
600 s->out.data = smtp_username; | |
601 s->mail_state = ngx_smtp_auth_login_username; | |
602 | |
603 return NGX_OK; | |
604 | |
605 case NGX_MAIL_AUTH_PLAIN: | |
606 | |
607 s->out.len = sizeof(smtp_next) - 1; | |
608 s->out.data = smtp_next; | |
609 s->mail_state = ngx_smtp_auth_plain; | |
610 | |
611 return NGX_OK; | |
612 | |
613 case NGX_MAIL_AUTH_CRAM_MD5: | |
614 | |
615 sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); | |
616 | |
617 if (!(sscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { | |
618 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
619 } | |
620 | |
621 if (s->salt.data == NULL) { | |
622 cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); | |
623 | |
624 if (ngx_mail_salt(s, c, cscf) != NGX_OK) { | |
625 return NGX_ERROR; | |
626 } | |
627 } | |
628 | |
629 if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK) { | |
630 s->mail_state = ngx_smtp_auth_cram_md5; | |
631 return NGX_OK; | |
632 } | |
633 | |
634 return NGX_ERROR; | |
635 } | |
636 | |
637 return rc; | |
638 } | |
639 | |
640 | |
641 static ngx_int_t | |
642 ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c) | |
643 { | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
644 ngx_str_t *arg, *end, cmd; |
409 | 645 ngx_mail_smtp_srv_conf_t *sscf; |
646 | |
647 sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); | |
648 | |
649 if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) { | |
650 ngx_mail_smtp_log_rejected_command(s, c, | |
651 "client was rejected: \"%V\""); | |
652 | |
653 s->out.len = sizeof(smtp_auth_required) - 1; | |
654 s->out.data = smtp_auth_required; | |
655 | |
656 return NGX_OK; | |
657 } | |
658 | |
659 /* auth none */ | |
660 | |
661 if (s->smtp_from.len) { | |
662 s->out.len = sizeof(smtp_bad_sequence) - 1; | |
663 s->out.data = smtp_bad_sequence; | |
664 return NGX_OK; | |
665 } | |
666 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
667 arg = s->args.elts; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
668 end = arg + s->args.nelts - 1; |
409 | 669 |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
670 cmd.len = end->data + end->len - arg->data; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
671 cmd.data = arg->data; |
409 | 672 |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
673 s->smtp_from.len = cmd.len; |
409 | 674 |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
675 s->smtp_from.data = ngx_palloc(c->pool, cmd.len); |
409 | 676 if (s->smtp_from.data == NULL) { |
677 return NGX_ERROR; | |
678 } | |
679 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
680 ngx_memcpy(s->smtp_from.data, cmd.data, cmd.len); |
409 | 681 |
682 s->out.len = sizeof(smtp_ok) - 1; | |
683 s->out.data = smtp_ok; | |
684 | |
685 return NGX_OK; | |
686 } | |
336 | 687 |
409 | 688 |
689 static ngx_int_t | |
690 ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c) | |
691 { | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
692 ngx_str_t *arg, *end, cmd; |
409 | 693 |
694 if (s->smtp_from.len == 0) { | |
695 s->out.len = sizeof(smtp_bad_sequence) - 1; | |
696 s->out.data = smtp_bad_sequence; | |
697 return NGX_OK; | |
698 } | |
699 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
700 arg = s->args.elts; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
701 end = arg + s->args.nelts - 1; |
409 | 702 |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
703 cmd.len = end->data + end->len - arg->data; |
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
704 cmd.data = arg->data; |
409 | 705 |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
706 s->smtp_to.len = cmd.len; |
409 | 707 |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
708 s->smtp_to.data = ngx_palloc(c->pool, cmd.len); |
409 | 709 if (s->smtp_to.data == NULL) { |
710 return NGX_ERROR; | |
711 } | |
712 | |
436
9b19e26b2660
Mail: smtp pipelining support.
Maxim Dounin <mdounin@mdounin.ru>
parents:
435
diff
changeset
|
713 ngx_memcpy(s->smtp_to.data, cmd.data, cmd.len); |
409 | 714 |
715 s->auth_method = NGX_MAIL_AUTH_NONE; | |
716 | |
717 return NGX_DONE; | |
718 } | |
719 | |
720 | |
721 static ngx_int_t | |
722 ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c) | |
723 { | |
724 s->smtp_from.len = 0; | |
725 s->smtp_from.data = NULL; | |
726 s->smtp_to.len = 0; | |
727 s->smtp_to.data = NULL; | |
728 | |
729 s->out.len = sizeof(smtp_ok) - 1; | |
730 s->out.data = smtp_ok; | |
336 | 731 |
732 return NGX_OK; | |
733 } | |
734 | |
735 | |
736 static ngx_int_t | |
737 ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c) | |
738 { | |
739 #if (NGX_MAIL_SSL) | |
740 ngx_mail_ssl_conf_t *sslcf; | |
741 | |
742 if (c->ssl == NULL) { | |
743 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); | |
744 if (sslcf->starttls) { | |
745 | |
746 /* | |
747 * RFC3207 requires us to discard any knowledge | |
748 * obtained from client before STARTTLS. | |
749 */ | |
750 | |
751 s->smtp_helo.len = 0; | |
752 s->smtp_helo.data = NULL; | |
409 | 753 s->smtp_from.len = 0; |
754 s->smtp_from.data = NULL; | |
755 s->smtp_to.len = 0; | |
756 s->smtp_to.data = NULL; | |
336 | 757 |
758 c->read->handler = ngx_mail_starttls_handler; | |
759 return NGX_OK; | |
760 } | |
761 } | |
762 | |
763 #endif | |
764 | |
765 return NGX_MAIL_PARSE_INVALID_COMMAND; | |
766 } | |
767 | |
768 | |
769 static ngx_int_t | |
770 ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c, | |
771 char *err) | |
772 { | |
773 ssize_t n; | |
774 | |
775 n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last); | |
776 | |
777 if (n == NGX_ERROR || n == 0) { | |
778 ngx_mail_close_connection(c); | |
779 return NGX_ERROR; | |
780 } | |
781 | |
782 if (n > 0) { | |
783 s->buffer->last += n; | |
784 } | |
785 | |
786 if (n == NGX_AGAIN) { | |
787 if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { | |
788 ngx_mail_session_internal_server_error(s); | |
789 return NGX_ERROR; | |
790 } | |
791 | |
792 return NGX_AGAIN; | |
793 } | |
794 | |
795 ngx_mail_smtp_log_rejected_command(s, c, err); | |
796 | |
797 s->buffer->pos = s->buffer->start; | |
798 s->buffer->last = s->buffer->start; | |
799 | |
800 return NGX_OK; | |
801 } | |
802 | |
803 | |
804 static void | |
805 ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, ngx_connection_t *c, | |
806 char *err) | |
807 { | |
808 u_char ch; | |
809 ngx_str_t cmd; | |
810 ngx_uint_t i; | |
811 | |
812 if (c->log->log_level < NGX_LOG_INFO) { | |
813 return; | |
814 } | |
815 | |
816 cmd.len = s->buffer->last - s->buffer->start; | |
817 cmd.data = s->buffer->start; | |
818 | |
819 for (i = 0; i < cmd.len; i++) { | |
820 ch = cmd.data[i]; | |
821 | |
822 if (ch != CR && ch != LF) { | |
823 continue; | |
824 } | |
825 | |
826 cmd.data[i] = '_'; | |
827 } | |
828 | |
829 cmd.len = i; | |
830 | |
831 ngx_log_error(NGX_LOG_INFO, c->log, 0, err, &cmd); | |
832 } |