Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_mail_handler.c
Go to the documentation of this file.
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
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;
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) {
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) {
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) {
136  return;
137  }
138 
139  ctx->client = &c->addr_text;
140  ctx->session = s;
141 
142  c->log->connection = c->number;
144  c->log->data = ctx;
145  c->log->action = "sending client greeting line";
146 
148 
149 #if (NGX_MAIL_SSL)
150  {
151  ngx_mail_ssl_conf_t *sslcf;
152 
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) {
168  "no \"ssl_certificate\" is defined "
169  "in server listening on SSL port");
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;
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 
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 {
211 
212  if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) {
214  return;
215  }
216 
217  if (ngx_ssl_handshake(c) == NGX_AGAIN) {
218 
219  s = c->data;
220 
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 {
239 
240  if (c->ssl->handshaked) {
241 
242  s = c->data;
243 
244  if (s->starttls) {
246 
247  c->read->handler = cscf->protocol->init_protocol;
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 
262 }
263 
264 #endif
265 
266 
267 static void
268 ngx_mail_init_session(ngx_connection_t *c)
269 {
272 
273  s = c->data;
274 
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) {
282  return;
283  }
284 
286 
287  cscf->protocol->init_session(s, c);
288 }
289 
290 
291 ngx_int_t
294 {
295  s->salt.data = ngx_pnalloc(c->pool,
296  sizeof(" <18446744073709551616.@>" CRLF) - 1
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 
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
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)
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) {
354  "client sent invalid base64 encoding in AUTH PLAIN command");
356  }
357 
358  p = plain.data;
359  last = p + plain.len;
360 
361  while (p < last && *p++) { /* void */ }
362 
363  if (p == last) {
365  "client sent invalid login in AUTH PLAIN command");
367  }
368 
369  s->login.data = p;
370 
371  while (p < last && *p) { p++; }
372 
373  if (p == last) {
375  "client sent invalid password in AUTH PLAIN 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)
386  "mail auth plain: \"%V\" \"%V\"", &s->login, &s->passwd);
387 #endif
388 
389  return NGX_DONE;
390 }
391 
392 
393 ngx_int_t
395  ngx_uint_t n)
396 {
397  ngx_str_t *arg;
398 
399  arg = s->args.elts;
400 
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) {
411  "client sent invalid base64 encoding in AUTH LOGIN command");
413  }
414 
416  "mail auth login username: \"%V\"", &s->login);
417 
418  return NGX_OK;
419 }
420 
421 
422 ngx_int_t
424 {
425  ngx_str_t *arg;
426 
427  arg = s->args.elts;
428 
429 #if (NGX_DEBUG_MAIL_PASSWD)
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) {
442  "client sent invalid base64 encoding in AUTH LOGIN command");
444  }
445 
446 #if (NGX_DEBUG_MAIL_PASSWD)
448  "mail auth login password: \"%V\"", &s->passwd);
449 #endif
450 
451  return NGX_DONE;
452 }
453 
454 
455 ngx_int_t
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
486 {
487  u_char *p, *last;
488  ngx_str_t *arg;
489 
490  arg = s->args.elts;
491 
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) {
502  "client sent invalid base64 encoding in AUTH CRAM-MD5 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) {
520  "client sent invalid CRAM-MD5 hash in AUTH CRAM-MD5 command");
522  }
523 
525  "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd);
526 
528 
529  return NGX_DONE;
530 }
531 
532 
533 void
535 {
536  ngx_int_t n;
537  ngx_connection_t *c;
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;
548  return;
549  }
550 
551  if (s->out.len == 0) {
552  if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
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) {
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) {
582  return;
583  }
584 
585  /* n == NGX_AGAIN */
586 
588 
589  ngx_add_timer(c->write, cscf->timeout);
590 
591  if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
593  return;
594  }
595 }
596 
597 
598 ngx_int_t
600 {
601  ssize_t n;
602  ngx_int_t rc;
603  ngx_str_t l;
605 
606  n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);
607 
608  if (n == NGX_ERROR || n == 0) {
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) {
620  return NGX_ERROR;
621  }
622 
623  return NGX_AGAIN;
624  }
625 
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 
640  "client sent too long command \"%V\"", &l);
641 
642  s->quit = 1;
643 
645  }
646 
647  if (rc == NGX_IMAP_NEXT || rc == NGX_MAIL_PARSE_INVALID_COMMAND) {
648  return rc;
649  }
650 
651  if (rc == NGX_ERROR) {
653  return NGX_ERROR;
654  }
655 
656  return NGX_OK;
657 }
658 
659 
660 void
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 
675 }
676 
677 
678 void
680 {
682 
684 
685  s->out = cscf->protocol->internal_server_error;
686  s->quit = 1;
687 
689 }
690 
691 
692 void
694 {
695  ngx_pool_t *pool;
696 
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 
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;
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 }