src/http/modules/ngx_http_tunnel_module.c - nginx

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Roman Arutyunyan
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. typedef struct {
  9.     ngx_http_upstream_conf_t       upstream;

  10.     ngx_array_t                   *tunnel_lengths;
  11.     ngx_array_t                   *tunnel_values;
  12. } ngx_http_tunnel_loc_conf_t;


  13. static ngx_int_t ngx_http_tunnel_eval(ngx_http_request_t *r,
  14.     ngx_http_tunnel_loc_conf_t *tlcf);
  15. static ngx_int_t ngx_http_tunnel_create_request(ngx_http_request_t *r);
  16. static ngx_int_t ngx_http_tunnel_reinit_request(ngx_http_request_t *r);
  17. static ngx_int_t ngx_http_tunnel_process_header(ngx_http_request_t *r);
  18. static void ngx_http_tunnel_abort_request(ngx_http_request_t *r);
  19. static void ngx_http_tunnel_finalize_request(ngx_http_request_t *r,
  20.     ngx_int_t rc);

  21. static void *ngx_http_tunnel_create_loc_conf(ngx_conf_t *cf);
  22. static char *ngx_http_tunnel_merge_loc_conf(ngx_conf_t *cf,
  23.     void *parent, void *child);

  24. static char *ngx_http_tunnel_pass(ngx_conf_t *cf, ngx_command_t *cmd,
  25.     void *conf);

  26. static char *ngx_http_tunnel_lowat_check(ngx_conf_t *cf, void *post, void *data);


  27. static ngx_conf_post_t  ngx_http_tunnel_lowat_post =
  28.     { ngx_http_tunnel_lowat_check };


  29. static ngx_conf_bitmask_t  ngx_http_tunnel_next_upstream_masks[] = {
  30.     { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
  31.     { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
  32.     { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
  33.     { ngx_null_string, 0 }
  34. };


  35. static ngx_command_t  ngx_http_tunnel_commands[] = {

  36.     { ngx_string("tunnel_pass"),
  37.       NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS
  38.       |NGX_CONF_TAKE1,
  39.       ngx_http_tunnel_pass,
  40.       NGX_HTTP_LOC_CONF_OFFSET,
  41.       0,
  42.       NULL },

  43.     { ngx_string("tunnel_bind"),
  44.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
  45.       ngx_http_upstream_bind_set_slot,
  46.       NGX_HTTP_LOC_CONF_OFFSET,
  47.       offsetof(ngx_http_tunnel_loc_conf_t, upstream.local),
  48.       NULL },

  49.     { ngx_string("tunnel_socket_keepalive"),
  50.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
  51.       ngx_conf_set_flag_slot,
  52.       NGX_HTTP_LOC_CONF_OFFSET,
  53.       offsetof(ngx_http_tunnel_loc_conf_t, upstream.socket_keepalive),
  54.       NULL },

  55.     { ngx_string("tunnel_connect_timeout"),
  56.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  57.       ngx_conf_set_msec_slot,
  58.       NGX_HTTP_LOC_CONF_OFFSET,
  59.       offsetof(ngx_http_tunnel_loc_conf_t, upstream.connect_timeout),
  60.       NULL },

  61.     { ngx_string("tunnel_send_timeout"),
  62.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  63.       ngx_conf_set_msec_slot,
  64.       NGX_HTTP_LOC_CONF_OFFSET,
  65.       offsetof(ngx_http_tunnel_loc_conf_t, upstream.send_timeout),
  66.       NULL },

  67.     { ngx_string("tunnel_send_lowat"),
  68.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  69.       ngx_conf_set_size_slot,
  70.       NGX_HTTP_LOC_CONF_OFFSET,
  71.       offsetof(ngx_http_tunnel_loc_conf_t, upstream.send_lowat),
  72.       &ngx_http_tunnel_lowat_post },

  73.     { ngx_string("tunnel_buffer_size"),
  74.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  75.       ngx_conf_set_size_slot,
  76.       NGX_HTTP_LOC_CONF_OFFSET,
  77.       offsetof(ngx_http_tunnel_loc_conf_t, upstream.buffer_size),
  78.       NULL },

  79.     { ngx_string("tunnel_read_timeout"),
  80.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  81.       ngx_conf_set_msec_slot,
  82.       NGX_HTTP_LOC_CONF_OFFSET,
  83.       offsetof(ngx_http_tunnel_loc_conf_t, upstream.read_timeout),
  84.       NULL },

  85.     { ngx_string("tunnel_next_upstream"),
  86.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  87.       ngx_conf_set_bitmask_slot,
  88.       NGX_HTTP_LOC_CONF_OFFSET,
  89.       offsetof(ngx_http_tunnel_loc_conf_t, upstream.next_upstream),
  90.       &ngx_http_tunnel_next_upstream_masks },

  91.     { ngx_string("tunnel_next_upstream_tries"),
  92.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  93.       ngx_conf_set_num_slot,
  94.       NGX_HTTP_LOC_CONF_OFFSET,
  95.       offsetof(ngx_http_tunnel_loc_conf_t, upstream.next_upstream_tries),
  96.       NULL },

  97.     { ngx_string("tunnel_next_upstream_timeout"),
  98.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  99.       ngx_conf_set_msec_slot,
  100.       NGX_HTTP_LOC_CONF_OFFSET,
  101.       offsetof(ngx_http_tunnel_loc_conf_t, upstream.next_upstream_timeout),
  102.       NULL },

  103.       ngx_null_command
  104. };


  105. static ngx_http_module_t  ngx_http_tunnel_module_ctx = {
  106.     NULL,                                  /* preconfiguration */
  107.     NULL,                                  /* postconfiguration */

  108.     NULL,                                  /* create main configuration */
  109.     NULL,                                  /* init main configuration */

  110.     NULL,                                  /* create server configuration */
  111.     NULL,                                  /* merge server configuration */

  112.     ngx_http_tunnel_create_loc_conf,       /* create location configuration */
  113.     ngx_http_tunnel_merge_loc_conf         /* merge location configuration */
  114. };


  115. ngx_module_t  ngx_http_tunnel_module = {
  116.     NGX_MODULE_V1,
  117.     &ngx_http_tunnel_module_ctx,           /* module context */
  118.     ngx_http_tunnel_commands,              /* module directives */
  119.     NGX_HTTP_MODULE,                       /* module type */
  120.     NULL,                                  /* init master */
  121.     NULL,                                  /* init module */
  122.     NULL,                                  /* init process */
  123.     NULL,                                  /* init thread */
  124.     NULL,                                  /* exit thread */
  125.     NULL,                                  /* exit process */
  126.     NULL,                                  /* exit master */
  127.     NGX_MODULE_V1_PADDING
  128. };


  129. static ngx_int_t
  130. ngx_http_tunnel_handler(ngx_http_request_t *r)
  131. {
  132.     ngx_int_t                    rc;
  133.     ngx_http_upstream_t         *u;
  134.     ngx_http_tunnel_loc_conf_t  *tlcf;

  135.     if (r->method != NGX_HTTP_CONNECT) {
  136.         return NGX_HTTP_NOT_ALLOWED;
  137.     }

  138.     if (ngx_http_upstream_create(r) != NGX_OK) {
  139.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  140.     }

  141.     tlcf = ngx_http_get_module_loc_conf(r, ngx_http_tunnel_module);

  142.     if (tlcf->tunnel_lengths) {
  143.         if (ngx_http_tunnel_eval(r, tlcf) != NGX_OK) {
  144.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  145.         }
  146.     }

  147.     u = r->upstream;

  148.     u->conf = &tlcf->upstream;

  149.     u->create_request = ngx_http_tunnel_create_request;
  150.     u->reinit_request = ngx_http_tunnel_reinit_request;
  151.     u->process_header = ngx_http_tunnel_process_header;
  152.     u->abort_request = ngx_http_tunnel_abort_request;
  153.     u->finalize_request = ngx_http_tunnel_finalize_request;

  154.     rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);

  155.     if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
  156.         return rc;
  157.     }

  158.     return NGX_DONE;
  159. }


  160. static ngx_int_t
  161. ngx_http_tunnel_eval(ngx_http_request_t *r, ngx_http_tunnel_loc_conf_t *tlcf)
  162. {
  163.     ngx_url_t             url;
  164.     ngx_http_upstream_t  *u;

  165.     ngx_memzero(&url, sizeof(ngx_url_t));

  166.     if (ngx_http_script_run(r, &url.url, tlcf->tunnel_lengths->elts, 0,
  167.                             tlcf->tunnel_values->elts)
  168.         == NULL)
  169.     {
  170.         return NGX_ERROR;
  171.     }

  172.     url.no_resolve = 1;

  173.     if (ngx_parse_url(r->pool, &url) != NGX_OK) {
  174.         if (url.err) {
  175.             ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
  176.                           "%s in upstream \"%V\"", url.err, &url.url);
  177.         }

  178.         return NGX_ERROR;
  179.     }

  180.     u = r->upstream;

  181.     u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
  182.     if (u->resolved == NULL) {
  183.         return NGX_ERROR;
  184.     }

  185.     if (url.addrs) {
  186.         u->resolved->sockaddr = url.addrs[0].sockaddr;
  187.         u->resolved->socklen = url.addrs[0].socklen;
  188.         u->resolved->name = url.addrs[0].name;
  189.         u->resolved->naddrs = 1;
  190.     }

  191.     u->resolved->host = url.host;
  192.     u->resolved->port = url.port;

  193.     return NGX_OK;
  194. }


  195. static ngx_int_t
  196. ngx_http_tunnel_create_request(ngx_http_request_t *r)
  197. {
  198.     /* u->request_bufs = NULL */

  199.     return NGX_OK;
  200. }


  201. static ngx_int_t
  202. ngx_http_tunnel_reinit_request(ngx_http_request_t *r)
  203. {
  204.     return NGX_OK;
  205. }


  206. static ngx_int_t
  207. ngx_http_tunnel_process_header(ngx_http_request_t *r)
  208. {
  209.     ngx_http_upstream_t  *u;

  210.     u = r->upstream;

  211.     u->headers_in.status_n = NGX_HTTP_OK;
  212.     ngx_str_set(&u->headers_in.status_line, "200 OK");

  213.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  214.                    "http tunnel status %ui \"%V\"",
  215.                    u->headers_in.status_n, &u->headers_in.status_line);

  216.     r->keepalive = 0;
  217.     u->keepalive = 0;
  218.     u->upgrade = 1;

  219.     return NGX_OK;
  220. }


  221. static void
  222. ngx_http_tunnel_abort_request(ngx_http_request_t *r)
  223. {
  224.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  225.                    "abort http tunnel request");

  226.     return;
  227. }


  228. static void
  229. ngx_http_tunnel_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
  230. {
  231.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  232.                    "finalize http tunnel request");

  233.     return;
  234. }


  235. static void *
  236. ngx_http_tunnel_create_loc_conf(ngx_conf_t *cf)
  237. {
  238.     ngx_http_tunnel_loc_conf_t  *conf;

  239.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_tunnel_loc_conf_t));
  240.     if (conf == NULL) {
  241.         return NULL;
  242.     }

  243.     /*
  244.      * set by ngx_pcalloc():
  245.      *
  246.      *     conf->upstream.next_upstream = 0;
  247.      */

  248.     conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;

  249.     conf->upstream.local = NGX_CONF_UNSET_PTR;
  250.     conf->upstream.socket_keepalive = NGX_CONF_UNSET;

  251.     conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
  252.     conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
  253.     conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
  254.     conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;

  255.     conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
  256.     conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;

  257.     conf->upstream.ignore_input = 1;

  258.     ngx_str_set(&conf->upstream.module, "tunnel");

  259.     return conf;
  260. }


  261. static char *
  262. ngx_http_tunnel_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  263. {
  264.     ngx_http_tunnel_loc_conf_t *prev = parent;
  265.     ngx_http_tunnel_loc_conf_t *conf = child;

  266.     ngx_http_core_loc_conf_t  *clcf;

  267.     ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,
  268.                               prev->upstream.next_upstream_tries, 0);

  269.     ngx_conf_merge_ptr_value(conf->upstream.local,
  270.                               prev->upstream.local, NULL);

  271.     ngx_conf_merge_value(conf->upstream.socket_keepalive,
  272.                               prev->upstream.socket_keepalive, 0);

  273.     ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
  274.                               prev->upstream.connect_timeout, 60000);

  275.     ngx_conf_merge_msec_value(conf->upstream.send_timeout,
  276.                               prev->upstream.send_timeout, 60000);

  277.     ngx_conf_merge_msec_value(conf->upstream.read_timeout,
  278.                               prev->upstream.read_timeout, 60000);

  279.     ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
  280.                               prev->upstream.next_upstream_timeout, 0);

  281.     ngx_conf_merge_size_value(conf->upstream.send_lowat,
  282.                               prev->upstream.send_lowat, 0);

  283.     ngx_conf_merge_size_value(conf->upstream.buffer_size,
  284.                               prev->upstream.buffer_size,
  285.                               (size_t) ngx_pagesize);

  286.     ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
  287.                               prev->upstream.next_upstream,
  288.                               (NGX_CONF_BITMASK_SET
  289.                                |NGX_HTTP_UPSTREAM_FT_ERROR
  290.                                |NGX_HTTP_UPSTREAM_FT_TIMEOUT));

  291.     if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
  292.         conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
  293.                                        |NGX_HTTP_UPSTREAM_FT_OFF;
  294.     }

  295.     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

  296.     if (clcf->noname
  297.         && conf->upstream.upstream == NULL && conf->tunnel_lengths == NULL)
  298.     {
  299.         conf->upstream.upstream = prev->upstream.upstream;

  300.         conf->tunnel_lengths = prev->tunnel_lengths;
  301.         conf->tunnel_values = prev->tunnel_values;
  302.     }

  303.     if (clcf->lmt_excpt && clcf->handler == NULL
  304.         && (conf->upstream.upstream || conf->tunnel_lengths))
  305.     {
  306.         clcf->handler = ngx_http_tunnel_handler;
  307.     }


  308.     return NGX_CONF_OK;
  309. }


  310. static char *
  311. ngx_http_tunnel_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  312. {
  313.     ngx_http_tunnel_loc_conf_t *tlcf = conf;

  314.     ngx_url_t                   u;
  315.     ngx_str_t                  *value, url;
  316.     ngx_uint_t                  n;
  317.     ngx_http_core_srv_conf_t   *cscf;
  318.     ngx_http_core_loc_conf_t   *clcf;
  319.     ngx_http_script_compile_t   sc;

  320.     if (tlcf->upstream.upstream || tlcf->tunnel_lengths) {
  321.         return "is duplicate";
  322.     }

  323.     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

  324.     clcf->handler = ngx_http_tunnel_handler;

  325.     cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);

  326.     cscf->allow_connect = 1;

  327.     value = cf->args->elts;

  328.     if (cf->args->nelts == 1) {
  329.         ngx_str_set(&url, "$host:$request_port");

  330.     } else {
  331.         url = value[1];
  332.     }

  333.     n = ngx_http_script_variables_count(&url);

  334.     if (n) {

  335.         ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

  336.         sc.cf = cf;
  337.         sc.source = &url;
  338.         sc.lengths = &tlcf->tunnel_lengths;
  339.         sc.values = &tlcf->tunnel_values;
  340.         sc.variables = n;
  341.         sc.complete_lengths = 1;
  342.         sc.complete_values = 1;

  343.         if (ngx_http_script_compile(&sc) != NGX_OK) {
  344.             return NGX_CONF_ERROR;
  345.         }

  346.         return NGX_CONF_OK;
  347.     }

  348.     ngx_memzero(&u, sizeof(ngx_url_t));

  349.     u.url = url;
  350.     u.no_resolve = 1;

  351.     tlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
  352.     if (tlcf->upstream.upstream == NULL) {
  353.         return NGX_CONF_ERROR;
  354.     }

  355.     return NGX_CONF_OK;
  356. }


  357. static char *
  358. ngx_http_tunnel_lowat_check(ngx_conf_t *cf, void *post, void *data)
  359. {
  360. #if (NGX_FREEBSD)
  361.     ssize_t *np = data;

  362.     if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {
  363.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  364.                            "\"tunnel_send_lowat\" must be less than %d "
  365.                            "(sysctl net.inet.tcp.sendspace)",
  366.                            ngx_freebsd_net_inet_tcp_sendspace);

  367.         return NGX_CONF_ERROR;
  368.     }

  369. #elif !(NGX_HAVE_SO_SNDLOWAT)
  370.     ssize_t *np = data;

  371.     ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  372.                        "\"tunnel_send_lowat\" is not supported, ignored");

  373.     *np = 0;

  374. #endif

  375.     return NGX_CONF_OK;
  376. }