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_http.h>
0011 
0012 
0013 #define NGX_HTTP_REFERER_NO_URI_PART  ((void *) 4)
0014 
0015 
0016 typedef struct {
0017     ngx_hash_combined_t      hash;
0018 
0019 #if (NGX_PCRE)
0020     ngx_array_t             *regex;
0021     ngx_array_t             *server_name_regex;
0022 #endif
0023 
0024     ngx_flag_t               no_referer;
0025     ngx_flag_t               blocked_referer;
0026     ngx_flag_t               server_names;
0027 
0028     ngx_hash_keys_arrays_t  *keys;
0029 
0030     ngx_uint_t               referer_hash_max_size;
0031     ngx_uint_t               referer_hash_bucket_size;
0032 } ngx_http_referer_conf_t;
0033 
0034 
0035 static void * ngx_http_referer_create_conf(ngx_conf_t *cf);
0036 static char * ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent,
0037     void *child);
0038 static char *ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd,
0039     void *conf);
0040 static ngx_int_t ngx_http_add_referer(ngx_conf_t *cf,
0041     ngx_hash_keys_arrays_t *keys, ngx_str_t *value, ngx_str_t *uri);
0042 static ngx_int_t ngx_http_add_regex_referer(ngx_conf_t *cf,
0043     ngx_http_referer_conf_t *rlcf, ngx_str_t *name);
0044 #if (NGX_PCRE)
0045 static ngx_int_t ngx_http_add_regex_server_name(ngx_conf_t *cf,
0046     ngx_http_referer_conf_t *rlcf, ngx_http_regex_t *regex);
0047 #endif
0048 static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,
0049     const void *two);
0050 
0051 
0052 static ngx_command_t  ngx_http_referer_commands[] = {
0053 
0054     { ngx_string("valid_referers"),
0055       NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
0056       ngx_http_valid_referers,
0057       NGX_HTTP_LOC_CONF_OFFSET,
0058       0,
0059       NULL },
0060 
0061     { ngx_string("referer_hash_max_size"),
0062       NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0063       ngx_conf_set_num_slot,
0064       NGX_HTTP_LOC_CONF_OFFSET,
0065       offsetof(ngx_http_referer_conf_t, referer_hash_max_size),
0066       NULL },
0067 
0068     { ngx_string("referer_hash_bucket_size"),
0069       NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0070       ngx_conf_set_num_slot,
0071       NGX_HTTP_LOC_CONF_OFFSET,
0072       offsetof(ngx_http_referer_conf_t, referer_hash_bucket_size),
0073       NULL },
0074 
0075       ngx_null_command
0076 };
0077 
0078 
0079 static ngx_http_module_t  ngx_http_referer_module_ctx = {
0080     NULL,                                  /* preconfiguration */
0081     NULL,                                  /* postconfiguration */
0082 
0083     NULL,                                  /* create main configuration */
0084     NULL,                                  /* init main configuration */
0085 
0086     NULL,                                  /* create server configuration */
0087     NULL,                                  /* merge server configuration */
0088 
0089     ngx_http_referer_create_conf,          /* create location configuration */
0090     ngx_http_referer_merge_conf            /* merge location configuration */
0091 };
0092 
0093 
0094 ngx_module_t  ngx_http_referer_module = {
0095     NGX_MODULE_V1,
0096     &ngx_http_referer_module_ctx,          /* module context */
0097     ngx_http_referer_commands,             /* module directives */
0098     NGX_HTTP_MODULE,                       /* module type */
0099     NULL,                                  /* init master */
0100     NULL,                                  /* init module */
0101     NULL,                                  /* init process */
0102     NULL,                                  /* init thread */
0103     NULL,                                  /* exit thread */
0104     NULL,                                  /* exit process */
0105     NULL,                                  /* exit master */
0106     NGX_MODULE_V1_PADDING
0107 };
0108 
0109 
0110 static ngx_int_t
0111 ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
0112     uintptr_t data)
0113 {
0114     u_char                    *p, *ref, *last;
0115     size_t                     len;
0116     ngx_str_t                 *uri;
0117     ngx_uint_t                 i, key;
0118     ngx_http_referer_conf_t   *rlcf;
0119     u_char                     buf[256];
0120 #if (NGX_PCRE)
0121     ngx_int_t                  rc;
0122     ngx_str_t                  referer;
0123 #endif
0124 
0125     rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);
0126 
0127     if (rlcf->hash.hash.buckets == NULL
0128         && rlcf->hash.wc_head == NULL
0129         && rlcf->hash.wc_tail == NULL
0130 #if (NGX_PCRE)
0131         && rlcf->regex == NULL
0132         && rlcf->server_name_regex == NULL
0133 #endif
0134        )
0135     {
0136         goto valid;
0137     }
0138 
0139     if (r->headers_in.referer == NULL) {
0140         if (rlcf->no_referer) {
0141             goto valid;
0142         }
0143 
0144         goto invalid;
0145     }
0146 
0147     len = r->headers_in.referer->value.len;
0148     ref = r->headers_in.referer->value.data;
0149 
0150     if (len >= sizeof("http://i.ru") - 1) {
0151         last = ref + len;
0152 
0153         if (ngx_strncasecmp(ref, (u_char *) "http://", 7) == 0) {
0154             ref += 7;
0155             len -= 7;
0156             goto valid_scheme;
0157 
0158         } else if (ngx_strncasecmp(ref, (u_char *) "https://", 8) == 0) {
0159             ref += 8;
0160             len -= 8;
0161             goto valid_scheme;
0162         }
0163     }
0164 
0165     if (rlcf->blocked_referer) {
0166         goto valid;
0167     }
0168 
0169     goto invalid;
0170 
0171 valid_scheme:
0172 
0173     i = 0;
0174     key = 0;
0175 
0176     for (p = ref; p < last; p++) {
0177         if (*p == '/' || *p == ':') {
0178             break;
0179         }
0180 
0181         if (i == 256) {
0182             goto invalid;
0183         }
0184 
0185         buf[i] = ngx_tolower(*p);
0186         key = ngx_hash(key, buf[i++]);
0187     }
0188 
0189     uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref);
0190 
0191     if (uri) {
0192         goto uri;
0193     }
0194 
0195 #if (NGX_PCRE)
0196 
0197     if (rlcf->server_name_regex) {
0198         referer.len = p - ref;
0199         referer.data = buf;
0200 
0201         rc = ngx_regex_exec_array(rlcf->server_name_regex, &referer,
0202                                   r->connection->log);
0203 
0204         if (rc == NGX_OK) {
0205             goto valid;
0206         }
0207 
0208         if (rc == NGX_ERROR) {
0209             return rc;
0210         }
0211 
0212         /* NGX_DECLINED */
0213     }
0214 
0215     if (rlcf->regex) {
0216         referer.len = len;
0217         referer.data = ref;
0218 
0219         rc = ngx_regex_exec_array(rlcf->regex, &referer, r->connection->log);
0220 
0221         if (rc == NGX_OK) {
0222             goto valid;
0223         }
0224 
0225         if (rc == NGX_ERROR) {
0226             return rc;
0227         }
0228 
0229         /* NGX_DECLINED */
0230     }
0231 
0232 #endif
0233 
0234 invalid:
0235 
0236     *v = ngx_http_variable_true_value;
0237 
0238     return NGX_OK;
0239 
0240 uri:
0241 
0242     for ( /* void */ ; p < last; p++) {
0243         if (*p == '/') {
0244             break;
0245         }
0246     }
0247 
0248     len = last - p;
0249 
0250     if (uri == NGX_HTTP_REFERER_NO_URI_PART) {
0251         goto valid;
0252     }
0253 
0254     if (len < uri->len || ngx_strncmp(uri->data, p, uri->len) != 0) {
0255         goto invalid;
0256     }
0257 
0258 valid:
0259 
0260     *v = ngx_http_variable_null_value;
0261 
0262     return NGX_OK;
0263 }
0264 
0265 
0266 static void *
0267 ngx_http_referer_create_conf(ngx_conf_t *cf)
0268 {
0269     ngx_http_referer_conf_t  *conf;
0270 
0271     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t));
0272     if (conf == NULL) {
0273         return NULL;
0274     }
0275 
0276     /*
0277      * set by ngx_pcalloc():
0278      *
0279      *     conf->hash = { NULL };
0280      *     conf->server_names = 0;
0281      *     conf->keys = NULL;
0282      */
0283 
0284 #if (NGX_PCRE)
0285     conf->regex = NGX_CONF_UNSET_PTR;
0286     conf->server_name_regex = NGX_CONF_UNSET_PTR;
0287 #endif
0288 
0289     conf->no_referer = NGX_CONF_UNSET;
0290     conf->blocked_referer = NGX_CONF_UNSET;
0291     conf->referer_hash_max_size = NGX_CONF_UNSET_UINT;
0292     conf->referer_hash_bucket_size = NGX_CONF_UNSET_UINT;
0293 
0294     return conf;
0295 }
0296 
0297 
0298 static char *
0299 ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child)
0300 {
0301     ngx_http_referer_conf_t *prev = parent;
0302     ngx_http_referer_conf_t *conf = child;
0303 
0304     ngx_uint_t                 n;
0305     ngx_hash_init_t            hash;
0306     ngx_http_server_name_t    *sn;
0307     ngx_http_core_srv_conf_t  *cscf;
0308 
0309     if (conf->keys == NULL) {
0310         conf->hash = prev->hash;
0311 
0312 #if (NGX_PCRE)
0313         ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
0314         ngx_conf_merge_ptr_value(conf->server_name_regex,
0315                                  prev->server_name_regex, NULL);
0316 #endif
0317         ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);
0318         ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0);
0319         ngx_conf_merge_uint_value(conf->referer_hash_max_size,
0320                                   prev->referer_hash_max_size, 2048);
0321         ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,
0322                                   prev->referer_hash_bucket_size, 64);
0323 
0324         return NGX_CONF_OK;
0325     }
0326 
0327     if (conf->server_names == 1) {
0328         cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
0329 
0330         sn = cscf->server_names.elts;
0331         for (n = 0; n < cscf->server_names.nelts; n++) {
0332 
0333 #if (NGX_PCRE)
0334             if (sn[n].regex) {
0335 
0336                 if (ngx_http_add_regex_server_name(cf, conf, sn[n].regex)
0337                     != NGX_OK)
0338                 {
0339                     return NGX_CONF_ERROR;
0340                 }
0341 
0342                 continue;
0343             }
0344 #endif
0345 
0346             if (ngx_http_add_referer(cf, conf->keys, &sn[n].name, NULL)
0347                 != NGX_OK)
0348             {
0349                 return NGX_CONF_ERROR;
0350             }
0351         }
0352     }
0353 
0354     if ((conf->no_referer == 1 || conf->blocked_referer == 1)
0355         && conf->keys->keys.nelts == 0
0356         && conf->keys->dns_wc_head.nelts == 0
0357         && conf->keys->dns_wc_tail.nelts == 0)
0358     {
0359         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
0360                       "the \"none\" or \"blocked\" referers are specified "
0361                       "in the \"valid_referers\" directive "
0362                       "without any valid referer");
0363         return NGX_CONF_ERROR;
0364     }
0365 
0366     ngx_conf_merge_uint_value(conf->referer_hash_max_size,
0367                               prev->referer_hash_max_size, 2048);
0368     ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,
0369                               prev->referer_hash_bucket_size, 64);
0370     conf->referer_hash_bucket_size = ngx_align(conf->referer_hash_bucket_size,
0371                                                ngx_cacheline_size);
0372 
0373     hash.key = ngx_hash_key_lc;
0374     hash.max_size = conf->referer_hash_max_size;
0375     hash.bucket_size = conf->referer_hash_bucket_size;
0376     hash.name = "referer_hash";
0377     hash.pool = cf->pool;
0378 
0379     if (conf->keys->keys.nelts) {
0380         hash.hash = &conf->hash.hash;
0381         hash.temp_pool = NULL;
0382 
0383         if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts)
0384             != NGX_OK)
0385         {
0386             return NGX_CONF_ERROR;
0387         }
0388     }
0389 
0390     if (conf->keys->dns_wc_head.nelts) {
0391 
0392         ngx_qsort(conf->keys->dns_wc_head.elts,
0393                   (size_t) conf->keys->dns_wc_head.nelts,
0394                   sizeof(ngx_hash_key_t),
0395                   ngx_http_cmp_referer_wildcards);
0396 
0397         hash.hash = NULL;
0398         hash.temp_pool = cf->temp_pool;
0399 
0400         if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_head.elts,
0401                                    conf->keys->dns_wc_head.nelts)
0402             != NGX_OK)
0403         {
0404             return NGX_CONF_ERROR;
0405         }
0406 
0407         conf->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
0408     }
0409 
0410     if (conf->keys->dns_wc_tail.nelts) {
0411 
0412         ngx_qsort(conf->keys->dns_wc_tail.elts,
0413                   (size_t) conf->keys->dns_wc_tail.nelts,
0414                   sizeof(ngx_hash_key_t),
0415                   ngx_http_cmp_referer_wildcards);
0416 
0417         hash.hash = NULL;
0418         hash.temp_pool = cf->temp_pool;
0419 
0420         if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_tail.elts,
0421                                    conf->keys->dns_wc_tail.nelts)
0422             != NGX_OK)
0423         {
0424             return NGX_CONF_ERROR;
0425         }
0426 
0427         conf->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
0428     }
0429 
0430 #if (NGX_PCRE)
0431     ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
0432     ngx_conf_merge_ptr_value(conf->server_name_regex, prev->server_name_regex,
0433                              NULL);
0434 #endif
0435 
0436     if (conf->no_referer == NGX_CONF_UNSET) {
0437         conf->no_referer = 0;
0438     }
0439 
0440     if (conf->blocked_referer == NGX_CONF_UNSET) {
0441         conf->blocked_referer = 0;
0442     }
0443 
0444     conf->keys = NULL;
0445 
0446     return NGX_CONF_OK;
0447 }
0448 
0449 
0450 static char *
0451 ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0452 {
0453     ngx_http_referer_conf_t  *rlcf = conf;
0454 
0455     u_char                    *p;
0456     ngx_str_t                 *value, uri, name;
0457     ngx_uint_t                 i;
0458     ngx_http_variable_t       *var;
0459 
0460     ngx_str_set(&name, "invalid_referer");
0461 
0462     var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
0463     if (var == NULL) {
0464         return NGX_CONF_ERROR;
0465     }
0466 
0467     var->get_handler = ngx_http_referer_variable;
0468 
0469     if (rlcf->keys == NULL) {
0470         rlcf->keys = ngx_pcalloc(cf->temp_pool, sizeof(ngx_hash_keys_arrays_t));
0471         if (rlcf->keys == NULL) {
0472             return NGX_CONF_ERROR;
0473         }
0474 
0475         rlcf->keys->pool = cf->pool;
0476         rlcf->keys->temp_pool = cf->pool;
0477 
0478         if (ngx_hash_keys_array_init(rlcf->keys, NGX_HASH_SMALL) != NGX_OK) {
0479             return NGX_CONF_ERROR;
0480         }
0481     }
0482 
0483     value = cf->args->elts;
0484 
0485     for (i = 1; i < cf->args->nelts; i++) {
0486         if (value[i].len == 0) {
0487             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0488                                "invalid referer \"%V\"", &value[i]);
0489             return NGX_CONF_ERROR;
0490         }
0491 
0492         if (ngx_strcmp(value[i].data, "none") == 0) {
0493             rlcf->no_referer = 1;
0494             continue;
0495         }
0496 
0497         if (ngx_strcmp(value[i].data, "blocked") == 0) {
0498             rlcf->blocked_referer = 1;
0499             continue;
0500         }
0501 
0502         if (ngx_strcmp(value[i].data, "server_names") == 0) {
0503             rlcf->server_names = 1;
0504             continue;
0505         }
0506 
0507         if (value[i].data[0] == '~') {
0508             if (ngx_http_add_regex_referer(cf, rlcf, &value[i]) != NGX_OK) {
0509                 return NGX_CONF_ERROR;
0510             }
0511 
0512             continue;
0513         }
0514 
0515         ngx_str_null(&uri);
0516 
0517         p = (u_char *) ngx_strchr(value[i].data, '/');
0518 
0519         if (p) {
0520             uri.len = (value[i].data + value[i].len) - p;
0521             uri.data = p;
0522             value[i].len = p - value[i].data;
0523         }
0524 
0525         if (ngx_http_add_referer(cf, rlcf->keys, &value[i], &uri) != NGX_OK) {
0526             return NGX_CONF_ERROR;
0527         }
0528     }
0529 
0530     return NGX_CONF_OK;
0531 }
0532 
0533 
0534 static ngx_int_t
0535 ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
0536     ngx_str_t *value, ngx_str_t *uri)
0537 {
0538     ngx_int_t   rc;
0539     ngx_str_t  *u;
0540 
0541     if (uri == NULL || uri->len == 0) {
0542         u = NGX_HTTP_REFERER_NO_URI_PART;
0543 
0544     } else {
0545         u = ngx_palloc(cf->pool, sizeof(ngx_str_t));
0546         if (u == NULL) {
0547             return NGX_ERROR;
0548         }
0549 
0550         *u = *uri;
0551     }
0552 
0553     rc = ngx_hash_add_key(keys, value, u, NGX_HASH_WILDCARD_KEY);
0554 
0555     if (rc == NGX_OK) {
0556         return NGX_OK;
0557     }
0558 
0559     if (rc == NGX_DECLINED) {
0560         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0561                            "invalid hostname or wildcard \"%V\"", value);
0562     }
0563 
0564     if (rc == NGX_BUSY) {
0565         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0566                            "conflicting parameter \"%V\"", value);
0567     }
0568 
0569     return NGX_ERROR;
0570 }
0571 
0572 
0573 static ngx_int_t
0574 ngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
0575     ngx_str_t *name)
0576 {
0577 #if (NGX_PCRE)
0578     ngx_regex_elt_t      *re;
0579     ngx_regex_compile_t   rc;
0580     u_char                errstr[NGX_MAX_CONF_ERRSTR];
0581 
0582     if (name->len == 1) {
0583         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty regex in \"%V\"", name);
0584         return NGX_ERROR;
0585     }
0586 
0587     if (rlcf->regex == NGX_CONF_UNSET_PTR) {
0588         rlcf->regex = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));
0589         if (rlcf->regex == NULL) {
0590             return NGX_ERROR;
0591         }
0592     }
0593 
0594     re = ngx_array_push(rlcf->regex);
0595     if (re == NULL) {
0596         return NGX_ERROR;
0597     }
0598 
0599     name->len--;
0600     name->data++;
0601 
0602     ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
0603 
0604     rc.pattern = *name;
0605     rc.pool = cf->pool;
0606     rc.options = NGX_REGEX_CASELESS;
0607     rc.err.len = NGX_MAX_CONF_ERRSTR;
0608     rc.err.data = errstr;
0609 
0610     if (ngx_regex_compile(&rc) != NGX_OK) {
0611         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
0612         return NGX_ERROR;
0613     }
0614 
0615     re->regex = rc.regex;
0616     re->name = name->data;
0617 
0618     return NGX_OK;
0619 
0620 #else
0621 
0622     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0623                        "the using of the regex \"%V\" requires PCRE library",
0624                        name);
0625 
0626     return NGX_ERROR;
0627 
0628 #endif
0629 }
0630 
0631 
0632 #if (NGX_PCRE)
0633 
0634 static ngx_int_t
0635 ngx_http_add_regex_server_name(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
0636     ngx_http_regex_t *regex)
0637 {
0638     ngx_regex_elt_t  *re;
0639 
0640     if (rlcf->server_name_regex == NGX_CONF_UNSET_PTR) {
0641         rlcf->server_name_regex = ngx_array_create(cf->pool, 2,
0642                                                    sizeof(ngx_regex_elt_t));
0643         if (rlcf->server_name_regex == NULL) {
0644             return NGX_ERROR;
0645         }
0646     }
0647 
0648     re = ngx_array_push(rlcf->server_name_regex);
0649     if (re == NULL) {
0650         return NGX_ERROR;
0651     }
0652 
0653     re->regex = regex->regex;
0654     re->name = regex->name.data;
0655 
0656     return NGX_OK;
0657 }
0658 
0659 #endif
0660 
0661 
0662 static int ngx_libc_cdecl
0663 ngx_http_cmp_referer_wildcards(const void *one, const void *two)
0664 {
0665     ngx_hash_key_t  *first, *second;
0666 
0667     first = (ngx_hash_key_t *) one;
0668     second = (ngx_hash_key_t *) two;
0669 
0670     return ngx_dns_strcmp(first->key.data, second->key.data);
0671 }