Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_mail_auth_http_module.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_event_connect.h>
12 #include <ngx_mail.h>
13 
14 
15 typedef struct {
17 
19 
23 
25 
26  u_char *file;
29 
30 
32 
35 
40 
42 
44 
46  u_char *header_name_end;
47  u_char *header_start;
48  u_char *header_end;
49 
55 
56  time_t sleep;
57 
59 };
60 
61 
62 static void ngx_mail_auth_http_write_handler(ngx_event_t *wev);
63 static void ngx_mail_auth_http_read_handler(ngx_event_t *rev);
64 static void ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
66 static void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
68 static void ngx_mail_auth_sleep_handler(ngx_event_t *rev);
69 static ngx_int_t ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
71 static void ngx_mail_auth_http_block_read(ngx_event_t *rev);
72 static void ngx_mail_auth_http_dummy_handler(ngx_event_t *ev);
73 static ngx_buf_t *ngx_mail_auth_http_create_request(ngx_mail_session_t *s,
75 static ngx_int_t ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text,
76  ngx_str_t *escaped);
77 
78 static void *ngx_mail_auth_http_create_conf(ngx_conf_t *cf);
79 static char *ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent,
80  void *child);
81 static char *ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
82 static char *ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd,
83  void *conf);
84 
85 
86 static ngx_command_t ngx_mail_auth_http_commands[] = {
87 
88  { ngx_string("auth_http"),
90  ngx_mail_auth_http,
92  0,
93  NULL },
94 
95  { ngx_string("auth_http_timeout"),
99  offsetof(ngx_mail_auth_http_conf_t, timeout),
100  NULL },
101 
102  { ngx_string("auth_http_header"),
104  ngx_mail_auth_http_header,
106  0,
107  NULL },
108 
110 };
111 
112 
113 static ngx_mail_module_t ngx_mail_auth_http_module_ctx = {
114  NULL, /* protocol */
115 
116  NULL, /* create main configuration */
117  NULL, /* init main configuration */
118 
119  ngx_mail_auth_http_create_conf, /* create server configuration */
120  ngx_mail_auth_http_merge_conf /* merge server configuration */
121 };
122 
123 
126  &ngx_mail_auth_http_module_ctx, /* module context */
127  ngx_mail_auth_http_commands, /* module directives */
128  NGX_MAIL_MODULE, /* module type */
129  NULL, /* init master */
130  NULL, /* init module */
131  NULL, /* init process */
132  NULL, /* init thread */
133  NULL, /* exit thread */
134  NULL, /* exit process */
135  NULL, /* exit master */
137 };
138 
139 
140 static ngx_str_t ngx_mail_auth_http_method[] = {
141  ngx_string("plain"),
142  ngx_string("plain"),
143  ngx_string("plain"),
144  ngx_string("apop"),
145  ngx_string("cram-md5"),
146  ngx_string("none")
147 };
148 
149 static ngx_str_t ngx_mail_smtp_errcode = ngx_string("535 5.7.0");
150 
151 
152 void
154 {
155  ngx_int_t rc;
156  ngx_pool_t *pool;
159 
160  s->connection->log->action = "in http auth state";
161 
162  pool = ngx_create_pool(2048, s->connection->log);
163  if (pool == NULL) {
165  return;
166  }
167 
168  ctx = ngx_pcalloc(pool, sizeof(ngx_mail_auth_http_ctx_t));
169  if (ctx == NULL) {
170  ngx_destroy_pool(pool);
172  return;
173  }
174 
175  ctx->pool = pool;
176 
177  ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);
178 
179  ctx->request = ngx_mail_auth_http_create_request(s, pool, ahcf);
180  if (ctx->request == NULL) {
181  ngx_destroy_pool(ctx->pool);
183  return;
184  }
185 
186  ngx_mail_set_ctx(s, ctx, ngx_mail_auth_http_module);
187 
188  ctx->peer.sockaddr = ahcf->peer->sockaddr;
189  ctx->peer.socklen = ahcf->peer->socklen;
190  ctx->peer.name = &ahcf->peer->name;
191  ctx->peer.get = ngx_event_get_peer;
192  ctx->peer.log = s->connection->log;
194 
195  rc = ngx_event_connect_peer(&ctx->peer);
196 
197  if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
198  if (ctx->peer.connection) {
200  }
201 
202  ngx_destroy_pool(ctx->pool);
204  return;
205  }
206 
207  ctx->peer.connection->data = s;
208  ctx->peer.connection->pool = s->connection->pool;
209 
210  s->connection->read->handler = ngx_mail_auth_http_block_read;
211  ctx->peer.connection->read->handler = ngx_mail_auth_http_read_handler;
212  ctx->peer.connection->write->handler = ngx_mail_auth_http_write_handler;
213 
214  ctx->handler = ngx_mail_auth_http_ignore_status_line;
215 
216  ngx_add_timer(ctx->peer.connection->read, ahcf->timeout);
217  ngx_add_timer(ctx->peer.connection->write, ahcf->timeout);
218 
219  if (rc == NGX_OK) {
220  ngx_mail_auth_http_write_handler(ctx->peer.connection->write);
221  return;
222  }
223 }
224 
225 
226 static void
227 ngx_mail_auth_http_write_handler(ngx_event_t *wev)
228 {
229  ssize_t n, size;
230  ngx_connection_t *c;
234 
235  c = wev->data;
236  s = c->data;
237 
238  ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
239 
241  "mail auth http write handler");
242 
243  if (wev->timedout) {
245  "auth http server %V timed out", ctx->peer.name);
247  ngx_destroy_pool(ctx->pool);
249  return;
250  }
251 
252  size = ctx->request->last - ctx->request->pos;
253 
254  n = ngx_send(c, ctx->request->pos, size);
255 
256  if (n == NGX_ERROR) {
258  ngx_destroy_pool(ctx->pool);
260  return;
261  }
262 
263  if (n > 0) {
264  ctx->request->pos += n;
265 
266  if (n == size) {
267  wev->handler = ngx_mail_auth_http_dummy_handler;
268 
269  if (wev->timer_set) {
270  ngx_del_timer(wev);
271  }
272 
273  if (ngx_handle_write_event(wev, 0) != NGX_OK) {
275  ngx_destroy_pool(ctx->pool);
277  }
278 
279  return;
280  }
281  }
282 
283  if (!wev->timer_set) {
284  ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);
285  ngx_add_timer(wev, ahcf->timeout);
286  }
287 }
288 
289 
290 static void
291 ngx_mail_auth_http_read_handler(ngx_event_t *rev)
292 {
293  ssize_t n, size;
294  ngx_connection_t *c;
297 
298  c = rev->data;
299  s = c->data;
300 
302  "mail auth http read handler");
303 
304  ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
305 
306  if (rev->timedout) {
308  "auth http server %V timed out", ctx->peer.name);
310  ngx_destroy_pool(ctx->pool);
312  return;
313  }
314 
315  if (ctx->response == NULL) {
316  ctx->response = ngx_create_temp_buf(ctx->pool, 1024);
317  if (ctx->response == NULL) {
319  ngx_destroy_pool(ctx->pool);
321  return;
322  }
323  }
324 
325  size = ctx->response->end - ctx->response->last;
326 
327  n = ngx_recv(c, ctx->response->pos, size);
328 
329  if (n > 0) {
330  ctx->response->last += n;
331 
332  ctx->handler(s, ctx);
333  return;
334  }
335 
336  if (n == NGX_AGAIN) {
337  return;
338  }
339 
341  ngx_destroy_pool(ctx->pool);
343 }
344 
345 
346 static void
347 ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
349 {
350  u_char *p, ch;
351  enum {
352  sw_start = 0,
353  sw_H,
354  sw_HT,
355  sw_HTT,
356  sw_HTTP,
357  sw_skip,
358  sw_almost_done
359  } state;
360 
362  "mail auth http process status line");
363 
364  state = ctx->state;
365 
366  for (p = ctx->response->pos; p < ctx->response->last; p++) {
367  ch = *p;
368 
369  switch (state) {
370 
371  /* "HTTP/" */
372  case sw_start:
373  if (ch == 'H') {
374  state = sw_H;
375  break;
376  }
377  goto next;
378 
379  case sw_H:
380  if (ch == 'T') {
381  state = sw_HT;
382  break;
383  }
384  goto next;
385 
386  case sw_HT:
387  if (ch == 'T') {
388  state = sw_HTT;
389  break;
390  }
391  goto next;
392 
393  case sw_HTT:
394  if (ch == 'P') {
395  state = sw_HTTP;
396  break;
397  }
398  goto next;
399 
400  case sw_HTTP:
401  if (ch == '/') {
402  state = sw_skip;
403  break;
404  }
405  goto next;
406 
407  /* any text until end of line */
408  case sw_skip:
409  switch (ch) {
410  case CR:
411  state = sw_almost_done;
412 
413  break;
414  case LF:
415  goto done;
416  }
417  break;
418 
419  /* end of status line */
420  case sw_almost_done:
421  if (ch == LF) {
422  goto done;
423  }
424 
426  "auth http server &V sent invalid response",
427  ctx->peer.name);
429  ngx_destroy_pool(ctx->pool);
431  return;
432  }
433  }
434 
435  ctx->response->pos = p;
436  ctx->state = state;
437 
438  return;
439 
440 next:
441 
442  p = ctx->response->start - 1;
443 
444 done:
445 
446  ctx->response->pos = p + 1;
447  ctx->state = 0;
448  ctx->handler = ngx_mail_auth_http_process_headers;
449  ctx->handler(s, ctx);
450 }
451 
452 
453 static void
454 ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
456 {
457  u_char *p;
458  time_t timer;
459  size_t len, size;
460  ngx_int_t rc, port, n;
461  ngx_addr_t *peer;
462  struct sockaddr_in *sin;
463 #if (NGX_HAVE_INET6)
464  struct sockaddr_in6 *sin6;
465 #endif
466 
468  "mail auth http process headers");
469 
470  for ( ;; ) {
471  rc = ngx_mail_auth_http_parse_header_line(s, ctx);
472 
473  if (rc == NGX_OK) {
474 
475 #if (NGX_DEBUG)
476  {
477  ngx_str_t key, value;
478 
479  key.len = ctx->header_name_end - ctx->header_name_start;
480  key.data = ctx->header_name_start;
481  value.len = ctx->header_end - ctx->header_start;
482  value.data = ctx->header_start;
483 
485  "mail auth http header: \"%V: %V\"",
486  &key, &value);
487  }
488 #endif
489 
490  len = ctx->header_name_end - ctx->header_name_start;
491 
492  if (len == sizeof("Auth-Status") - 1
494  (u_char *) "Auth-Status",
495  sizeof("Auth-Status") - 1)
496  == 0)
497  {
498  len = ctx->header_end - ctx->header_start;
499 
500  if (len == 2
501  && ctx->header_start[0] == 'O'
502  && ctx->header_start[1] == 'K')
503  {
504  continue;
505  }
506 
507  if (len == 4
508  && ctx->header_start[0] == 'W'
509  && ctx->header_start[1] == 'A'
510  && ctx->header_start[2] == 'I'
511  && ctx->header_start[3] == 'T')
512  {
513  s->auth_wait = 1;
514  continue;
515  }
516 
517  ctx->errmsg.len = len;
518  ctx->errmsg.data = ctx->header_start;
519 
520  switch (s->protocol) {
521 
523  size = sizeof("-ERR ") - 1 + len + sizeof(CRLF) - 1;
524  break;
525 
527  size = s->tag.len + sizeof("NO ") - 1 + len
528  + sizeof(CRLF) - 1;
529  break;
530 
531  default: /* NGX_MAIL_SMTP_PROTOCOL */
532  ctx->err = ctx->errmsg;
533  continue;
534  }
535 
536  p = ngx_pnalloc(s->connection->pool, size);
537  if (p == NULL) {
539  ngx_destroy_pool(ctx->pool);
541  return;
542  }
543 
544  ctx->err.data = p;
545 
546  switch (s->protocol) {
547 
549  *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R'; *p++ = ' ';
550  break;
551 
553  p = ngx_cpymem(p, s->tag.data, s->tag.len);
554  *p++ = 'N'; *p++ = 'O'; *p++ = ' ';
555  break;
556 
557  default: /* NGX_MAIL_SMTP_PROTOCOL */
558  break;
559  }
560 
561  p = ngx_cpymem(p, ctx->header_start, len);
562  *p++ = CR; *p++ = LF;
563 
564  ctx->err.len = p - ctx->err.data;
565 
566  continue;
567  }
568 
569  if (len == sizeof("Auth-Server") - 1
571  (u_char *) "Auth-Server",
572  sizeof("Auth-Server") - 1)
573  == 0)
574  {
575  ctx->addr.len = ctx->header_end - ctx->header_start;
576  ctx->addr.data = ctx->header_start;
577 
578  continue;
579  }
580 
581  if (len == sizeof("Auth-Port") - 1
583  (u_char *) "Auth-Port",
584  sizeof("Auth-Port") - 1)
585  == 0)
586  {
587  ctx->port.len = ctx->header_end - ctx->header_start;
588  ctx->port.data = ctx->header_start;
589 
590  continue;
591  }
592 
593  if (len == sizeof("Auth-User") - 1
595  (u_char *) "Auth-User",
596  sizeof("Auth-User") - 1)
597  == 0)
598  {
599  s->login.len = ctx->header_end - ctx->header_start;
600 
601  s->login.data = ngx_pnalloc(s->connection->pool, s->login.len);
602  if (s->login.data == NULL) {
604  ngx_destroy_pool(ctx->pool);
606  return;
607  }
608 
609  ngx_memcpy(s->login.data, ctx->header_start, s->login.len);
610 
611  continue;
612  }
613 
614  if (len == sizeof("Auth-Pass") - 1
616  (u_char *) "Auth-Pass",
617  sizeof("Auth-Pass") - 1)
618  == 0)
619  {
620  s->passwd.len = ctx->header_end - ctx->header_start;
621 
623  s->passwd.len);
624  if (s->passwd.data == NULL) {
626  ngx_destroy_pool(ctx->pool);
628  return;
629  }
630 
632 
633  continue;
634  }
635 
636  if (len == sizeof("Auth-Wait") - 1
638  (u_char *) "Auth-Wait",
639  sizeof("Auth-Wait") - 1)
640  == 0)
641  {
642  n = ngx_atoi(ctx->header_start,
643  ctx->header_end - ctx->header_start);
644 
645  if (n != NGX_ERROR) {
646  ctx->sleep = n;
647  }
648 
649  continue;
650  }
651 
652  if (len == sizeof("Auth-Error-Code") - 1
654  (u_char *) "Auth-Error-Code",
655  sizeof("Auth-Error-Code") - 1)
656  == 0)
657  {
658  ctx->errcode.len = ctx->header_end - ctx->header_start;
659 
661  ctx->errcode.len);
662  if (ctx->errcode.data == NULL) {
664  ngx_destroy_pool(ctx->pool);
666  return;
667  }
668 
669  ngx_memcpy(ctx->errcode.data, ctx->header_start,
670  ctx->errcode.len);
671 
672  continue;
673  }
674 
675  /* ignore other headers */
676 
677  continue;
678  }
679 
680  if (rc == NGX_DONE) {
682  "mail auth http header done");
683 
685 
686  if (ctx->err.len) {
687 
689  "client login failed: \"%V\"", &ctx->errmsg);
690 
691  if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {
692 
693  if (ctx->errcode.len == 0) {
694  ctx->errcode = ngx_mail_smtp_errcode;
695  }
696 
697  ctx->err.len = ctx->errcode.len + ctx->errmsg.len
698  + sizeof(" " CRLF) - 1;
699 
700  p = ngx_pnalloc(s->connection->pool, ctx->err.len);
701  if (p == NULL) {
703  ngx_destroy_pool(ctx->pool);
705  return;
706  }
707 
708  ctx->err.data = p;
709 
710  p = ngx_cpymem(p, ctx->errcode.data, ctx->errcode.len);
711  *p++ = ' ';
712  p = ngx_cpymem(p, ctx->errmsg.data, ctx->errmsg.len);
713  *p++ = CR; *p = LF;
714  }
715 
716  s->out = ctx->err;
717  timer = ctx->sleep;
718 
719  ngx_destroy_pool(ctx->pool);
720 
721  if (timer == 0) {
722  s->quit = 1;
724  return;
725  }
726 
727  ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
728 
729  s->connection->read->handler = ngx_mail_auth_sleep_handler;
730 
731  return;
732  }
733 
734  if (s->auth_wait) {
735  timer = ctx->sleep;
736 
737  ngx_destroy_pool(ctx->pool);
738 
739  if (timer == 0) {
741  return;
742  }
743 
744  ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
745 
746  s->connection->read->handler = ngx_mail_auth_sleep_handler;
747 
748  return;
749  }
750 
751  if (ctx->addr.len == 0 || ctx->port.len == 0) {
753  "auth http server %V did not send server or port",
754  ctx->peer.name);
755  ngx_destroy_pool(ctx->pool);
757  return;
758  }
759 
760  if (s->passwd.data == NULL
762  {
764  "auth http server %V did not send password",
765  ctx->peer.name);
766  ngx_destroy_pool(ctx->pool);
768  return;
769  }
770 
771  peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_addr_t));
772  if (peer == NULL) {
773  ngx_destroy_pool(ctx->pool);
775  return;
776  }
777 
778  rc = ngx_parse_addr(s->connection->pool, peer,
779  ctx->addr.data, ctx->addr.len);
780 
781  switch (rc) {
782  case NGX_OK:
783  break;
784 
785  case NGX_DECLINED:
787  "auth http server %V sent invalid server "
788  "address:\"%V\"",
789  ctx->peer.name, &ctx->addr);
790  /* fall through */
791 
792  default:
793  ngx_destroy_pool(ctx->pool);
795  return;
796  }
797 
798  port = ngx_atoi(ctx->port.data, ctx->port.len);
799  if (port == NGX_ERROR || port < 1 || port > 65535) {
801  "auth http server %V sent invalid server "
802  "port:\"%V\"",
803  ctx->peer.name, &ctx->port);
804  ngx_destroy_pool(ctx->pool);
806  return;
807  }
808 
809  switch (peer->sockaddr->sa_family) {
810 
811 #if (NGX_HAVE_INET6)
812  case AF_INET6:
813  sin6 = (struct sockaddr_in6 *) peer->sockaddr;
814  sin6->sin6_port = htons((in_port_t) port);
815  break;
816 #endif
817 
818  default: /* AF_INET */
819  sin = (struct sockaddr_in *) peer->sockaddr;
820  sin->sin_port = htons((in_port_t) port);
821  break;
822  }
823 
824  len = ctx->addr.len + 1 + ctx->port.len;
825 
826  peer->name.len = len;
827 
828  peer->name.data = ngx_pnalloc(s->connection->pool, len);
829  if (peer->name.data == NULL) {
830  ngx_destroy_pool(ctx->pool);
832  return;
833  }
834 
835  len = ctx->addr.len;
836 
837  ngx_memcpy(peer->name.data, ctx->addr.data, len);
838 
839  peer->name.data[len++] = ':';
840 
841  ngx_memcpy(peer->name.data + len, ctx->port.data, ctx->port.len);
842 
843  ngx_destroy_pool(ctx->pool);
844  ngx_mail_proxy_init(s, peer);
845 
846  return;
847  }
848 
849  if (rc == NGX_AGAIN ) {
850  return;
851  }
852 
853  /* rc == NGX_ERROR */
854 
856  "auth http server %V sent invalid header in response",
857  ctx->peer.name);
859  ngx_destroy_pool(ctx->pool);
861 
862  return;
863  }
864 }
865 
866 
867 static void
868 ngx_mail_auth_sleep_handler(ngx_event_t *rev)
869 {
870  ngx_connection_t *c;
873 
874  ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth sleep handler");
875 
876  c = rev->data;
877  s = c->data;
878 
879  if (rev->timedout) {
880 
881  rev->timedout = 0;
882 
883  if (s->auth_wait) {
884  s->auth_wait = 0;
886  return;
887  }
888 
890 
891  rev->handler = cscf->protocol->auth_state;
892 
893  s->mail_state = 0;
895 
896  c->log->action = "in auth state";
897 
898  ngx_mail_send(c->write);
899 
900  if (c->destroyed) {
901  return;
902  }
903 
904  ngx_add_timer(rev, cscf->timeout);
905 
906  if (rev->ready) {
907  rev->handler(rev);
908  return;
909  }
910 
911  if (ngx_handle_read_event(rev, 0) != NGX_OK) {
913  }
914 
915  return;
916  }
917 
918  if (rev->active) {
919  if (ngx_handle_read_event(rev, 0) != NGX_OK) {
921  }
922  }
923 }
924 
925 
926 static ngx_int_t
927 ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
929 {
930  u_char c, ch, *p;
931  enum {
932  sw_start = 0,
933  sw_name,
934  sw_space_before_value,
935  sw_value,
936  sw_space_after_value,
937  sw_almost_done,
938  sw_header_almost_done
939  } state;
940 
941  state = ctx->state;
942 
943  for (p = ctx->response->pos; p < ctx->response->last; p++) {
944  ch = *p;
945 
946  switch (state) {
947 
948  /* first char */
949  case sw_start:
950 
951  switch (ch) {
952  case CR:
953  ctx->header_end = p;
954  state = sw_header_almost_done;
955  break;
956  case LF:
957  ctx->header_end = p;
958  goto header_done;
959  default:
960  state = sw_name;
961  ctx->header_name_start = p;
962 
963  c = (u_char) (ch | 0x20);
964  if (c >= 'a' && c <= 'z') {
965  break;
966  }
967 
968  if (ch >= '0' && ch <= '9') {
969  break;
970  }
971 
972  return NGX_ERROR;
973  }
974  break;
975 
976  /* header name */
977  case sw_name:
978  c = (u_char) (ch | 0x20);
979  if (c >= 'a' && c <= 'z') {
980  break;
981  }
982 
983  if (ch == ':') {
984  ctx->header_name_end = p;
985  state = sw_space_before_value;
986  break;
987  }
988 
989  if (ch == '-') {
990  break;
991  }
992 
993  if (ch >= '0' && ch <= '9') {
994  break;
995  }
996 
997  if (ch == CR) {
998  ctx->header_name_end = p;
999  ctx->header_start = p;
1000  ctx->header_end = p;
1001  state = sw_almost_done;
1002  break;
1003  }
1004 
1005  if (ch == LF) {
1006  ctx->header_name_end = p;
1007  ctx->header_start = p;
1008  ctx->header_end = p;
1009  goto done;
1010  }
1011 
1012  return NGX_ERROR;
1013 
1014  /* space* before header value */
1015  case sw_space_before_value:
1016  switch (ch) {
1017  case ' ':
1018  break;
1019  case CR:
1020  ctx->header_start = p;
1021  ctx->header_end = p;
1022  state = sw_almost_done;
1023  break;
1024  case LF:
1025  ctx->header_start = p;
1026  ctx->header_end = p;
1027  goto done;
1028  default:
1029  ctx->header_start = p;
1030  state = sw_value;
1031  break;
1032  }
1033  break;
1034 
1035  /* header value */
1036  case sw_value:
1037  switch (ch) {
1038  case ' ':
1039  ctx->header_end = p;
1040  state = sw_space_after_value;
1041  break;
1042  case CR:
1043  ctx->header_end = p;
1044  state = sw_almost_done;
1045  break;
1046  case LF:
1047  ctx->header_end = p;
1048  goto done;
1049  }
1050  break;
1051 
1052  /* space* before end of header line */
1053  case sw_space_after_value:
1054  switch (ch) {
1055  case ' ':
1056  break;
1057  case CR:
1058  state = sw_almost_done;
1059  break;
1060  case LF:
1061  goto done;
1062  default:
1063  state = sw_value;
1064  break;
1065  }
1066  break;
1067 
1068  /* end of header line */
1069  case sw_almost_done:
1070  switch (ch) {
1071  case LF:
1072  goto done;
1073  default:
1074  return NGX_ERROR;
1075  }
1076 
1077  /* end of header */
1078  case sw_header_almost_done:
1079  switch (ch) {
1080  case LF:
1081  goto header_done;
1082  default:
1083  return NGX_ERROR;
1084  }
1085  }
1086  }
1087 
1088  ctx->response->pos = p;
1089  ctx->state = state;
1090 
1091  return NGX_AGAIN;
1092 
1093 done:
1094 
1095  ctx->response->pos = p + 1;
1096  ctx->state = sw_start;
1097 
1098  return NGX_OK;
1099 
1100 header_done:
1101 
1102  ctx->response->pos = p + 1;
1103  ctx->state = sw_start;
1104 
1105  return NGX_DONE;
1106 }
1107 
1108 
1109 static void
1110 ngx_mail_auth_http_block_read(ngx_event_t *rev)
1111 {
1112  ngx_connection_t *c;
1113  ngx_mail_session_t *s;
1115 
1117  "mail auth http block read");
1118 
1119  if (ngx_handle_read_event(rev, 0) != NGX_OK) {
1120  c = rev->data;
1121  s = c->data;
1122 
1123  ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
1124 
1126  ngx_destroy_pool(ctx->pool);
1128  }
1129 }
1130 
1131 
1132 static void
1133 ngx_mail_auth_http_dummy_handler(ngx_event_t *ev)
1134 {
1136  "mail auth http dummy handler");
1137 }
1138 
1139 
1140 static ngx_buf_t *
1141 ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool,
1143 {
1144  size_t len;
1145  ngx_buf_t *b;
1146  ngx_str_t login, passwd;
1148 
1149  if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {
1150  return NULL;
1151  }
1152 
1153  if (ngx_mail_auth_http_escape(pool, &s->passwd, &passwd) != NGX_OK) {
1154  return NULL;
1155  }
1156 
1158 
1159  len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1
1160  + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1
1161  + sizeof("Auth-Method: ") - 1
1162  + ngx_mail_auth_http_method[s->auth_method].len
1163  + sizeof(CRLF) - 1
1164  + sizeof("Auth-User: ") - 1 + login.len + sizeof(CRLF) - 1
1165  + sizeof("Auth-Pass: ") - 1 + passwd.len + sizeof(CRLF) - 1
1166  + sizeof("Auth-Salt: ") - 1 + s->salt.len
1167  + sizeof("Auth-Protocol: ") - 1 + cscf->protocol->name.len
1168  + sizeof(CRLF) - 1
1169  + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN
1170  + sizeof(CRLF) - 1
1171  + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
1172  + sizeof(CRLF) - 1
1173  + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1
1174  + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len
1175  + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len
1176  + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len
1177  + ahcf->header.len
1178  + sizeof(CRLF) - 1;
1179 
1180  b = ngx_create_temp_buf(pool, len);
1181  if (b == NULL) {
1182  return NULL;
1183  }
1184 
1185  b->last = ngx_cpymem(b->last, "GET ", sizeof("GET ") - 1);
1186  b->last = ngx_copy(b->last, ahcf->uri.data, ahcf->uri.len);
1187  b->last = ngx_cpymem(b->last, " HTTP/1.0" CRLF,
1188  sizeof(" HTTP/1.0" CRLF) - 1);
1189 
1190  b->last = ngx_cpymem(b->last, "Host: ", sizeof("Host: ") - 1);
1191  b->last = ngx_copy(b->last, ahcf->host_header.data,
1192  ahcf->host_header.len);
1193  *b->last++ = CR; *b->last++ = LF;
1194 
1195  b->last = ngx_cpymem(b->last, "Auth-Method: ",
1196  sizeof("Auth-Method: ") - 1);
1197  b->last = ngx_cpymem(b->last,
1198  ngx_mail_auth_http_method[s->auth_method].data,
1199  ngx_mail_auth_http_method[s->auth_method].len);
1200  *b->last++ = CR; *b->last++ = LF;
1201 
1202  b->last = ngx_cpymem(b->last, "Auth-User: ", sizeof("Auth-User: ") - 1);
1203  b->last = ngx_copy(b->last, login.data, login.len);
1204  *b->last++ = CR; *b->last++ = LF;
1205 
1206  b->last = ngx_cpymem(b->last, "Auth-Pass: ", sizeof("Auth-Pass: ") - 1);
1207  b->last = ngx_copy(b->last, passwd.data, passwd.len);
1208  *b->last++ = CR; *b->last++ = LF;
1209 
1210  if (s->auth_method != NGX_MAIL_AUTH_PLAIN && s->salt.len) {
1211  b->last = ngx_cpymem(b->last, "Auth-Salt: ", sizeof("Auth-Salt: ") - 1);
1212  b->last = ngx_copy(b->last, s->salt.data, s->salt.len);
1213 
1214  s->passwd.data = NULL;
1215  }
1216 
1217  b->last = ngx_cpymem(b->last, "Auth-Protocol: ",
1218  sizeof("Auth-Protocol: ") - 1);
1219  b->last = ngx_cpymem(b->last, cscf->protocol->name.data,
1220  cscf->protocol->name.len);
1221  *b->last++ = CR; *b->last++ = LF;
1222 
1223  b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF,
1224  s->login_attempt);
1225 
1226  b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);
1227  b->last = ngx_copy(b->last, s->connection->addr_text.data,
1228  s->connection->addr_text.len);
1229  *b->last++ = CR; *b->last++ = LF;
1230 
1231  if (s->host.len) {
1232  b->last = ngx_cpymem(b->last, "Client-Host: ",
1233  sizeof("Client-Host: ") - 1);
1234  b->last = ngx_copy(b->last, s->host.data, s->host.len);
1235  *b->last++ = CR; *b->last++ = LF;
1236  }
1237 
1238  if (s->auth_method == NGX_MAIL_AUTH_NONE) {
1239 
1240  /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */
1241 
1242  b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ",
1243  sizeof("Auth-SMTP-Helo: ") - 1);
1244  b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len);
1245  *b->last++ = CR; *b->last++ = LF;
1246 
1247  b->last = ngx_cpymem(b->last, "Auth-SMTP-From: ",
1248  sizeof("Auth-SMTP-From: ") - 1);
1249  b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len);
1250  *b->last++ = CR; *b->last++ = LF;
1251 
1252  b->last = ngx_cpymem(b->last, "Auth-SMTP-To: ",
1253  sizeof("Auth-SMTP-To: ") - 1);
1254  b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len);
1255  *b->last++ = CR; *b->last++ = LF;
1256 
1257  }
1258 
1259  if (ahcf->header.len) {
1260  b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len);
1261  }
1262 
1263  /* add "\r\n" at the header end */
1264  *b->last++ = CR; *b->last++ = LF;
1265 
1266 #if (NGX_DEBUG_MAIL_PASSWD)
1267  {
1268  ngx_str_t l;
1269 
1270  l.len = b->last - b->pos;
1271  l.data = b->pos;
1273  "mail auth http header:\n\"%V\"", &l);
1274  }
1275 #endif
1276 
1277  return b;
1278 }
1279 
1280 
1281 static ngx_int_t
1282 ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped)
1283 {
1284  u_char *p;
1285  uintptr_t n;
1286 
1287  n = ngx_escape_uri(NULL, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
1288 
1289  if (n == 0) {
1290  *escaped = *text;
1291  return NGX_OK;
1292  }
1293 
1294  escaped->len = text->len + n * 2;
1295 
1296  p = ngx_pnalloc(pool, escaped->len);
1297  if (p == NULL) {
1298  return NGX_ERROR;
1299  }
1300 
1301  (void) ngx_escape_uri(p, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
1302 
1303  escaped->data = p;
1304 
1305  return NGX_OK;
1306 }
1307 
1308 
1309 static void *
1310 ngx_mail_auth_http_create_conf(ngx_conf_t *cf)
1311 {
1313 
1314  ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_auth_http_conf_t));
1315  if (ahcf == NULL) {
1316  return NULL;
1317  }
1318 
1319  ahcf->timeout = NGX_CONF_UNSET_MSEC;
1320 
1321  ahcf->file = cf->conf_file->file.name.data;
1322  ahcf->line = cf->conf_file->line;
1323 
1324  return ahcf;
1325 }
1326 
1327 
1328 static char *
1329 ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1330 {
1331  ngx_mail_auth_http_conf_t *prev = parent;
1332  ngx_mail_auth_http_conf_t *conf = child;
1333 
1334  u_char *p;
1335  size_t len;
1336  ngx_uint_t i;
1337  ngx_table_elt_t *header;
1338 
1339  if (conf->peer == NULL) {
1340  conf->peer = prev->peer;
1341  conf->host_header = prev->host_header;
1342  conf->uri = prev->uri;
1343 
1344  if (conf->peer == NULL) {
1346  "no \"auth_http\" is defined for server in %s:%ui",
1347  conf->file, conf->line);
1348 
1349  return NGX_CONF_ERROR;
1350  }
1351  }
1352 
1353  ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
1354 
1355  if (conf->headers == NULL) {
1356  conf->headers = prev->headers;
1357  conf->header = prev->header;
1358  }
1359 
1360  if (conf->headers && conf->header.len == 0) {
1361  len = 0;
1362  header = conf->headers->elts;
1363  for (i = 0; i < conf->headers->nelts; i++) {
1364  len += header[i].key.len + 2 + header[i].value.len + 2;
1365  }
1366 
1367  p = ngx_pnalloc(cf->pool, len);
1368  if (p == NULL) {
1369  return NGX_CONF_ERROR;
1370  }
1371 
1372  conf->header.len = len;
1373  conf->header.data = p;
1374 
1375  for (i = 0; i < conf->headers->nelts; i++) {
1376  p = ngx_cpymem(p, header[i].key.data, header[i].key.len);
1377  *p++ = ':'; *p++ = ' ';
1378  p = ngx_cpymem(p, header[i].value.data, header[i].value.len);
1379  *p++ = CR; *p++ = LF;
1380  }
1381  }
1382 
1383  return NGX_CONF_OK;
1384 }
1385 
1386 
1387 static char *
1388 ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1389 {
1390  ngx_mail_auth_http_conf_t *ahcf = conf;
1391 
1392  ngx_str_t *value;
1393  ngx_url_t u;
1394 
1395  value = cf->args->elts;
1396 
1397  ngx_memzero(&u, sizeof(ngx_url_t));
1398 
1399  u.url = value[1];
1400  u.default_port = 80;
1401  u.uri_part = 1;
1402 
1403  if (ngx_strncmp(u.url.data, "http://", 7) == 0) {
1404  u.url.len -= 7;
1405  u.url.data += 7;
1406  }
1407 
1408  if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
1409  if (u.err) {
1411  "%s in auth_http \"%V\"", u.err, &u.url);
1412  }
1413 
1414  return NGX_CONF_ERROR;
1415  }
1416 
1417  ahcf->peer = u.addrs;
1418 
1419  if (u.family != AF_UNIX) {
1420  ahcf->host_header = u.host;
1421 
1422  } else {
1423  ngx_str_set(&ahcf->host_header, "localhost");
1424  }
1425 
1426  ahcf->uri = u.uri;
1427 
1428  if (ahcf->uri.len == 0) {
1429  ngx_str_set(&ahcf->uri, "/");
1430  }
1431 
1432  return NGX_CONF_OK;
1433 }
1434 
1435 
1436 static char *
1437 ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1438 {
1439  ngx_mail_auth_http_conf_t *ahcf = conf;
1440 
1441  ngx_str_t *value;
1442  ngx_table_elt_t *header;
1443 
1444  if (ahcf->headers == NULL) {
1445  ahcf->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_table_elt_t));
1446  if (ahcf->headers == NULL) {
1447  return NGX_CONF_ERROR;
1448  }
1449  }
1450 
1451  header = ngx_array_push(ahcf->headers);
1452  if (header == NULL) {
1453  return NGX_CONF_ERROR;
1454  }
1455 
1456  value = cf->args->elts;
1457 
1458  header->key = value[1];
1459  header->value = value[2];
1460 
1461  return NGX_CONF_OK;
1462 }