Mercurial > hg > nginx-quic
annotate src/http/modules/ngx_http_split_clients_module.c @ 4664:356c91151658 stable-1.2
Merge of r4618: rewrite escaping fix (ticket #162).
The following code resulted in incorrect escaping of uri and possible
segfault:
location / {
rewrite ^(.*) $1?c=$1;
return 200 "$uri";
}
If there were arguments in a rewrite's replacement string, and length was
actually calculated (due to duplicate captures as in the example above,
or variables present), the is_args flag was set and incorrectly copied
after length calculation. This resulted in escaping applied to the uri part
of the replacement, resulting in incorrect escaping. Additionally, buffer
was allocated without escaping expected, thus this also resulted in buffer
overrun and possible segfault.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 04 Jun 2012 11:07:19 +0000 |
parents | 834049edae24 |
children | 9c9fbdbe9383 |
rev | line source |
---|---|
3513 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4412 | 4 * Copyright (C) Nginx, Inc. |
3513 | 5 */ |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_http.h> | |
11 | |
12 | |
13 typedef struct { | |
14 uint32_t percent; | |
15 ngx_http_variable_value_t value; | |
16 } ngx_http_split_clients_part_t; | |
17 | |
18 | |
19 typedef struct { | |
20 ngx_http_complex_value_t value; | |
21 ngx_array_t parts; | |
22 } ngx_http_split_clients_ctx_t; | |
23 | |
24 | |
25 static char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, | |
26 void *conf); | |
27 static char *ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, | |
28 void *conf); | |
29 | |
30 static ngx_command_t ngx_http_split_clients_commands[] = { | |
31 | |
32 { ngx_string("split_clients"), | |
33 NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, | |
34 ngx_conf_split_clients_block, | |
35 NGX_HTTP_MAIN_CONF_OFFSET, | |
36 0, | |
37 NULL }, | |
38 | |
39 ngx_null_command | |
40 }; | |
41 | |
42 | |
43 static ngx_http_module_t ngx_http_split_clients_module_ctx = { | |
44 NULL, /* preconfiguration */ | |
45 NULL, /* postconfiguration */ | |
46 | |
47 NULL, /* create main configuration */ | |
48 NULL, /* init main configuration */ | |
49 | |
50 NULL, /* create server configuration */ | |
51 NULL, /* merge server configuration */ | |
52 | |
53 NULL, /* create location configuration */ | |
54 NULL /* merge location configuration */ | |
55 }; | |
56 | |
57 | |
58 ngx_module_t ngx_http_split_clients_module = { | |
59 NGX_MODULE_V1, | |
60 &ngx_http_split_clients_module_ctx, /* module context */ | |
61 ngx_http_split_clients_commands, /* module directives */ | |
62 NGX_HTTP_MODULE, /* module type */ | |
63 NULL, /* init master */ | |
64 NULL, /* init module */ | |
65 NULL, /* init process */ | |
66 NULL, /* init thread */ | |
67 NULL, /* exit thread */ | |
68 NULL, /* exit process */ | |
69 NULL, /* exit master */ | |
70 NGX_MODULE_V1_PADDING | |
71 }; | |
72 | |
73 | |
74 static ngx_int_t | |
75 ngx_http_split_clients_variable(ngx_http_request_t *r, | |
76 ngx_http_variable_value_t *v, uintptr_t data) | |
77 { | |
78 ngx_http_split_clients_ctx_t *ctx = (ngx_http_split_clients_ctx_t *) data; | |
79 | |
80 uint32_t hash; | |
81 ngx_str_t val; | |
82 ngx_uint_t i; | |
83 ngx_http_split_clients_part_t *part; | |
84 | |
85 *v = ngx_http_variable_null_value; | |
86 | |
87 if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) { | |
88 return NGX_OK; | |
89 } | |
90 | |
3892
12d8d2f30205
use MurmurHash2 for split_clients, because
Igor Sysoev <igor@sysoev.ru>
parents:
3607
diff
changeset
|
91 hash = ngx_murmur_hash2(val.data, val.len); |
3513 | 92 |
93 part = ctx->parts.elts; | |
94 | |
95 for (i = 0; i < ctx->parts.nelts; i++) { | |
96 | |
97 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3918 | 98 "http split: %uD %uD", hash, part[i].percent); |
3513 | 99 |
4561
ae63013cbffa
Fixed calculation of range boundaries.
Ruslan Ermilov <ru@nginx.com>
parents:
4412
diff
changeset
|
100 if (hash < part[i].percent || part[i].percent == 0) { |
3513 | 101 *v = part[i].value; |
102 return NGX_OK; | |
103 } | |
104 } | |
105 | |
106 return NGX_OK; | |
107 } | |
108 | |
109 | |
110 static char * | |
111 ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
112 { | |
113 char *rv; | |
4561
ae63013cbffa
Fixed calculation of range boundaries.
Ruslan Ermilov <ru@nginx.com>
parents:
4412
diff
changeset
|
114 uint32_t sum, last; |
3513 | 115 ngx_str_t *value, name; |
4561
ae63013cbffa
Fixed calculation of range boundaries.
Ruslan Ermilov <ru@nginx.com>
parents:
4412
diff
changeset
|
116 ngx_uint_t i; |
3513 | 117 ngx_conf_t save; |
118 ngx_http_variable_t *var; | |
119 ngx_http_split_clients_ctx_t *ctx; | |
120 ngx_http_split_clients_part_t *part; | |
121 ngx_http_compile_complex_value_t ccv; | |
122 | |
123 ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_split_clients_ctx_t)); | |
124 if (ctx == NULL) { | |
125 return NGX_CONF_ERROR; | |
126 } | |
127 | |
128 value = cf->args->elts; | |
129 | |
130 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); | |
131 | |
132 ccv.cf = cf; | |
133 ccv.value = &value[1]; | |
134 ccv.complex_value = &ctx->value; | |
135 | |
136 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { | |
137 return NGX_CONF_ERROR; | |
138 } | |
139 | |
140 name = value[2]; | |
141 name.len--; | |
142 name.data++; | |
143 | |
144 var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); | |
145 if (var == NULL) { | |
146 return NGX_CONF_ERROR; | |
147 } | |
148 | |
149 var->get_handler = ngx_http_split_clients_variable; | |
150 var->data = (uintptr_t) ctx; | |
151 | |
152 if (ngx_array_init(&ctx->parts, cf->pool, 2, | |
153 sizeof(ngx_http_split_clients_part_t)) | |
154 != NGX_OK) | |
155 { | |
156 return NGX_CONF_ERROR; | |
157 } | |
158 | |
159 save = *cf; | |
160 cf->ctx = ctx; | |
161 cf->handler = ngx_http_split_clients; | |
162 cf->handler_conf = conf; | |
163 | |
164 rv = ngx_conf_parse(cf, NULL); | |
165 | |
166 *cf = save; | |
167 | |
3607
8bff43217171
do not try to calculate procent sum if there was an error
Igor Sysoev <igor@sysoev.ru>
parents:
3513
diff
changeset
|
168 if (rv != NGX_CONF_OK) { |
8bff43217171
do not try to calculate procent sum if there was an error
Igor Sysoev <igor@sysoev.ru>
parents:
3513
diff
changeset
|
169 return rv; |
8bff43217171
do not try to calculate procent sum if there was an error
Igor Sysoev <igor@sysoev.ru>
parents:
3513
diff
changeset
|
170 } |
8bff43217171
do not try to calculate procent sum if there was an error
Igor Sysoev <igor@sysoev.ru>
parents:
3513
diff
changeset
|
171 |
3513 | 172 sum = 0; |
173 last = 0; | |
174 part = ctx->parts.elts; | |
175 | |
176 for (i = 0; i < ctx->parts.nelts; i++) { | |
177 sum = part[i].percent ? sum + part[i].percent : 10000; | |
178 if (sum > 10000) { | |
4561
ae63013cbffa
Fixed calculation of range boundaries.
Ruslan Ermilov <ru@nginx.com>
parents:
4412
diff
changeset
|
179 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
4593
834049edae24
Fixed grammar in error messages.
Ruslan Ermilov <ru@nginx.com>
parents:
4561
diff
changeset
|
180 "percent total is greater than 100%%"); |
4561
ae63013cbffa
Fixed calculation of range boundaries.
Ruslan Ermilov <ru@nginx.com>
parents:
4412
diff
changeset
|
181 return NGX_CONF_ERROR; |
3513 | 182 } |
183 | |
184 if (part[i].percent) { | |
4561
ae63013cbffa
Fixed calculation of range boundaries.
Ruslan Ermilov <ru@nginx.com>
parents:
4412
diff
changeset
|
185 last += part[i].percent * (uint64_t) 0xffffffff / 10000; |
ae63013cbffa
Fixed calculation of range boundaries.
Ruslan Ermilov <ru@nginx.com>
parents:
4412
diff
changeset
|
186 part[i].percent = last; |
3513 | 187 } |
188 } | |
189 | |
190 return rv; | |
191 } | |
192 | |
193 | |
194 static char * | |
195 ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) | |
196 { | |
197 ngx_int_t n; | |
198 ngx_str_t *value; | |
199 ngx_http_split_clients_ctx_t *ctx; | |
200 ngx_http_split_clients_part_t *part; | |
201 | |
202 ctx = cf->ctx; | |
203 value = cf->args->elts; | |
204 | |
205 part = ngx_array_push(&ctx->parts); | |
206 if (part == NULL) { | |
207 return NGX_CONF_ERROR; | |
208 } | |
209 | |
210 if (value[0].len == 1 && value[0].data[0] == '*') { | |
211 part->percent = 0; | |
212 | |
213 } else { | |
214 if (value[0].data[value[0].len - 1] != '%') { | |
215 goto invalid; | |
216 } | |
217 | |
218 n = ngx_atofp(value[0].data, value[0].len - 1, 2); | |
219 if (n == NGX_ERROR || n == 0) { | |
220 goto invalid; | |
221 } | |
222 | |
223 part->percent = (uint32_t) n; | |
224 } | |
225 | |
226 part->value.len = value[1].len; | |
227 part->value.valid = 1; | |
228 part->value.no_cacheable = 0; | |
229 part->value.not_found = 0; | |
230 part->value.data = value[1].data; | |
231 | |
232 return NGX_CONF_OK; | |
233 | |
234 invalid: | |
235 | |
236 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
237 "invalid percent value \"%V\"", &value[0]); | |
238 return NGX_CONF_ERROR; | |
239 } |