src/stream/ngx_stream_map_module.c - nginx source code

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


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


  8. typedef struct {
  9.     ngx_uint_t                    hash_max_size;
  10.     ngx_uint_t                    hash_bucket_size;
  11. } ngx_stream_map_conf_t;


  12. typedef struct {
  13.     ngx_hash_keys_arrays_t        keys;

  14.     ngx_array_t                  *values_hash;
  15. #if (NGX_PCRE)
  16.     ngx_array_t                   regexes;
  17. #endif

  18.     ngx_stream_variable_value_t  *default_value;
  19.     ngx_conf_t                   *cf;
  20.     unsigned                      hostnames:1;
  21.     unsigned                      no_cacheable:1;
  22. } ngx_stream_map_conf_ctx_t;


  23. typedef struct {
  24.     ngx_stream_map_t              map;
  25.     ngx_stream_complex_value_t    value;
  26.     ngx_stream_variable_value_t  *default_value;
  27.     ngx_uint_t                    hostnames;      /* unsigned  hostnames:1 */
  28. } ngx_stream_map_ctx_t;


  29. static int ngx_libc_cdecl ngx_stream_map_cmp_dns_wildcards(const void *one,
  30.     const void *two);
  31. static void *ngx_stream_map_create_conf(ngx_conf_t *cf);
  32. static char *ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd,
  33.     void *conf);
  34. static char *ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);


  35. static ngx_command_t  ngx_stream_map_commands[] = {

  36.     { ngx_string("map"),
  37.       NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
  38.       ngx_stream_map_block,
  39.       NGX_STREAM_MAIN_CONF_OFFSET,
  40.       0,
  41.       NULL },

  42.     { ngx_string("map_hash_max_size"),
  43.       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
  44.       ngx_conf_set_num_slot,
  45.       NGX_STREAM_MAIN_CONF_OFFSET,
  46.       offsetof(ngx_stream_map_conf_t, hash_max_size),
  47.       NULL },

  48.     { ngx_string("map_hash_bucket_size"),
  49.       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
  50.       ngx_conf_set_num_slot,
  51.       NGX_STREAM_MAIN_CONF_OFFSET,
  52.       offsetof(ngx_stream_map_conf_t, hash_bucket_size),
  53.       NULL },

  54.       ngx_null_command
  55. };


  56. static ngx_stream_module_t  ngx_stream_map_module_ctx = {
  57.     NULL,                                  /* preconfiguration */
  58.     NULL,                                  /* postconfiguration */

  59.     ngx_stream_map_create_conf,            /* create main configuration */
  60.     NULL,                                  /* init main configuration */

  61.     NULL,                                  /* create server configuration */
  62.     NULL                                   /* merge server configuration */
  63. };


  64. ngx_module_t  ngx_stream_map_module = {
  65.     NGX_MODULE_V1,
  66.     &ngx_stream_map_module_ctx,            /* module context */
  67.     ngx_stream_map_commands,               /* module directives */
  68.     NGX_STREAM_MODULE,                     /* module type */
  69.     NULL,                                  /* init master */
  70.     NULL,                                  /* init module */
  71.     NULL,                                  /* init process */
  72.     NULL,                                  /* init thread */
  73.     NULL,                                  /* exit thread */
  74.     NULL,                                  /* exit process */
  75.     NULL,                                  /* exit master */
  76.     NGX_MODULE_V1_PADDING
  77. };


  78. static ngx_int_t
  79. ngx_stream_map_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,
  80.     uintptr_t data)
  81. {
  82.     ngx_stream_map_ctx_t  *map = (ngx_stream_map_ctx_t *) data;

  83.     ngx_str_t                     val, str;
  84.     ngx_stream_complex_value_t   *cv;
  85.     ngx_stream_variable_value_t  *value;

  86.     ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  87.                    "stream map started");

  88.     if (ngx_stream_complex_value(s, &map->value, &val) != NGX_OK) {
  89.         return NGX_ERROR;
  90.     }

  91.     if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {
  92.         val.len--;
  93.     }

  94.     value = ngx_stream_map_find(s, &map->map, &val);

  95.     if (value == NULL) {
  96.         value = map->default_value;
  97.     }

  98.     if (!value->valid) {
  99.         cv = (ngx_stream_complex_value_t *) value->data;

  100.         if (ngx_stream_complex_value(s, cv, &str) != NGX_OK) {
  101.             return NGX_ERROR;
  102.         }

  103.         v->valid = 1;
  104.         v->no_cacheable = 0;
  105.         v->not_found = 0;
  106.         v->len = str.len;
  107.         v->data = str.data;

  108.     } else {
  109.         *v = *value;
  110.     }

  111.     ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  112.                    "stream map: \"%V\" \"%v\"", &val, v);

  113.     return NGX_OK;
  114. }


  115. static void *
  116. ngx_stream_map_create_conf(ngx_conf_t *cf)
  117. {
  118.     ngx_stream_map_conf_t  *mcf;

  119.     mcf = ngx_palloc(cf->pool, sizeof(ngx_stream_map_conf_t));
  120.     if (mcf == NULL) {
  121.         return NULL;
  122.     }

  123.     mcf->hash_max_size = NGX_CONF_UNSET_UINT;
  124.     mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;

  125.     return mcf;
  126. }


  127. static char *
  128. ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  129. {
  130.     ngx_stream_map_conf_t  *mcf = conf;

  131.     char                                *rv;
  132.     ngx_str_t                           *value, name;
  133.     ngx_conf_t                           save;
  134.     ngx_pool_t                          *pool;
  135.     ngx_hash_init_t                      hash;
  136.     ngx_stream_map_ctx_t                *map;
  137.     ngx_stream_variable_t               *var;
  138.     ngx_stream_map_conf_ctx_t            ctx;
  139.     ngx_stream_compile_complex_value_t   ccv;

  140.     if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {
  141.         mcf->hash_max_size = 2048;
  142.     }

  143.     if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {
  144.         mcf->hash_bucket_size = ngx_cacheline_size;

  145.     } else {
  146.         mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,
  147.                                           ngx_cacheline_size);
  148.     }

  149.     map = ngx_pcalloc(cf->pool, sizeof(ngx_stream_map_ctx_t));
  150.     if (map == NULL) {
  151.         return NGX_CONF_ERROR;
  152.     }

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

  154.     ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));

  155.     ccv.cf = cf;
  156.     ccv.value = &value[1];
  157.     ccv.complex_value = &map->value;

  158.     if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
  159.         return NGX_CONF_ERROR;
  160.     }

  161.     name = value[2];

  162.     if (name.data[0] != '$') {
  163.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  164.                            "invalid variable name \"%V\"", &name);
  165.         return NGX_CONF_ERROR;
  166.     }

  167.     name.len--;
  168.     name.data++;

  169.     var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);
  170.     if (var == NULL) {
  171.         return NGX_CONF_ERROR;
  172.     }

  173.     var->get_handler = ngx_stream_map_variable;
  174.     var->data = (uintptr_t) map;

  175.     pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
  176.     if (pool == NULL) {
  177.         return NGX_CONF_ERROR;
  178.     }

  179.     ctx.keys.pool = cf->pool;
  180.     ctx.keys.temp_pool = pool;

  181.     if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {
  182.         ngx_destroy_pool(pool);
  183.         return NGX_CONF_ERROR;
  184.     }

  185.     ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize);
  186.     if (ctx.values_hash == NULL) {
  187.         ngx_destroy_pool(pool);
  188.         return NGX_CONF_ERROR;
  189.     }

  190. #if (NGX_PCRE)
  191.     if (ngx_array_init(&ctx.regexes, cf->pool, 2,
  192.                        sizeof(ngx_stream_map_regex_t))
  193.         != NGX_OK)
  194.     {
  195.         ngx_destroy_pool(pool);
  196.         return NGX_CONF_ERROR;
  197.     }
  198. #endif

  199.     ctx.default_value = NULL;
  200.     ctx.cf = &save;
  201.     ctx.hostnames = 0;
  202.     ctx.no_cacheable = 0;

  203.     save = *cf;
  204.     cf->pool = pool;
  205.     cf->ctx = &ctx;
  206.     cf->handler = ngx_stream_map;
  207.     cf->handler_conf = conf;

  208.     rv = ngx_conf_parse(cf, NULL);

  209.     *cf = save;

  210.     if (rv != NGX_CONF_OK) {
  211.         ngx_destroy_pool(pool);
  212.         return rv;
  213.     }

  214.     if (ctx.no_cacheable) {
  215.         var->flags |= NGX_STREAM_VAR_NOCACHEABLE;
  216.     }

  217.     map->default_value = ctx.default_value ? ctx.default_value:
  218.                                              &ngx_stream_variable_null_value;

  219.     map->hostnames = ctx.hostnames;

  220.     hash.key = ngx_hash_key_lc;
  221.     hash.max_size = mcf->hash_max_size;
  222.     hash.bucket_size = mcf->hash_bucket_size;
  223.     hash.name = "map_hash";
  224.     hash.pool = cf->pool;

  225.     if (ctx.keys.keys.nelts) {
  226.         hash.hash = &map->map.hash.hash;
  227.         hash.temp_pool = NULL;

  228.         if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)
  229.             != NGX_OK)
  230.         {
  231.             ngx_destroy_pool(pool);
  232.             return NGX_CONF_ERROR;
  233.         }
  234.     }

  235.     if (ctx.keys.dns_wc_head.nelts) {

  236.         ngx_qsort(ctx.keys.dns_wc_head.elts,
  237.                   (size_t) ctx.keys.dns_wc_head.nelts,
  238.                   sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards);

  239.         hash.hash = NULL;
  240.         hash.temp_pool = pool;

  241.         if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,
  242.                                    ctx.keys.dns_wc_head.nelts)
  243.             != NGX_OK)
  244.         {
  245.             ngx_destroy_pool(pool);
  246.             return NGX_CONF_ERROR;
  247.         }

  248.         map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
  249.     }

  250.     if (ctx.keys.dns_wc_tail.nelts) {

  251.         ngx_qsort(ctx.keys.dns_wc_tail.elts,
  252.                   (size_t) ctx.keys.dns_wc_tail.nelts,
  253.                   sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards);

  254.         hash.hash = NULL;
  255.         hash.temp_pool = pool;

  256.         if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,
  257.                                    ctx.keys.dns_wc_tail.nelts)
  258.             != NGX_OK)
  259.         {
  260.             ngx_destroy_pool(pool);
  261.             return NGX_CONF_ERROR;
  262.         }

  263.         map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
  264.     }

  265. #if (NGX_PCRE)

  266.     if (ctx.regexes.nelts) {
  267.         map->map.regex = ctx.regexes.elts;
  268.         map->map.nregex = ctx.regexes.nelts;
  269.     }

  270. #endif

  271.     ngx_destroy_pool(pool);

  272.     return rv;
  273. }


  274. static int ngx_libc_cdecl
  275. ngx_stream_map_cmp_dns_wildcards(const void *one, const void *two)
  276. {
  277.     ngx_hash_key_t  *first, *second;

  278.     first = (ngx_hash_key_t *) one;
  279.     second = (ngx_hash_key_t *) two;

  280.     return ngx_dns_strcmp(first->key.data, second->key.data);
  281. }


  282. static char *
  283. ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
  284. {
  285.     u_char                              *data;
  286.     size_t                               len;
  287.     ngx_int_t                            rv;
  288.     ngx_str_t                           *value, v;
  289.     ngx_uint_t                           i, key;
  290.     ngx_stream_map_conf_ctx_t           *ctx;
  291.     ngx_stream_complex_value_t           cv, *cvp;
  292.     ngx_stream_variable_value_t         *var, **vp;
  293.     ngx_stream_compile_complex_value_t   ccv;

  294.     ctx = cf->ctx;

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

  296.     if (cf->args->nelts == 1
  297.         && ngx_strcmp(value[0].data, "hostnames") == 0)
  298.     {
  299.         ctx->hostnames = 1;
  300.         return NGX_CONF_OK;
  301.     }

  302.     if (cf->args->nelts == 1
  303.         && ngx_strcmp(value[0].data, "volatile") == 0)
  304.     {
  305.         ctx->no_cacheable = 1;
  306.         return NGX_CONF_OK;
  307.     }

  308.     if (cf->args->nelts != 2) {
  309.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  310.                            "invalid number of the map parameters");
  311.         return NGX_CONF_ERROR;
  312.     }

  313.     if (ngx_strcmp(value[0].data, "include") == 0) {
  314.         return ngx_conf_include(cf, dummy, conf);
  315.     }

  316.     key = 0;

  317.     for (i = 0; i < value[1].len; i++) {
  318.         key = ngx_hash(key, value[1].data[i]);
  319.     }

  320.     key %= ctx->keys.hsize;

  321.     vp = ctx->values_hash[key].elts;

  322.     if (vp) {
  323.         for (i = 0; i < ctx->values_hash[key].nelts; i++) {

  324.             if (vp[i]->valid) {
  325.                 data = vp[i]->data;
  326.                 len = vp[i]->len;

  327.             } else {
  328.                 cvp = (ngx_stream_complex_value_t *) vp[i]->data;
  329.                 data = cvp->value.data;
  330.                 len = cvp->value.len;
  331.             }

  332.             if (value[1].len != len) {
  333.                 continue;
  334.             }

  335.             if (ngx_strncmp(value[1].data, data, len) == 0) {
  336.                 var = vp[i];
  337.                 goto found;
  338.             }
  339.         }

  340.     } else {
  341.         if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,
  342.                            sizeof(ngx_stream_variable_value_t *))
  343.             != NGX_OK)
  344.         {
  345.             return NGX_CONF_ERROR;
  346.         }
  347.     }

  348.     var = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_variable_value_t));
  349.     if (var == NULL) {
  350.         return NGX_CONF_ERROR;
  351.     }

  352.     v.len = value[1].len;
  353.     v.data = ngx_pstrdup(ctx->keys.pool, &value[1]);
  354.     if (v.data == NULL) {
  355.         return NGX_CONF_ERROR;
  356.     }

  357.     ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));

  358.     ccv.cf = ctx->cf;
  359.     ccv.value = &v;
  360.     ccv.complex_value = &cv;

  361.     if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
  362.         return NGX_CONF_ERROR;
  363.     }

  364.     if (cv.lengths != NULL) {
  365.         cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_complex_value_t));
  366.         if (cvp == NULL) {
  367.             return NGX_CONF_ERROR;
  368.         }

  369.         *cvp = cv;

  370.         var->len = 0;
  371.         var->data = (u_char *) cvp;
  372.         var->valid = 0;

  373.     } else {
  374.         var->len = v.len;
  375.         var->data = v.data;
  376.         var->valid = 1;
  377.     }

  378.     var->no_cacheable = 0;
  379.     var->not_found = 0;

  380.     vp = ngx_array_push(&ctx->values_hash[key]);
  381.     if (vp == NULL) {
  382.         return NGX_CONF_ERROR;
  383.     }

  384.     *vp = var;

  385. found:

  386.     if (ngx_strcmp(value[0].data, "default") == 0) {

  387.         if (ctx->default_value) {
  388.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  389.                                "duplicate default map parameter");
  390.             return NGX_CONF_ERROR;
  391.         }

  392.         ctx->default_value = var;

  393.         return NGX_CONF_OK;
  394.     }

  395. #if (NGX_PCRE)

  396.     if (value[0].len && value[0].data[0] == '~') {
  397.         ngx_regex_compile_t      rc;
  398.         ngx_stream_map_regex_t  *regex;
  399.         u_char                   errstr[NGX_MAX_CONF_ERRSTR];

  400.         regex = ngx_array_push(&ctx->regexes);
  401.         if (regex == NULL) {
  402.             return NGX_CONF_ERROR;
  403.         }

  404.         value[0].len--;
  405.         value[0].data++;

  406.         ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

  407.         if (value[0].data[0] == '*') {
  408.             value[0].len--;
  409.             value[0].data++;
  410.             rc.options = NGX_REGEX_CASELESS;
  411.         }

  412.         rc.pattern = value[0];
  413.         rc.err.len = NGX_MAX_CONF_ERRSTR;
  414.         rc.err.data = errstr;

  415.         regex->regex = ngx_stream_regex_compile(ctx->cf, &rc);
  416.         if (regex->regex == NULL) {
  417.             return NGX_CONF_ERROR;
  418.         }

  419.         regex->value = var;

  420.         return NGX_CONF_OK;
  421.     }

  422. #endif

  423.     if (value[0].len && value[0].data[0] == '\\') {
  424.         value[0].len--;
  425.         value[0].data++;
  426.     }

  427.     rv = ngx_hash_add_key(&ctx->keys, &value[0], var,
  428.                           (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);

  429.     if (rv == NGX_OK) {
  430.         return NGX_CONF_OK;
  431.     }

  432.     if (rv == NGX_DECLINED) {
  433.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  434.                            "invalid hostname or wildcard \"%V\"", &value[0]);
  435.     }

  436.     if (rv == NGX_BUSY) {
  437.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  438.                            "conflicting parameter \"%V\"", &value[0]);
  439.     }

  440.     return NGX_CONF_ERROR;
  441. }