6607
|
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_stream.h>
|
|
11 #include <nginx.h>
|
|
12
|
|
13
|
|
14 static ngx_int_t ngx_stream_variable_nginx_version(ngx_stream_session_t *s,
|
|
15 ngx_stream_variable_value_t *v, uintptr_t data);
|
|
16
|
|
17
|
|
18 static ngx_stream_variable_t ngx_stream_core_variables[] = {
|
|
19
|
|
20 { ngx_string("nginx_version"), NULL, ngx_stream_variable_nginx_version,
|
|
21 0, 0, 0 },
|
|
22
|
|
23 { ngx_null_string, NULL, NULL, 0, 0, 0 }
|
|
24 };
|
|
25
|
|
26
|
|
27 ngx_stream_variable_value_t ngx_stream_variable_null_value =
|
|
28 ngx_stream_variable("");
|
|
29 ngx_stream_variable_value_t ngx_stream_variable_true_value =
|
|
30 ngx_stream_variable("1");
|
|
31
|
|
32
|
|
33 ngx_stream_variable_t *
|
|
34 ngx_stream_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
|
|
35 {
|
|
36 ngx_int_t rc;
|
|
37 ngx_uint_t i;
|
|
38 ngx_hash_key_t *key;
|
|
39 ngx_stream_variable_t *v;
|
|
40 ngx_stream_core_main_conf_t *cmcf;
|
|
41
|
|
42 if (name->len == 0) {
|
|
43 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
44 "invalid variable name \"$\"");
|
|
45 return NULL;
|
|
46 }
|
|
47
|
|
48 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
|
|
49
|
|
50 key = cmcf->variables_keys->keys.elts;
|
|
51 for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) {
|
|
52 if (name->len != key[i].key.len
|
|
53 || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0)
|
|
54 {
|
|
55 continue;
|
|
56 }
|
|
57
|
|
58 v = key[i].value;
|
|
59
|
|
60 if (!(v->flags & NGX_STREAM_VAR_CHANGEABLE)) {
|
|
61 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
62 "the duplicate \"%V\" variable", name);
|
|
63 return NULL;
|
|
64 }
|
|
65
|
|
66 return v;
|
|
67 }
|
|
68
|
|
69 v = ngx_palloc(cf->pool, sizeof(ngx_stream_variable_t));
|
|
70 if (v == NULL) {
|
|
71 return NULL;
|
|
72 }
|
|
73
|
|
74 v->name.len = name->len;
|
|
75 v->name.data = ngx_pnalloc(cf->pool, name->len);
|
|
76 if (v->name.data == NULL) {
|
|
77 return NULL;
|
|
78 }
|
|
79
|
|
80 ngx_strlow(v->name.data, name->data, name->len);
|
|
81
|
|
82 v->set_handler = NULL;
|
|
83 v->get_handler = NULL;
|
|
84 v->data = 0;
|
|
85 v->flags = flags;
|
|
86 v->index = 0;
|
|
87
|
|
88 rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0);
|
|
89
|
|
90 if (rc == NGX_ERROR) {
|
|
91 return NULL;
|
|
92 }
|
|
93
|
|
94 if (rc == NGX_BUSY) {
|
|
95 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
96 "conflicting variable name \"%V\"", name);
|
|
97 return NULL;
|
|
98 }
|
|
99
|
|
100 return v;
|
|
101 }
|
|
102
|
|
103
|
|
104 ngx_int_t
|
|
105 ngx_stream_get_variable_index(ngx_conf_t *cf, ngx_str_t *name)
|
|
106 {
|
|
107 ngx_uint_t i;
|
|
108 ngx_stream_variable_t *v;
|
|
109 ngx_stream_core_main_conf_t *cmcf;
|
|
110
|
|
111 if (name->len == 0) {
|
|
112 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
113 "invalid variable name \"$\"");
|
|
114 return NGX_ERROR;
|
|
115 }
|
|
116
|
|
117 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
|
|
118
|
|
119 v = cmcf->variables.elts;
|
|
120
|
|
121 if (v == NULL) {
|
|
122 if (ngx_array_init(&cmcf->variables, cf->pool, 4,
|
|
123 sizeof(ngx_stream_variable_t))
|
|
124 != NGX_OK)
|
|
125 {
|
|
126 return NGX_ERROR;
|
|
127 }
|
|
128
|
|
129 } else {
|
|
130 for (i = 0; i < cmcf->variables.nelts; i++) {
|
|
131 if (name->len != v[i].name.len
|
|
132 || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
|
|
133 {
|
|
134 continue;
|
|
135 }
|
|
136
|
|
137 return i;
|
|
138 }
|
|
139 }
|
|
140
|
|
141 v = ngx_array_push(&cmcf->variables);
|
|
142 if (v == NULL) {
|
|
143 return NGX_ERROR;
|
|
144 }
|
|
145
|
|
146 v->name.len = name->len;
|
|
147 v->name.data = ngx_pnalloc(cf->pool, name->len);
|
|
148 if (v->name.data == NULL) {
|
|
149 return NGX_ERROR;
|
|
150 }
|
|
151
|
|
152 ngx_strlow(v->name.data, name->data, name->len);
|
|
153
|
|
154 v->set_handler = NULL;
|
|
155 v->get_handler = NULL;
|
|
156 v->data = 0;
|
|
157 v->flags = 0;
|
|
158 v->index = cmcf->variables.nelts - 1;
|
|
159
|
|
160 return v->index;
|
|
161 }
|
|
162
|
|
163
|
|
164 ngx_stream_variable_value_t *
|
|
165 ngx_stream_get_indexed_variable(ngx_stream_session_t *s, ngx_uint_t index)
|
|
166 {
|
|
167 ngx_stream_variable_t *v;
|
|
168 ngx_stream_core_main_conf_t *cmcf;
|
|
169
|
|
170 cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
|
|
171
|
|
172 if (cmcf->variables.nelts <= index) {
|
|
173 ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0,
|
|
174 "unknown variable index: %ui", index);
|
|
175 return NULL;
|
|
176 }
|
|
177
|
|
178 if (s->variables[index].not_found || s->variables[index].valid) {
|
|
179 return &s->variables[index];
|
|
180 }
|
|
181
|
|
182 v = cmcf->variables.elts;
|
|
183
|
|
184 if (v[index].get_handler(s, &s->variables[index], v[index].data)
|
|
185 == NGX_OK)
|
|
186 {
|
|
187 if (v[index].flags & NGX_STREAM_VAR_NOCACHEABLE) {
|
|
188 s->variables[index].no_cacheable = 1;
|
|
189 }
|
|
190
|
|
191 return &s->variables[index];
|
|
192 }
|
|
193
|
|
194 s->variables[index].valid = 0;
|
|
195 s->variables[index].not_found = 1;
|
|
196
|
|
197 return NULL;
|
|
198 }
|
|
199
|
|
200
|
|
201 ngx_stream_variable_value_t *
|
|
202 ngx_stream_get_flushed_variable(ngx_stream_session_t *s, ngx_uint_t index)
|
|
203 {
|
|
204 ngx_stream_variable_value_t *v;
|
|
205
|
|
206 v = &s->variables[index];
|
|
207
|
|
208 if (v->valid || v->not_found) {
|
|
209 if (!v->no_cacheable) {
|
|
210 return v;
|
|
211 }
|
|
212
|
|
213 v->valid = 0;
|
|
214 v->not_found = 0;
|
|
215 }
|
|
216
|
|
217 return ngx_stream_get_indexed_variable(s, index);
|
|
218 }
|
|
219
|
|
220
|
|
221 ngx_stream_variable_value_t *
|
|
222 ngx_stream_get_variable(ngx_stream_session_t *s, ngx_str_t *name,
|
|
223 ngx_uint_t key)
|
|
224 {
|
|
225 ngx_stream_variable_t *v;
|
|
226 ngx_stream_variable_value_t *vv;
|
|
227 ngx_stream_core_main_conf_t *cmcf;
|
|
228
|
|
229 cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
|
|
230
|
|
231 v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);
|
|
232
|
|
233 if (v) {
|
|
234 if (v->flags & NGX_STREAM_VAR_INDEXED) {
|
|
235 return ngx_stream_get_flushed_variable(s, v->index);
|
|
236
|
|
237 } else {
|
|
238
|
|
239 vv = ngx_palloc(s->connection->pool,
|
|
240 sizeof(ngx_stream_variable_value_t));
|
|
241
|
|
242 if (vv && v->get_handler(s, vv, v->data) == NGX_OK) {
|
|
243 return vv;
|
|
244 }
|
|
245
|
|
246 return NULL;
|
|
247 }
|
|
248 }
|
|
249
|
|
250 vv = ngx_palloc(s->connection->pool, sizeof(ngx_stream_variable_value_t));
|
|
251 if (vv == NULL) {
|
|
252 return NULL;
|
|
253 }
|
|
254
|
|
255 vv->not_found = 1;
|
|
256
|
|
257 return vv;
|
|
258 }
|
|
259
|
|
260
|
|
261 static ngx_int_t
|
|
262 ngx_stream_variable_nginx_version(ngx_stream_session_t *s,
|
|
263 ngx_stream_variable_value_t *v, uintptr_t data)
|
|
264 {
|
|
265 v->len = sizeof(NGINX_VERSION) - 1;
|
|
266 v->valid = 1;
|
|
267 v->no_cacheable = 0;
|
|
268 v->not_found = 0;
|
|
269 v->data = (u_char *) NGINX_VERSION;
|
|
270
|
|
271 return NGX_OK;
|
|
272 }
|
|
273
|
|
274
|
|
275 void *
|
|
276 ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map,
|
|
277 ngx_str_t *match)
|
|
278 {
|
|
279 void *value;
|
|
280 u_char *low;
|
|
281 size_t len;
|
|
282 ngx_uint_t key;
|
|
283
|
|
284 len = match->len;
|
|
285
|
|
286 if (len) {
|
|
287 low = ngx_pnalloc(s->connection->pool, len);
|
|
288 if (low == NULL) {
|
|
289 return NULL;
|
|
290 }
|
|
291
|
|
292 } else {
|
|
293 low = NULL;
|
|
294 }
|
|
295
|
|
296 key = ngx_hash_strlow(low, match->data, len);
|
|
297
|
|
298 value = ngx_hash_find_combined(&map->hash, key, low, len);
|
|
299 if (value) {
|
|
300 return value;
|
|
301 }
|
|
302
|
|
303 #if (NGX_PCRE)
|
|
304
|
|
305 if (len && map->nregex) {
|
|
306 ngx_int_t n;
|
|
307 ngx_uint_t i;
|
|
308 ngx_stream_map_regex_t *reg;
|
|
309
|
|
310 reg = map->regex;
|
|
311
|
|
312 for (i = 0; i < map->nregex; i++) {
|
|
313
|
|
314 n = ngx_stream_regex_exec(s, reg[i].regex, match);
|
|
315
|
|
316 if (n == NGX_OK) {
|
|
317 return reg[i].value;
|
|
318 }
|
|
319
|
|
320 if (n == NGX_DECLINED) {
|
|
321 continue;
|
|
322 }
|
|
323
|
|
324 /* NGX_ERROR */
|
|
325
|
|
326 return NULL;
|
|
327 }
|
|
328 }
|
|
329
|
|
330 #endif
|
|
331
|
|
332 return NULL;
|
|
333 }
|
|
334
|
|
335
|
|
336 #if (NGX_PCRE)
|
|
337
|
|
338 static ngx_int_t
|
|
339 ngx_stream_variable_not_found(ngx_stream_session_t *s,
|
|
340 ngx_stream_variable_value_t *v, uintptr_t data)
|
|
341 {
|
|
342 v->not_found = 1;
|
|
343 return NGX_OK;
|
|
344 }
|
|
345
|
|
346
|
|
347 ngx_stream_regex_t *
|
|
348 ngx_stream_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc)
|
|
349 {
|
|
350 u_char *p;
|
|
351 size_t size;
|
|
352 ngx_str_t name;
|
|
353 ngx_uint_t i, n;
|
|
354 ngx_stream_variable_t *v;
|
|
355 ngx_stream_regex_t *re;
|
|
356 ngx_stream_regex_variable_t *rv;
|
|
357 ngx_stream_core_main_conf_t *cmcf;
|
|
358
|
|
359 rc->pool = cf->pool;
|
|
360
|
|
361 if (ngx_regex_compile(rc) != NGX_OK) {
|
|
362 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc->err);
|
|
363 return NULL;
|
|
364 }
|
|
365
|
|
366 re = ngx_pcalloc(cf->pool, sizeof(ngx_stream_regex_t));
|
|
367 if (re == NULL) {
|
|
368 return NULL;
|
|
369 }
|
|
370
|
|
371 re->regex = rc->regex;
|
|
372 re->ncaptures = rc->captures;
|
|
373 re->name = rc->pattern;
|
|
374
|
|
375 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
|
|
376 cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures);
|
|
377
|
|
378 n = (ngx_uint_t) rc->named_captures;
|
|
379
|
|
380 if (n == 0) {
|
|
381 return re;
|
|
382 }
|
|
383
|
|
384 rv = ngx_palloc(rc->pool, n * sizeof(ngx_stream_regex_variable_t));
|
|
385 if (rv == NULL) {
|
|
386 return NULL;
|
|
387 }
|
|
388
|
|
389 re->variables = rv;
|
|
390 re->nvariables = n;
|
|
391
|
|
392 size = rc->name_size;
|
|
393 p = rc->names;
|
|
394
|
|
395 for (i = 0; i < n; i++) {
|
|
396 rv[i].capture = 2 * ((p[0] << 8) + p[1]);
|
|
397
|
|
398 name.data = &p[2];
|
|
399 name.len = ngx_strlen(name.data);
|
|
400
|
|
401 v = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);
|
|
402 if (v == NULL) {
|
|
403 return NULL;
|
|
404 }
|
|
405
|
|
406 rv[i].index = ngx_stream_get_variable_index(cf, &name);
|
|
407 if (rv[i].index == NGX_ERROR) {
|
|
408 return NULL;
|
|
409 }
|
|
410
|
|
411 v->get_handler = ngx_stream_variable_not_found;
|
|
412
|
|
413 p += size;
|
|
414 }
|
|
415
|
|
416 return re;
|
|
417 }
|
|
418
|
|
419
|
|
420 ngx_int_t
|
|
421 ngx_stream_regex_exec(ngx_stream_session_t *s, ngx_stream_regex_t *re,
|
|
422 ngx_str_t *str)
|
|
423 {
|
|
424 ngx_int_t rc, index;
|
|
425 ngx_uint_t i, n, len;
|
|
426 ngx_stream_variable_value_t *vv;
|
|
427 ngx_stream_core_main_conf_t *cmcf;
|
|
428
|
|
429 cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
|
|
430
|
|
431 if (re->ncaptures) {
|
|
432 len = cmcf->ncaptures;
|
|
433
|
|
434 if (s->captures == NULL) {
|
|
435 s->captures = ngx_palloc(s->connection->pool, len * sizeof(int));
|
|
436 if (s->captures == NULL) {
|
|
437 return NGX_ERROR;
|
|
438 }
|
|
439 }
|
|
440
|
|
441 } else {
|
|
442 len = 0;
|
|
443 }
|
|
444
|
|
445 rc = ngx_regex_exec(re->regex, str, s->captures, len);
|
|
446
|
|
447 if (rc == NGX_REGEX_NO_MATCHED) {
|
|
448 return NGX_DECLINED;
|
|
449 }
|
|
450
|
|
451 if (rc < 0) {
|
|
452 ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0,
|
|
453 ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
|
|
454 rc, str, &re->name);
|
|
455 return NGX_ERROR;
|
|
456 }
|
|
457
|
|
458 for (i = 0; i < re->nvariables; i++) {
|
|
459
|
|
460 n = re->variables[i].capture;
|
|
461 index = re->variables[i].index;
|
|
462 vv = &s->variables[index];
|
|
463
|
|
464 vv->len = s->captures[n + 1] - s->captures[n];
|
|
465 vv->valid = 1;
|
|
466 vv->no_cacheable = 0;
|
|
467 vv->not_found = 0;
|
|
468 vv->data = &str->data[s->captures[n]];
|
|
469
|
|
470 #if (NGX_DEBUG)
|
|
471 {
|
|
472 ngx_stream_variable_t *v;
|
|
473
|
|
474 v = cmcf->variables.elts;
|
|
475
|
|
476 ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
|
|
477 "stream regex set $%V to \"%v\"", &v[index].name, vv);
|
|
478 }
|
|
479 #endif
|
|
480 }
|
|
481
|
|
482 s->ncaptures = rc * 2;
|
|
483 s->captures_data = str->data;
|
|
484
|
|
485 return NGX_OK;
|
|
486 }
|
|
487
|
|
488 #endif
|
|
489
|
|
490
|
|
491 ngx_int_t
|
|
492 ngx_stream_variables_add_core_vars(ngx_conf_t *cf)
|
|
493 {
|
|
494 ngx_int_t rc;
|
|
495 ngx_stream_variable_t *cv, *v;
|
|
496 ngx_stream_core_main_conf_t *cmcf;
|
|
497
|
|
498 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
|
|
499
|
|
500 cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,
|
|
501 sizeof(ngx_hash_keys_arrays_t));
|
|
502 if (cmcf->variables_keys == NULL) {
|
|
503 return NGX_ERROR;
|
|
504 }
|
|
505
|
|
506 cmcf->variables_keys->pool = cf->pool;
|
|
507 cmcf->variables_keys->temp_pool = cf->pool;
|
|
508
|
|
509 if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)
|
|
510 != NGX_OK)
|
|
511 {
|
|
512 return NGX_ERROR;
|
|
513 }
|
|
514
|
|
515 for (cv = ngx_stream_core_variables; cv->name.len; cv++) {
|
|
516 v = ngx_palloc(cf->pool, sizeof(ngx_stream_variable_t));
|
|
517 if (v == NULL) {
|
|
518 return NGX_ERROR;
|
|
519 }
|
|
520
|
|
521 *v = *cv;
|
|
522
|
|
523 rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v,
|
|
524 NGX_HASH_READONLY_KEY);
|
|
525
|
|
526 if (rc == NGX_OK) {
|
|
527 continue;
|
|
528 }
|
|
529
|
|
530 if (rc == NGX_BUSY) {
|
|
531 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
|
532 "conflicting variable name \"%V\"", &v->name);
|
|
533 }
|
|
534
|
|
535 return NGX_ERROR;
|
|
536 }
|
|
537
|
|
538 return NGX_OK;
|
|
539 }
|
|
540
|
|
541
|
|
542 ngx_int_t
|
|
543 ngx_stream_variables_init_vars(ngx_conf_t *cf)
|
|
544 {
|
|
545 ngx_uint_t i, n;
|
|
546 ngx_hash_key_t *key;
|
|
547 ngx_hash_init_t hash;
|
|
548 ngx_stream_variable_t *v, *av;
|
|
549 ngx_stream_core_main_conf_t *cmcf;
|
|
550
|
|
551 /* set the handlers for the indexed stream variables */
|
|
552
|
|
553 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
|
|
554
|
|
555 v = cmcf->variables.elts;
|
|
556 key = cmcf->variables_keys->keys.elts;
|
|
557
|
|
558 for (i = 0; i < cmcf->variables.nelts; i++) {
|
|
559
|
|
560 for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
|
|
561
|
|
562 av = key[n].value;
|
|
563
|
|
564 if (v[i].name.len == key[n].key.len
|
|
565 && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len)
|
|
566 == 0)
|
|
567 {
|
|
568 v[i].get_handler = av->get_handler;
|
|
569 v[i].data = av->data;
|
|
570
|
|
571 av->flags |= NGX_STREAM_VAR_INDEXED;
|
|
572 v[i].flags = av->flags;
|
|
573
|
|
574 av->index = i;
|
|
575
|
|
576 if (av->get_handler == NULL) {
|
|
577 break;
|
|
578 }
|
|
579
|
|
580 goto next;
|
|
581 }
|
|
582 }
|
|
583
|
|
584 ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
|
585 "unknown \"%V\" variable", &v[i].name);
|
|
586
|
|
587 return NGX_ERROR;
|
|
588
|
|
589 next:
|
|
590 continue;
|
|
591 }
|
|
592
|
|
593
|
|
594 for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
|
|
595 av = key[n].value;
|
|
596
|
|
597 if (av->flags & NGX_STREAM_VAR_NOHASH) {
|
|
598 key[n].key.data = NULL;
|
|
599 }
|
|
600 }
|
|
601
|
|
602
|
|
603 hash.hash = &cmcf->variables_hash;
|
|
604 hash.key = ngx_hash_key;
|
|
605 hash.max_size = cmcf->variables_hash_max_size;
|
|
606 hash.bucket_size = cmcf->variables_hash_bucket_size;
|
|
607 hash.name = "variables_hash";
|
|
608 hash.pool = cf->pool;
|
|
609 hash.temp_pool = NULL;
|
|
610
|
|
611 if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts,
|
|
612 cmcf->variables_keys->keys.nelts)
|
|
613 != NGX_OK)
|
|
614 {
|
|
615 return NGX_ERROR;
|
|
616 }
|
|
617
|
|
618 cmcf->variables_keys = NULL;
|
|
619
|
|
620 return NGX_OK;
|
|
621 }
|