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