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