1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_event.h>
11 #include <ngx_mail.h>
12 
13 
14 static void ngx_mail_init_session(ngx_connection_t *c);
15 
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 static ngx_int_t ngx_mail_verify_cert(ngx_mail_session_t *s,
20     ngx_connection_t *c);
21 #endif
22 
23 
24 void
ngx_mail_init_connection(ngx_connection_t * c)25 ngx_mail_init_connection(ngx_connection_t *c)
26 {
27     size_t                     len;
28     ngx_uint_t                 i;
29     ngx_mail_port_t           *port;
30     struct sockaddr           *sa;
31     struct sockaddr_in        *sin;
32     ngx_mail_log_ctx_t        *ctx;
33     ngx_mail_in_addr_t        *addr;
34     ngx_mail_session_t        *s;
35     ngx_mail_addr_conf_t      *addr_conf;
36     ngx_mail_core_srv_conf_t  *cscf;
37     u_char                     text[NGX_SOCKADDR_STRLEN];
38 #if (NGX_HAVE_INET6)
39     struct sockaddr_in6       *sin6;
40     ngx_mail_in6_addr_t       *addr6;
41 #endif
42 
43 
44     /* find the server configuration for the address:port */
45 
46     port = c->listening->servers;
47 
48     if (port->naddrs > 1) {
49 
50         /*
51          * There are several addresses on this port and one of them
52          * is the "*:port" wildcard so getsockname() is needed to determine
53          * the server address.
54          *
55          * AcceptEx() already gave this address.
56          */
57 
58         if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
59             ngx_mail_close_connection(c);
60             return;
61         }
62 
63         sa = c->local_sockaddr;
64 
65         switch (sa->sa_family) {
66 
67 #if (NGX_HAVE_INET6)
68         case AF_INET6:
69             sin6 = (struct sockaddr_in6 *) sa;
70 
71             addr6 = port->addrs;
72 
73             /* the last address is "*" */
74 
75             for (i = 0; i < port->naddrs - 1; i++) {
76                 if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
77                     break;
78                 }
79             }
80 
81             addr_conf = &addr6[i].conf;
82 
83             break;
84 #endif
85 
86         default: /* AF_INET */
87             sin = (struct sockaddr_in *) sa;
88 
89             addr = port->addrs;
90 
91             /* the last address is "*" */
92 
93             for (i = 0; i < port->naddrs - 1; i++) {
94                 if (addr[i].addr == sin->sin_addr.s_addr) {
95                     break;
96                 }
97             }
98 
99             addr_conf = &addr[i].conf;
100 
101             break;
102         }
103 
104     } else {
105         switch (c->local_sockaddr->sa_family) {
106 
107 #if (NGX_HAVE_INET6)
108         case AF_INET6:
109             addr6 = port->addrs;
110             addr_conf = &addr6[0].conf;
111             break;
112 #endif
113 
114         default: /* AF_INET */
115             addr = port->addrs;
116             addr_conf = &addr[0].conf;
117             break;
118         }
119     }
120 
121     s = ngx_pcalloc(c->pool, sizeof(ngx_mail_session_t));
122     if (s == NULL) {
123         ngx_mail_close_connection(c);
124         return;
125     }
126 
127     s->signature = NGX_MAIL_MODULE;
128 
129     s->main_conf = addr_conf->ctx->main_conf;
130     s->srv_conf = addr_conf->ctx->srv_conf;
131 
132     s->addr_text = &addr_conf->addr_text;
133 
134     c->data = s;
135     s->connection = c;
136 
137     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
138 
139     ngx_set_connection_log(c, cscf->error_log);
140 
141     len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1);
142 
143     ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA client %*s connected to %V",
144                   c->number, len, text, s->addr_text);
145 
146     ctx = ngx_palloc(c->pool, sizeof(ngx_mail_log_ctx_t));
147     if (ctx == NULL) {
148         ngx_mail_close_connection(c);
149         return;
150     }
151 
152     ctx->client = &c->addr_text;
153     ctx->session = s;
154 
155     c->log->connection = c->number;
156     c->log->handler = ngx_mail_log_error;
157     c->log->data = ctx;
158     c->log->action = "sending client greeting line";
159 
160     c->log_error = NGX_ERROR_INFO;
161 
162 #if (NGX_MAIL_SSL)
163     {
164     ngx_mail_ssl_conf_t  *sslcf;
165 
166     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
167 
168     if (sslcf->enable || addr_conf->ssl) {
169         c->log->action = "SSL handshaking";
170 
171         ngx_mail_ssl_init_connection(&sslcf->ssl, c);
172         return;
173     }
174 
175     }
176 #endif
177 
178     ngx_mail_init_session(c);
179 }
180 
181 
182 #if (NGX_MAIL_SSL)
183 
184 void
ngx_mail_starttls_handler(ngx_event_t * rev)185 ngx_mail_starttls_handler(ngx_event_t *rev)
186 {
187     ngx_connection_t     *c;
188     ngx_mail_session_t   *s;
189     ngx_mail_ssl_conf_t  *sslcf;
190 
191     c = rev->data;
192     s = c->data;
193     s->starttls = 1;
194 
195     c->log->action = "in starttls state";
196 
197     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
198 
199     ngx_mail_ssl_init_connection(&sslcf->ssl, c);
200 }
201 
202 
203 static void
ngx_mail_ssl_init_connection(ngx_ssl_t * ssl,ngx_connection_t * c)204 ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)
205 {
206     ngx_mail_session_t        *s;
207     ngx_mail_core_srv_conf_t  *cscf;
208 
209     if (ngx_ssl_create_connection(ssl, c, 0) != NGX_OK) {
210         ngx_mail_close_connection(c);
211         return;
212     }
213 
214     if (ngx_ssl_handshake(c) == NGX_AGAIN) {
215 
216         s = c->data;
217 
218         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
219 
220         ngx_add_timer(c->read, cscf->timeout);
221 
222         c->ssl->handler = ngx_mail_ssl_handshake_handler;
223 
224         return;
225     }
226 
227     ngx_mail_ssl_handshake_handler(c);
228 }
229 
230 
231 static void
ngx_mail_ssl_handshake_handler(ngx_connection_t * c)232 ngx_mail_ssl_handshake_handler(ngx_connection_t *c)
233 {
234     ngx_mail_session_t        *s;
235     ngx_mail_core_srv_conf_t  *cscf;
236 
237     if (c->ssl->handshaked) {
238 
239         s = c->data;
240 
241         if (ngx_mail_verify_cert(s, c) != NGX_OK) {
242             return;
243         }
244 
245         if (s->starttls) {
246             cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
247 
248             c->read->handler = cscf->protocol->init_protocol;
249             c->write->handler = ngx_mail_send;
250 
251             cscf->protocol->init_protocol(c->read);
252 
253             return;
254         }
255 
256         c->read->ready = 0;
257 
258         ngx_mail_init_session(c);
259         return;
260     }
261 
262     ngx_mail_close_connection(c);
263 }
264 
265 
266 static ngx_int_t
ngx_mail_verify_cert(ngx_mail_session_t * s,ngx_connection_t * c)267 ngx_mail_verify_cert(ngx_mail_session_t *s, ngx_connection_t *c)
268 {
269     long                       rc;
270     X509                      *cert;
271     ngx_mail_ssl_conf_t       *sslcf;
272     ngx_mail_core_srv_conf_t  *cscf;
273 
274     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
275 
276     if (!sslcf->verify) {
277         return NGX_OK;
278     }
279 
280     rc = SSL_get_verify_result(c->ssl->connection);
281 
282     if (rc != X509_V_OK
283         && (sslcf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))
284     {
285         ngx_log_error(NGX_LOG_INFO, c->log, 0,
286                       "client SSL certificate verify error: (%l:%s)",
287                       rc, X509_verify_cert_error_string(rc));
288 
289         ngx_ssl_remove_cached_session(c->ssl->session_ctx,
290                                       (SSL_get0_session(c->ssl->connection)));
291 
292         cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
293 
294         s->out = cscf->protocol->cert_error;
295         s->quit = 1;
296 
297         c->write->handler = ngx_mail_send;
298 
299         ngx_mail_send(s->connection->write);
300         return NGX_ERROR;
301     }
302 
303     if (sslcf->verify == 1) {
304         cert = SSL_get_peer_certificate(c->ssl->connection);
305 
306         if (cert == NULL) {
307             ngx_log_error(NGX_LOG_INFO, c->log, 0,
308                           "client sent no required SSL certificate");
309 
310             ngx_ssl_remove_cached_session(c->ssl->session_ctx,
311                                        (SSL_get0_session(c->ssl->connection)));
312 
313             cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
314 
315             s->out = cscf->protocol->no_cert;
316             s->quit = 1;
317 
318             c->write->handler = ngx_mail_send;
319 
320             ngx_mail_send(s->connection->write);
321             return NGX_ERROR;
322         }
323 
324         X509_free(cert);
325     }
326 
327     return NGX_OK;
328 }
329 
330 #endif
331 
332 
333 static void
ngx_mail_init_session(ngx_connection_t * c)334 ngx_mail_init_session(ngx_connection_t *c)
335 {
336     ngx_mail_session_t        *s;
337     ngx_mail_core_srv_conf_t  *cscf;
338 
339     s = c->data;
340 
341     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
342 
343     s->protocol = cscf->protocol->type;
344 
345     s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_mail_max_module);
346     if (s->ctx == NULL) {
347         ngx_mail_session_internal_server_error(s);
348         return;
349     }
350 
351     c->write->handler = ngx_mail_send;
352 
353     cscf->protocol->init_session(s, c);
354 }
355 
356 
357 ngx_int_t
ngx_mail_salt(ngx_mail_session_t * s,ngx_connection_t * c,ngx_mail_core_srv_conf_t * cscf)358 ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c,
359     ngx_mail_core_srv_conf_t *cscf)
360 {
361     s->salt.data = ngx_pnalloc(c->pool,
362                                sizeof(" <18446744073709551616.@>" CRLF) - 1
363                                + NGX_TIME_T_LEN
364                                + cscf->server_name.len);
365     if (s->salt.data == NULL) {
366         return NGX_ERROR;
367     }
368 
369     s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF,
370                               ngx_random(), ngx_time(), &cscf->server_name)
371                   - s->salt.data;
372 
373     return NGX_OK;
374 }
375 
376 
377 #if (NGX_MAIL_SSL)
378 
379 ngx_int_t
ngx_mail_starttls_only(ngx_mail_session_t * s,ngx_connection_t * c)380 ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c)
381 {
382     ngx_mail_ssl_conf_t  *sslcf;
383 
384     if (c->ssl) {
385         return 0;
386     }
387 
388     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
389 
390     if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
391         return 1;
392     }
393 
394     return 0;
395 }
396 
397 #endif
398 
399 
400 ngx_int_t
ngx_mail_auth_plain(ngx_mail_session_t * s,ngx_connection_t * c,ngx_uint_t n)401 ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)
402 {
403     u_char     *p, *last;
404     ngx_str_t  *arg, plain;
405 
406     arg = s->args.elts;
407 
408 #if (NGX_DEBUG_MAIL_PASSWD)
409     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
410                    "mail auth plain: \"%V\"", &arg[n]);
411 #endif
412 
413     plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
414     if (plain.data == NULL) {
415         return NGX_ERROR;
416     }
417 
418     if (ngx_decode_base64(&plain, &arg[n]) != NGX_OK) {
419         ngx_log_error(NGX_LOG_INFO, c->log, 0,
420             "client sent invalid base64 encoding in AUTH PLAIN command");
421         return NGX_MAIL_PARSE_INVALID_COMMAND;
422     }
423 
424     p = plain.data;
425     last = p + plain.len;
426 
427     while (p < last && *p++) { /* void */ }
428 
429     if (p == last) {
430         ngx_log_error(NGX_LOG_INFO, c->log, 0,
431                       "client sent invalid login in AUTH PLAIN command");
432         return NGX_MAIL_PARSE_INVALID_COMMAND;
433     }
434 
435     s->login.data = p;
436 
437     while (p < last && *p) { p++; }
438 
439     if (p == last) {
440         ngx_log_error(NGX_LOG_INFO, c->log, 0,
441                       "client sent invalid password in AUTH PLAIN command");
442         return NGX_MAIL_PARSE_INVALID_COMMAND;
443     }
444 
445     s->login.len = p++ - s->login.data;
446 
447     s->passwd.len = last - p;
448     s->passwd.data = p;
449 
450 #if (NGX_DEBUG_MAIL_PASSWD)
451     ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
452                    "mail auth plain: \"%V\" \"%V\"", &s->login, &s->passwd);
453 #endif
454 
455     return NGX_DONE;
456 }
457 
458 
459 ngx_int_t
ngx_mail_auth_login_username(ngx_mail_session_t * s,ngx_connection_t * c,ngx_uint_t n)460 ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c,
461     ngx_uint_t n)
462 {
463     ngx_str_t  *arg;
464 
465     arg = s->args.elts;
466 
467     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
468                    "mail auth login username: \"%V\"", &arg[n]);
469 
470     s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
471     if (s->login.data == NULL) {
472         return NGX_ERROR;
473     }
474 
475     if (ngx_decode_base64(&s->login, &arg[n]) != NGX_OK) {
476         ngx_log_error(NGX_LOG_INFO, c->log, 0,
477             "client sent invalid base64 encoding in AUTH LOGIN command");
478         return NGX_MAIL_PARSE_INVALID_COMMAND;
479     }
480 
481     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
482                    "mail auth login username: \"%V\"", &s->login);
483 
484     return NGX_OK;
485 }
486 
487 
488 ngx_int_t
ngx_mail_auth_login_password(ngx_mail_session_t * s,ngx_connection_t * c)489 ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)
490 {
491     ngx_str_t  *arg;
492 
493     arg = s->args.elts;
494 
495 #if (NGX_DEBUG_MAIL_PASSWD)
496     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
497                    "mail auth login password: \"%V\"", &arg[0]);
498 #endif
499 
500     s->passwd.data = ngx_pnalloc(c->pool,
501                                  ngx_base64_decoded_length(arg[0].len));
502     if (s->passwd.data == NULL) {
503         return NGX_ERROR;
504     }
505 
506     if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) {
507         ngx_log_error(NGX_LOG_INFO, c->log, 0,
508             "client sent invalid base64 encoding in AUTH LOGIN command");
509         return NGX_MAIL_PARSE_INVALID_COMMAND;
510     }
511 
512 #if (NGX_DEBUG_MAIL_PASSWD)
513     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
514                    "mail auth login password: \"%V\"", &s->passwd);
515 #endif
516 
517     return NGX_DONE;
518 }
519 
520 
521 ngx_int_t
ngx_mail_auth_cram_md5_salt(ngx_mail_session_t * s,ngx_connection_t * c,char * prefix,size_t len)522 ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c,
523     char *prefix, size_t len)
524 {
525     u_char      *p;
526     ngx_str_t    salt;
527     ngx_uint_t   n;
528 
529     p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);
530     if (p == NULL) {
531         return NGX_ERROR;
532     }
533 
534     salt.data = ngx_cpymem(p, prefix, len);
535     s->salt.len -= 2;
536 
537     ngx_encode_base64(&salt, &s->salt);
538 
539     s->salt.len += 2;
540     n = len + salt.len;
541     p[n++] = CR; p[n++] = LF;
542 
543     s->out.len = n;
544     s->out.data = p;
545 
546     return NGX_OK;
547 }
548 
549 
550 ngx_int_t
ngx_mail_auth_cram_md5(ngx_mail_session_t * s,ngx_connection_t * c)551 ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c)
552 {
553     u_char     *p, *last;
554     ngx_str_t  *arg;
555 
556     arg = s->args.elts;
557 
558     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
559                    "mail auth cram-md5: \"%V\"", &arg[0]);
560 
561     s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
562     if (s->login.data == NULL) {
563         return NGX_ERROR;
564     }
565 
566     if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {
567         ngx_log_error(NGX_LOG_INFO, c->log, 0,
568             "client sent invalid base64 encoding in AUTH CRAM-MD5 command");
569         return NGX_MAIL_PARSE_INVALID_COMMAND;
570     }
571 
572     p = s->login.data;
573     last = p + s->login.len;
574 
575     while (p < last) {
576         if (*p++ == ' ') {
577             s->login.len = p - s->login.data - 1;
578             s->passwd.len = last - p;
579             s->passwd.data = p;
580             break;
581         }
582     }
583 
584     if (s->passwd.len != 32) {
585         ngx_log_error(NGX_LOG_INFO, c->log, 0,
586             "client sent invalid CRAM-MD5 hash in AUTH CRAM-MD5 command");
587         return NGX_MAIL_PARSE_INVALID_COMMAND;
588     }
589 
590     ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
591                    "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd);
592 
593     s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;
594 
595     return NGX_DONE;
596 }
597 
598 
599 ngx_int_t
ngx_mail_auth_external(ngx_mail_session_t * s,ngx_connection_t * c,ngx_uint_t n)600 ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c,
601     ngx_uint_t n)
602 {
603     ngx_str_t  *arg, external;
604 
605     arg = s->args.elts;
606 
607     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
608                    "mail auth external: \"%V\"", &arg[n]);
609 
610     external.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
611     if (external.data == NULL) {
612         return NGX_ERROR;
613     }
614 
615     if (ngx_decode_base64(&external, &arg[n]) != NGX_OK) {
616         ngx_log_error(NGX_LOG_INFO, c->log, 0,
617             "client sent invalid base64 encoding in AUTH EXTERNAL command");
618         return NGX_MAIL_PARSE_INVALID_COMMAND;
619     }
620 
621     s->login.len = external.len;
622     s->login.data = external.data;
623 
624     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
625                    "mail auth external: \"%V\"", &s->login);
626 
627     s->auth_method = NGX_MAIL_AUTH_EXTERNAL;
628 
629     return NGX_DONE;
630 }
631 
632 
633 void
ngx_mail_send(ngx_event_t * wev)634 ngx_mail_send(ngx_event_t *wev)
635 {
636     ngx_int_t                  n;
637     ngx_connection_t          *c;
638     ngx_mail_session_t        *s;
639     ngx_mail_core_srv_conf_t  *cscf;
640 
641     c = wev->data;
642     s = c->data;
643 
644     if (wev->timedout) {
645         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
646         c->timedout = 1;
647         ngx_mail_close_connection(c);
648         return;
649     }
650 
651     if (s->out.len == 0) {
652         if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
653             ngx_mail_close_connection(c);
654         }
655 
656         return;
657     }
658 
659     n = c->send(c, s->out.data, s->out.len);
660 
661     if (n > 0) {
662         s->out.data += n;
663         s->out.len -= n;
664 
665         if (s->out.len != 0) {
666             goto again;
667         }
668 
669         if (wev->timer_set) {
670             ngx_del_timer(wev);
671         }
672 
673         if (s->quit) {
674             ngx_mail_close_connection(c);
675             return;
676         }
677 
678         if (s->blocked) {
679             c->read->handler(c->read);
680         }
681 
682         return;
683     }
684 
685     if (n == NGX_ERROR) {
686         ngx_mail_close_connection(c);
687         return;
688     }
689 
690     /* n == NGX_AGAIN */
691 
692 again:
693 
694     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
695 
696     ngx_add_timer(c->write, cscf->timeout);
697 
698     if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
699         ngx_mail_close_connection(c);
700         return;
701     }
702 }
703 
704 
705 ngx_int_t
ngx_mail_read_command(ngx_mail_session_t * s,ngx_connection_t * c)706 ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c)
707 {
708     ssize_t                    n;
709     ngx_int_t                  rc;
710     ngx_str_t                  l;
711     ngx_mail_core_srv_conf_t  *cscf;
712 
713     n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);
714 
715     if (n == NGX_ERROR || n == 0) {
716         ngx_mail_close_connection(c);
717         return NGX_ERROR;
718     }
719 
720     if (n > 0) {
721         s->buffer->last += n;
722     }
723 
724     if (n == NGX_AGAIN) {
725         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
726             ngx_mail_session_internal_server_error(s);
727             return NGX_ERROR;
728         }
729 
730         if (s->buffer->pos == s->buffer->last) {
731             return NGX_AGAIN;
732         }
733     }
734 
735     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
736 
737     rc = cscf->protocol->parse_command(s);
738 
739     if (rc == NGX_AGAIN) {
740 
741         if (s->buffer->last < s->buffer->end) {
742             return rc;
743         }
744 
745         l.len = s->buffer->last - s->buffer->start;
746         l.data = s->buffer->start;
747 
748         ngx_log_error(NGX_LOG_INFO, c->log, 0,
749                       "client sent too long command \"%V\"", &l);
750 
751         s->quit = 1;
752 
753         return NGX_MAIL_PARSE_INVALID_COMMAND;
754     }
755 
756     if (rc == NGX_IMAP_NEXT || rc == NGX_MAIL_PARSE_INVALID_COMMAND) {
757         return rc;
758     }
759 
760     if (rc == NGX_ERROR) {
761         ngx_mail_close_connection(c);
762         return NGX_ERROR;
763     }
764 
765     return NGX_OK;
766 }
767 
768 
769 void
ngx_mail_auth(ngx_mail_session_t * s,ngx_connection_t * c)770 ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c)
771 {
772     s->args.nelts = 0;
773 
774     if (s->buffer->pos == s->buffer->last) {
775         s->buffer->pos = s->buffer->start;
776         s->buffer->last = s->buffer->start;
777     }
778 
779     s->state = 0;
780 
781     if (c->read->timer_set) {
782         ngx_del_timer(c->read);
783     }
784 
785     s->login_attempt++;
786 
787     ngx_mail_auth_http_init(s);
788 }
789 
790 
791 void
ngx_mail_session_internal_server_error(ngx_mail_session_t * s)792 ngx_mail_session_internal_server_error(ngx_mail_session_t *s)
793 {
794     ngx_mail_core_srv_conf_t  *cscf;
795 
796     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
797 
798     s->out = cscf->protocol->internal_server_error;
799     s->quit = 1;
800 
801     ngx_mail_send(s->connection->write);
802 }
803 
804 
805 void
ngx_mail_close_connection(ngx_connection_t * c)806 ngx_mail_close_connection(ngx_connection_t *c)
807 {
808     ngx_pool_t  *pool;
809 
810     ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
811                    "close mail connection: %d", c->fd);
812 
813 #if (NGX_MAIL_SSL)
814 
815     if (c->ssl) {
816         if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
817             c->ssl->handler = ngx_mail_close_connection;
818             return;
819         }
820     }
821 
822 #endif
823 
824 #if (NGX_STAT_STUB)
825     (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
826 #endif
827 
828     c->destroyed = 1;
829 
830     pool = c->pool;
831 
832     ngx_close_connection(c);
833 
834     ngx_destroy_pool(pool);
835 }
836 
837 
838 u_char *
ngx_mail_log_error(ngx_log_t * log,u_char * buf,size_t len)839 ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len)
840 {
841     u_char              *p;
842     ngx_mail_session_t  *s;
843     ngx_mail_log_ctx_t  *ctx;
844 
845     if (log->action) {
846         p = ngx_snprintf(buf, len, " while %s", log->action);
847         len -= p - buf;
848         buf = p;
849     }
850 
851     ctx = log->data;
852 
853     p = ngx_snprintf(buf, len, ", client: %V", ctx->client);
854     len -= p - buf;
855     buf = p;
856 
857     s = ctx->session;
858 
859     if (s == NULL) {
860         return p;
861     }
862 
863     p = ngx_snprintf(buf, len, "%s, server: %V",
864                      s->starttls ? " using starttls" : "",
865                      s->addr_text);
866     len -= p - buf;
867     buf = p;
868 
869     if (s->login.len == 0) {
870         return p;
871     }
872 
873     p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login);
874     len -= p - buf;
875     buf = p;
876 
877     if (s->proxy == NULL) {
878         return p;
879     }
880 
881     p = ngx_snprintf(buf, len, ", upstream: %V", s->proxy->upstream.name);
882 
883     return p;
884 }
885