3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
10 #include <ngx_event.h>
12 #include <ngx_mail_smtp_module.h>
15 static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t
*ctx
);
16 static void ngx_mail_smtp_resolve_name(ngx_event_t
*rev
);
17 static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t
*ctx
);
18 static void ngx_mail_smtp_greeting(ngx_mail_session_t
*s
, ngx_connection_t
*c
);
19 static void ngx_mail_smtp_invalid_pipelining(ngx_event_t
*rev
);
20 static ngx_int_t
ngx_mail_smtp_create_buffer(ngx_mail_session_t
*s
,
23 static ngx_int_t
ngx_mail_smtp_helo(ngx_mail_session_t
*s
, ngx_connection_t
*c
);
24 static ngx_int_t
ngx_mail_smtp_auth(ngx_mail_session_t
*s
, ngx_connection_t
*c
);
25 static ngx_int_t
ngx_mail_smtp_mail(ngx_mail_session_t
*s
, ngx_connection_t
*c
);
26 static ngx_int_t
ngx_mail_smtp_starttls(ngx_mail_session_t
*s
,
28 static ngx_int_t
ngx_mail_smtp_rset(ngx_mail_session_t
*s
, ngx_connection_t
*c
);
29 static ngx_int_t
ngx_mail_smtp_rcpt(ngx_mail_session_t
*s
, ngx_connection_t
*c
);
31 static ngx_int_t
ngx_mail_smtp_discard_command(ngx_mail_session_t
*s
,
32 ngx_connection_t
*c
, char *err
);
33 static void ngx_mail_smtp_log_rejected_command(ngx_mail_session_t
*s
,
34 ngx_connection_t
*c
, char *err
);
37 static u_char smtp_ok
[] = "250 2.0.0 OK" CRLF
;
38 static u_char smtp_bye
[] = "221 2.0.0 Bye" CRLF
;
39 static u_char smtp_starttls
[] = "220 2.0.0 Start TLS" CRLF
;
40 static u_char smtp_next
[] = "334 " CRLF
;
41 static u_char smtp_username
[] = "334 VXNlcm5hbWU6" CRLF
;
42 static u_char smtp_password
[] = "334 UGFzc3dvcmQ6" CRLF
;
43 static u_char smtp_invalid_command
[] = "500 5.5.1 Invalid command" CRLF
;
44 static u_char smtp_invalid_pipelining
[] =
45 "503 5.5.0 Improper use of SMTP command pipelining" CRLF
;
46 static u_char smtp_invalid_argument
[] = "501 5.5.4 Invalid argument" CRLF
;
47 static u_char smtp_auth_required
[] = "530 5.7.1 Authentication required" CRLF
;
48 static u_char smtp_bad_sequence
[] = "503 5.5.1 Bad sequence of commands" CRLF
;
51 static ngx_str_t smtp_unavailable
= ngx_string("[UNAVAILABLE]");
52 static ngx_str_t smtp_tempunavail
= ngx_string("[TEMPUNAVAIL]");
56 ngx_mail_smtp_init_session(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
58 struct sockaddr_in
*sin
;
59 ngx_resolver_ctx_t
*ctx
;
60 ngx_mail_core_srv_conf_t
*cscf
;
62 cscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_core_module
);
64 if (cscf
->resolver
== NULL
) {
65 s
->host
= smtp_unavailable
;
66 ngx_mail_smtp_greeting(s
, c
);
70 if (c
->sockaddr
->sa_family
!= AF_INET
) {
71 s
->host
= smtp_tempunavail
;
72 ngx_mail_smtp_greeting(s
, c
);
76 c
->log
->action
= "in resolving client address";
78 ctx
= ngx_resolve_start(cscf
->resolver
, NULL
);
80 ngx_mail_close_connection(c
);
86 sin
= (struct sockaddr_in
*) c
->sockaddr
;
88 ctx
->addr
= sin
->sin_addr
.s_addr
;
89 ctx
->handler
= ngx_mail_smtp_resolve_addr_handler
;
91 ctx
->timeout
= cscf
->resolver_timeout
;
93 if (ngx_resolve_addr(ctx
) != NGX_OK
) {
94 ngx_mail_close_connection(c
);
100 ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t
*ctx
)
103 ngx_mail_session_t
*s
;
109 ngx_log_error(NGX_LOG_ERR
, c
->log
, 0,
110 "%V could not be resolved (%i: %s)",
111 &c
->addr_text
, ctx
->state
,
112 ngx_resolver_strerror(ctx
->state
));
114 if (ctx
->state
== NGX_RESOLVE_NXDOMAIN
) {
115 s
->host
= smtp_unavailable
;
118 s
->host
= smtp_tempunavail
;
121 ngx_resolve_addr_done(ctx
);
123 ngx_mail_smtp_greeting(s
, s
->connection
);
128 c
->log
->action
= "in resolving client hostname";
130 s
->host
.data
= ngx_pstrdup(c
->pool
, &ctx
->name
);
131 if (s
->host
.data
== NULL
) {
132 ngx_resolve_addr_done(ctx
);
133 ngx_mail_close_connection(c
);
137 s
->host
.len
= ctx
->name
.len
;
139 ngx_resolve_addr_done(ctx
);
141 ngx_log_debug1(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
142 "address resolved: %V", &s
->host
);
144 c
->read
->handler
= ngx_mail_smtp_resolve_name
;
146 ngx_post_event(c
->read
, &ngx_posted_events
);
151 ngx_mail_smtp_resolve_name(ngx_event_t
*rev
)
154 ngx_mail_session_t
*s
;
155 ngx_resolver_ctx_t
*ctx
;
156 ngx_mail_core_srv_conf_t
*cscf
;
161 cscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_core_module
);
163 ctx
= ngx_resolve_start(cscf
->resolver
, NULL
);
165 ngx_mail_close_connection(c
);
170 ctx
->type
= NGX_RESOLVE_A
;
171 ctx
->handler
= ngx_mail_smtp_resolve_name_handler
;
173 ctx
->timeout
= cscf
->resolver_timeout
;
175 if (ngx_resolve_name(ctx
) != NGX_OK
) {
176 ngx_mail_close_connection(c
);
182 ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t
*ctx
)
187 struct sockaddr_in
*sin
;
188 ngx_mail_session_t
*s
;
194 ngx_log_error(NGX_LOG_ERR
, c
->log
, 0,
195 "\"%V\" could not be resolved (%i: %s)",
196 &ctx
->name
, ctx
->state
,
197 ngx_resolver_strerror(ctx
->state
));
199 if (ctx
->state
== NGX_RESOLVE_NXDOMAIN
) {
200 s
->host
= smtp_unavailable
;
203 s
->host
= smtp_tempunavail
;
210 sin
= (struct sockaddr_in
*) c
->sockaddr
;
212 for (i
= 0; i
< ctx
->naddrs
; i
++) {
214 addr
= ctx
->addrs
[i
];
216 ngx_log_debug4(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
217 "name was resolved to %ud.%ud.%ud.%ud",
218 (ntohl(addr
) >> 24) & 0xff,
219 (ntohl(addr
) >> 16) & 0xff,
220 (ntohl(addr
) >> 8) & 0xff,
223 if (addr
== sin
->sin_addr
.s_addr
) {
228 s
->host
= smtp_unavailable
;
233 ngx_resolve_name_done(ctx
);
235 ngx_mail_smtp_greeting(s
, c
);
240 ngx_mail_smtp_greeting(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
243 ngx_mail_core_srv_conf_t
*cscf
;
244 ngx_mail_smtp_srv_conf_t
*sscf
;
246 ngx_log_debug1(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
247 "smtp greeting for \"%V\"", &s
->host
);
249 cscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_core_module
);
250 sscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_smtp_module
);
252 timeout
= sscf
->greeting_delay
? sscf
->greeting_delay
: cscf
->timeout
;
253 ngx_add_timer(c
->read
, timeout
);
255 if (ngx_handle_read_event(c
->read
, 0) != NGX_OK
) {
256 ngx_mail_close_connection(c
);
259 if (sscf
->greeting_delay
) {
260 c
->read
->handler
= ngx_mail_smtp_invalid_pipelining
;
264 c
->read
->handler
= ngx_mail_smtp_init_protocol
;
266 s
->out
= sscf
->greeting
;
268 ngx_mail_send(c
->write
);
273 ngx_mail_smtp_invalid_pipelining(ngx_event_t
*rev
)
276 ngx_mail_session_t
*s
;
277 ngx_mail_core_srv_conf_t
*cscf
;
278 ngx_mail_smtp_srv_conf_t
*sscf
;
283 c
->log
->action
= "in delay pipelining state";
287 ngx_log_debug0(NGX_LOG_DEBUG_MAIL
, c
->log
, 0, "delay greeting");
291 cscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_core_module
);
293 c
->read
->handler
= ngx_mail_smtp_init_protocol
;
295 ngx_add_timer(c
->read
, cscf
->timeout
);
297 if (ngx_handle_read_event(c
->read
, 0) != NGX_OK
) {
298 ngx_mail_close_connection(c
);
302 sscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_smtp_module
);
304 s
->out
= sscf
->greeting
;
308 ngx_log_debug0(NGX_LOG_DEBUG_MAIL
, c
->log
, 0, "invalid pipelining");
310 if (s
->buffer
== NULL
) {
311 if (ngx_mail_smtp_create_buffer(s
, c
) != NGX_OK
) {
316 if (ngx_mail_smtp_discard_command(s
, c
,
317 "client was rejected before greeting: \"%V\"")
323 ngx_str_set(&s
->out
, smtp_invalid_pipelining
);
326 ngx_mail_send(c
->write
);
331 ngx_mail_smtp_init_protocol(ngx_event_t
*rev
)
334 ngx_mail_session_t
*s
;
338 c
->log
->action
= "in auth state";
341 ngx_log_error(NGX_LOG_INFO
, c
->log
, NGX_ETIMEDOUT
, "client timed out");
343 ngx_mail_close_connection(c
);
349 if (s
->buffer
== NULL
) {
350 if (ngx_mail_smtp_create_buffer(s
, c
) != NGX_OK
) {
355 s
->mail_state
= ngx_smtp_start
;
356 c
->read
->handler
= ngx_mail_smtp_auth_state
;
358 ngx_mail_smtp_auth_state(rev
);
363 ngx_mail_smtp_create_buffer(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
365 ngx_mail_smtp_srv_conf_t
*sscf
;
367 if (ngx_array_init(&s
->args
, c
->pool
, 2, sizeof(ngx_str_t
)) == NGX_ERROR
) {
368 ngx_mail_session_internal_server_error(s
);
372 sscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_smtp_module
);
374 s
->buffer
= ngx_create_temp_buf(c
->pool
, sscf
->client_buffer_size
);
375 if (s
->buffer
== NULL
) {
376 ngx_mail_session_internal_server_error(s
);
385 ngx_mail_smtp_auth_state(ngx_event_t
*rev
)
389 ngx_mail_session_t
*s
;
394 ngx_log_debug0(NGX_LOG_DEBUG_MAIL
, c
->log
, 0, "smtp auth state");
397 ngx_log_error(NGX_LOG_INFO
, c
->log
, NGX_ETIMEDOUT
, "client timed out");
399 ngx_mail_close_connection(c
);
404 ngx_log_debug0(NGX_LOG_DEBUG_MAIL
, c
->log
, 0, "smtp send handler busy");
411 rc
= ngx_mail_read_command(s
, c
);
413 if (rc
== NGX_AGAIN
|| rc
== NGX_ERROR
) {
417 ngx_str_set(&s
->out
, smtp_ok
);
420 switch (s
->mail_state
) {
424 switch (s
->command
) {
428 rc
= ngx_mail_smtp_helo(s
, c
);
432 rc
= ngx_mail_smtp_auth(s
, c
);
437 ngx_str_set(&s
->out
, smtp_bye
);
441 rc
= ngx_mail_smtp_mail(s
, c
);
445 rc
= ngx_mail_smtp_rcpt(s
, c
);
449 rc
= ngx_mail_smtp_rset(s
, c
);
455 case NGX_SMTP_STARTTLS
:
456 rc
= ngx_mail_smtp_starttls(s
, c
);
457 ngx_str_set(&s
->out
, smtp_starttls
);
461 rc
= NGX_MAIL_PARSE_INVALID_COMMAND
;
467 case ngx_smtp_auth_login_username
:
468 rc
= ngx_mail_auth_login_username(s
, c
, 0);
470 ngx_str_set(&s
->out
, smtp_password
);
471 s
->mail_state
= ngx_smtp_auth_login_password
;
474 case ngx_smtp_auth_login_password
:
475 rc
= ngx_mail_auth_login_password(s
, c
);
478 case ngx_smtp_auth_plain
:
479 rc
= ngx_mail_auth_plain(s
, c
, 0);
482 case ngx_smtp_auth_cram_md5
:
483 rc
= ngx_mail_auth_cram_md5(s
, c
);
495 ngx_mail_session_internal_server_error(s
);
498 case NGX_MAIL_PARSE_INVALID_COMMAND
:
499 s
->mail_state
= ngx_smtp_start
;
501 ngx_str_set(&s
->out
, smtp_invalid_command
);
507 s
->buffer
->pos
= s
->buffer
->start
;
508 s
->buffer
->last
= s
->buffer
->start
;
511 s
->arg_start
= s
->buffer
->start
;
514 ngx_mail_send(c
->write
);
520 ngx_mail_smtp_helo(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
523 ngx_mail_smtp_srv_conf_t
*sscf
;
525 if (s
->args
.nelts
!= 1) {
526 ngx_str_set(&s
->out
, smtp_invalid_argument
);
533 s
->smtp_helo
.len
= arg
[0].len
;
535 s
->smtp_helo
.data
= ngx_pnalloc(c
->pool
, arg
[0].len
);
536 if (s
->smtp_helo
.data
== NULL
) {
540 ngx_memcpy(s
->smtp_helo
.data
, arg
[0].data
, arg
[0].len
);
542 ngx_str_null(&s
->smtp_from
);
543 ngx_str_null(&s
->smtp_to
);
545 sscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_smtp_module
);
547 if (s
->command
== NGX_SMTP_HELO
) {
548 s
->out
= sscf
->server_name
;
555 if (c
->ssl
== NULL
) {
556 ngx_mail_ssl_conf_t
*sslcf
;
558 sslcf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_ssl_module
);
560 if (sslcf
->starttls
== NGX_MAIL_STARTTLS_ON
) {
561 s
->out
= sscf
->starttls_capability
;
565 if (sslcf
->starttls
== NGX_MAIL_STARTTLS_ONLY
) {
566 s
->out
= sscf
->starttls_only_capability
;
572 s
->out
= sscf
->capability
;
580 ngx_mail_smtp_auth(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
583 ngx_mail_core_srv_conf_t
*cscf
;
584 ngx_mail_smtp_srv_conf_t
*sscf
;
587 if (ngx_mail_starttls_only(s
, c
)) {
588 return NGX_MAIL_PARSE_INVALID_COMMAND
;
592 if (s
->args
.nelts
== 0) {
593 ngx_str_set(&s
->out
, smtp_invalid_argument
);
598 rc
= ngx_mail_auth_parse(s
, c
);
602 case NGX_MAIL_AUTH_LOGIN
:
604 ngx_str_set(&s
->out
, smtp_username
);
605 s
->mail_state
= ngx_smtp_auth_login_username
;
609 case NGX_MAIL_AUTH_LOGIN_USERNAME
:
611 ngx_str_set(&s
->out
, smtp_password
);
612 s
->mail_state
= ngx_smtp_auth_login_password
;
614 return ngx_mail_auth_login_username(s
, c
, 1);
616 case NGX_MAIL_AUTH_PLAIN
:
618 ngx_str_set(&s
->out
, smtp_next
);
619 s
->mail_state
= ngx_smtp_auth_plain
;
623 case NGX_MAIL_AUTH_CRAM_MD5
:
625 sscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_smtp_module
);
627 if (!(sscf
->auth_methods
& NGX_MAIL_AUTH_CRAM_MD5_ENABLED
)) {
628 return NGX_MAIL_PARSE_INVALID_COMMAND
;
631 if (s
->salt
.data
== NULL
) {
632 cscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_core_module
);
634 if (ngx_mail_salt(s
, c
, cscf
) != NGX_OK
) {
639 if (ngx_mail_auth_cram_md5_salt(s
, c
, "334 ", 4) == NGX_OK
) {
640 s
->mail_state
= ngx_smtp_auth_cram_md5
;
652 ngx_mail_smtp_mail(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
657 ngx_mail_smtp_srv_conf_t
*sscf
;
659 sscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_smtp_module
);
661 if (!(sscf
->auth_methods
& NGX_MAIL_AUTH_NONE_ENABLED
)) {
662 ngx_mail_smtp_log_rejected_command(s
, c
, "client was rejected: \"%V\"");
663 ngx_str_set(&s
->out
, smtp_auth_required
);
669 if (s
->smtp_from
.len
) {
670 ngx_str_set(&s
->out
, smtp_bad_sequence
);
674 l
.len
= s
->buffer
->last
- s
->buffer
->start
;
675 l
.data
= s
->buffer
->start
;
677 for (i
= 0; i
< l
.len
; i
++) {
680 if (ch
!= CR
&& ch
!= LF
) {
688 if (l
.data
[i
- 1] != ' ') {
697 s
->smtp_from
.len
= l
.len
;
699 s
->smtp_from
.data
= ngx_pnalloc(c
->pool
, l
.len
);
700 if (s
->smtp_from
.data
== NULL
) {
704 ngx_memcpy(s
->smtp_from
.data
, l
.data
, l
.len
);
706 ngx_log_debug1(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
707 "smtp mail from:\"%V\"", &s
->smtp_from
);
709 ngx_str_set(&s
->out
, smtp_ok
);
716 ngx_mail_smtp_rcpt(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
722 if (s
->smtp_from
.len
== 0) {
723 ngx_str_set(&s
->out
, smtp_bad_sequence
);
727 l
.len
= s
->buffer
->last
- s
->buffer
->start
;
728 l
.data
= s
->buffer
->start
;
730 for (i
= 0; i
< l
.len
; i
++) {
733 if (ch
!= CR
&& ch
!= LF
) {
741 if (l
.data
[i
- 1] != ' ') {
750 s
->smtp_to
.len
= l
.len
;
752 s
->smtp_to
.data
= ngx_pnalloc(c
->pool
, l
.len
);
753 if (s
->smtp_to
.data
== NULL
) {
757 ngx_memcpy(s
->smtp_to
.data
, l
.data
, l
.len
);
759 ngx_log_debug1(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
760 "smtp rcpt to:\"%V\"", &s
->smtp_to
);
762 s
->auth_method
= NGX_MAIL_AUTH_NONE
;
769 ngx_mail_smtp_rset(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
771 ngx_str_null(&s
->smtp_from
);
772 ngx_str_null(&s
->smtp_to
);
773 ngx_str_set(&s
->out
, smtp_ok
);
780 ngx_mail_smtp_starttls(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
783 ngx_mail_ssl_conf_t
*sslcf
;
785 if (c
->ssl
== NULL
) {
786 sslcf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_ssl_module
);
787 if (sslcf
->starttls
) {
790 * RFC3207 requires us to discard any knowledge
791 * obtained from client before STARTTLS.
794 ngx_str_null(&s
->smtp_helo
);
795 ngx_str_null(&s
->smtp_from
);
796 ngx_str_null(&s
->smtp_to
);
798 c
->read
->handler
= ngx_mail_starttls_handler
;
805 return NGX_MAIL_PARSE_INVALID_COMMAND
;
810 ngx_mail_smtp_discard_command(ngx_mail_session_t
*s
, ngx_connection_t
*c
,
815 n
= c
->recv(c
, s
->buffer
->last
, s
->buffer
->end
- s
->buffer
->last
);
817 if (n
== NGX_ERROR
|| n
== 0) {
818 ngx_mail_close_connection(c
);
823 s
->buffer
->last
+= n
;
826 if (n
== NGX_AGAIN
) {
827 if (ngx_handle_read_event(c
->read
, 0) != NGX_OK
) {
828 ngx_mail_session_internal_server_error(s
);
835 ngx_mail_smtp_log_rejected_command(s
, c
, err
);
837 s
->buffer
->pos
= s
->buffer
->start
;
838 s
->buffer
->last
= s
->buffer
->start
;
845 ngx_mail_smtp_log_rejected_command(ngx_mail_session_t
*s
, ngx_connection_t
*c
,
852 if (c
->log
->log_level
< NGX_LOG_INFO
) {
856 cmd
.len
= s
->buffer
->last
- s
->buffer
->start
;
857 cmd
.data
= s
->buffer
->start
;
859 for (i
= 0; i
< cmd
.len
; i
++) {
862 if (ch
!= CR
&& ch
!= LF
) {
871 ngx_log_error(NGX_LOG_INFO
, c
->log
, 0, err
, &cmd
);