Annotation of embedaddon/nginx/src/http/modules/ngx_http_referer_module.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * Copyright (C) Igor Sysoev
                      4:  * Copyright (C) Nginx, Inc.
                      5:  */
                      6: 
                      7: 
                      8: #include <ngx_config.h>
                      9: #include <ngx_core.h>
                     10: #include <ngx_http.h>
                     11: 
                     12: 
                     13: #define NGX_HTTP_REFERER_NO_URI_PART  ((void *) 4)
                     14: 
                     15: #if !(NGX_PCRE)
                     16: 
                     17: #define ngx_regex_t          void
                     18: 
                     19: #endif
                     20: 
                     21: 
                     22: typedef struct {
                     23:     ngx_hash_combined_t      hash;
                     24: 
                     25: #if (NGX_PCRE)
                     26:     ngx_array_t             *regex;
                     27: #endif
                     28: 
                     29:     ngx_flag_t               no_referer;
                     30:     ngx_flag_t               blocked_referer;
                     31: 
                     32:     ngx_hash_keys_arrays_t  *keys;
                     33: 
                     34:     ngx_uint_t               referer_hash_max_size;
                     35:     ngx_uint_t               referer_hash_bucket_size;
                     36: } ngx_http_referer_conf_t;
                     37: 
                     38: 
                     39: static void * ngx_http_referer_create_conf(ngx_conf_t *cf);
                     40: static char * ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent,
                     41:     void *child);
                     42: static char *ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd,
                     43:     void *conf);
                     44: static char *ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
                     45:     ngx_str_t *value, ngx_str_t *uri);
                     46: static char *ngx_http_add_regex_referer(ngx_conf_t *cf,
                     47:     ngx_http_referer_conf_t *rlcf, ngx_str_t *name, ngx_regex_t *regex);
                     48: static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,
                     49:     const void *two);
                     50: 
                     51: 
                     52: static ngx_command_t  ngx_http_referer_commands[] = {
                     53: 
                     54:     { ngx_string("valid_referers"),
                     55:       NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
                     56:       ngx_http_valid_referers,
                     57:       NGX_HTTP_LOC_CONF_OFFSET,
                     58:       0,
                     59:       NULL },
                     60: 
                     61:     { ngx_string("referer_hash_max_size"),
                     62:       NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
                     63:       ngx_conf_set_num_slot,
                     64:       NGX_HTTP_LOC_CONF_OFFSET,
                     65:       offsetof(ngx_http_referer_conf_t, referer_hash_max_size),
                     66:       NULL },
                     67: 
                     68:     { ngx_string("referer_hash_bucket_size"),
                     69:       NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
                     70:       ngx_conf_set_num_slot,
                     71:       NGX_HTTP_LOC_CONF_OFFSET,
                     72:       offsetof(ngx_http_referer_conf_t, referer_hash_bucket_size),
                     73:       NULL },
                     74: 
                     75:       ngx_null_command
                     76: };
                     77: 
                     78: 
                     79: static ngx_http_module_t  ngx_http_referer_module_ctx = {
                     80:     NULL,                                  /* preconfiguration */
                     81:     NULL,                                  /* postconfiguration */
                     82: 
                     83:     NULL,                                  /* create main configuration */
                     84:     NULL,                                  /* init main configuration */
                     85: 
                     86:     NULL,                                  /* create server configuration */
                     87:     NULL,                                  /* merge server configuration */
                     88: 
                     89:     ngx_http_referer_create_conf,          /* create location configuration */
                     90:     ngx_http_referer_merge_conf            /* merge location configuration */
                     91: };
                     92: 
                     93: 
                     94: ngx_module_t  ngx_http_referer_module = {
                     95:     NGX_MODULE_V1,
                     96:     &ngx_http_referer_module_ctx,          /* module context */
                     97:     ngx_http_referer_commands,             /* module directives */
                     98:     NGX_HTTP_MODULE,                       /* module type */
                     99:     NULL,                                  /* init master */
                    100:     NULL,                                  /* init module */
                    101:     NULL,                                  /* init process */
                    102:     NULL,                                  /* init thread */
                    103:     NULL,                                  /* exit thread */
                    104:     NULL,                                  /* exit process */
                    105:     NULL,                                  /* exit master */
                    106:     NGX_MODULE_V1_PADDING
                    107: };
                    108: 
                    109: 
                    110: static ngx_int_t
                    111: ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
                    112:      uintptr_t data)
                    113: {
                    114:     u_char                    *p, *ref, *last;
                    115:     size_t                     len;
                    116:     ngx_str_t                 *uri;
                    117:     ngx_uint_t                 i, key;
                    118:     ngx_http_referer_conf_t   *rlcf;
                    119:     u_char                     buf[256];
                    120: 
                    121:     rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);
                    122: 
                    123:     if (rlcf->hash.hash.buckets == NULL
                    124:         && rlcf->hash.wc_head == NULL
                    125:         && rlcf->hash.wc_tail == NULL
                    126: #if (NGX_PCRE)
                    127:         && rlcf->regex == NULL
                    128: #endif
                    129:        )
                    130:     {
                    131:         goto valid;
                    132:     }
                    133: 
                    134:     if (r->headers_in.referer == NULL) {
                    135:         if (rlcf->no_referer) {
                    136:             goto valid;
                    137:         }
                    138: 
                    139:         goto invalid;
                    140:     }
                    141: 
                    142:     len = r->headers_in.referer->value.len;
                    143:     ref = r->headers_in.referer->value.data;
                    144: 
                    145:     if (len >= sizeof("http://i.ru") - 1) {
                    146:         last = ref + len;
                    147: 
                    148:         if (ngx_strncasecmp(ref, (u_char *) "http://", 7) == 0) {
                    149:             ref += 7;
                    150:             goto valid_scheme;
                    151: 
                    152:         } else if (ngx_strncasecmp(ref, (u_char *) "https://", 8) == 0) {
                    153:             ref += 8;
                    154:             goto valid_scheme;
                    155:         }
                    156:     }
                    157: 
                    158:     if (rlcf->blocked_referer) {
                    159:         goto valid;
                    160:     }
                    161: 
                    162:     goto invalid;
                    163: 
                    164: valid_scheme:
                    165: 
                    166:     i = 0;
                    167:     key = 0;
                    168: 
                    169:     for (p = ref; p < last; p++) {
                    170:         if (*p == '/' || *p == ':') {
                    171:             break;
                    172:         }
                    173: 
                    174:         buf[i] = ngx_tolower(*p);
                    175:         key = ngx_hash(key, buf[i++]);
                    176: 
                    177:         if (i == 256) {
                    178:             goto invalid;
                    179:         }
                    180:     }
                    181: 
                    182:     uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref);
                    183: 
                    184:     if (uri) {
                    185:         goto uri;
                    186:     }
                    187: 
                    188: #if (NGX_PCRE)
                    189: 
                    190:     if (rlcf->regex) {
                    191:         ngx_int_t  rc;
                    192:         ngx_str_t  referer;
                    193: 
                    194:         referer.len = len - 7;
                    195:         referer.data = ref;
                    196: 
                    197:         rc = ngx_regex_exec_array(rlcf->regex, &referer, r->connection->log);
                    198: 
                    199:         if (rc == NGX_OK) {
                    200:             goto valid;
                    201:         }
                    202: 
                    203:         if (rc == NGX_ERROR) {
                    204:             return rc;
                    205:         }
                    206: 
                    207:         /* NGX_DECLINED */
                    208:     }
                    209: 
                    210: #endif
                    211: 
                    212: invalid:
                    213: 
                    214:     *v = ngx_http_variable_true_value;
                    215: 
                    216:     return NGX_OK;
                    217: 
                    218: uri:
                    219: 
                    220:     for ( /* void */ ; p < last; p++) {
                    221:         if (*p == '/') {
                    222:             break;
                    223:         }
                    224:     }
                    225: 
                    226:     len = last - p;
                    227: 
                    228:     if (uri == NGX_HTTP_REFERER_NO_URI_PART) {
                    229:         goto valid;
                    230:     }
                    231: 
                    232:     if (len < uri->len || ngx_strncmp(uri->data, p, uri->len) != 0) {
                    233:         goto invalid;
                    234:     }
                    235: 
                    236: valid:
                    237: 
                    238:     *v = ngx_http_variable_null_value;
                    239: 
                    240:     return NGX_OK;
                    241: }
                    242: 
                    243: 
                    244: static void *
                    245: ngx_http_referer_create_conf(ngx_conf_t *cf)
                    246: {
                    247:     ngx_http_referer_conf_t  *conf;
                    248: 
                    249:     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t));
                    250:     if (conf == NULL) {
                    251:         return NULL;
                    252:     }
                    253: 
                    254: #if (NGX_PCRE)
                    255:     conf->regex = NGX_CONF_UNSET_PTR;
                    256: #endif
                    257: 
                    258:     conf->no_referer = NGX_CONF_UNSET;
                    259:     conf->blocked_referer = NGX_CONF_UNSET;
                    260:     conf->referer_hash_max_size = NGX_CONF_UNSET_UINT;
                    261:     conf->referer_hash_bucket_size = NGX_CONF_UNSET_UINT;
                    262: 
                    263:     return conf;
                    264: }
                    265: 
                    266: 
                    267: static char *
                    268: ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child)
                    269: {
                    270:     ngx_http_referer_conf_t *prev = parent;
                    271:     ngx_http_referer_conf_t *conf = child;
                    272: 
                    273:     ngx_hash_init_t  hash;
                    274: 
                    275:     if (conf->keys == NULL) {
                    276:         conf->hash = prev->hash;
                    277: 
                    278: #if (NGX_PCRE)
                    279:         ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
                    280: #endif
                    281:         ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);
                    282:         ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0);
                    283:         ngx_conf_merge_uint_value(conf->referer_hash_max_size,
                    284:                                   prev->referer_hash_max_size, 2048);
                    285:         ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,
                    286:                                   prev->referer_hash_bucket_size, 64);
                    287: 
                    288:         return NGX_CONF_OK;
                    289:     }
                    290: 
                    291:     if ((conf->no_referer == 1 || conf->blocked_referer == 1)
                    292:         && conf->keys->keys.nelts == 0
                    293:         && conf->keys->dns_wc_head.nelts == 0
                    294:         && conf->keys->dns_wc_tail.nelts == 0)
                    295:     {
                    296:         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
                    297:                       "the \"none\" or \"blocked\" referers are specified "
                    298:                       "in the \"valid_referers\" directive "
                    299:                       "without any valid referer");
                    300:         return NGX_CONF_ERROR;
                    301:     }
                    302: 
                    303:     ngx_conf_merge_uint_value(conf->referer_hash_max_size,
                    304:                               prev->referer_hash_max_size, 2048);
                    305:     ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,
                    306:                               prev->referer_hash_bucket_size, 64);
                    307:     conf->referer_hash_bucket_size = ngx_align(conf->referer_hash_bucket_size,
                    308:                                                ngx_cacheline_size);
                    309: 
                    310:     hash.key = ngx_hash_key_lc;
                    311:     hash.max_size = conf->referer_hash_max_size;
                    312:     hash.bucket_size = conf->referer_hash_bucket_size;
                    313:     hash.name = "referer_hash";
                    314:     hash.pool = cf->pool;
                    315: 
                    316:     if (conf->keys->keys.nelts) {
                    317:         hash.hash = &conf->hash.hash;
                    318:         hash.temp_pool = NULL;
                    319: 
                    320:         if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts)
                    321:             != NGX_OK)
                    322:         {
                    323:             return NGX_CONF_ERROR;
                    324:         }
                    325:     }
                    326: 
                    327:     if (conf->keys->dns_wc_head.nelts) {
                    328: 
                    329:         ngx_qsort(conf->keys->dns_wc_head.elts,
                    330:                   (size_t) conf->keys->dns_wc_head.nelts,
                    331:                   sizeof(ngx_hash_key_t),
                    332:                   ngx_http_cmp_referer_wildcards);
                    333: 
                    334:         hash.hash = NULL;
                    335:         hash.temp_pool = cf->temp_pool;
                    336: 
                    337:         if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_head.elts,
                    338:                                    conf->keys->dns_wc_head.nelts)
                    339:             != NGX_OK)
                    340:         {
                    341:             return NGX_CONF_ERROR;
                    342:         }
                    343: 
                    344:         conf->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
                    345:     }
                    346: 
                    347:     if (conf->keys->dns_wc_tail.nelts) {
                    348: 
                    349:         ngx_qsort(conf->keys->dns_wc_tail.elts,
                    350:                   (size_t) conf->keys->dns_wc_tail.nelts,
                    351:                   sizeof(ngx_hash_key_t),
                    352:                   ngx_http_cmp_referer_wildcards);
                    353: 
                    354:         hash.hash = NULL;
                    355:         hash.temp_pool = cf->temp_pool;
                    356: 
                    357:         if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_tail.elts,
                    358:                                    conf->keys->dns_wc_tail.nelts)
                    359:             != NGX_OK)
                    360:         {
                    361:             return NGX_CONF_ERROR;
                    362:         }
                    363: 
                    364:         conf->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
                    365:     }
                    366: 
                    367: #if (NGX_PCRE)
                    368:     ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
                    369: #endif
                    370: 
                    371:     if (conf->no_referer == NGX_CONF_UNSET) {
                    372:         conf->no_referer = 0;
                    373:     }
                    374: 
                    375:     if (conf->blocked_referer == NGX_CONF_UNSET) {
                    376:         conf->blocked_referer = 0;
                    377:     }
                    378: 
                    379:     conf->keys = NULL;
                    380: 
                    381:     return NGX_CONF_OK;
                    382: }
                    383: 
                    384: 
                    385: static char *
                    386: ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
                    387: {
                    388:     ngx_http_referer_conf_t  *rlcf = conf;
                    389: 
                    390:     u_char                    *p;
                    391:     ngx_str_t                 *value, uri, name;
                    392:     ngx_uint_t                 i, n;
                    393:     ngx_http_variable_t       *var;
                    394:     ngx_http_server_name_t    *sn;
                    395:     ngx_http_core_srv_conf_t  *cscf;
                    396: 
                    397:     ngx_str_set(&name, "invalid_referer");
                    398: 
                    399:     var = ngx_http_add_variable(cf, &name,
                    400:                                 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOHASH);
                    401:     if (var == NULL) {
                    402:         return NGX_CONF_ERROR;
                    403:     }
                    404: 
                    405:     var->get_handler = ngx_http_referer_variable;
                    406: 
                    407:     if (rlcf->keys == NULL) {
                    408:         rlcf->keys = ngx_pcalloc(cf->temp_pool, sizeof(ngx_hash_keys_arrays_t));
                    409:         if (rlcf->keys == NULL) {
                    410:             return NGX_CONF_ERROR;
                    411:         }
                    412: 
                    413:         rlcf->keys->pool = cf->pool;
                    414:         rlcf->keys->temp_pool = cf->pool;
                    415: 
                    416:         if (ngx_hash_keys_array_init(rlcf->keys, NGX_HASH_SMALL) != NGX_OK) {
                    417:             return NGX_CONF_ERROR;
                    418:         }
                    419:     }
                    420: 
                    421:     value = cf->args->elts;
                    422: 
                    423:     for (i = 1; i < cf->args->nelts; i++) {
                    424:         if (value[i].len == 0) {
                    425:             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                    426:                                "invalid referer \"%V\"", &value[i]);
                    427:             return NGX_CONF_ERROR;
                    428:         }
                    429: 
                    430:         if (ngx_strcmp(value[i].data, "none") == 0) {
                    431:             rlcf->no_referer = 1;
                    432:             continue;
                    433:         }
                    434: 
                    435:         if (ngx_strcmp(value[i].data, "blocked") == 0) {
                    436:             rlcf->blocked_referer = 1;
                    437:             continue;
                    438:         }
                    439: 
                    440:         ngx_str_null(&uri);
                    441: 
                    442:         if (ngx_strcmp(value[i].data, "server_names") == 0) {
                    443: 
                    444:             cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
                    445: 
                    446:             sn = cscf->server_names.elts;
                    447:             for (n = 0; n < cscf->server_names.nelts; n++) {
                    448: 
                    449: #if (NGX_PCRE)
                    450:                 if (sn[n].regex) {
                    451: 
                    452:                     if (ngx_http_add_regex_referer(cf, rlcf, &sn[n].name,
                    453:                                                    sn[n].regex->regex)
                    454:                         != NGX_OK)
                    455:                     {
                    456:                         return NGX_CONF_ERROR;
                    457:                     }
                    458: 
                    459:                     continue;
                    460:                 }
                    461: #endif
                    462: 
                    463:                 if (ngx_http_add_referer(cf, rlcf->keys, &sn[n].name, &uri)
                    464:                     != NGX_OK)
                    465:                 {
                    466:                     return NGX_CONF_ERROR;
                    467:                 }
                    468:             }
                    469: 
                    470:             continue;
                    471:         }
                    472: 
                    473:         if (value[i].data[0] == '~') {
                    474:             if (ngx_http_add_regex_referer(cf, rlcf, &value[i], NULL) != NGX_OK)
                    475:             {
                    476:                 return NGX_CONF_ERROR;
                    477:             }
                    478: 
                    479:             continue;
                    480:         }
                    481: 
                    482:         p = (u_char *) ngx_strchr(value[i].data, '/');
                    483: 
                    484:         if (p) {
                    485:             uri.len = (value[i].data + value[i].len) - p;
                    486:             uri.data = p;
                    487:             value[i].len = p - value[i].data;
                    488:         }
                    489: 
                    490:         if (ngx_http_add_referer(cf, rlcf->keys, &value[i], &uri) != NGX_OK) {
                    491:             return NGX_CONF_ERROR;
                    492:         }
                    493:     }
                    494: 
                    495:     return NGX_CONF_OK;
                    496: }
                    497: 
                    498: 
                    499: static char *
                    500: ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
                    501:     ngx_str_t *value, ngx_str_t *uri)
                    502: {
                    503:     ngx_int_t   rc;
                    504:     ngx_str_t  *u;
                    505: 
                    506:     if (uri->len == 0) {
                    507:         u = NGX_HTTP_REFERER_NO_URI_PART;
                    508: 
                    509:     } else {
                    510:         u = ngx_palloc(cf->pool, sizeof(ngx_str_t));
                    511:         if (u == NULL) {
                    512:             return NGX_CONF_ERROR;
                    513:         }
                    514: 
                    515:         *u = *uri;
                    516:     }
                    517: 
                    518:     rc = ngx_hash_add_key(keys, value, u, NGX_HASH_WILDCARD_KEY);
                    519: 
                    520:     if (rc == NGX_OK) {
                    521:         return NGX_CONF_OK;
                    522:     }
                    523: 
                    524:     if (rc == NGX_DECLINED) {
                    525:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                    526:                            "invalid hostname or wildcard \"%V\"", value);
                    527:     }
                    528: 
                    529:     if (rc == NGX_BUSY) {
                    530:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                    531:                            "conflicting parameter \"%V\"", value);
                    532:     }
                    533: 
                    534:     return NGX_CONF_ERROR;
                    535: }
                    536: 
                    537: 
                    538: static char *
                    539: ngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
                    540:     ngx_str_t *name, ngx_regex_t *regex)
                    541: {
                    542: #if (NGX_PCRE)
                    543:     ngx_regex_elt_t      *re;
                    544:     ngx_regex_compile_t   rc;
                    545:     u_char                errstr[NGX_MAX_CONF_ERRSTR];
                    546: 
                    547:     if (name->len == 1) {
                    548:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty regex in \"%V\"", name);
                    549:         return NGX_CONF_ERROR;
                    550:     }
                    551: 
                    552:     if (rlcf->regex == NGX_CONF_UNSET_PTR) {
                    553:         rlcf->regex = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));
                    554:         if (rlcf->regex == NULL) {
                    555:             return NGX_CONF_ERROR;
                    556:         }
                    557:     }
                    558: 
                    559:     re = ngx_array_push(rlcf->regex);
                    560:     if (re == NULL) {
                    561:         return NGX_CONF_ERROR;
                    562:     }
                    563: 
                    564:     if (regex) {
                    565:         re->regex = regex;
                    566:         re->name = name->data;
                    567: 
                    568:         return NGX_CONF_OK;
                    569:     }
                    570: 
                    571:     name->len--;
                    572:     name->data++;
                    573: 
                    574:     ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
                    575: 
                    576:     rc.pattern = *name;
                    577:     rc.pool = cf->pool;
                    578:     rc.options = NGX_REGEX_CASELESS;
                    579:     rc.err.len = NGX_MAX_CONF_ERRSTR;
                    580:     rc.err.data = errstr;
                    581: 
                    582:     if (ngx_regex_compile(&rc) != NGX_OK) {
                    583:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
                    584:         return NGX_CONF_ERROR;
                    585:     }
                    586: 
                    587:     re->regex = rc.regex;
                    588:     re->name = name->data;
                    589: 
                    590:     return NGX_CONF_OK;
                    591: 
                    592: #else
                    593: 
                    594:     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                    595:                        "the using of the regex \"%V\" requires PCRE library",
                    596:                        name);
                    597: 
                    598:     return NGX_CONF_ERROR;
                    599: 
                    600: #endif
                    601: }
                    602: 
                    603: 
                    604: static int ngx_libc_cdecl
                    605: ngx_http_cmp_referer_wildcards(const void *one, const void *two)
                    606: {
                    607:     ngx_hash_key_t  *first, *second;
                    608: 
                    609:     first = (ngx_hash_key_t *) one;
                    610:     second = (ngx_hash_key_t *) two;
                    611: 
                    612:     return ngx_dns_strcmp(first->key.data, second->key.data);
                    613: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>