comparison src/http/modules/ngx_http_referer_module.c @ 142:84910468f6de NGINX_0_3_18

nginx 0.3.18 *) Feature: the "server_names" directive supports the ".domain.tld" names. *) Feature: the "server_names" directive uses the hash for the "*.domain.tld" names and more effective hash for usual names. *) Change: the "server_names_hash_max_size" and "server_names_hash_bucket_size" directives. *) Change: the "server_names_hash" and "server_names_hash_threshold" directives were canceled. *) Feature: the "valid_referers" directive uses the hash site names. *) Change: now the "valid_referers" directive checks the site names only without the URI part. *) Bugfix: some ".domain.tld" names incorrectly processed by the ngx_http_map_module. *) Bugfix: segmentation fault was occurred if configuration file did not exist; bug appeared in 0.3.12. *) Bugfix: on 64-bit platforms segmentation fault may occurred on start; bug appeared in 0.3.16.
author Igor Sysoev <http://sysoev.ru>
date Mon, 26 Dec 2005 00:00:00 +0300
parents 91372f004adf
children e1c6ac408b68
comparison
equal deleted inserted replaced
141:249e67502bf3 142:84910468f6de
8 #include <ngx_core.h> 8 #include <ngx_core.h>
9 #include <ngx_http.h> 9 #include <ngx_http.h>
10 10
11 11
12 typedef struct { 12 typedef struct {
13 ngx_str_t name; 13 ngx_hash_t hash;
14 ngx_uint_t wildcard; 14 ngx_hash_wildcard_t *dns_wildcards;
15 } ngx_http_referer_t; 15
16 16 ngx_flag_t no_referer;
17 typedef struct { 17 ngx_flag_t blocked_referer;
18 ngx_array_t *referers; /* ngx_http_referer_t */ 18
19 19 ngx_hash_keys_arrays_t *keys;
20 ngx_flag_t no_referer;
21 ngx_flag_t blocked_referer;
22 } ngx_http_referer_conf_t; 20 } ngx_http_referer_conf_t;
23 21
24 22
25 static void * ngx_http_referer_create_conf(ngx_conf_t *cf); 23 static void * ngx_http_referer_create_conf(ngx_conf_t *cf);
26 static char * ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, 24 static char * ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent,
27 void *child); 25 void *child);
28 static char *ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, 26 static char *ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd,
29 void *conf); 27 void *conf);
28 static char *ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
29 ngx_str_t *value);
30 static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,
31 const void *two);
30 32
31 33
32 static ngx_command_t ngx_http_referer_commands[] = { 34 static ngx_command_t ngx_http_referer_commands[] = {
33 35
34 { ngx_string("valid_referers"), 36 { ngx_string("valid_referers"),
75 77
76 static ngx_int_t 78 static ngx_int_t
77 ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, 79 ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
78 uintptr_t data) 80 uintptr_t data)
79 { 81 {
80 u_char *ref; 82 u_char *p, *ref;
81 size_t len; 83 size_t len;
82 ngx_uint_t i, n; 84 ngx_http_referer_conf_t *rlcf;
83 ngx_http_referer_t *refs; 85
84 ngx_http_referer_conf_t *cf; 86 rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);
85 87
86 cf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module); 88 if (rlcf->hash.buckets == NULL
87 89 && rlcf->dns_wildcards == NULL
88 if (cf->referers == NULL) { 90 && rlcf->dns_wildcards->hash.buckets == NULL)
91 {
89 *v = ngx_http_variable_null_value; 92 *v = ngx_http_variable_null_value;
90 return NGX_OK; 93 return NGX_OK;
91 } 94 }
92 95
93 if (r->headers_in.referer == NULL) { 96 if (r->headers_in.referer == NULL) {
94 if (cf->no_referer) { 97 if (rlcf->no_referer) {
95 *v = ngx_http_variable_null_value; 98 *v = ngx_http_variable_null_value;
96 return NGX_OK; 99 return NGX_OK;
97 100
98 } else { 101 } else {
99 *v = ngx_http_variable_true_value; 102 *v = ngx_http_variable_true_value;
105 ref = r->headers_in.referer->value.data; 108 ref = r->headers_in.referer->value.data;
106 109
107 if (len < sizeof("http://i.ru") - 1 110 if (len < sizeof("http://i.ru") - 1
108 || (ngx_strncasecmp(ref, "http://", 7) != 0)) 111 || (ngx_strncasecmp(ref, "http://", 7) != 0))
109 { 112 {
110 if (cf->blocked_referer) { 113 if (rlcf->blocked_referer) {
111 *v = ngx_http_variable_null_value; 114 *v = ngx_http_variable_null_value;
112 return NGX_OK; 115 return NGX_OK;
113 116
114 } else { 117 } else {
115 *v = ngx_http_variable_true_value; 118 *v = ngx_http_variable_true_value;
118 } 121 }
119 122
120 len -= 7; 123 len -= 7;
121 ref += 7; 124 ref += 7;
122 125
123 refs = cf->referers->elts; 126 for (p = ref; p < ref + len; p++) {
124 for (i = 0; i < cf->referers->nelts; i++ ){ 127 if (*p == '/' || *p == ':') {
125 128 break;
126 if (refs[i].name.len > len) { 129 }
127 continue; 130 }
128 } 131
129 132 len = p - ref;
130 if (refs[i].wildcard) { 133
131 for (n = 0; n < len; n++) { 134 if (rlcf->hash.buckets) {
132 if (ref[n] == '/' || ref[n] == ':') { 135 if (ngx_hash_find(&rlcf->hash, ngx_hash_key_lc(ref, len), ref, len)) {
133 break; 136 *v = ngx_http_variable_null_value;
134 } 137 return NGX_OK;
135 138 }
136 if (ref[n] != '.') { 139 }
137 continue; 140
138 } 141 if (rlcf->dns_wildcards && rlcf->dns_wildcards->hash.buckets) {
139 142 if (ngx_hash_find_wildcard(rlcf->dns_wildcards, ref, len)) {
140 if (ngx_strncmp(&ref[n], refs[i].name.data, 143 *v = ngx_http_variable_null_value;
141 refs[i].name.len) == 0) 144 return NGX_OK;
142 {
143 *v = ngx_http_variable_null_value;
144 return NGX_OK;
145 }
146 }
147
148 } else {
149 if (ngx_strncasecmp(refs[i].name.data, ref, refs[i].name.len) == 0)
150 {
151 *v = ngx_http_variable_null_value;
152 return NGX_OK;
153 }
154 } 145 }
155 } 146 }
156 147
157 *v = ngx_http_variable_true_value; 148 *v = ngx_http_variable_true_value;
158 149
163 static void * 154 static void *
164 ngx_http_referer_create_conf(ngx_conf_t *cf) 155 ngx_http_referer_create_conf(ngx_conf_t *cf)
165 { 156 {
166 ngx_http_referer_conf_t *conf; 157 ngx_http_referer_conf_t *conf;
167 158
168 conf = ngx_palloc(cf->pool, sizeof(ngx_http_referer_conf_t)); 159 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t));
169 if (conf == NULL) { 160 if (conf == NULL) {
170 return NGX_CONF_ERROR; 161 return NGX_CONF_ERROR;
171 } 162 }
172 163
173 conf->referers = NULL;
174 conf->no_referer = NGX_CONF_UNSET; 164 conf->no_referer = NGX_CONF_UNSET;
175 conf->blocked_referer = NGX_CONF_UNSET; 165 conf->blocked_referer = NGX_CONF_UNSET;
176 166
177 return conf; 167 return conf;
178 } 168 }
182 ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child) 172 ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child)
183 { 173 {
184 ngx_http_referer_conf_t *prev = parent; 174 ngx_http_referer_conf_t *prev = parent;
185 ngx_http_referer_conf_t *conf = child; 175 ngx_http_referer_conf_t *conf = child;
186 176
187 if (conf->referers == NULL) { 177 ngx_hash_init_t hash;
188 conf->referers = prev->referers; 178
179 if (conf->keys == NULL) {
180 conf->hash = prev->hash;
181 conf->dns_wildcards = prev->dns_wildcards;
182
189 ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0); 183 ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);
190 ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0); 184 ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0);
185
186 return NGX_CONF_OK;
187 }
188
189 hash.key = ngx_hash_key_lc;
190 hash.max_size = 2048; /* TODO: referer_hash_max_size; */
191 hash.bucket_size = 64; /* TODO: referer_hash_bucket_size; */
192 hash.name = "referers_hash";
193 hash.pool = cf->pool;
194
195 if (conf->keys->keys.nelts) {
196 hash.hash = &conf->hash;
197 hash.temp_pool = NULL;
198
199 if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts)
200 != NGX_OK)
201 {
202 return NGX_CONF_ERROR;
203 }
204 }
205
206 if (conf->keys->dns_wildcards.nelts) {
207
208 ngx_qsort(conf->keys->dns_wildcards.elts,
209 (size_t) conf->keys->dns_wildcards.nelts,
210 sizeof(ngx_hash_key_t),
211 ngx_http_cmp_referer_wildcards);
212
213 hash.hash = NULL;
214 hash.temp_pool = cf->temp_pool;
215
216 if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wildcards.elts,
217 conf->keys->dns_wildcards.nelts)
218 != NGX_OK)
219 {
220 return NGX_CONF_ERROR;
221 }
222
223 conf->dns_wildcards = (ngx_hash_wildcard_t *) hash.hash;
191 } 224 }
192 225
193 if (conf->no_referer == NGX_CONF_UNSET) { 226 if (conf->no_referer == NGX_CONF_UNSET) {
194 conf->no_referer = 0; 227 conf->no_referer = 0;
195 } 228 }
203 236
204 237
205 static char * 238 static char *
206 ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 239 ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
207 { 240 {
208 ngx_http_referer_conf_t *lcf = conf; 241 ngx_http_referer_conf_t *rlcf = conf;
209 242
210 ngx_uint_t i, server_names; 243 u_char *p;
211 ngx_str_t *value, name; 244 ngx_str_t *value, name;
212 ngx_http_referer_t *ref; 245 ngx_uint_t i, n;
213 ngx_http_variable_t *var; 246 ngx_http_variable_t *var;
214 ngx_http_server_name_t *sn; 247 ngx_http_server_name_t *sn;
215 ngx_http_core_srv_conf_t *cscf; 248 ngx_http_core_srv_conf_t *cscf;
216 249
217 name.len = sizeof("invalid_referer") - 1; 250 name.len = sizeof("invalid_referer") - 1;
223 return NGX_CONF_ERROR; 256 return NGX_CONF_ERROR;
224 } 257 }
225 258
226 var->handler = ngx_http_referer_variable; 259 var->handler = ngx_http_referer_variable;
227 260
228 cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module); 261 if (rlcf->keys == NULL) {
229 262 rlcf->keys = ngx_pcalloc(cf->temp_pool, sizeof(ngx_hash_keys_arrays_t));
230 if (lcf->referers == NULL) { 263 if (rlcf->keys == NULL) {
231 lcf->referers = ngx_array_create(cf->pool, 264 return NGX_CONF_ERROR;
232 cf->args->nelts + cscf->server_names.nelts, 265 }
233 sizeof(ngx_http_referer_t)); 266
234 if (lcf->referers == NULL) { 267 rlcf->keys->pool = cf->pool;
268 rlcf->keys->temp_pool = cf->pool;
269
270 if (ngx_hash_keys_array_init(rlcf->keys, NGX_HASH_SMALL) != NGX_OK) {
235 return NGX_CONF_ERROR; 271 return NGX_CONF_ERROR;
236 } 272 }
237 } 273 }
238 274
239 value = cf->args->elts; 275 value = cf->args->elts;
240 server_names = 0;
241 276
242 for (i = 1; i < cf->args->nelts; i++) { 277 for (i = 1; i < cf->args->nelts; i++) {
243 if (value[i].len == 0) { 278 if (value[i].len == 0) {
244 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 279 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
245 "invalid referer \"%V\"", &value[i]); 280 "invalid referer \"%V\"", &value[i]);
246 return NGX_CONF_ERROR; 281 return NGX_CONF_ERROR;
247 } 282 }
248 283
249 if (ngx_strcmp(value[i].data, "none") == 0) { 284 if (ngx_strcmp(value[i].data, "none") == 0) {
250 lcf->no_referer = 1; 285 rlcf->no_referer = 1;
251 continue; 286 continue;
252 } 287 }
253 288
254 if (ngx_strcmp(value[i].data, "blocked") == 0) { 289 if (ngx_strcmp(value[i].data, "blocked") == 0) {
255 lcf->blocked_referer = 1; 290 rlcf->blocked_referer = 1;
256 continue; 291 continue;
257 } 292 }
258 293
259 if (ngx_strcmp(value[i].data, "server_names") == 0) { 294 if (ngx_strcmp(value[i].data, "server_names") == 0) {
260 server_names = 1; 295
296 cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
297
298 sn = cscf->server_names.elts;
299 for (n = 0; n < cscf->server_names.nelts; n++) {
300 if (ngx_http_add_referer(cf, rlcf->keys, &sn[n].name) != NGX_OK) {
301 return NGX_CONF_ERROR;
302 }
303 }
304
261 continue; 305 continue;
262 } 306 }
263 307
264 ref = ngx_array_push(lcf->referers); 308 p = (u_char *) ngx_strstr(value[i].data, "/");
265 if (ref == NULL) { 309
266 return NGX_CONF_ERROR; 310 if (p) {
267 }
268
269 if (value[i].data[0] != '*') {
270 ref->name = value[i];
271 ref->wildcard = 0;
272 continue;
273 }
274
275
276 if (value[i].data[1] != '.') {
277 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 311 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
278 "invalid wildcard referer \"%V\"", &value[i]); 312 "URI part \"%s\" is deprecated, ignored", p);
279 return NGX_CONF_ERROR; 313
280 } 314 value[i].len = p - value[i].data;
281 315 }
282 ref->name.len = value[i].len - 1; 316
283 ref->name.data = value[i].data + 1; 317 if (ngx_http_add_referer(cf, rlcf->keys, &value[i]) != NGX_OK) {
284 ref->wildcard = 1; 318 return NGX_CONF_ERROR;
285 } 319 }
286 320 }
287 if (!server_names) { 321
322 return NGX_CONF_OK;
323 }
324
325
326 static char *
327 ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
328 ngx_str_t *value)
329 {
330 u_char ch;
331 ngx_int_t rc;
332 ngx_uint_t flags;
333
334 ch = value->data[0];
335
336 if ((ch == '*' && (value->len < 3 || value->data[1] != '.'))
337 || (ch == '.' && value->len < 2))
338 {
339 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
340 "invalid DNS wildcard \"%V\"", value);
341
342 return NGX_CONF_ERROR;
343 }
344
345 flags = (ch == '*' || ch == '.') ? NGX_HASH_WILDCARD_KEY : 0;
346
347 rc = ngx_hash_add_key(keys, value, (void *) 4, flags);
348
349 if (rc == NGX_OK) {
288 return NGX_CONF_OK; 350 return NGX_CONF_OK;
289 } 351 }
290 352
291 sn = cscf->server_names.elts; 353 if (rc == NGX_BUSY) {
292 for (i = 0; i < cscf->server_names.nelts; i++) { 354 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
293 ref = ngx_array_push(lcf->referers); 355 "conflicting parameter \"%V\"", value);
294 if (ref == NULL) { 356 }
295 return NGX_CONF_ERROR; 357
296 } 358 return NGX_CONF_ERROR;
297 359 }
298 ref->name.len = sn[i].name.len + 1; 360
299 ref->name.data = ngx_palloc(cf->pool, ref->name.len); 361
300 if (ref->name.data == NULL) { 362 static int ngx_libc_cdecl
301 return NGX_CONF_ERROR; 363 ngx_http_cmp_referer_wildcards(const void *one, const void *two)
302 } 364 {
303 365 ngx_hash_key_t *first, *second;
304 ngx_memcpy(ref->name.data, sn[i].name.data, sn[i].name.len); 366
305 ref->name.data[sn[i].name.len] = '/'; 367 first = (ngx_hash_key_t *) one;
306 ref->wildcard = sn[i].wildcard; 368 second = (ngx_hash_key_t *) two;
307 } 369
308 370 return ngx_strcmp(first->key.data, second->key.data);
309 return NGX_CONF_OK; 371 }
310 }