Update and clean Tomato RAF files
[tomato.git] / release / src / router / nginx / src / mail / ngx_mail_handler.c
blobae955f9c6aea04cf64a72d4ea306af2ceb2b81d4
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_event.h>
11 #include <ngx_mail.h>
14 static void ngx_mail_init_session(ngx_connection_t *c);
16 #if (NGX_MAIL_SSL)
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);
19 #endif
22 void
23 ngx_mail_init_connection(ngx_connection_t *c)
25 ngx_uint_t i;
26 ngx_mail_port_t *port;
27 struct sockaddr *sa;
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;
33 #if (NGX_HAVE_INET6)
34 struct sockaddr_in6 *sin6;
35 ngx_mail_in6_addr_t *addr6;
36 #endif
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
48 * the server address.
50 * AcceptEx() already gave this address.
53 if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
54 ngx_mail_close_connection(c);
55 return;
58 sa = c->local_sockaddr;
60 switch (sa->sa_family) {
62 #if (NGX_HAVE_INET6)
63 case AF_INET6:
64 sin6 = (struct sockaddr_in6 *) sa;
66 addr6 = port->addrs;
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) {
72 break;
76 addr_conf = &addr6[i].conf;
78 break;
79 #endif
81 default: /* AF_INET */
82 sin = (struct sockaddr_in *) sa;
84 addr = port->addrs;
86 /* the last address is "*" */
88 for (i = 0; i < port->naddrs - 1; i++) {
89 if (addr[i].addr == sin->sin_addr.s_addr) {
90 break;
94 addr_conf = &addr[i].conf;
96 break;
99 } else {
100 switch (c->local_sockaddr->sa_family) {
102 #if (NGX_HAVE_INET6)
103 case AF_INET6:
104 addr6 = port->addrs;
105 addr_conf = &addr6[0].conf;
106 break;
107 #endif
109 default: /* AF_INET */
110 addr = port->addrs;
111 addr_conf = &addr[0].conf;
112 break;
116 s = ngx_pcalloc(c->pool, sizeof(ngx_mail_session_t));
117 if (s == NULL) {
118 ngx_mail_close_connection(c);
119 return;
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;
127 c->data = s;
128 s->connection = c;
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));
134 if (ctx == NULL) {
135 ngx_mail_close_connection(c);
136 return;
139 ctx->client = &c->addr_text;
140 ctx->session = s;
142 c->log->connection = c->number;
143 c->log->handler = ngx_mail_log_error;
144 c->log->data = ctx;
145 c->log->action = "sending client greeting line";
147 c->log_error = NGX_ERROR_INFO;
149 #if (NGX_MAIL_SSL)
151 ngx_mail_ssl_conf_t *sslcf;
153 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
155 if (sslcf->enable) {
156 c->log->action = "SSL handshaking";
158 ngx_mail_ssl_init_connection(&sslcf->ssl, c);
159 return;
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);
171 return;
174 ngx_mail_ssl_init_connection(&sslcf->ssl, c);
175 return;
179 #endif
181 ngx_mail_init_session(c);
185 #if (NGX_MAIL_SSL)
187 void
188 ngx_mail_starttls_handler(ngx_event_t *rev)
190 ngx_connection_t *c;
191 ngx_mail_session_t *s;
192 ngx_mail_ssl_conf_t *sslcf;
194 c = rev->data;
195 s = c->data;
196 s->starttls = 1;
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);
206 static void
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);
214 return;
217 if (ngx_ssl_handshake(c) == NGX_AGAIN) {
219 s = c->data;
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;
227 return;
230 ngx_mail_ssl_handshake_handler(c);
234 static void
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) {
242 s = c->data;
244 if (s->starttls) {
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);
252 return;
255 c->read->ready = 0;
257 ngx_mail_init_session(c);
258 return;
261 ngx_mail_close_connection(c);
264 #endif
267 static void
268 ngx_mail_init_session(ngx_connection_t *c)
270 ngx_mail_session_t *s;
271 ngx_mail_core_srv_conf_t *cscf;
273 s = c->data;
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);
282 return;
285 c->write->handler = ngx_mail_send;
287 cscf->protocol->init_session(s, c);
291 ngx_int_t
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
297 + NGX_TIME_T_LEN
298 + cscf->server_name.len);
299 if (s->salt.data == NULL) {
300 return NGX_ERROR;
303 s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF,
304 ngx_random(), ngx_time(), &cscf->server_name)
305 - s->salt.data;
307 return NGX_OK;
311 #if (NGX_MAIL_SSL)
313 ngx_int_t
314 ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c)
316 ngx_mail_ssl_conf_t *sslcf;
318 if (c->ssl) {
319 return 0;
322 sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
324 if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
325 return 1;
328 return 0;
331 #endif
334 ngx_int_t
335 ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)
337 u_char *p, *last;
338 ngx_str_t *arg, plain;
340 arg = s->args.elts;
342 #if (NGX_DEBUG_MAIL_PASSWD)
343 ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
344 "mail auth plain: \"%V\"", &arg[n]);
345 #endif
347 plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
348 if (plain.data == NULL) {
349 return NGX_ERROR;
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;
358 p = plain.data;
359 last = p + plain.len;
361 while (p < last && *p++) { /* void */ }
363 if (p == last) {
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;
369 s->login.data = p;
371 while (p < last && *p) { p++; }
373 if (p == last) {
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;
382 s->passwd.data = 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);
387 #endif
389 return NGX_DONE;
393 ngx_int_t
394 ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c,
395 ngx_uint_t n)
397 ngx_str_t *arg;
399 arg = s->args.elts;
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) {
406 return NGX_ERROR;
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);
418 return NGX_OK;
422 ngx_int_t
423 ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)
425 ngx_str_t *arg;
427 arg = s->args.elts;
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]);
432 #endif
434 s->passwd.data = ngx_pnalloc(c->pool,
435 ngx_base64_decoded_length(arg[0].len));
436 if (s->passwd.data == NULL) {
437 return NGX_ERROR;
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);
449 #endif
451 return NGX_DONE;
455 ngx_int_t
456 ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c,
457 char *prefix, size_t len)
459 u_char *p;
460 ngx_str_t salt;
461 ngx_uint_t n;
463 p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);
464 if (p == NULL) {
465 return NGX_ERROR;
468 salt.data = ngx_cpymem(p, prefix, len);
469 s->salt.len -= 2;
471 ngx_encode_base64(&salt, &s->salt);
473 s->salt.len += 2;
474 n = len + salt.len;
475 p[n++] = CR; p[n++] = LF;
477 s->out.len = n;
478 s->out.data = p;
480 return NGX_OK;
484 ngx_int_t
485 ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c)
487 u_char *p, *last;
488 ngx_str_t *arg;
490 arg = s->args.elts;
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) {
497 return NGX_ERROR;
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;
506 p = s->login.data;
507 last = p + s->login.len;
509 while (p < last) {
510 if (*p++ == ' ') {
511 s->login.len = p - s->login.data - 1;
512 s->passwd.len = last - p;
513 s->passwd.data = p;
514 break;
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;
529 return NGX_DONE;
533 void
534 ngx_mail_send(ngx_event_t *wev)
536 ngx_int_t n;
537 ngx_connection_t *c;
538 ngx_mail_session_t *s;
539 ngx_mail_core_srv_conf_t *cscf;
541 c = wev->data;
542 s = c->data;
544 if (wev->timedout) {
545 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
546 c->timedout = 1;
547 ngx_mail_close_connection(c);
548 return;
551 if (s->out.len == 0) {
552 if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
553 ngx_mail_close_connection(c);
556 return;
559 n = c->send(c, s->out.data, s->out.len);
561 if (n > 0) {
562 s->out.len -= n;
564 if (wev->timer_set) {
565 ngx_del_timer(wev);
568 if (s->quit) {
569 ngx_mail_close_connection(c);
570 return;
573 if (s->blocked) {
574 c->read->handler(c->read);
577 return;
580 if (n == NGX_ERROR) {
581 ngx_mail_close_connection(c);
582 return;
585 /* n == NGX_AGAIN */
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);
593 return;
598 ngx_int_t
599 ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c)
601 ssize_t n;
602 ngx_int_t rc;
603 ngx_str_t l;
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);
610 return NGX_ERROR;
613 if (n > 0) {
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);
620 return NGX_ERROR;
623 return NGX_AGAIN;
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) {
633 return rc;
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);
642 s->quit = 1;
644 return NGX_MAIL_PARSE_INVALID_COMMAND;
647 if (rc == NGX_IMAP_NEXT || rc == NGX_MAIL_PARSE_INVALID_COMMAND) {
648 return rc;
651 if (rc == NGX_ERROR) {
652 ngx_mail_close_connection(c);
653 return NGX_ERROR;
656 return NGX_OK;
660 void
661 ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c)
663 s->args.nelts = 0;
664 s->buffer->pos = s->buffer->start;
665 s->buffer->last = s->buffer->start;
666 s->state = 0;
668 if (c->read->timer_set) {
669 ngx_del_timer(c->read);
672 s->login_attempt++;
674 ngx_mail_auth_http_init(s);
678 void
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;
686 s->quit = 1;
688 ngx_mail_send(s->connection->write);
692 void
693 ngx_mail_close_connection(ngx_connection_t *c)
695 ngx_pool_t *pool;
697 ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
698 "close mail connection: %d", c->fd);
700 #if (NGX_MAIL_SSL)
702 if (c->ssl) {
703 if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
704 c->ssl->handler = ngx_mail_close_connection;
705 return;
709 #endif
711 #if (NGX_STAT_STUB)
712 (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
713 #endif
715 c->destroyed = 1;
717 pool = c->pool;
719 ngx_close_connection(c);
721 ngx_destroy_pool(pool);
725 u_char *
726 ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len)
728 u_char *p;
729 ngx_mail_session_t *s;
730 ngx_mail_log_ctx_t *ctx;
732 if (log->action) {
733 p = ngx_snprintf(buf, len, " while %s", log->action);
734 len -= p - buf;
735 buf = p;
738 ctx = log->data;
740 p = ngx_snprintf(buf, len, ", client: %V", ctx->client);
741 len -= p - buf;
742 buf = p;
744 s = ctx->session;
746 if (s == NULL) {
747 return p;
750 p = ngx_snprintf(buf, len, "%s, server: %V",
751 s->starttls ? " using starttls" : "",
752 s->addr_text);
753 len -= p - buf;
754 buf = p;
756 if (s->login.len == 0) {
757 return p;
760 p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login);
761 len -= p - buf;
762 buf = p;
764 if (s->proxy == NULL) {
765 return p;
768 p = ngx_snprintf(buf, len, ", upstream: %V", s->proxy->upstream.name);
770 return p;