3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
10 #include <ngx_event.h>
14 static void ngx_mail_init_session(ngx_connection_t
*c
);
17 static void ngx_mail_ssl_init_connection(ngx_ssl_t
*ssl
, ngx_connection_t
*c
);
18 static void ngx_mail_ssl_handshake_handler(ngx_connection_t
*c
);
23 ngx_mail_init_connection(ngx_connection_t
*c
)
26 ngx_mail_port_t
*port
;
28 struct sockaddr_in
*sin
;
29 ngx_mail_log_ctx_t
*ctx
;
30 ngx_mail_in_addr_t
*addr
;
31 ngx_mail_session_t
*s
;
32 ngx_mail_addr_conf_t
*addr_conf
;
34 struct sockaddr_in6
*sin6
;
35 ngx_mail_in6_addr_t
*addr6
;
39 /* find the server configuration for the address:port */
41 port
= c
->listening
->servers
;
43 if (port
->naddrs
> 1) {
46 * There are several addresses on this port and one of them
47 * is the "*:port" wildcard so getsockname() is needed to determine
50 * AcceptEx() already gave this address.
53 if (ngx_connection_local_sockaddr(c
, NULL
, 0) != NGX_OK
) {
54 ngx_mail_close_connection(c
);
58 sa
= c
->local_sockaddr
;
60 switch (sa
->sa_family
) {
64 sin6
= (struct sockaddr_in6
*) sa
;
68 /* the last address is "*" */
70 for (i
= 0; i
< port
->naddrs
- 1; i
++) {
71 if (ngx_memcmp(&addr6
[i
].addr6
, &sin6
->sin6_addr
, 16) == 0) {
76 addr_conf
= &addr6
[i
].conf
;
81 default: /* AF_INET */
82 sin
= (struct sockaddr_in
*) sa
;
86 /* the last address is "*" */
88 for (i
= 0; i
< port
->naddrs
- 1; i
++) {
89 if (addr
[i
].addr
== sin
->sin_addr
.s_addr
) {
94 addr_conf
= &addr
[i
].conf
;
100 switch (c
->local_sockaddr
->sa_family
) {
105 addr_conf
= &addr6
[0].conf
;
109 default: /* AF_INET */
111 addr_conf
= &addr
[0].conf
;
116 s
= ngx_pcalloc(c
->pool
, sizeof(ngx_mail_session_t
));
118 ngx_mail_close_connection(c
);
122 s
->main_conf
= addr_conf
->ctx
->main_conf
;
123 s
->srv_conf
= addr_conf
->ctx
->srv_conf
;
125 s
->addr_text
= &addr_conf
->addr_text
;
130 ngx_log_error(NGX_LOG_INFO
, c
->log
, 0, "*%ui client %V connected to %V",
131 c
->number
, &c
->addr_text
, s
->addr_text
);
133 ctx
= ngx_palloc(c
->pool
, sizeof(ngx_mail_log_ctx_t
));
135 ngx_mail_close_connection(c
);
139 ctx
->client
= &c
->addr_text
;
142 c
->log
->connection
= c
->number
;
143 c
->log
->handler
= ngx_mail_log_error
;
145 c
->log
->action
= "sending client greeting line";
147 c
->log_error
= NGX_ERROR_INFO
;
151 ngx_mail_ssl_conf_t
*sslcf
;
153 sslcf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_ssl_module
);
156 c
->log
->action
= "SSL handshaking";
158 ngx_mail_ssl_init_connection(&sslcf
->ssl
, c
);
162 if (addr_conf
->ssl
) {
164 c
->log
->action
= "SSL handshaking";
166 if (sslcf
->ssl
.ctx
== NULL
) {
167 ngx_log_error(NGX_LOG_ERR
, c
->log
, 0,
168 "no \"ssl_certificate\" is defined "
169 "in server listening on SSL port");
170 ngx_mail_close_connection(c
);
174 ngx_mail_ssl_init_connection(&sslcf
->ssl
, c
);
181 ngx_mail_init_session(c
);
188 ngx_mail_starttls_handler(ngx_event_t
*rev
)
191 ngx_mail_session_t
*s
;
192 ngx_mail_ssl_conf_t
*sslcf
;
198 c
->log
->action
= "in starttls state";
200 sslcf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_ssl_module
);
202 ngx_mail_ssl_init_connection(&sslcf
->ssl
, c
);
207 ngx_mail_ssl_init_connection(ngx_ssl_t
*ssl
, ngx_connection_t
*c
)
209 ngx_mail_session_t
*s
;
210 ngx_mail_core_srv_conf_t
*cscf
;
212 if (ngx_ssl_create_connection(ssl
, c
, 0) == NGX_ERROR
) {
213 ngx_mail_close_connection(c
);
217 if (ngx_ssl_handshake(c
) == NGX_AGAIN
) {
221 cscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_core_module
);
223 ngx_add_timer(c
->read
, cscf
->timeout
);
225 c
->ssl
->handler
= ngx_mail_ssl_handshake_handler
;
230 ngx_mail_ssl_handshake_handler(c
);
235 ngx_mail_ssl_handshake_handler(ngx_connection_t
*c
)
237 ngx_mail_session_t
*s
;
238 ngx_mail_core_srv_conf_t
*cscf
;
240 if (c
->ssl
->handshaked
) {
245 cscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_core_module
);
247 c
->read
->handler
= cscf
->protocol
->init_protocol
;
248 c
->write
->handler
= ngx_mail_send
;
250 cscf
->protocol
->init_protocol(c
->read
);
257 ngx_mail_init_session(c
);
261 ngx_mail_close_connection(c
);
268 ngx_mail_init_session(ngx_connection_t
*c
)
270 ngx_mail_session_t
*s
;
271 ngx_mail_core_srv_conf_t
*cscf
;
275 cscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_core_module
);
277 s
->protocol
= cscf
->protocol
->type
;
279 s
->ctx
= ngx_pcalloc(c
->pool
, sizeof(void *) * ngx_mail_max_module
);
280 if (s
->ctx
== NULL
) {
281 ngx_mail_session_internal_server_error(s
);
285 c
->write
->handler
= ngx_mail_send
;
287 cscf
->protocol
->init_session(s
, c
);
292 ngx_mail_salt(ngx_mail_session_t
*s
, ngx_connection_t
*c
,
293 ngx_mail_core_srv_conf_t
*cscf
)
295 s
->salt
.data
= ngx_pnalloc(c
->pool
,
296 sizeof(" <18446744073709551616.@>" CRLF
) - 1
298 + cscf
->server_name
.len
);
299 if (s
->salt
.data
== NULL
) {
303 s
->salt
.len
= ngx_sprintf(s
->salt
.data
, "<%ul.%T@%V>" CRLF
,
304 ngx_random(), ngx_time(), &cscf
->server_name
)
314 ngx_mail_starttls_only(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
316 ngx_mail_ssl_conf_t
*sslcf
;
322 sslcf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_ssl_module
);
324 if (sslcf
->starttls
== NGX_MAIL_STARTTLS_ONLY
) {
335 ngx_mail_auth_plain(ngx_mail_session_t
*s
, ngx_connection_t
*c
, ngx_uint_t n
)
338 ngx_str_t
*arg
, plain
;
342 #if (NGX_DEBUG_MAIL_PASSWD)
343 ngx_log_debug1(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
344 "mail auth plain: \"%V\"", &arg
[n
]);
347 plain
.data
= ngx_pnalloc(c
->pool
, ngx_base64_decoded_length(arg
[n
].len
));
348 if (plain
.data
== NULL
) {
352 if (ngx_decode_base64(&plain
, &arg
[n
]) != NGX_OK
) {
353 ngx_log_error(NGX_LOG_INFO
, c
->log
, 0,
354 "client sent invalid base64 encoding in AUTH PLAIN command");
355 return NGX_MAIL_PARSE_INVALID_COMMAND
;
359 last
= p
+ plain
.len
;
361 while (p
< last
&& *p
++) { /* void */ }
364 ngx_log_error(NGX_LOG_INFO
, c
->log
, 0,
365 "client sent invalid login in AUTH PLAIN command");
366 return NGX_MAIL_PARSE_INVALID_COMMAND
;
371 while (p
< last
&& *p
) { p
++; }
374 ngx_log_error(NGX_LOG_INFO
, c
->log
, 0,
375 "client sent invalid password in AUTH PLAIN command");
376 return NGX_MAIL_PARSE_INVALID_COMMAND
;
379 s
->login
.len
= p
++ - s
->login
.data
;
381 s
->passwd
.len
= last
- p
;
384 #if (NGX_DEBUG_MAIL_PASSWD)
385 ngx_log_debug2(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
386 "mail auth plain: \"%V\" \"%V\"", &s
->login
, &s
->passwd
);
394 ngx_mail_auth_login_username(ngx_mail_session_t
*s
, ngx_connection_t
*c
,
401 ngx_log_debug1(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
402 "mail auth login username: \"%V\"", &arg
[n
]);
404 s
->login
.data
= ngx_pnalloc(c
->pool
, ngx_base64_decoded_length(arg
[n
].len
));
405 if (s
->login
.data
== NULL
) {
409 if (ngx_decode_base64(&s
->login
, &arg
[n
]) != NGX_OK
) {
410 ngx_log_error(NGX_LOG_INFO
, c
->log
, 0,
411 "client sent invalid base64 encoding in AUTH LOGIN command");
412 return NGX_MAIL_PARSE_INVALID_COMMAND
;
415 ngx_log_debug1(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
416 "mail auth login username: \"%V\"", &s
->login
);
423 ngx_mail_auth_login_password(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
429 #if (NGX_DEBUG_MAIL_PASSWD)
430 ngx_log_debug1(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
431 "mail auth login password: \"%V\"", &arg
[0]);
434 s
->passwd
.data
= ngx_pnalloc(c
->pool
,
435 ngx_base64_decoded_length(arg
[0].len
));
436 if (s
->passwd
.data
== NULL
) {
440 if (ngx_decode_base64(&s
->passwd
, &arg
[0]) != NGX_OK
) {
441 ngx_log_error(NGX_LOG_INFO
, c
->log
, 0,
442 "client sent invalid base64 encoding in AUTH LOGIN command");
443 return NGX_MAIL_PARSE_INVALID_COMMAND
;
446 #if (NGX_DEBUG_MAIL_PASSWD)
447 ngx_log_debug1(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
448 "mail auth login password: \"%V\"", &s
->passwd
);
456 ngx_mail_auth_cram_md5_salt(ngx_mail_session_t
*s
, ngx_connection_t
*c
,
457 char *prefix
, size_t len
)
463 p
= ngx_pnalloc(c
->pool
, len
+ ngx_base64_encoded_length(s
->salt
.len
) + 2);
468 salt
.data
= ngx_cpymem(p
, prefix
, len
);
471 ngx_encode_base64(&salt
, &s
->salt
);
475 p
[n
++] = CR
; p
[n
++] = LF
;
485 ngx_mail_auth_cram_md5(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
492 ngx_log_debug1(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
493 "mail auth cram-md5: \"%V\"", &arg
[0]);
495 s
->login
.data
= ngx_pnalloc(c
->pool
, ngx_base64_decoded_length(arg
[0].len
));
496 if (s
->login
.data
== NULL
) {
500 if (ngx_decode_base64(&s
->login
, &arg
[0]) != NGX_OK
) {
501 ngx_log_error(NGX_LOG_INFO
, c
->log
, 0,
502 "client sent invalid base64 encoding in AUTH CRAM-MD5 command");
503 return NGX_MAIL_PARSE_INVALID_COMMAND
;
507 last
= p
+ s
->login
.len
;
511 s
->login
.len
= p
- s
->login
.data
- 1;
512 s
->passwd
.len
= last
- p
;
518 if (s
->passwd
.len
!= 32) {
519 ngx_log_error(NGX_LOG_INFO
, c
->log
, 0,
520 "client sent invalid CRAM-MD5 hash in AUTH CRAM-MD5 command");
521 return NGX_MAIL_PARSE_INVALID_COMMAND
;
524 ngx_log_debug2(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
525 "mail auth cram-md5: \"%V\" \"%V\"", &s
->login
, &s
->passwd
);
527 s
->auth_method
= NGX_MAIL_AUTH_CRAM_MD5
;
534 ngx_mail_send(ngx_event_t
*wev
)
538 ngx_mail_session_t
*s
;
539 ngx_mail_core_srv_conf_t
*cscf
;
545 ngx_log_error(NGX_LOG_INFO
, c
->log
, NGX_ETIMEDOUT
, "client timed out");
547 ngx_mail_close_connection(c
);
551 if (s
->out
.len
== 0) {
552 if (ngx_handle_write_event(c
->write
, 0) != NGX_OK
) {
553 ngx_mail_close_connection(c
);
559 n
= c
->send(c
, s
->out
.data
, s
->out
.len
);
564 if (wev
->timer_set
) {
569 ngx_mail_close_connection(c
);
574 c
->read
->handler(c
->read
);
580 if (n
== NGX_ERROR
) {
581 ngx_mail_close_connection(c
);
587 cscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_core_module
);
589 ngx_add_timer(c
->write
, cscf
->timeout
);
591 if (ngx_handle_write_event(c
->write
, 0) != NGX_OK
) {
592 ngx_mail_close_connection(c
);
599 ngx_mail_read_command(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
604 ngx_mail_core_srv_conf_t
*cscf
;
606 n
= c
->recv(c
, s
->buffer
->last
, s
->buffer
->end
- s
->buffer
->last
);
608 if (n
== NGX_ERROR
|| n
== 0) {
609 ngx_mail_close_connection(c
);
614 s
->buffer
->last
+= n
;
617 if (n
== NGX_AGAIN
) {
618 if (ngx_handle_read_event(c
->read
, 0) != NGX_OK
) {
619 ngx_mail_session_internal_server_error(s
);
626 cscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_core_module
);
628 rc
= cscf
->protocol
->parse_command(s
);
630 if (rc
== NGX_AGAIN
) {
632 if (s
->buffer
->last
< s
->buffer
->end
) {
636 l
.len
= s
->buffer
->last
- s
->buffer
->start
;
637 l
.data
= s
->buffer
->start
;
639 ngx_log_error(NGX_LOG_INFO
, c
->log
, 0,
640 "client sent too long command \"%V\"", &l
);
644 return NGX_MAIL_PARSE_INVALID_COMMAND
;
647 if (rc
== NGX_IMAP_NEXT
|| rc
== NGX_MAIL_PARSE_INVALID_COMMAND
) {
651 if (rc
== NGX_ERROR
) {
652 ngx_mail_close_connection(c
);
661 ngx_mail_auth(ngx_mail_session_t
*s
, ngx_connection_t
*c
)
664 s
->buffer
->pos
= s
->buffer
->start
;
665 s
->buffer
->last
= s
->buffer
->start
;
668 if (c
->read
->timer_set
) {
669 ngx_del_timer(c
->read
);
674 ngx_mail_auth_http_init(s
);
679 ngx_mail_session_internal_server_error(ngx_mail_session_t
*s
)
681 ngx_mail_core_srv_conf_t
*cscf
;
683 cscf
= ngx_mail_get_module_srv_conf(s
, ngx_mail_core_module
);
685 s
->out
= cscf
->protocol
->internal_server_error
;
688 ngx_mail_send(s
->connection
->write
);
693 ngx_mail_close_connection(ngx_connection_t
*c
)
697 ngx_log_debug1(NGX_LOG_DEBUG_MAIL
, c
->log
, 0,
698 "close mail connection: %d", c
->fd
);
703 if (ngx_ssl_shutdown(c
) == NGX_AGAIN
) {
704 c
->ssl
->handler
= ngx_mail_close_connection
;
712 (void) ngx_atomic_fetch_add(ngx_stat_active
, -1);
719 ngx_close_connection(c
);
721 ngx_destroy_pool(pool
);
726 ngx_mail_log_error(ngx_log_t
*log
, u_char
*buf
, size_t len
)
729 ngx_mail_session_t
*s
;
730 ngx_mail_log_ctx_t
*ctx
;
733 p
= ngx_snprintf(buf
, len
, " while %s", log
->action
);
740 p
= ngx_snprintf(buf
, len
, ", client: %V", ctx
->client
);
750 p
= ngx_snprintf(buf
, len
, "%s, server: %V",
751 s
->starttls
? " using starttls" : "",
756 if (s
->login
.len
== 0) {
760 p
= ngx_snprintf(buf
, len
, ", login: \"%V\"", &s
->login
);
764 if (s
->proxy
== NULL
) {
768 p
= ngx_snprintf(buf
, len
, ", upstream: %V", s
->proxy
->upstream
.name
);