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