Annotation of embedaddon/nginx/src/mail/ngx_mail_handler.c, revision 1.1.1.1
1.1 misho 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: #endif
20:
21:
22: void
23: ngx_mail_init_connection(ngx_connection_t *c)
24: {
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
37:
38:
39: /* find the server configuration for the address:port */
40:
41: port = c->listening->servers;
42:
43: if (port->naddrs > 1) {
44:
45: /*
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.
49: *
50: * AcceptEx() already gave this address.
51: */
52:
53: if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
54: ngx_mail_close_connection(c);
55: return;
56: }
57:
58: sa = c->local_sockaddr;
59:
60: switch (sa->sa_family) {
61:
62: #if (NGX_HAVE_INET6)
63: case AF_INET6:
64: sin6 = (struct sockaddr_in6 *) sa;
65:
66: addr6 = port->addrs;
67:
68: /* the last address is "*" */
69:
70: for (i = 0; i < port->naddrs - 1; i++) {
71: if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
72: break;
73: }
74: }
75:
76: addr_conf = &addr6[i].conf;
77:
78: break;
79: #endif
80:
81: default: /* AF_INET */
82: sin = (struct sockaddr_in *) sa;
83:
84: addr = port->addrs;
85:
86: /* the last address is "*" */
87:
88: for (i = 0; i < port->naddrs - 1; i++) {
89: if (addr[i].addr == sin->sin_addr.s_addr) {
90: break;
91: }
92: }
93:
94: addr_conf = &addr[i].conf;
95:
96: break;
97: }
98:
99: } else {
100: switch (c->local_sockaddr->sa_family) {
101:
102: #if (NGX_HAVE_INET6)
103: case AF_INET6:
104: addr6 = port->addrs;
105: addr_conf = &addr6[0].conf;
106: break;
107: #endif
108:
109: default: /* AF_INET */
110: addr = port->addrs;
111: addr_conf = &addr[0].conf;
112: break;
113: }
114: }
115:
116: s = ngx_pcalloc(c->pool, sizeof(ngx_mail_session_t));
117: if (s == NULL) {
118: ngx_mail_close_connection(c);
119: return;
120: }
121:
122: s->main_conf = addr_conf->ctx->main_conf;
123: s->srv_conf = addr_conf->ctx->srv_conf;
124:
125: s->addr_text = &addr_conf->addr_text;
126:
127: c->data = s;
128: s->connection = c;
129:
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);
132:
133: ctx = ngx_palloc(c->pool, sizeof(ngx_mail_log_ctx_t));
134: if (ctx == NULL) {
135: ngx_mail_close_connection(c);
136: return;
137: }
138:
139: ctx->client = &c->addr_text;
140: ctx->session = s;
141:
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";
146:
147: c->log_error = NGX_ERROR_INFO;
148:
149: #if (NGX_MAIL_SSL)
150: {
151: ngx_mail_ssl_conf_t *sslcf;
152:
153: sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
154:
155: if (sslcf->enable) {
156: c->log->action = "SSL handshaking";
157:
158: ngx_mail_ssl_init_connection(&sslcf->ssl, c);
159: return;
160: }
161:
162: if (addr_conf->ssl) {
163:
164: c->log->action = "SSL handshaking";
165:
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;
172: }
173:
174: ngx_mail_ssl_init_connection(&sslcf->ssl, c);
175: return;
176: }
177:
178: }
179: #endif
180:
181: ngx_mail_init_session(c);
182: }
183:
184:
185: #if (NGX_MAIL_SSL)
186:
187: void
188: ngx_mail_starttls_handler(ngx_event_t *rev)
189: {
190: ngx_connection_t *c;
191: ngx_mail_session_t *s;
192: ngx_mail_ssl_conf_t *sslcf;
193:
194: c = rev->data;
195: s = c->data;
196: s->starttls = 1;
197:
198: c->log->action = "in starttls state";
199:
200: sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
201:
202: ngx_mail_ssl_init_connection(&sslcf->ssl, c);
203: }
204:
205:
206: static void
207: ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)
208: {
209: ngx_mail_session_t *s;
210: ngx_mail_core_srv_conf_t *cscf;
211:
212: if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) {
213: ngx_mail_close_connection(c);
214: return;
215: }
216:
217: if (ngx_ssl_handshake(c) == NGX_AGAIN) {
218:
219: s = c->data;
220:
221: cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
222:
223: ngx_add_timer(c->read, cscf->timeout);
224:
225: c->ssl->handler = ngx_mail_ssl_handshake_handler;
226:
227: return;
228: }
229:
230: ngx_mail_ssl_handshake_handler(c);
231: }
232:
233:
234: static void
235: ngx_mail_ssl_handshake_handler(ngx_connection_t *c)
236: {
237: ngx_mail_session_t *s;
238: ngx_mail_core_srv_conf_t *cscf;
239:
240: if (c->ssl->handshaked) {
241:
242: s = c->data;
243:
244: if (s->starttls) {
245: cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
246:
247: c->read->handler = cscf->protocol->init_protocol;
248: c->write->handler = ngx_mail_send;
249:
250: cscf->protocol->init_protocol(c->read);
251:
252: return;
253: }
254:
255: c->read->ready = 0;
256:
257: ngx_mail_init_session(c);
258: return;
259: }
260:
261: ngx_mail_close_connection(c);
262: }
263:
264: #endif
265:
266:
267: static void
268: ngx_mail_init_session(ngx_connection_t *c)
269: {
270: ngx_mail_session_t *s;
271: ngx_mail_core_srv_conf_t *cscf;
272:
273: s = c->data;
274:
275: cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
276:
277: s->protocol = cscf->protocol->type;
278:
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;
283: }
284:
285: c->write->handler = ngx_mail_send;
286:
287: cscf->protocol->init_session(s, c);
288: }
289:
290:
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)
294: {
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;
301: }
302:
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;
306:
307: return NGX_OK;
308: }
309:
310:
311: #if (NGX_MAIL_SSL)
312:
313: ngx_int_t
314: ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c)
315: {
316: ngx_mail_ssl_conf_t *sslcf;
317:
318: if (c->ssl) {
319: return 0;
320: }
321:
322: sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
323:
324: if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
325: return 1;
326: }
327:
328: return 0;
329: }
330:
331: #endif
332:
333:
334: ngx_int_t
335: ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)
336: {
337: u_char *p, *last;
338: ngx_str_t *arg, plain;
339:
340: arg = s->args.elts;
341:
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
346:
347: plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
348: if (plain.data == NULL) {
349: return NGX_ERROR;
350: }
351:
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;
356: }
357:
358: p = plain.data;
359: last = p + plain.len;
360:
361: while (p < last && *p++) { /* void */ }
362:
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;
367: }
368:
369: s->login.data = p;
370:
371: while (p < last && *p) { p++; }
372:
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;
377: }
378:
379: s->login.len = p++ - s->login.data;
380:
381: s->passwd.len = last - p;
382: s->passwd.data = p;
383:
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
388:
389: return NGX_DONE;
390: }
391:
392:
393: ngx_int_t
394: ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c,
395: ngx_uint_t n)
396: {
397: ngx_str_t *arg;
398:
399: arg = s->args.elts;
400:
401: ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
402: "mail auth login username: \"%V\"", &arg[n]);
403:
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;
407: }
408:
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;
413: }
414:
415: ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
416: "mail auth login username: \"%V\"", &s->login);
417:
418: return NGX_OK;
419: }
420:
421:
422: ngx_int_t
423: ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)
424: {
425: ngx_str_t *arg;
426:
427: arg = s->args.elts;
428:
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
433:
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;
438: }
439:
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;
444: }
445:
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
450:
451: return NGX_DONE;
452: }
453:
454:
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)
458: {
459: u_char *p;
460: ngx_str_t salt;
461: ngx_uint_t n;
462:
463: p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);
464: if (p == NULL) {
465: return NGX_ERROR;
466: }
467:
468: salt.data = ngx_cpymem(p, prefix, len);
469: s->salt.len -= 2;
470:
471: ngx_encode_base64(&salt, &s->salt);
472:
473: s->salt.len += 2;
474: n = len + salt.len;
475: p[n++] = CR; p[n++] = LF;
476:
477: s->out.len = n;
478: s->out.data = p;
479:
480: return NGX_OK;
481: }
482:
483:
484: ngx_int_t
485: ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c)
486: {
487: u_char *p, *last;
488: ngx_str_t *arg;
489:
490: arg = s->args.elts;
491:
492: ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
493: "mail auth cram-md5: \"%V\"", &arg[0]);
494:
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;
498: }
499:
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;
504: }
505:
506: p = s->login.data;
507: last = p + s->login.len;
508:
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;
515: }
516: }
517:
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;
522: }
523:
524: ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
525: "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd);
526:
527: s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;
528:
529: return NGX_DONE;
530: }
531:
532:
533: void
534: ngx_mail_send(ngx_event_t *wev)
535: {
536: ngx_int_t n;
537: ngx_connection_t *c;
538: ngx_mail_session_t *s;
539: ngx_mail_core_srv_conf_t *cscf;
540:
541: c = wev->data;
542: s = c->data;
543:
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;
549: }
550:
551: if (s->out.len == 0) {
552: if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
553: ngx_mail_close_connection(c);
554: }
555:
556: return;
557: }
558:
559: n = c->send(c, s->out.data, s->out.len);
560:
561: if (n > 0) {
562: s->out.len -= n;
563:
564: if (wev->timer_set) {
565: ngx_del_timer(wev);
566: }
567:
568: if (s->quit) {
569: ngx_mail_close_connection(c);
570: return;
571: }
572:
573: if (s->blocked) {
574: c->read->handler(c->read);
575: }
576:
577: return;
578: }
579:
580: if (n == NGX_ERROR) {
581: ngx_mail_close_connection(c);
582: return;
583: }
584:
585: /* n == NGX_AGAIN */
586:
587: cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
588:
589: ngx_add_timer(c->write, cscf->timeout);
590:
591: if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
592: ngx_mail_close_connection(c);
593: return;
594: }
595: }
596:
597:
598: ngx_int_t
599: ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c)
600: {
601: ssize_t n;
602: ngx_int_t rc;
603: ngx_str_t l;
604: ngx_mail_core_srv_conf_t *cscf;
605:
606: n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);
607:
608: if (n == NGX_ERROR || n == 0) {
609: ngx_mail_close_connection(c);
610: return NGX_ERROR;
611: }
612:
613: if (n > 0) {
614: s->buffer->last += n;
615: }
616:
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;
621: }
622:
623: return NGX_AGAIN;
624: }
625:
626: cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
627:
628: rc = cscf->protocol->parse_command(s);
629:
630: if (rc == NGX_AGAIN) {
631:
632: if (s->buffer->last < s->buffer->end) {
633: return rc;
634: }
635:
636: l.len = s->buffer->last - s->buffer->start;
637: l.data = s->buffer->start;
638:
639: ngx_log_error(NGX_LOG_INFO, c->log, 0,
640: "client sent too long command \"%V\"", &l);
641:
642: s->quit = 1;
643:
644: return NGX_MAIL_PARSE_INVALID_COMMAND;
645: }
646:
647: if (rc == NGX_IMAP_NEXT || rc == NGX_MAIL_PARSE_INVALID_COMMAND) {
648: return rc;
649: }
650:
651: if (rc == NGX_ERROR) {
652: ngx_mail_close_connection(c);
653: return NGX_ERROR;
654: }
655:
656: return NGX_OK;
657: }
658:
659:
660: void
661: ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c)
662: {
663: s->args.nelts = 0;
664: s->buffer->pos = s->buffer->start;
665: s->buffer->last = s->buffer->start;
666: s->state = 0;
667:
668: if (c->read->timer_set) {
669: ngx_del_timer(c->read);
670: }
671:
672: s->login_attempt++;
673:
674: ngx_mail_auth_http_init(s);
675: }
676:
677:
678: void
679: ngx_mail_session_internal_server_error(ngx_mail_session_t *s)
680: {
681: ngx_mail_core_srv_conf_t *cscf;
682:
683: cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
684:
685: s->out = cscf->protocol->internal_server_error;
686: s->quit = 1;
687:
688: ngx_mail_send(s->connection->write);
689: }
690:
691:
692: void
693: ngx_mail_close_connection(ngx_connection_t *c)
694: {
695: ngx_pool_t *pool;
696:
697: ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
698: "close mail connection: %d", c->fd);
699:
700: #if (NGX_MAIL_SSL)
701:
702: if (c->ssl) {
703: if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
704: c->ssl->handler = ngx_mail_close_connection;
705: return;
706: }
707: }
708:
709: #endif
710:
711: #if (NGX_STAT_STUB)
712: (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
713: #endif
714:
715: c->destroyed = 1;
716:
717: pool = c->pool;
718:
719: ngx_close_connection(c);
720:
721: ngx_destroy_pool(pool);
722: }
723:
724:
725: u_char *
726: ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len)
727: {
728: u_char *p;
729: ngx_mail_session_t *s;
730: ngx_mail_log_ctx_t *ctx;
731:
732: if (log->action) {
733: p = ngx_snprintf(buf, len, " while %s", log->action);
734: len -= p - buf;
735: buf = p;
736: }
737:
738: ctx = log->data;
739:
740: p = ngx_snprintf(buf, len, ", client: %V", ctx->client);
741: len -= p - buf;
742: buf = p;
743:
744: s = ctx->session;
745:
746: if (s == NULL) {
747: return p;
748: }
749:
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;
755:
756: if (s->login.len == 0) {
757: return p;
758: }
759:
760: p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login);
761: len -= p - buf;
762: buf = p;
763:
764: if (s->proxy == NULL) {
765: return p;
766: }
767:
768: p = ngx_snprintf(buf, len, ", upstream: %V", s->proxy->upstream.name);
769:
770: return p;
771: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>