ngx_stream_variables.c revision e18a033b
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
13static ngx_stream_variable_t *ngx_stream_add_prefix_variable(ngx_conf_t *cf,
14    ngx_str_t *name, ngx_uint_t flags);
15
16static ngx_int_t ngx_stream_variable_binary_remote_addr(
17    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
18static ngx_int_t ngx_stream_variable_remote_addr(ngx_stream_session_t *s,
19    ngx_stream_variable_value_t *v, uintptr_t data);
20static ngx_int_t ngx_stream_variable_remote_port(ngx_stream_session_t *s,
21    ngx_stream_variable_value_t *v, uintptr_t data);
22static ngx_int_t ngx_stream_variable_proxy_protocol_addr(
23    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
24static ngx_int_t ngx_stream_variable_proxy_protocol_port(
25    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
26static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s,
27    ngx_stream_variable_value_t *v, uintptr_t data);
28static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s,
29    ngx_stream_variable_value_t *v, uintptr_t data);
30static ngx_int_t ngx_stream_variable_bytes(ngx_stream_session_t *s,
31    ngx_stream_variable_value_t *v, uintptr_t data);
32static ngx_int_t ngx_stream_variable_session_time(ngx_stream_session_t *s,
33    ngx_stream_variable_value_t *v, uintptr_t data);
34static ngx_int_t ngx_stream_variable_status(ngx_stream_session_t *s,
35    ngx_stream_variable_value_t *v, uintptr_t data);
36static ngx_int_t ngx_stream_variable_connection(ngx_stream_session_t *s,
37    ngx_stream_variable_value_t *v, uintptr_t data);
38
39static ngx_int_t ngx_stream_variable_nginx_version(ngx_stream_session_t *s,
40    ngx_stream_variable_value_t *v, uintptr_t data);
41static ngx_int_t ngx_stream_variable_hostname(ngx_stream_session_t *s,
42    ngx_stream_variable_value_t *v, uintptr_t data);
43static ngx_int_t ngx_stream_variable_pid(ngx_stream_session_t *s,
44    ngx_stream_variable_value_t *v, uintptr_t data);
45static ngx_int_t ngx_stream_variable_msec(ngx_stream_session_t *s,
46    ngx_stream_variable_value_t *v, uintptr_t data);
47static ngx_int_t ngx_stream_variable_time_iso8601(ngx_stream_session_t *s,
48    ngx_stream_variable_value_t *v, uintptr_t data);
49static ngx_int_t ngx_stream_variable_time_local(ngx_stream_session_t *s,
50    ngx_stream_variable_value_t *v, uintptr_t data);
51static ngx_int_t ngx_stream_variable_protocol(ngx_stream_session_t *s,
52    ngx_stream_variable_value_t *v, uintptr_t data);
53
54
55static ngx_stream_variable_t  ngx_stream_core_variables[] = {
56
57    { ngx_string("binary_remote_addr"), NULL,
58      ngx_stream_variable_binary_remote_addr, 0, 0, 0 },
59
60    { ngx_string("remote_addr"), NULL,
61      ngx_stream_variable_remote_addr, 0, 0, 0 },
62
63    { ngx_string("remote_port"), NULL,
64      ngx_stream_variable_remote_port, 0, 0, 0 },
65
66    { ngx_string("proxy_protocol_addr"), NULL,
67      ngx_stream_variable_proxy_protocol_addr, 0, 0, 0 },
68
69    { ngx_string("proxy_protocol_port"), NULL,
70      ngx_stream_variable_proxy_protocol_port, 0, 0, 0 },
71
72    { ngx_string("server_addr"), NULL,
73      ngx_stream_variable_server_addr, 0, 0, 0 },
74
75    { ngx_string("server_port"), NULL,
76      ngx_stream_variable_server_port, 0, 0, 0 },
77
78    { ngx_string("bytes_sent"), NULL, ngx_stream_variable_bytes,
79      0, 0, 0 },
80
81    { ngx_string("bytes_received"), NULL, ngx_stream_variable_bytes,
82      1, 0, 0 },
83
84    { ngx_string("session_time"), NULL, ngx_stream_variable_session_time,
85      0, NGX_STREAM_VAR_NOCACHEABLE, 0 },
86
87    { ngx_string("status"), NULL, ngx_stream_variable_status,
88      0, NGX_STREAM_VAR_NOCACHEABLE, 0 },
89
90    { ngx_string("connection"), NULL,
91      ngx_stream_variable_connection, 0, 0, 0 },
92
93    { ngx_string("nginx_version"), NULL, ngx_stream_variable_nginx_version,
94      0, 0, 0 },
95
96    { ngx_string("hostname"), NULL, ngx_stream_variable_hostname,
97      0, 0, 0 },
98
99    { ngx_string("pid"), NULL, ngx_stream_variable_pid,
100      0, 0, 0 },
101
102    { ngx_string("msec"), NULL, ngx_stream_variable_msec,
103      0, NGX_STREAM_VAR_NOCACHEABLE, 0 },
104
105    { ngx_string("time_iso8601"), NULL, ngx_stream_variable_time_iso8601,
106      0, NGX_STREAM_VAR_NOCACHEABLE, 0 },
107
108    { ngx_string("time_local"), NULL, ngx_stream_variable_time_local,
109      0, NGX_STREAM_VAR_NOCACHEABLE, 0 },
110
111    { ngx_string("protocol"), NULL,
112      ngx_stream_variable_protocol, 0, 0, 0 },
113
114    { ngx_null_string, NULL, NULL, 0, 0, 0 }
115};
116
117
118ngx_stream_variable_value_t  ngx_stream_variable_null_value =
119    ngx_stream_variable("");
120ngx_stream_variable_value_t  ngx_stream_variable_true_value =
121    ngx_stream_variable("1");
122
123
124static ngx_uint_t  ngx_stream_variable_depth = 100;
125
126
127ngx_stream_variable_t *
128ngx_stream_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
129{
130    ngx_int_t                     rc;
131    ngx_uint_t                    i;
132    ngx_hash_key_t               *key;
133    ngx_stream_variable_t        *v;
134    ngx_stream_core_main_conf_t  *cmcf;
135
136    if (name->len == 0) {
137        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
138                           "invalid variable name \"$\"");
139        return NULL;
140    }
141
142    if (flags & NGX_STREAM_VAR_PREFIX) {
143        return ngx_stream_add_prefix_variable(cf, name, flags);
144    }
145
146    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
147
148    key = cmcf->variables_keys->keys.elts;
149    for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) {
150        if (name->len != key[i].key.len
151            || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0)
152        {
153            continue;
154        }
155
156        v = key[i].value;
157
158        if (!(v->flags & NGX_STREAM_VAR_CHANGEABLE)) {
159            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
160                               "the duplicate \"%V\" variable", name);
161            return NULL;
162        }
163
164        v->flags &= flags | ~NGX_STREAM_VAR_WEAK;
165
166        return v;
167    }
168
169    v = ngx_palloc(cf->pool, sizeof(ngx_stream_variable_t));
170    if (v == NULL) {
171        return NULL;
172    }
173
174    v->name.len = name->len;
175    v->name.data = ngx_pnalloc(cf->pool, name->len);
176    if (v->name.data == NULL) {
177        return NULL;
178    }
179
180    ngx_strlow(v->name.data, name->data, name->len);
181
182    v->set_handler = NULL;
183    v->get_handler = NULL;
184    v->data = 0;
185    v->flags = flags;
186    v->index = 0;
187
188    rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0);
189
190    if (rc == NGX_ERROR) {
191        return NULL;
192    }
193
194    if (rc == NGX_BUSY) {
195        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
196                           "conflicting variable name \"%V\"", name);
197        return NULL;
198    }
199
200    return v;
201}
202
203
204static ngx_stream_variable_t *
205ngx_stream_add_prefix_variable(ngx_conf_t *cf, ngx_str_t *name,
206    ngx_uint_t flags)
207{
208    ngx_uint_t                    i;
209    ngx_stream_variable_t        *v;
210    ngx_stream_core_main_conf_t  *cmcf;
211
212    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
213
214    v = cmcf->prefix_variables.elts;
215    for (i = 0; i < cmcf->prefix_variables.nelts; i++) {
216        if (name->len != v[i].name.len
217            || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
218        {
219            continue;
220        }
221
222        v = &v[i];
223
224        if (!(v->flags & NGX_STREAM_VAR_CHANGEABLE)) {
225            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
226                               "the duplicate \"%V\" variable", name);
227            return NULL;
228        }
229
230        v->flags &= flags | ~NGX_STREAM_VAR_WEAK;
231
232        return v;
233    }
234
235    v = ngx_array_push(&cmcf->prefix_variables);
236    if (v == NULL) {
237        return NULL;
238    }
239
240    v->name.len = name->len;
241    v->name.data = ngx_pnalloc(cf->pool, name->len);
242    if (v->name.data == NULL) {
243        return NULL;
244    }
245
246    ngx_strlow(v->name.data, name->data, name->len);
247
248    v->set_handler = NULL;
249    v->get_handler = NULL;
250    v->data = 0;
251    v->flags = flags;
252    v->index = 0;
253
254    return v;
255}
256
257
258ngx_int_t
259ngx_stream_get_variable_index(ngx_conf_t *cf, ngx_str_t *name)
260{
261    ngx_uint_t                    i;
262    ngx_stream_variable_t        *v;
263    ngx_stream_core_main_conf_t  *cmcf;
264
265    if (name->len == 0) {
266        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
267                           "invalid variable name \"$\"");
268        return NGX_ERROR;
269    }
270
271    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
272
273    v = cmcf->variables.elts;
274
275    if (v == NULL) {
276        if (ngx_array_init(&cmcf->variables, cf->pool, 4,
277                           sizeof(ngx_stream_variable_t))
278            != NGX_OK)
279        {
280            return NGX_ERROR;
281        }
282
283    } else {
284        for (i = 0; i < cmcf->variables.nelts; i++) {
285            if (name->len != v[i].name.len
286                || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
287            {
288                continue;
289            }
290
291            return i;
292        }
293    }
294
295    v = ngx_array_push(&cmcf->variables);
296    if (v == NULL) {
297        return NGX_ERROR;
298    }
299
300    v->name.len = name->len;
301    v->name.data = ngx_pnalloc(cf->pool, name->len);
302    if (v->name.data == NULL) {
303        return NGX_ERROR;
304    }
305
306    ngx_strlow(v->name.data, name->data, name->len);
307
308    v->set_handler = NULL;
309    v->get_handler = NULL;
310    v->data = 0;
311    v->flags = 0;
312    v->index = cmcf->variables.nelts - 1;
313
314    return v->index;
315}
316
317
318ngx_stream_variable_value_t *
319ngx_stream_get_indexed_variable(ngx_stream_session_t *s, ngx_uint_t index)
320{
321    ngx_stream_variable_t        *v;
322    ngx_stream_core_main_conf_t  *cmcf;
323
324    cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
325
326    if (cmcf->variables.nelts <= index) {
327        ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0,
328                      "unknown variable index: %ui", index);
329        return NULL;
330    }
331
332    if (s->variables[index].not_found || s->variables[index].valid) {
333        return &s->variables[index];
334    }
335
336    v = cmcf->variables.elts;
337
338    if (ngx_stream_variable_depth == 0) {
339        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
340                      "cycle while evaluating variable \"%V\"",
341                      &v[index].name);
342        return NULL;
343    }
344
345    ngx_stream_variable_depth--;
346
347    if (v[index].get_handler(s, &s->variables[index], v[index].data)
348        == NGX_OK)
349    {
350        ngx_stream_variable_depth++;
351
352        if (v[index].flags & NGX_STREAM_VAR_NOCACHEABLE) {
353            s->variables[index].no_cacheable = 1;
354        }
355
356        return &s->variables[index];
357    }
358
359    ngx_stream_variable_depth++;
360
361    s->variables[index].valid = 0;
362    s->variables[index].not_found = 1;
363
364    return NULL;
365}
366
367
368ngx_stream_variable_value_t *
369ngx_stream_get_flushed_variable(ngx_stream_session_t *s, ngx_uint_t index)
370{
371    ngx_stream_variable_value_t  *v;
372
373    v = &s->variables[index];
374
375    if (v->valid || v->not_found) {
376        if (!v->no_cacheable) {
377            return v;
378        }
379
380        v->valid = 0;
381        v->not_found = 0;
382    }
383
384    return ngx_stream_get_indexed_variable(s, index);
385}
386
387
388ngx_stream_variable_value_t *
389ngx_stream_get_variable(ngx_stream_session_t *s, ngx_str_t *name,
390    ngx_uint_t key)
391{
392    size_t                        len;
393    ngx_uint_t                    i, n;
394    ngx_stream_variable_t        *v;
395    ngx_stream_variable_value_t  *vv;
396    ngx_stream_core_main_conf_t  *cmcf;
397
398    cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
399
400    v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);
401
402    if (v) {
403        if (v->flags & NGX_STREAM_VAR_INDEXED) {
404            return ngx_stream_get_flushed_variable(s, v->index);
405        }
406
407        if (ngx_stream_variable_depth == 0) {
408            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
409                          "cycle while evaluating variable \"%V\"", name);
410            return NULL;
411        }
412
413        ngx_stream_variable_depth--;
414
415        vv = ngx_palloc(s->connection->pool,
416                        sizeof(ngx_stream_variable_value_t));
417
418        if (vv && v->get_handler(s, vv, v->data) == NGX_OK) {
419            ngx_stream_variable_depth++;
420            return vv;
421        }
422
423        ngx_stream_variable_depth++;
424        return NULL;
425    }
426
427    vv = ngx_palloc(s->connection->pool, sizeof(ngx_stream_variable_value_t));
428    if (vv == NULL) {
429        return NULL;
430    }
431
432    len = 0;
433
434    v = cmcf->prefix_variables.elts;
435    n = cmcf->prefix_variables.nelts;
436
437    for (i = 0; i < cmcf->prefix_variables.nelts; i++) {
438        if (name->len >= v[i].name.len && name->len > len
439            && ngx_strncmp(name->data, v[i].name.data, v[i].name.len) == 0)
440        {
441            len = v[i].name.len;
442            n = i;
443        }
444    }
445
446    if (n != cmcf->prefix_variables.nelts) {
447        if (v[n].get_handler(s, vv, (uintptr_t) name) == NGX_OK) {
448            return vv;
449        }
450
451        return NULL;
452    }
453
454    vv->not_found = 1;
455
456    return vv;
457}
458
459
460static ngx_int_t
461ngx_stream_variable_binary_remote_addr(ngx_stream_session_t *s,
462     ngx_stream_variable_value_t *v, uintptr_t data)
463 {
464    struct sockaddr_in   *sin;
465#if (NGX_HAVE_INET6)
466    struct sockaddr_in6  *sin6;
467#endif
468
469    switch (s->connection->sockaddr->sa_family) {
470
471#if (NGX_HAVE_INET6)
472    case AF_INET6:
473        sin6 = (struct sockaddr_in6 *) s->connection->sockaddr;
474
475        v->len = sizeof(struct in6_addr);
476        v->valid = 1;
477        v->no_cacheable = 0;
478        v->not_found = 0;
479        v->data = sin6->sin6_addr.s6_addr;
480
481        break;
482#endif
483
484    default: /* AF_INET */
485        sin = (struct sockaddr_in *) s->connection->sockaddr;
486
487        v->len = sizeof(in_addr_t);
488        v->valid = 1;
489        v->no_cacheable = 0;
490        v->not_found = 0;
491        v->data = (u_char *) &sin->sin_addr;
492
493        break;
494    }
495
496    return NGX_OK;
497}
498
499
500static ngx_int_t
501ngx_stream_variable_remote_addr(ngx_stream_session_t *s,
502    ngx_stream_variable_value_t *v, uintptr_t data)
503{
504    v->len = s->connection->addr_text.len;
505    v->valid = 1;
506    v->no_cacheable = 0;
507    v->not_found = 0;
508    v->data = s->connection->addr_text.data;
509
510    return NGX_OK;
511}
512
513
514static ngx_int_t
515ngx_stream_variable_remote_port(ngx_stream_session_t *s,
516    ngx_stream_variable_value_t *v, uintptr_t data)
517{
518    ngx_uint_t  port;
519
520    v->len = 0;
521    v->valid = 1;
522    v->no_cacheable = 0;
523    v->not_found = 0;
524
525    v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1);
526    if (v->data == NULL) {
527        return NGX_ERROR;
528    }
529
530    port = ngx_inet_get_port(s->connection->sockaddr);
531
532    if (port > 0 && port < 65536) {
533        v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
534    }
535
536    return NGX_OK;
537}
538
539
540static ngx_int_t
541ngx_stream_variable_proxy_protocol_addr(ngx_stream_session_t *s,
542    ngx_stream_variable_value_t *v, uintptr_t data)
543{
544    v->len = s->connection->proxy_protocol_addr.len;
545    v->valid = 1;
546    v->no_cacheable = 0;
547    v->not_found = 0;
548    v->data = s->connection->proxy_protocol_addr.data;
549
550    return NGX_OK;
551}
552
553
554static ngx_int_t
555ngx_stream_variable_proxy_protocol_port(ngx_stream_session_t *s,
556    ngx_stream_variable_value_t *v, uintptr_t data)
557{
558    ngx_uint_t  port;
559
560    v->len = 0;
561    v->valid = 1;
562    v->no_cacheable = 0;
563    v->not_found = 0;
564
565    v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1);
566    if (v->data == NULL) {
567        return NGX_ERROR;
568    }
569
570    port = s->connection->proxy_protocol_port;
571
572    if (port > 0 && port < 65536) {
573        v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
574    }
575
576    return NGX_OK;
577}
578
579
580static ngx_int_t
581ngx_stream_variable_server_addr(ngx_stream_session_t *s,
582    ngx_stream_variable_value_t *v, uintptr_t data)
583{
584    ngx_str_t  str;
585    u_char     addr[NGX_SOCKADDR_STRLEN];
586
587    str.len = NGX_SOCKADDR_STRLEN;
588    str.data = addr;
589
590    if (ngx_connection_local_sockaddr(s->connection, &str, 0) != NGX_OK) {
591        return NGX_ERROR;
592    }
593
594    str.data = ngx_pnalloc(s->connection->pool, str.len);
595    if (str.data == NULL) {
596        return NGX_ERROR;
597    }
598
599    ngx_memcpy(str.data, addr, str.len);
600
601    v->len = str.len;
602    v->valid = 1;
603    v->no_cacheable = 0;
604    v->not_found = 0;
605    v->data = str.data;
606
607    return NGX_OK;
608}
609
610
611static ngx_int_t
612ngx_stream_variable_server_port(ngx_stream_session_t *s,
613    ngx_stream_variable_value_t *v, uintptr_t data)
614{
615    ngx_uint_t  port;
616
617    v->len = 0;
618    v->valid = 1;
619    v->no_cacheable = 0;
620    v->not_found = 0;
621
622    if (ngx_connection_local_sockaddr(s->connection, NULL, 0) != NGX_OK) {
623        return NGX_ERROR;
624    }
625
626    v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1);
627    if (v->data == NULL) {
628        return NGX_ERROR;
629    }
630
631    port = ngx_inet_get_port(s->connection->local_sockaddr);
632
633    if (port > 0 && port < 65536) {
634        v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
635    }
636
637    return NGX_OK;
638}
639
640
641static ngx_int_t
642ngx_stream_variable_bytes(ngx_stream_session_t *s,
643    ngx_stream_variable_value_t *v, uintptr_t data)
644{
645    u_char  *p;
646
647    p = ngx_pnalloc(s->connection->pool, NGX_OFF_T_LEN);
648    if (p == NULL) {
649        return NGX_ERROR;
650    }
651
652    if (data == 1) {
653        v->len = ngx_sprintf(p, "%O", s->received) - p;
654
655    } else {
656        v->len = ngx_sprintf(p, "%O", s->connection->sent) - p;
657    }
658
659    v->valid = 1;
660    v->no_cacheable = 0;
661    v->not_found = 0;
662    v->data = p;
663
664    return NGX_OK;
665}
666
667
668static ngx_int_t
669ngx_stream_variable_session_time(ngx_stream_session_t *s,
670    ngx_stream_variable_value_t *v, uintptr_t data)
671{
672    u_char          *p;
673    ngx_time_t      *tp;
674    ngx_msec_int_t   ms;
675
676    p = ngx_pnalloc(s->connection->pool, NGX_TIME_T_LEN + 4);
677    if (p == NULL) {
678        return NGX_ERROR;
679    }
680
681    tp = ngx_timeofday();
682
683    ms = (ngx_msec_int_t)
684             ((tp->sec - s->start_sec) * 1000 + (tp->msec - s->start_msec));
685    ms = ngx_max(ms, 0);
686
687    v->len = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000) - p;
688    v->valid = 1;
689    v->no_cacheable = 0;
690    v->not_found = 0;
691    v->data = p;
692
693    return NGX_OK;
694}
695
696
697static ngx_int_t
698ngx_stream_variable_status(ngx_stream_session_t *s,
699    ngx_stream_variable_value_t *v, uintptr_t data)
700{
701    v->data = ngx_pnalloc(s->connection->pool, NGX_INT_T_LEN);
702    if (v->data == NULL) {
703        return NGX_ERROR;
704    }
705
706    v->len = ngx_sprintf(v->data, "%03ui", s->status) - v->data;
707    v->valid = 1;
708    v->no_cacheable = 0;
709    v->not_found = 0;
710
711    return NGX_OK;
712}
713
714
715static ngx_int_t
716ngx_stream_variable_connection(ngx_stream_session_t *s,
717    ngx_stream_variable_value_t *v, uintptr_t data)
718{
719    u_char  *p;
720
721    p = ngx_pnalloc(s->connection->pool, NGX_ATOMIC_T_LEN);
722    if (p == NULL) {
723        return NGX_ERROR;
724    }
725
726    v->len = ngx_sprintf(p, "%uA", s->connection->number) - p;
727    v->valid = 1;
728    v->no_cacheable = 0;
729    v->not_found = 0;
730    v->data = p;
731
732    return NGX_OK;
733}
734
735
736static ngx_int_t
737ngx_stream_variable_nginx_version(ngx_stream_session_t *s,
738    ngx_stream_variable_value_t *v, uintptr_t data)
739{
740    v->len = sizeof(NGINX_VERSION) - 1;
741    v->valid = 1;
742    v->no_cacheable = 0;
743    v->not_found = 0;
744    v->data = (u_char *) NGINX_VERSION;
745
746    return NGX_OK;
747}
748
749
750static ngx_int_t
751ngx_stream_variable_hostname(ngx_stream_session_t *s,
752    ngx_stream_variable_value_t *v, uintptr_t data)
753{
754    v->len = ngx_cycle->hostname.len;
755    v->valid = 1;
756    v->no_cacheable = 0;
757    v->not_found = 0;
758    v->data = ngx_cycle->hostname.data;
759
760    return NGX_OK;
761}
762
763
764static ngx_int_t
765ngx_stream_variable_pid(ngx_stream_session_t *s,
766    ngx_stream_variable_value_t *v, uintptr_t data)
767{
768    u_char  *p;
769
770    p = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN);
771    if (p == NULL) {
772        return NGX_ERROR;
773    }
774
775    v->len = ngx_sprintf(p, "%P", ngx_pid) - p;
776    v->valid = 1;
777    v->no_cacheable = 0;
778    v->not_found = 0;
779    v->data = p;
780
781    return NGX_OK;
782}
783
784
785static ngx_int_t
786ngx_stream_variable_msec(ngx_stream_session_t *s,
787    ngx_stream_variable_value_t *v, uintptr_t data)
788{
789    u_char      *p;
790    ngx_time_t  *tp;
791
792    p = ngx_pnalloc(s->connection->pool, NGX_TIME_T_LEN + 4);
793    if (p == NULL) {
794        return NGX_ERROR;
795    }
796
797    tp = ngx_timeofday();
798
799    v->len = ngx_sprintf(p, "%T.%03M", tp->sec, tp->msec) - p;
800    v->valid = 1;
801    v->no_cacheable = 0;
802    v->not_found = 0;
803    v->data = p;
804
805    return NGX_OK;
806}
807
808
809static ngx_int_t
810ngx_stream_variable_time_iso8601(ngx_stream_session_t *s,
811    ngx_stream_variable_value_t *v, uintptr_t data)
812{
813    u_char  *p;
814
815    p = ngx_pnalloc(s->connection->pool, ngx_cached_http_log_iso8601.len);
816    if (p == NULL) {
817        return NGX_ERROR;
818    }
819
820    ngx_memcpy(p, ngx_cached_http_log_iso8601.data,
821               ngx_cached_http_log_iso8601.len);
822
823    v->len = ngx_cached_http_log_iso8601.len;
824    v->valid = 1;
825    v->no_cacheable = 0;
826    v->not_found = 0;
827    v->data = p;
828
829    return NGX_OK;
830}
831
832
833static ngx_int_t
834ngx_stream_variable_time_local(ngx_stream_session_t *s,
835    ngx_stream_variable_value_t *v, uintptr_t data)
836{
837    u_char  *p;
838
839    p = ngx_pnalloc(s->connection->pool, ngx_cached_http_log_time.len);
840    if (p == NULL) {
841        return NGX_ERROR;
842    }
843
844    ngx_memcpy(p, ngx_cached_http_log_time.data, ngx_cached_http_log_time.len);
845
846    v->len = ngx_cached_http_log_time.len;
847    v->valid = 1;
848    v->no_cacheable = 0;
849    v->not_found = 0;
850    v->data = p;
851
852    return NGX_OK;
853}
854
855
856static ngx_int_t
857ngx_stream_variable_protocol(ngx_stream_session_t *s,
858    ngx_stream_variable_value_t *v, uintptr_t data)
859{
860    v->len = 3;
861    v->valid = 1;
862    v->no_cacheable = 0;
863    v->not_found = 0;
864    v->data = (u_char *) (s->connection->type == SOCK_DGRAM ? "UDP" : "TCP");
865
866    return NGX_OK;
867}
868
869
870void *
871ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map,
872    ngx_str_t *match)
873{
874    void        *value;
875    u_char      *low;
876    size_t       len;
877    ngx_uint_t   key;
878
879    len = match->len;
880
881    if (len) {
882        low = ngx_pnalloc(s->connection->pool, len);
883        if (low == NULL) {
884            return NULL;
885        }
886
887    } else {
888        low = NULL;
889    }
890
891    key = ngx_hash_strlow(low, match->data, len);
892
893    value = ngx_hash_find_combined(&map->hash, key, low, len);
894    if (value) {
895        return value;
896    }
897
898#if (NGX_PCRE)
899
900    if (len && map->nregex) {
901        ngx_int_t                n;
902        ngx_uint_t               i;
903        ngx_stream_map_regex_t  *reg;
904
905        reg = map->regex;
906
907        for (i = 0; i < map->nregex; i++) {
908
909            n = ngx_stream_regex_exec(s, reg[i].regex, match);
910
911            if (n == NGX_OK) {
912                return reg[i].value;
913            }
914
915            if (n == NGX_DECLINED) {
916                continue;
917            }
918
919            /* NGX_ERROR */
920
921            return NULL;
922        }
923    }
924
925#endif
926
927    return NULL;
928}
929
930
931#if (NGX_PCRE)
932
933static ngx_int_t
934ngx_stream_variable_not_found(ngx_stream_session_t *s,
935    ngx_stream_variable_value_t *v, uintptr_t data)
936{
937    v->not_found = 1;
938    return NGX_OK;
939}
940
941
942ngx_stream_regex_t *
943ngx_stream_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc)
944{
945    u_char                       *p;
946    size_t                        size;
947    ngx_str_t                     name;
948    ngx_uint_t                    i, n;
949    ngx_stream_variable_t        *v;
950    ngx_stream_regex_t           *re;
951    ngx_stream_regex_variable_t  *rv;
952    ngx_stream_core_main_conf_t  *cmcf;
953
954    rc->pool = cf->pool;
955
956    if (ngx_regex_compile(rc) != NGX_OK) {
957        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc->err);
958        return NULL;
959    }
960
961    re = ngx_pcalloc(cf->pool, sizeof(ngx_stream_regex_t));
962    if (re == NULL) {
963        return NULL;
964    }
965
966    re->regex = rc->regex;
967    re->ncaptures = rc->captures;
968    re->name = rc->pattern;
969
970    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
971    cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures);
972
973    n = (ngx_uint_t) rc->named_captures;
974
975    if (n == 0) {
976        return re;
977    }
978
979    rv = ngx_palloc(rc->pool, n * sizeof(ngx_stream_regex_variable_t));
980    if (rv == NULL) {
981        return NULL;
982    }
983
984    re->variables = rv;
985    re->nvariables = n;
986
987    size = rc->name_size;
988    p = rc->names;
989
990    for (i = 0; i < n; i++) {
991        rv[i].capture = 2 * ((p[0] << 8) + p[1]);
992
993        name.data = &p[2];
994        name.len = ngx_strlen(name.data);
995
996        v = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);
997        if (v == NULL) {
998            return NULL;
999        }
1000
1001        rv[i].index = ngx_stream_get_variable_index(cf, &name);
1002        if (rv[i].index == NGX_ERROR) {
1003            return NULL;
1004        }
1005
1006        v->get_handler = ngx_stream_variable_not_found;
1007
1008        p += size;
1009    }
1010
1011    return re;
1012}
1013
1014
1015ngx_int_t
1016ngx_stream_regex_exec(ngx_stream_session_t *s, ngx_stream_regex_t *re,
1017    ngx_str_t *str)
1018{
1019    ngx_int_t                     rc, index;
1020    ngx_uint_t                    i, n, len;
1021    ngx_stream_variable_value_t  *vv;
1022    ngx_stream_core_main_conf_t  *cmcf;
1023
1024    cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
1025
1026    if (re->ncaptures) {
1027        len = cmcf->ncaptures;
1028
1029        if (s->captures == NULL) {
1030            s->captures = ngx_palloc(s->connection->pool, len * sizeof(int));
1031            if (s->captures == NULL) {
1032                return NGX_ERROR;
1033            }
1034        }
1035
1036    } else {
1037        len = 0;
1038    }
1039
1040    rc = ngx_regex_exec(re->regex, str, s->captures, len);
1041
1042    if (rc == NGX_REGEX_NO_MATCHED) {
1043        return NGX_DECLINED;
1044    }
1045
1046    if (rc < 0) {
1047        ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0,
1048                      ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
1049                      rc, str, &re->name);
1050        return NGX_ERROR;
1051    }
1052
1053    for (i = 0; i < re->nvariables; i++) {
1054
1055        n = re->variables[i].capture;
1056        index = re->variables[i].index;
1057        vv = &s->variables[index];
1058
1059        vv->len = s->captures[n + 1] - s->captures[n];
1060        vv->valid = 1;
1061        vv->no_cacheable = 0;
1062        vv->not_found = 0;
1063        vv->data = &str->data[s->captures[n]];
1064
1065#if (NGX_DEBUG)
1066        {
1067        ngx_stream_variable_t  *v;
1068
1069        v = cmcf->variables.elts;
1070
1071        ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
1072                       "stream regex set $%V to \"%v\"", &v[index].name, vv);
1073        }
1074#endif
1075    }
1076
1077    s->ncaptures = rc * 2;
1078    s->captures_data = str->data;
1079
1080    return NGX_OK;
1081}
1082
1083#endif
1084
1085
1086ngx_int_t
1087ngx_stream_variables_add_core_vars(ngx_conf_t *cf)
1088{
1089    ngx_stream_variable_t        *cv, *v;
1090    ngx_stream_core_main_conf_t  *cmcf;
1091
1092    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
1093
1094    cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,
1095                                       sizeof(ngx_hash_keys_arrays_t));
1096    if (cmcf->variables_keys == NULL) {
1097        return NGX_ERROR;
1098    }
1099
1100    cmcf->variables_keys->pool = cf->pool;
1101    cmcf->variables_keys->temp_pool = cf->pool;
1102
1103    if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)
1104        != NGX_OK)
1105    {
1106        return NGX_ERROR;
1107    }
1108
1109    if (ngx_array_init(&cmcf->prefix_variables, cf->pool, 8,
1110                       sizeof(ngx_stream_variable_t))
1111        != NGX_OK)
1112    {
1113        return NGX_ERROR;
1114    }
1115
1116    for (cv = ngx_stream_core_variables; cv->name.len; cv++) {
1117        v = ngx_stream_add_variable(cf, &cv->name, cv->flags);
1118        if (v == NULL) {
1119            return NGX_ERROR;
1120        }
1121
1122        *v = *cv;
1123    }
1124
1125    return NGX_OK;
1126}
1127
1128
1129ngx_int_t
1130ngx_stream_variables_init_vars(ngx_conf_t *cf)
1131{
1132    size_t                        len;
1133    ngx_uint_t                    i, n;
1134    ngx_hash_key_t               *key;
1135    ngx_hash_init_t               hash;
1136    ngx_stream_variable_t        *v, *av, *pv;
1137    ngx_stream_core_main_conf_t  *cmcf;
1138
1139    /* set the handlers for the indexed stream variables */
1140
1141    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
1142
1143    v = cmcf->variables.elts;
1144    pv = cmcf->prefix_variables.elts;
1145    key = cmcf->variables_keys->keys.elts;
1146
1147    for (i = 0; i < cmcf->variables.nelts; i++) {
1148
1149        for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
1150
1151            av = key[n].value;
1152
1153            if (v[i].name.len == key[n].key.len
1154                && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len)
1155                   == 0)
1156            {
1157                v[i].get_handler = av->get_handler;
1158                v[i].data = av->data;
1159
1160                av->flags |= NGX_STREAM_VAR_INDEXED;
1161                v[i].flags = av->flags;
1162
1163                av->index = i;
1164
1165                if (av->get_handler == NULL
1166                    || (av->flags & NGX_STREAM_VAR_WEAK))
1167                {
1168                    break;
1169                }
1170
1171                goto next;
1172            }
1173        }
1174
1175        len = 0;
1176        av = NULL;
1177
1178        for (n = 0; n < cmcf->prefix_variables.nelts; n++) {
1179            if (v[i].name.len >= pv[n].name.len && v[i].name.len > len
1180                && ngx_strncmp(v[i].name.data, pv[n].name.data, pv[n].name.len)
1181                   == 0)
1182            {
1183                av = &pv[n];
1184                len = pv[n].name.len;
1185            }
1186        }
1187
1188        if (av) {
1189            v[i].get_handler = av->get_handler;
1190            v[i].data = (uintptr_t) &v[i].name;
1191            v[i].flags = av->flags;
1192
1193            goto next;
1194         }
1195
1196        if (v[i].get_handler == NULL) {
1197            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
1198                          "unknown \"%V\" variable", &v[i].name);
1199            return NGX_ERROR;
1200        }
1201
1202    next:
1203        continue;
1204    }
1205
1206
1207    for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
1208        av = key[n].value;
1209
1210        if (av->flags & NGX_STREAM_VAR_NOHASH) {
1211            key[n].key.data = NULL;
1212        }
1213    }
1214
1215
1216    hash.hash = &cmcf->variables_hash;
1217    hash.key = ngx_hash_key;
1218    hash.max_size = cmcf->variables_hash_max_size;
1219    hash.bucket_size = cmcf->variables_hash_bucket_size;
1220    hash.name = "variables_hash";
1221    hash.pool = cf->pool;
1222    hash.temp_pool = NULL;
1223
1224    if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts,
1225                      cmcf->variables_keys->keys.nelts)
1226        != NGX_OK)
1227    {
1228        return NGX_ERROR;
1229    }
1230
1231    cmcf->variables_keys = NULL;
1232
1233    return NGX_OK;
1234}
1235