Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_stream.h>
0011 
0012 
0013 typedef struct {
0014     ngx_uint_t                    hash_max_size;
0015     ngx_uint_t                    hash_bucket_size;
0016 } ngx_stream_map_conf_t;
0017 
0018 
0019 typedef struct {
0020     ngx_hash_keys_arrays_t        keys;
0021 
0022     ngx_array_t                  *values_hash;
0023 #if (NGX_PCRE)
0024     ngx_array_t                   regexes;
0025 #endif
0026 
0027     ngx_stream_variable_value_t  *default_value;
0028     ngx_conf_t                   *cf;
0029     unsigned                      hostnames:1;
0030     unsigned                      no_cacheable:1;
0031 } ngx_stream_map_conf_ctx_t;
0032 
0033 
0034 typedef struct {
0035     ngx_stream_map_t              map;
0036     ngx_stream_complex_value_t    value;
0037     ngx_stream_variable_value_t  *default_value;
0038     ngx_uint_t                    hostnames;      /* unsigned  hostnames:1 */
0039 } ngx_stream_map_ctx_t;
0040 
0041 
0042 static int ngx_libc_cdecl ngx_stream_map_cmp_dns_wildcards(const void *one,
0043     const void *two);
0044 static void *ngx_stream_map_create_conf(ngx_conf_t *cf);
0045 static char *ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd,
0046     void *conf);
0047 static char *ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
0048 
0049 
0050 static ngx_command_t  ngx_stream_map_commands[] = {
0051 
0052     { ngx_string("map"),
0053       NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
0054       ngx_stream_map_block,
0055       NGX_STREAM_MAIN_CONF_OFFSET,
0056       0,
0057       NULL },
0058 
0059     { ngx_string("map_hash_max_size"),
0060       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
0061       ngx_conf_set_num_slot,
0062       NGX_STREAM_MAIN_CONF_OFFSET,
0063       offsetof(ngx_stream_map_conf_t, hash_max_size),
0064       NULL },
0065 
0066     { ngx_string("map_hash_bucket_size"),
0067       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
0068       ngx_conf_set_num_slot,
0069       NGX_STREAM_MAIN_CONF_OFFSET,
0070       offsetof(ngx_stream_map_conf_t, hash_bucket_size),
0071       NULL },
0072 
0073       ngx_null_command
0074 };
0075 
0076 
0077 static ngx_stream_module_t  ngx_stream_map_module_ctx = {
0078     NULL,                                  /* preconfiguration */
0079     NULL,                                  /* postconfiguration */
0080 
0081     ngx_stream_map_create_conf,            /* create main configuration */
0082     NULL,                                  /* init main configuration */
0083 
0084     NULL,                                  /* create server configuration */
0085     NULL                                   /* merge server configuration */
0086 };
0087 
0088 
0089 ngx_module_t  ngx_stream_map_module = {
0090     NGX_MODULE_V1,
0091     &ngx_stream_map_module_ctx,            /* module context */
0092     ngx_stream_map_commands,               /* module directives */
0093     NGX_STREAM_MODULE,                     /* module type */
0094     NULL,                                  /* init master */
0095     NULL,                                  /* init module */
0096     NULL,                                  /* init process */
0097     NULL,                                  /* init thread */
0098     NULL,                                  /* exit thread */
0099     NULL,                                  /* exit process */
0100     NULL,                                  /* exit master */
0101     NGX_MODULE_V1_PADDING
0102 };
0103 
0104 
0105 static ngx_int_t
0106 ngx_stream_map_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,
0107     uintptr_t data)
0108 {
0109     ngx_stream_map_ctx_t  *map = (ngx_stream_map_ctx_t *) data;
0110 
0111     ngx_str_t                     val, str;
0112     ngx_stream_complex_value_t   *cv;
0113     ngx_stream_variable_value_t  *value;
0114 
0115     ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
0116                    "stream map started");
0117 
0118     if (ngx_stream_complex_value(s, &map->value, &val) != NGX_OK) {
0119         return NGX_ERROR;
0120     }
0121 
0122     if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {
0123         val.len--;
0124     }
0125 
0126     value = ngx_stream_map_find(s, &map->map, &val);
0127 
0128     if (value == NULL) {
0129         value = map->default_value;
0130     }
0131 
0132     if (!value->valid) {
0133         cv = (ngx_stream_complex_value_t *) value->data;
0134 
0135         if (ngx_stream_complex_value(s, cv, &str) != NGX_OK) {
0136             return NGX_ERROR;
0137         }
0138 
0139         v->valid = 1;
0140         v->no_cacheable = 0;
0141         v->not_found = 0;
0142         v->len = str.len;
0143         v->data = str.data;
0144 
0145     } else {
0146         *v = *value;
0147     }
0148 
0149     ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
0150                    "stream map: \"%V\" \"%v\"", &val, v);
0151 
0152     return NGX_OK;
0153 }
0154 
0155 
0156 static void *
0157 ngx_stream_map_create_conf(ngx_conf_t *cf)
0158 {
0159     ngx_stream_map_conf_t  *mcf;
0160 
0161     mcf = ngx_palloc(cf->pool, sizeof(ngx_stream_map_conf_t));
0162     if (mcf == NULL) {
0163         return NULL;
0164     }
0165 
0166     mcf->hash_max_size = NGX_CONF_UNSET_UINT;
0167     mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;
0168 
0169     return mcf;
0170 }
0171 
0172 
0173 static char *
0174 ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0175 {
0176     ngx_stream_map_conf_t  *mcf = conf;
0177 
0178     char                                *rv;
0179     ngx_str_t                           *value, name;
0180     ngx_conf_t                           save;
0181     ngx_pool_t                          *pool;
0182     ngx_hash_init_t                      hash;
0183     ngx_stream_map_ctx_t                *map;
0184     ngx_stream_variable_t               *var;
0185     ngx_stream_map_conf_ctx_t            ctx;
0186     ngx_stream_compile_complex_value_t   ccv;
0187 
0188     if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {
0189         mcf->hash_max_size = 2048;
0190     }
0191 
0192     if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {
0193         mcf->hash_bucket_size = ngx_cacheline_size;
0194 
0195     } else {
0196         mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,
0197                                           ngx_cacheline_size);
0198     }
0199 
0200     map = ngx_pcalloc(cf->pool, sizeof(ngx_stream_map_ctx_t));
0201     if (map == NULL) {
0202         return NGX_CONF_ERROR;
0203     }
0204 
0205     value = cf->args->elts;
0206 
0207     ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
0208 
0209     ccv.cf = cf;
0210     ccv.value = &value[1];
0211     ccv.complex_value = &map->value;
0212 
0213     if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
0214         return NGX_CONF_ERROR;
0215     }
0216 
0217     name = value[2];
0218 
0219     if (name.data[0] != '$') {
0220         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0221                            "invalid variable name \"%V\"", &name);
0222         return NGX_CONF_ERROR;
0223     }
0224 
0225     name.len--;
0226     name.data++;
0227 
0228     var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);
0229     if (var == NULL) {
0230         return NGX_CONF_ERROR;
0231     }
0232 
0233     var->get_handler = ngx_stream_map_variable;
0234     var->data = (uintptr_t) map;
0235 
0236     pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
0237     if (pool == NULL) {
0238         return NGX_CONF_ERROR;
0239     }
0240 
0241     ctx.keys.pool = cf->pool;
0242     ctx.keys.temp_pool = pool;
0243 
0244     if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {
0245         ngx_destroy_pool(pool);
0246         return NGX_CONF_ERROR;
0247     }
0248 
0249     ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize);
0250     if (ctx.values_hash == NULL) {
0251         ngx_destroy_pool(pool);
0252         return NGX_CONF_ERROR;
0253     }
0254 
0255 #if (NGX_PCRE)
0256     if (ngx_array_init(&ctx.regexes, cf->pool, 2,
0257                        sizeof(ngx_stream_map_regex_t))
0258         != NGX_OK)
0259     {
0260         ngx_destroy_pool(pool);
0261         return NGX_CONF_ERROR;
0262     }
0263 #endif
0264 
0265     ctx.default_value = NULL;
0266     ctx.cf = &save;
0267     ctx.hostnames = 0;
0268     ctx.no_cacheable = 0;
0269 
0270     save = *cf;
0271     cf->pool = pool;
0272     cf->ctx = &ctx;
0273     cf->handler = ngx_stream_map;
0274     cf->handler_conf = conf;
0275 
0276     rv = ngx_conf_parse(cf, NULL);
0277 
0278     *cf = save;
0279 
0280     if (rv != NGX_CONF_OK) {
0281         ngx_destroy_pool(pool);
0282         return rv;
0283     }
0284 
0285     if (ctx.no_cacheable) {
0286         var->flags |= NGX_STREAM_VAR_NOCACHEABLE;
0287     }
0288 
0289     map->default_value = ctx.default_value ? ctx.default_value:
0290                                              &ngx_stream_variable_null_value;
0291 
0292     map->hostnames = ctx.hostnames;
0293 
0294     hash.key = ngx_hash_key_lc;
0295     hash.max_size = mcf->hash_max_size;
0296     hash.bucket_size = mcf->hash_bucket_size;
0297     hash.name = "map_hash";
0298     hash.pool = cf->pool;
0299 
0300     if (ctx.keys.keys.nelts) {
0301         hash.hash = &map->map.hash.hash;
0302         hash.temp_pool = NULL;
0303 
0304         if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)
0305             != NGX_OK)
0306         {
0307             ngx_destroy_pool(pool);
0308             return NGX_CONF_ERROR;
0309         }
0310     }
0311 
0312     if (ctx.keys.dns_wc_head.nelts) {
0313 
0314         ngx_qsort(ctx.keys.dns_wc_head.elts,
0315                   (size_t) ctx.keys.dns_wc_head.nelts,
0316                   sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards);
0317 
0318         hash.hash = NULL;
0319         hash.temp_pool = pool;
0320 
0321         if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,
0322                                    ctx.keys.dns_wc_head.nelts)
0323             != NGX_OK)
0324         {
0325             ngx_destroy_pool(pool);
0326             return NGX_CONF_ERROR;
0327         }
0328 
0329         map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
0330     }
0331 
0332     if (ctx.keys.dns_wc_tail.nelts) {
0333 
0334         ngx_qsort(ctx.keys.dns_wc_tail.elts,
0335                   (size_t) ctx.keys.dns_wc_tail.nelts,
0336                   sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards);
0337 
0338         hash.hash = NULL;
0339         hash.temp_pool = pool;
0340 
0341         if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,
0342                                    ctx.keys.dns_wc_tail.nelts)
0343             != NGX_OK)
0344         {
0345             ngx_destroy_pool(pool);
0346             return NGX_CONF_ERROR;
0347         }
0348 
0349         map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
0350     }
0351 
0352 #if (NGX_PCRE)
0353 
0354     if (ctx.regexes.nelts) {
0355         map->map.regex = ctx.regexes.elts;
0356         map->map.nregex = ctx.regexes.nelts;
0357     }
0358 
0359 #endif
0360 
0361     ngx_destroy_pool(pool);
0362 
0363     return rv;
0364 }
0365 
0366 
0367 static int ngx_libc_cdecl
0368 ngx_stream_map_cmp_dns_wildcards(const void *one, const void *two)
0369 {
0370     ngx_hash_key_t  *first, *second;
0371 
0372     first = (ngx_hash_key_t *) one;
0373     second = (ngx_hash_key_t *) two;
0374 
0375     return ngx_dns_strcmp(first->key.data, second->key.data);
0376 }
0377 
0378 
0379 static char *
0380 ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
0381 {
0382     u_char                              *data;
0383     size_t                               len;
0384     ngx_int_t                            rv;
0385     ngx_str_t                           *value, v;
0386     ngx_uint_t                           i, key;
0387     ngx_stream_map_conf_ctx_t           *ctx;
0388     ngx_stream_complex_value_t           cv, *cvp;
0389     ngx_stream_variable_value_t         *var, **vp;
0390     ngx_stream_compile_complex_value_t   ccv;
0391 
0392     ctx = cf->ctx;
0393 
0394     value = cf->args->elts;
0395 
0396     if (cf->args->nelts == 1
0397         && ngx_strcmp(value[0].data, "hostnames") == 0)
0398     {
0399         ctx->hostnames = 1;
0400         return NGX_CONF_OK;
0401     }
0402 
0403     if (cf->args->nelts == 1
0404         && ngx_strcmp(value[0].data, "volatile") == 0)
0405     {
0406         ctx->no_cacheable = 1;
0407         return NGX_CONF_OK;
0408     }
0409 
0410     if (cf->args->nelts != 2) {
0411         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0412                            "invalid number of the map parameters");
0413         return NGX_CONF_ERROR;
0414     }
0415 
0416     if (ngx_strcmp(value[0].data, "include") == 0) {
0417         return ngx_conf_include(cf, dummy, conf);
0418     }
0419 
0420     key = 0;
0421 
0422     for (i = 0; i < value[1].len; i++) {
0423         key = ngx_hash(key, value[1].data[i]);
0424     }
0425 
0426     key %= ctx->keys.hsize;
0427 
0428     vp = ctx->values_hash[key].elts;
0429 
0430     if (vp) {
0431         for (i = 0; i < ctx->values_hash[key].nelts; i++) {
0432 
0433             if (vp[i]->valid) {
0434                 data = vp[i]->data;
0435                 len = vp[i]->len;
0436 
0437             } else {
0438                 cvp = (ngx_stream_complex_value_t *) vp[i]->data;
0439                 data = cvp->value.data;
0440                 len = cvp->value.len;
0441             }
0442 
0443             if (value[1].len != len) {
0444                 continue;
0445             }
0446 
0447             if (ngx_strncmp(value[1].data, data, len) == 0) {
0448                 var = vp[i];
0449                 goto found;
0450             }
0451         }
0452 
0453     } else {
0454         if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,
0455                            sizeof(ngx_stream_variable_value_t *))
0456             != NGX_OK)
0457         {
0458             return NGX_CONF_ERROR;
0459         }
0460     }
0461 
0462     var = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_variable_value_t));
0463     if (var == NULL) {
0464         return NGX_CONF_ERROR;
0465     }
0466 
0467     v.len = value[1].len;
0468     v.data = ngx_pstrdup(ctx->keys.pool, &value[1]);
0469     if (v.data == NULL) {
0470         return NGX_CONF_ERROR;
0471     }
0472 
0473     ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
0474 
0475     ccv.cf = ctx->cf;
0476     ccv.value = &v;
0477     ccv.complex_value = &cv;
0478 
0479     if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
0480         return NGX_CONF_ERROR;
0481     }
0482 
0483     if (cv.lengths != NULL) {
0484         cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_complex_value_t));
0485         if (cvp == NULL) {
0486             return NGX_CONF_ERROR;
0487         }
0488 
0489         *cvp = cv;
0490 
0491         var->len = 0;
0492         var->data = (u_char *) cvp;
0493         var->valid = 0;
0494 
0495     } else {
0496         var->len = v.len;
0497         var->data = v.data;
0498         var->valid = 1;
0499     }
0500 
0501     var->no_cacheable = 0;
0502     var->not_found = 0;
0503 
0504     vp = ngx_array_push(&ctx->values_hash[key]);
0505     if (vp == NULL) {
0506         return NGX_CONF_ERROR;
0507     }
0508 
0509     *vp = var;
0510 
0511 found:
0512 
0513     if (ngx_strcmp(value[0].data, "default") == 0) {
0514 
0515         if (ctx->default_value) {
0516             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0517                                "duplicate default map parameter");
0518             return NGX_CONF_ERROR;
0519         }
0520 
0521         ctx->default_value = var;
0522 
0523         return NGX_CONF_OK;
0524     }
0525 
0526 #if (NGX_PCRE)
0527 
0528     if (value[0].len && value[0].data[0] == '~') {
0529         ngx_regex_compile_t      rc;
0530         ngx_stream_map_regex_t  *regex;
0531         u_char                   errstr[NGX_MAX_CONF_ERRSTR];
0532 
0533         regex = ngx_array_push(&ctx->regexes);
0534         if (regex == NULL) {
0535             return NGX_CONF_ERROR;
0536         }
0537 
0538         value[0].len--;
0539         value[0].data++;
0540 
0541         ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
0542 
0543         if (value[0].data[0] == '*') {
0544             value[0].len--;
0545             value[0].data++;
0546             rc.options = NGX_REGEX_CASELESS;
0547         }
0548 
0549         rc.pattern = value[0];
0550         rc.err.len = NGX_MAX_CONF_ERRSTR;
0551         rc.err.data = errstr;
0552 
0553         regex->regex = ngx_stream_regex_compile(ctx->cf, &rc);
0554         if (regex->regex == NULL) {
0555             return NGX_CONF_ERROR;
0556         }
0557 
0558         regex->value = var;
0559 
0560         return NGX_CONF_OK;
0561     }
0562 
0563 #endif
0564 
0565     if (value[0].len && value[0].data[0] == '\\') {
0566         value[0].len--;
0567         value[0].data++;
0568     }
0569 
0570     rv = ngx_hash_add_key(&ctx->keys, &value[0], var,
0571                           (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);
0572 
0573     if (rv == NGX_OK) {
0574         return NGX_CONF_OK;
0575     }
0576 
0577     if (rv == NGX_DECLINED) {
0578         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0579                            "invalid hostname or wildcard \"%V\"", &value[0]);
0580     }
0581 
0582     if (rv == NGX_BUSY) {
0583         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0584                            "conflicting parameter \"%V\"", &value[0]);
0585     }
0586 
0587     return NGX_CONF_ERROR;
0588 }