Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_mail_smtp_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 #include <ngx_mail_smtp_module.h>
13 
14 
15 static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx);
16 static void ngx_mail_smtp_resolve_name(ngx_event_t *rev);
17 static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx);
18 static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c);
19 static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev);
20 static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s,
21  ngx_connection_t *c);
22 
23 static ngx_int_t ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c);
24 static ngx_int_t ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c);
25 static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c);
26 static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s,
27  ngx_connection_t *c);
28 static ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c);
29 static ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c);
30 
31 static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s,
32  ngx_connection_t *c, char *err);
33 static void ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s,
34  ngx_connection_t *c, char *err);
35 
36 
37 static u_char smtp_ok[] = "250 2.0.0 OK" CRLF;
38 static u_char smtp_bye[] = "221 2.0.0 Bye" CRLF;
39 static u_char smtp_starttls[] = "220 2.0.0 Start TLS" CRLF;
40 static u_char smtp_next[] = "334 " CRLF;
41 static u_char smtp_username[] = "334 VXNlcm5hbWU6" CRLF;
42 static u_char smtp_password[] = "334 UGFzc3dvcmQ6" CRLF;
43 static u_char smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF;
44 static u_char smtp_invalid_pipelining[] =
45  "503 5.5.0 Improper use of SMTP command pipelining" CRLF;
46 static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF;
47 static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF;
48 static u_char smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF;
49 
50 
51 static ngx_str_t smtp_unavailable = ngx_string("[UNAVAILABLE]");
52 static ngx_str_t smtp_tempunavail = ngx_string("[TEMPUNAVAIL]");
53 
54 
55 void
57 {
58  struct sockaddr_in *sin;
59  ngx_resolver_ctx_t *ctx;
61 
63 
64  if (cscf->resolver == NULL) {
65  s->host = smtp_unavailable;
66  ngx_mail_smtp_greeting(s, c);
67  return;
68  }
69 
70  if (c->sockaddr->sa_family != AF_INET) {
71  s->host = smtp_tempunavail;
72  ngx_mail_smtp_greeting(s, c);
73  return;
74  }
75 
76  c->log->action = "in resolving client address";
77 
78  ctx = ngx_resolve_start(cscf->resolver, NULL);
79  if (ctx == NULL) {
81  return;
82  }
83 
84  /* AF_INET only */
85 
86  sin = (struct sockaddr_in *) c->sockaddr;
87 
88  ctx->addr = sin->sin_addr.s_addr;
89  ctx->handler = ngx_mail_smtp_resolve_addr_handler;
90  ctx->data = s;
91  ctx->timeout = cscf->resolver_timeout;
92 
93  if (ngx_resolve_addr(ctx) != NGX_OK) {
95  }
96 }
97 
98 
99 static void
100 ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx)
101 {
102  ngx_connection_t *c;
104 
105  s = ctx->data;
106  c = s->connection;
107 
108  if (ctx->state) {
110  "%V could not be resolved (%i: %s)",
111  &c->addr_text, ctx->state,
113 
114  if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
115  s->host = smtp_unavailable;
116 
117  } else {
118  s->host = smtp_tempunavail;
119  }
120 
122 
123  ngx_mail_smtp_greeting(s, s->connection);
124 
125  return;
126  }
127 
128  c->log->action = "in resolving client hostname";
129 
130  s->host.data = ngx_pstrdup(c->pool, &ctx->name);
131  if (s->host.data == NULL) {
134  return;
135  }
136 
137  s->host.len = ctx->name.len;
138 
140 
142  "address resolved: %V", &s->host);
143 
144  c->read->handler = ngx_mail_smtp_resolve_name;
145 
147 }
148 
149 
150 static void
151 ngx_mail_smtp_resolve_name(ngx_event_t *rev)
152 {
153  ngx_connection_t *c;
155  ngx_resolver_ctx_t *ctx;
157 
158  c = rev->data;
159  s = c->data;
160 
162 
163  ctx = ngx_resolve_start(cscf->resolver, NULL);
164  if (ctx == NULL) {
166  return;
167  }
168 
169  ctx->name = s->host;
170  ctx->type = NGX_RESOLVE_A;
171  ctx->handler = ngx_mail_smtp_resolve_name_handler;
172  ctx->data = s;
173  ctx->timeout = cscf->resolver_timeout;
174 
175  if (ngx_resolve_name(ctx) != NGX_OK) {
177  }
178 }
179 
180 
181 static void
182 ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx)
183 {
184  in_addr_t addr;
185  ngx_uint_t i;
186  ngx_connection_t *c;
187  struct sockaddr_in *sin;
189 
190  s = ctx->data;
191  c = s->connection;
192 
193  if (ctx->state) {
195  "\"%V\" could not be resolved (%i: %s)",
196  &ctx->name, ctx->state,
198 
199  if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
200  s->host = smtp_unavailable;
201 
202  } else {
203  s->host = smtp_tempunavail;
204  }
205 
206  } else {
207 
208  /* AF_INET only */
209 
210  sin = (struct sockaddr_in *) c->sockaddr;
211 
212  for (i = 0; i < ctx->naddrs; i++) {
213 
214  addr = ctx->addrs[i];
215 
217  "name was resolved to %ud.%ud.%ud.%ud",
218  (ntohl(addr) >> 24) & 0xff,
219  (ntohl(addr) >> 16) & 0xff,
220  (ntohl(addr) >> 8) & 0xff,
221  ntohl(addr) & 0xff);
222 
223  if (addr == sin->sin_addr.s_addr) {
224  goto found;
225  }
226  }
227 
228  s->host = smtp_unavailable;
229  }
230 
231 found:
232 
234 
235  ngx_mail_smtp_greeting(s, c);
236 }
237 
238 
239 static void
240 ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c)
241 {
242  ngx_msec_t timeout;
245 
247  "smtp greeting for \"%V\"", &s->host);
248 
251 
252  timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout;
253  ngx_add_timer(c->read, timeout);
254 
255  if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
257  }
258 
259  if (sscf->greeting_delay) {
260  c->read->handler = ngx_mail_smtp_invalid_pipelining;
261  return;
262  }
263 
265 
266  s->out = sscf->greeting;
267 
268  ngx_mail_send(c->write);
269 }
270 
271 
272 static void
273 ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev)
274 {
275  ngx_connection_t *c;
279 
280  c = rev->data;
281  s = c->data;
282 
283  c->log->action = "in delay pipelining state";
284 
285  if (rev->timedout) {
286 
287  ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "delay greeting");
288 
289  rev->timedout = 0;
290 
292 
294 
295  ngx_add_timer(c->read, cscf->timeout);
296 
297  if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
299  return;
300  }
301 
303 
304  s->out = sscf->greeting;
305 
306  } else {
307 
308  ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "invalid pipelining");
309 
310  if (s->buffer == NULL) {
311  if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {
312  return;
313  }
314  }
315 
316  if (ngx_mail_smtp_discard_command(s, c,
317  "client was rejected before greeting: \"%V\"")
318  != NGX_OK)
319  {
320  return;
321  }
322 
323  ngx_str_set(&s->out, smtp_invalid_pipelining);
324  }
325 
326  ngx_mail_send(c->write);
327 }
328 
329 
330 void
332 {
333  ngx_connection_t *c;
335 
336  c = rev->data;
337 
338  c->log->action = "in auth state";
339 
340  if (rev->timedout) {
341  ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
342  c->timedout = 1;
344  return;
345  }
346 
347  s = c->data;
348 
349  if (s->buffer == NULL) {
350  if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {
351  return;
352  }
353  }
354 
357 
359 }
360 
361 
362 static ngx_int_t
363 ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c)
364 {
366 
367  if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) {
369  return NGX_ERROR;
370  }
371 
373 
375  if (s->buffer == NULL) {
377  return NGX_ERROR;
378  }
379 
380  return NGX_OK;
381 }
382 
383 
384 void
386 {
387  ngx_int_t rc;
388  ngx_connection_t *c;
390 
391  c = rev->data;
392  s = c->data;
393 
394  ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state");
395 
396  if (rev->timedout) {
397  ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
398  c->timedout = 1;
400  return;
401  }
402 
403  if (s->out.len) {
404  ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy");
405  s->blocked = 1;
406  return;
407  }
408 
409  s->blocked = 0;
410 
411  rc = ngx_mail_read_command(s, c);
412 
413  if (rc == NGX_AGAIN || rc == NGX_ERROR) {
414  return;
415  }
416 
417  ngx_str_set(&s->out, smtp_ok);
418 
419  if (rc == NGX_OK) {
420  switch (s->mail_state) {
421 
422  case ngx_smtp_start:
423 
424  switch (s->command) {
425 
426  case NGX_SMTP_HELO:
427  case NGX_SMTP_EHLO:
428  rc = ngx_mail_smtp_helo(s, c);
429  break;
430 
431  case NGX_SMTP_AUTH:
432  rc = ngx_mail_smtp_auth(s, c);
433  break;
434 
435  case NGX_SMTP_QUIT:
436  s->quit = 1;
437  ngx_str_set(&s->out, smtp_bye);
438  break;
439 
440  case NGX_SMTP_MAIL:
441  rc = ngx_mail_smtp_mail(s, c);
442  break;
443 
444  case NGX_SMTP_RCPT:
445  rc = ngx_mail_smtp_rcpt(s, c);
446  break;
447 
448  case NGX_SMTP_RSET:
449  rc = ngx_mail_smtp_rset(s, c);
450  break;
451 
452  case NGX_SMTP_NOOP:
453  break;
454 
455  case NGX_SMTP_STARTTLS:
456  rc = ngx_mail_smtp_starttls(s, c);
457  ngx_str_set(&s->out, smtp_starttls);
458  break;
459 
460  default:
462  break;
463  }
464 
465  break;
466 
468  rc = ngx_mail_auth_login_username(s, c, 0);
469 
470  ngx_str_set(&s->out, smtp_password);
472  break;
473 
475  rc = ngx_mail_auth_login_password(s, c);
476  break;
477 
478  case ngx_smtp_auth_plain:
479  rc = ngx_mail_auth_plain(s, c, 0);
480  break;
481 
483  rc = ngx_mail_auth_cram_md5(s, c);
484  break;
485  }
486  }
487 
488  switch (rc) {
489 
490  case NGX_DONE:
491  ngx_mail_auth(s, c);
492  return;
493 
494  case NGX_ERROR:
496  return;
497 
500  s->state = 0;
501  ngx_str_set(&s->out, smtp_invalid_command);
502 
503  /* fall through */
504 
505  case NGX_OK:
506  s->args.nelts = 0;
507  s->buffer->pos = s->buffer->start;
508  s->buffer->last = s->buffer->start;
509 
510  if (s->state) {
511  s->arg_start = s->buffer->start;
512  }
513 
514  ngx_mail_send(c->write);
515  }
516 }
517 
518 
519 static ngx_int_t
520 ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c)
521 {
522  ngx_str_t *arg;
524 
525  if (s->args.nelts != 1) {
526  ngx_str_set(&s->out, smtp_invalid_argument);
527  s->state = 0;
528  return NGX_OK;
529  }
530 
531  arg = s->args.elts;
532 
533  s->smtp_helo.len = arg[0].len;
534 
535  s->smtp_helo.data = ngx_pnalloc(c->pool, arg[0].len);
536  if (s->smtp_helo.data == NULL) {
537  return NGX_ERROR;
538  }
539 
540  ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len);
541 
542  ngx_str_null(&s->smtp_from);
543  ngx_str_null(&s->smtp_to);
544 
546 
547  if (s->command == NGX_SMTP_HELO) {
548  s->out = sscf->server_name;
549 
550  } else {
551  s->esmtp = 1;
552 
553 #if (NGX_MAIL_SSL)
554 
555  if (c->ssl == NULL) {
556  ngx_mail_ssl_conf_t *sslcf;
557 
559 
560  if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
561  s->out = sscf->starttls_capability;
562  return NGX_OK;
563  }
564 
565  if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
566  s->out = sscf->starttls_only_capability;
567  return NGX_OK;
568  }
569  }
570 #endif
571 
572  s->out = sscf->capability;
573  }
574 
575  return NGX_OK;
576 }
577 
578 
579 static ngx_int_t
580 ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c)
581 {
582  ngx_int_t rc;
585 
586 #if (NGX_MAIL_SSL)
587  if (ngx_mail_starttls_only(s, c)) {
589  }
590 #endif
591 
592  if (s->args.nelts == 0) {
593  ngx_str_set(&s->out, smtp_invalid_argument);
594  s->state = 0;
595  return NGX_OK;
596  }
597 
598  rc = ngx_mail_auth_parse(s, c);
599 
600  switch (rc) {
601 
602  case NGX_MAIL_AUTH_LOGIN:
603 
604  ngx_str_set(&s->out, smtp_username);
606 
607  return NGX_OK;
608 
610 
611  ngx_str_set(&s->out, smtp_password);
613 
614  return ngx_mail_auth_login_username(s, c, 1);
615 
616  case NGX_MAIL_AUTH_PLAIN:
617 
618  ngx_str_set(&s->out, smtp_next);
620 
621  return NGX_OK;
622 
624 
626 
629  }
630 
631  if (s->salt.data == NULL) {
633 
634  if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
635  return NGX_ERROR;
636  }
637  }
638 
639  if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK) {
641  return NGX_OK;
642  }
643 
644  return NGX_ERROR;
645  }
646 
647  return rc;
648 }
649 
650 
651 static ngx_int_t
652 ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c)
653 {
654  u_char ch;
655  ngx_str_t l;
656  ngx_uint_t i;
658 
660 
661  if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) {
662  ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\"");
663  ngx_str_set(&s->out, smtp_auth_required);
664  return NGX_OK;
665  }
666 
667  /* auth none */
668 
669  if (s->smtp_from.len) {
670  ngx_str_set(&s->out, smtp_bad_sequence);
671  return NGX_OK;
672  }
673 
674  l.len = s->buffer->last - s->buffer->start;
675  l.data = s->buffer->start;
676 
677  for (i = 0; i < l.len; i++) {
678  ch = l.data[i];
679 
680  if (ch != CR && ch != LF) {
681  continue;
682  }
683 
684  l.data[i] = ' ';
685  }
686 
687  while (i) {
688  if (l.data[i - 1] != ' ') {
689  break;
690  }
691 
692  i--;
693  }
694 
695  l.len = i;
696 
697  s->smtp_from.len = l.len;
698 
699  s->smtp_from.data = ngx_pnalloc(c->pool, l.len);
700  if (s->smtp_from.data == NULL) {
701  return NGX_ERROR;
702  }
703 
704  ngx_memcpy(s->smtp_from.data, l.data, l.len);
705 
707  "smtp mail from:\"%V\"", &s->smtp_from);
708 
709  ngx_str_set(&s->out, smtp_ok);
710 
711  return NGX_OK;
712 }
713 
714 
715 static ngx_int_t
716 ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c)
717 {
718  u_char ch;
719  ngx_str_t l;
720  ngx_uint_t i;
721 
722  if (s->smtp_from.len == 0) {
723  ngx_str_set(&s->out, smtp_bad_sequence);
724  return NGX_OK;
725  }
726 
727  l.len = s->buffer->last - s->buffer->start;
728  l.data = s->buffer->start;
729 
730  for (i = 0; i < l.len; i++) {
731  ch = l.data[i];
732 
733  if (ch != CR && ch != LF) {
734  continue;
735  }
736 
737  l.data[i] = ' ';
738  }
739 
740  while (i) {
741  if (l.data[i - 1] != ' ') {
742  break;
743  }
744 
745  i--;
746  }
747 
748  l.len = i;
749 
750  s->smtp_to.len = l.len;
751 
752  s->smtp_to.data = ngx_pnalloc(c->pool, l.len);
753  if (s->smtp_to.data == NULL) {
754  return NGX_ERROR;
755  }
756 
757  ngx_memcpy(s->smtp_to.data, l.data, l.len);
758 
760  "smtp rcpt to:\"%V\"", &s->smtp_to);
761 
763 
764  return NGX_DONE;
765 }
766 
767 
768 static ngx_int_t
769 ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c)
770 {
771  ngx_str_null(&s->smtp_from);
772  ngx_str_null(&s->smtp_to);
773  ngx_str_set(&s->out, smtp_ok);
774 
775  return NGX_OK;
776 }
777 
778 
779 static ngx_int_t
780 ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c)
781 {
782 #if (NGX_MAIL_SSL)
783  ngx_mail_ssl_conf_t *sslcf;
784 
785  if (c->ssl == NULL) {
787  if (sslcf->starttls) {
788 
789  /*
790  * RFC3207 requires us to discard any knowledge
791  * obtained from client before STARTTLS.
792  */
793 
794  ngx_str_null(&s->smtp_helo);
795  ngx_str_null(&s->smtp_from);
796  ngx_str_null(&s->smtp_to);
797 
798  c->read->handler = ngx_mail_starttls_handler;
799  return NGX_OK;
800  }
801  }
802 
803 #endif
804 
806 }
807 
808 
809 static ngx_int_t
810 ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c,
811  char *err)
812 {
813  ssize_t n;
814 
815  n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);
816 
817  if (n == NGX_ERROR || n == 0) {
819  return NGX_ERROR;
820  }
821 
822  if (n > 0) {
823  s->buffer->last += n;
824  }
825 
826  if (n == NGX_AGAIN) {
827  if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
829  return NGX_ERROR;
830  }
831 
832  return NGX_AGAIN;
833  }
834 
835  ngx_mail_smtp_log_rejected_command(s, c, err);
836 
837  s->buffer->pos = s->buffer->start;
838  s->buffer->last = s->buffer->start;
839 
840  return NGX_OK;
841 }
842 
843 
844 static void
845 ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, ngx_connection_t *c,
846  char *err)
847 {
848  u_char ch;
849  ngx_str_t cmd;
850  ngx_uint_t i;
851 
852  if (c->log->log_level < NGX_LOG_INFO) {
853  return;
854  }
855 
856  cmd.len = s->buffer->last - s->buffer->start;
857  cmd.data = s->buffer->start;
858 
859  for (i = 0; i < cmd.len; i++) {
860  ch = cmd.data[i];
861 
862  if (ch != CR && ch != LF) {
863  continue;
864  }
865 
866  cmd.data[i] = '_';
867  }
868 
869  cmd.len = i;
870 
871  ngx_log_error(NGX_LOG_INFO, c->log, 0, err, &cmd);
872 }