/src/httpd/server/apreq_parser_urlencoded.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** Licensed to the Apache Software Foundation (ASF) under one or more |
3 | | ** contributor license agreements. See the NOTICE file distributed with |
4 | | ** this work for additional information regarding copyright ownership. |
5 | | ** The ASF licenses this file to You under the Apache License, Version 2.0 |
6 | | ** (the "License"); you may not use this file except in compliance with |
7 | | ** the License. You may obtain a copy of the License at |
8 | | ** |
9 | | ** http://www.apache.org/licenses/LICENSE-2.0 |
10 | | ** |
11 | | ** Unless required by applicable law or agreed to in writing, software |
12 | | ** distributed under the License is distributed on an "AS IS" BASIS, |
13 | | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | ** See the License for the specific language governing permissions and |
15 | | ** limitations under the License. |
16 | | */ |
17 | | |
18 | | #include "apreq_parser.h" |
19 | | #include "apreq_util.h" |
20 | | #include "apreq_error.h" |
21 | | |
22 | | |
23 | 165 | #define PARSER_STATUS_CHECK(PREFIX) do { \ |
24 | 165 | if (ctx->status == PREFIX##_ERROR) \ |
25 | 165 | return APREQ_ERROR_GENERAL; \ |
26 | 165 | else if (ctx->status == PREFIX##_COMPLETE) \ |
27 | 165 | return APR_SUCCESS; \ |
28 | 165 | else if (bb == NULL) \ |
29 | 165 | return APR_INCOMPLETE; \ |
30 | 165 | } while (0); |
31 | | |
32 | | |
33 | | |
34 | | struct url_ctx { |
35 | | apr_bucket_brigade *bb; |
36 | | apr_size_t nlen; |
37 | | apr_size_t vlen; |
38 | | enum { |
39 | | URL_NAME, |
40 | | URL_VALUE, |
41 | | URL_COMPLETE, |
42 | | URL_ERROR |
43 | | } status; |
44 | | }; |
45 | | |
46 | | |
47 | | /******************** application/x-www-form-urlencoded ********************/ |
48 | | |
49 | | static apr_status_t split_urlword(apreq_param_t **p, apr_pool_t *pool, |
50 | | apr_bucket_brigade *bb, |
51 | | apr_size_t nlen, |
52 | | apr_size_t vlen) |
53 | 570 | { |
54 | 570 | apreq_param_t *param; |
55 | 570 | apreq_value_t *v; |
56 | 570 | apr_bucket *e, *f; |
57 | 570 | apr_status_t s; |
58 | 570 | struct iovec vec[APREQ_DEFAULT_NELTS]; |
59 | 570 | apr_array_header_t arr; |
60 | 570 | apr_size_t mark; |
61 | 570 | apreq_charset_t charset; |
62 | | |
63 | 570 | if (nlen == 0) |
64 | 1 | return APR_EBADARG; |
65 | | |
66 | 569 | param = apreq_param_make(pool, NULL, nlen, NULL, vlen); |
67 | 569 | *(const apreq_value_t **)&v = ¶m->v; |
68 | | |
69 | 569 | arr.pool = pool; |
70 | 569 | arr.elt_size = sizeof(struct iovec); |
71 | 569 | arr.nelts = 0; |
72 | 569 | arr.nalloc = APREQ_DEFAULT_NELTS; |
73 | 569 | arr.elts = (char *)vec; |
74 | | |
75 | 569 | ++nlen, ++vlen; |
76 | 569 | e = APR_BRIGADE_FIRST(bb); |
77 | | |
78 | 569 | while (!APR_BUCKET_IS_EOS(e)) { |
79 | 569 | struct iovec *iov = apr_array_push(&arr); |
80 | 569 | apr_size_t len; |
81 | 569 | s = apr_bucket_read(e, (const char **)&iov->iov_base, |
82 | 569 | &len, APR_BLOCK_READ); |
83 | 569 | if (s != APR_SUCCESS) |
84 | 0 | return s; |
85 | | |
86 | 569 | iov->iov_len = len; |
87 | 569 | nlen -= len; |
88 | | |
89 | 569 | e = APR_BUCKET_NEXT(e); |
90 | | |
91 | 569 | if (nlen == 0) { |
92 | 569 | iov->iov_len--; |
93 | 569 | break; |
94 | 569 | } |
95 | 569 | } |
96 | | |
97 | 569 | mark = arr.nelts; |
98 | | |
99 | 569 | while (!APR_BUCKET_IS_EOS(e)) { |
100 | 569 | struct iovec *iov = apr_array_push(&arr); |
101 | 569 | apr_size_t len; |
102 | 569 | s = apr_bucket_read(e, (const char **)&iov->iov_base, |
103 | 569 | &len, APR_BLOCK_READ); |
104 | 569 | if (s != APR_SUCCESS) |
105 | 0 | return s; |
106 | | |
107 | 569 | iov->iov_len = len; |
108 | 569 | vlen -= len; |
109 | | |
110 | 569 | e = APR_BUCKET_NEXT(e); |
111 | | |
112 | 569 | if (vlen == 0) { |
113 | 569 | iov->iov_len--; |
114 | 569 | break; |
115 | 569 | } |
116 | | |
117 | 569 | } |
118 | | |
119 | 569 | s = apreq_decodev(v->data, &vlen, |
120 | 569 | (struct iovec *)arr.elts + mark, arr.nelts - mark); |
121 | 569 | if (s != APR_SUCCESS) |
122 | 34 | return s; |
123 | | |
124 | 535 | charset = apreq_charset_divine(v->data, vlen); |
125 | | |
126 | 535 | v->name = v->data + vlen + 1; |
127 | 535 | v->dlen = vlen; |
128 | | |
129 | 535 | s = apreq_decodev(v->name, &nlen, (struct iovec *)arr.elts, mark); |
130 | 535 | if (s != APR_SUCCESS) |
131 | 35 | return s; |
132 | | |
133 | 500 | switch (apreq_charset_divine(v->name, nlen)) { |
134 | 92 | case APREQ_CHARSET_UTF8: |
135 | 92 | if (charset == APREQ_CHARSET_ASCII) |
136 | 59 | charset = APREQ_CHARSET_UTF8; |
137 | 286 | case APREQ_CHARSET_ASCII: |
138 | 286 | break; |
139 | | |
140 | 158 | case APREQ_CHARSET_LATIN1: |
141 | 158 | if (charset != APREQ_CHARSET_CP1252) |
142 | 122 | charset = APREQ_CHARSET_LATIN1; |
143 | 158 | break; |
144 | 56 | case APREQ_CHARSET_CP1252: |
145 | 56 | charset = APREQ_CHARSET_CP1252; |
146 | 500 | } |
147 | | |
148 | 500 | v->nlen = nlen; |
149 | | |
150 | 1.50k | while ((f = APR_BRIGADE_FIRST(bb)) != e) |
151 | 1.00k | apr_bucket_delete(f); |
152 | | |
153 | 500 | apreq_param_tainted_on(param); |
154 | 500 | apreq_param_charset_set(param, charset); |
155 | 500 | *p = param; |
156 | 500 | return APR_SUCCESS; |
157 | 500 | } |
158 | | |
159 | | APREQ_DECLARE_PARSER(apreq_parse_urlencoded) |
160 | 165 | { |
161 | 165 | apr_pool_t *pool = parser->pool; |
162 | 165 | apr_bucket *e; |
163 | 165 | struct url_ctx *ctx; |
164 | | |
165 | 165 | if (parser->ctx == NULL) { |
166 | 165 | ctx = apr_pcalloc(pool, sizeof *ctx); |
167 | 165 | ctx->bb = apr_brigade_create(pool, parser->bucket_alloc); |
168 | 165 | parser->ctx = ctx; |
169 | 165 | ctx->status = URL_NAME; |
170 | 165 | } |
171 | 0 | else |
172 | 0 | ctx = parser->ctx; |
173 | | |
174 | 165 | PARSER_STATUS_CHECK(URL); |
175 | 165 | e = APR_BRIGADE_LAST(ctx->bb); |
176 | 165 | APR_BRIGADE_CONCAT(ctx->bb, bb); |
177 | | |
178 | 665 | parse_url_brigade: |
179 | | |
180 | 665 | for (e = APR_BUCKET_NEXT(e); |
181 | 760 | e != APR_BRIGADE_SENTINEL(ctx->bb); |
182 | 665 | e = APR_BUCKET_NEXT(e)) |
183 | 665 | { |
184 | 665 | apreq_param_t *param; |
185 | 665 | apr_size_t off = 0, dlen; |
186 | 665 | const char *data; |
187 | 665 | apr_status_t s; |
188 | | |
189 | 665 | if (APR_BUCKET_IS_EOS(e)) { |
190 | 0 | if (ctx->status == URL_NAME) { |
191 | 0 | s = APR_SUCCESS; |
192 | 0 | } |
193 | 0 | else { |
194 | 0 | s = split_urlword(¶m, pool, ctx->bb, ctx->nlen, ctx->vlen); |
195 | 0 | if (parser->hook != NULL && s == APR_SUCCESS) |
196 | 0 | s = apreq_hook_run(parser->hook, param, NULL); |
197 | |
|
198 | 0 | if (s == APR_SUCCESS) { |
199 | 0 | apreq_value_table_add(¶m->v, t); |
200 | 0 | ctx->status = URL_COMPLETE; |
201 | 0 | } |
202 | 0 | else { |
203 | 0 | ctx->status = URL_ERROR; |
204 | 0 | } |
205 | 0 | } |
206 | |
|
207 | 0 | APR_BRIGADE_CONCAT(bb, ctx->bb); |
208 | 0 | return s; |
209 | 0 | } |
210 | | |
211 | 665 | s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); |
212 | 665 | if ( s != APR_SUCCESS ) { |
213 | 0 | ctx->status = URL_ERROR; |
214 | 0 | return s; |
215 | 0 | } |
216 | | |
217 | 1.25k | parse_url_bucket: |
218 | | |
219 | 1.25k | switch (ctx->status) { |
220 | | |
221 | 665 | case URL_NAME: |
222 | 5.75k | while (off < dlen) { |
223 | 5.68k | switch (data[off++]) { |
224 | 593 | case '=': |
225 | 593 | apr_bucket_split(e, off); |
226 | 593 | dlen -= off; |
227 | 593 | data += off; |
228 | 593 | off = 0; |
229 | 593 | e = APR_BUCKET_NEXT(e); |
230 | 593 | ctx->status = URL_VALUE; |
231 | 593 | goto parse_url_bucket; |
232 | 5.09k | default: |
233 | 5.09k | ++ctx->nlen; |
234 | 5.68k | } |
235 | 5.68k | } |
236 | 72 | break; |
237 | | |
238 | 593 | case URL_VALUE: |
239 | 3.91k | while (off < dlen) { |
240 | | |
241 | 3.89k | switch (data[off++]) { |
242 | 253 | case '&': |
243 | 570 | case ';': |
244 | 570 | apr_bucket_split(e, off); |
245 | 570 | s = split_urlword(¶m, pool, ctx->bb, |
246 | 570 | ctx->nlen, ctx->vlen); |
247 | 570 | if (parser->hook != NULL && s == APR_SUCCESS) |
248 | 500 | s = apreq_hook_run(parser->hook, param, NULL); |
249 | | |
250 | 570 | if (s != APR_SUCCESS) { |
251 | 70 | ctx->status = URL_ERROR; |
252 | 70 | return s; |
253 | 70 | } |
254 | | |
255 | 500 | apreq_value_table_add(¶m->v, t); |
256 | 500 | ctx->status = URL_NAME; |
257 | 500 | ctx->nlen = 0; |
258 | 500 | ctx->vlen = 0; |
259 | 500 | e = APR_BRIGADE_SENTINEL(ctx->bb); |
260 | 500 | goto parse_url_brigade; |
261 | | |
262 | 3.32k | default: |
263 | 3.32k | ++ctx->vlen; |
264 | 3.89k | } |
265 | 3.89k | } |
266 | 23 | break; |
267 | 23 | default: |
268 | 0 | ; /* not reached */ |
269 | 1.25k | } |
270 | 1.25k | } |
271 | 95 | apreq_brigade_setaside(ctx->bb, pool); |
272 | 95 | return APR_INCOMPLETE; |
273 | 665 | } |
274 | | |
275 | | |