/src/httpd/modules/http/http_protocol.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Licensed to the Apache Software Foundation (ASF) under one or more |
2 | | * contributor license agreements. See the NOTICE file distributed with |
3 | | * this work for additional information regarding copyright ownership. |
4 | | * The ASF licenses this file to You under the Apache License, Version 2.0 |
5 | | * (the "License"); you may not use this file except in compliance with |
6 | | * the License. You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | /* |
18 | | * http_protocol.c --- routines which directly communicate with the client. |
19 | | * |
20 | | * Code originally by Rob McCool; much redone by Robert S. Thau |
21 | | * and the Apache Software Foundation. |
22 | | */ |
23 | | |
24 | | #include "apr.h" |
25 | | #include "apr_strings.h" |
26 | | #include "apr_buckets.h" |
27 | | #include "apr_lib.h" |
28 | | #include "apr_signal.h" |
29 | | |
30 | | #define APR_WANT_STDIO /* for sscanf */ |
31 | | #define APR_WANT_STRFUNC |
32 | | #define APR_WANT_MEMFUNC |
33 | | #include "apr_want.h" |
34 | | |
35 | | #include "util_filter.h" |
36 | | #include "ap_config.h" |
37 | | #include "httpd.h" |
38 | | #include "http_config.h" |
39 | | #include "http_core.h" |
40 | | #include "http_protocol.h" |
41 | | #include "http_main.h" |
42 | | #include "http_request.h" |
43 | | #include "http_vhost.h" |
44 | | #include "http_log.h" /* For errors detected in basic auth common |
45 | | * support code... */ |
46 | | #include "apr_date.h" /* For apr_date_parse_http and APR_DATE_BAD */ |
47 | | #include "util_charset.h" |
48 | | #include "util_ebcdic.h" |
49 | | #include "util_time.h" |
50 | | #include "ap_mpm.h" |
51 | | |
52 | | #include "mod_core.h" |
53 | | |
54 | | #if APR_HAVE_STDARG_H |
55 | | #include <stdarg.h> |
56 | | #endif |
57 | | #if APR_HAVE_UNISTD_H |
58 | | #include <unistd.h> |
59 | | #endif |
60 | | |
61 | | APLOG_USE_MODULE(http); |
62 | | |
63 | | /* New Apache routine to map status codes into array indices |
64 | | * e.g. 100 -> 0, 101 -> 1, 200 -> 2 ... |
65 | | * The number of status lines must equal the value of |
66 | | * RESPONSE_CODES (httpd.h) and must be listed in order. |
67 | | * No gaps are allowed between X00 and the largest Xnn |
68 | | * for any X (see ap_index_of_response). |
69 | | * When adding a new code here, add a define to httpd.h |
70 | | * as well. |
71 | | */ |
72 | | |
73 | | static const char * const status_lines[RESPONSE_CODES] = |
74 | | { |
75 | | "100 Continue", |
76 | | "101 Switching Protocols", |
77 | | "102 Processing", |
78 | 0 | #define LEVEL_200 3 |
79 | | "200 OK", |
80 | | "201 Created", |
81 | | "202 Accepted", |
82 | | "203 Non-Authoritative Information", |
83 | | "204 No Content", |
84 | | "205 Reset Content", |
85 | | "206 Partial Content", |
86 | | "207 Multi-Status", |
87 | | "208 Already Reported", |
88 | | NULL, /* 209 */ |
89 | | NULL, /* 210 */ |
90 | | NULL, /* 211 */ |
91 | | NULL, /* 212 */ |
92 | | NULL, /* 213 */ |
93 | | NULL, /* 214 */ |
94 | | NULL, /* 215 */ |
95 | | NULL, /* 216 */ |
96 | | NULL, /* 217 */ |
97 | | NULL, /* 218 */ |
98 | | NULL, /* 219 */ |
99 | | NULL, /* 220 */ |
100 | | NULL, /* 221 */ |
101 | | NULL, /* 222 */ |
102 | | NULL, /* 223 */ |
103 | | NULL, /* 224 */ |
104 | | NULL, /* 225 */ |
105 | | "226 IM Used", |
106 | 0 | #define LEVEL_300 30 |
107 | | "300 Multiple Choices", |
108 | | "301 Moved Permanently", |
109 | | "302 Found", |
110 | | "303 See Other", |
111 | | "304 Not Modified", |
112 | | "305 Use Proxy", |
113 | | NULL, /* 306 */ |
114 | | "307 Temporary Redirect", |
115 | | "308 Permanent Redirect", |
116 | 0 | #define LEVEL_400 39 |
117 | | "400 Bad Request", |
118 | | "401 Unauthorized", |
119 | | "402 Payment Required", |
120 | | "403 Forbidden", |
121 | | "404 Not Found", |
122 | | "405 Method Not Allowed", |
123 | | "406 Not Acceptable", |
124 | | "407 Proxy Authentication Required", |
125 | | "408 Request Timeout", |
126 | | "409 Conflict", |
127 | | "410 Gone", |
128 | | "411 Length Required", |
129 | | "412 Precondition Failed", |
130 | | "413 Request Entity Too Large", |
131 | | "414 Request-URI Too Long", |
132 | | "415 Unsupported Media Type", |
133 | | "416 Requested Range Not Satisfiable", |
134 | | "417 Expectation Failed", |
135 | | "418 I'm A Teapot", |
136 | | NULL, /* 419 */ |
137 | | NULL, /* 420 */ |
138 | | "421 Misdirected Request", |
139 | | "422 Unprocessable Entity", |
140 | | "423 Locked", |
141 | | "424 Failed Dependency", |
142 | | "425 Too Early", |
143 | | "426 Upgrade Required", |
144 | | NULL, /* 427 */ |
145 | | "428 Precondition Required", |
146 | | "429 Too Many Requests", |
147 | | NULL, /* 430 */ |
148 | | "431 Request Header Fields Too Large", |
149 | | NULL, /* 432 */ |
150 | | NULL, /* 433 */ |
151 | | NULL, /* 434 */ |
152 | | NULL, /* 435 */ |
153 | | NULL, /* 436 */ |
154 | | NULL, /* 437 */ |
155 | | NULL, /* 438 */ |
156 | | NULL, /* 439 */ |
157 | | NULL, /* 440 */ |
158 | | NULL, /* 441 */ |
159 | | NULL, /* 442 */ |
160 | | NULL, /* 443 */ |
161 | | NULL, /* 444 */ |
162 | | NULL, /* 445 */ |
163 | | NULL, /* 446 */ |
164 | | NULL, /* 447 */ |
165 | | NULL, /* 448 */ |
166 | | NULL, /* 449 */ |
167 | | NULL, /* 450 */ |
168 | | "451 Unavailable For Legal Reasons", |
169 | 0 | #define LEVEL_500 91 |
170 | | "500 Internal Server Error", |
171 | | "501 Not Implemented", |
172 | | "502 Bad Gateway", |
173 | | "503 Service Unavailable", |
174 | | "504 Gateway Timeout", |
175 | | "505 HTTP Version Not Supported", |
176 | | "506 Variant Also Negotiates", |
177 | | "507 Insufficient Storage", |
178 | | "508 Loop Detected", |
179 | | NULL, /* 509 */ |
180 | | "510 Not Extended", |
181 | | "511 Network Authentication Required" |
182 | | }; |
183 | | |
184 | | APR_HOOK_STRUCT( |
185 | | APR_HOOK_LINK(insert_error_filter) |
186 | | ) |
187 | | |
188 | | AP_IMPLEMENT_HOOK_VOID(insert_error_filter, (request_rec *r), (r)) |
189 | | |
190 | | /* The index of the first bit field that is used to index into a limit |
191 | | * bitmask. M_INVALID + 1 to METHOD_NUMBER_LAST. |
192 | | */ |
193 | 531 | #define METHOD_NUMBER_FIRST (M_INVALID + 1) |
194 | | |
195 | | /* The max method number. Method numbers are used to shift bitmasks, |
196 | | * so this cannot exceed 63, and all bits high is equal to -1, which is a |
197 | | * special flag, so the last bit used has index 62. |
198 | | */ |
199 | 0 | #define METHOD_NUMBER_LAST 62 |
200 | | |
201 | | static int is_mpm_running(void) |
202 | 0 | { |
203 | 0 | int mpm_state = 0; |
204 | |
|
205 | 0 | if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) { |
206 | 0 | return 0; |
207 | 0 | } |
208 | | |
209 | 0 | if (mpm_state == AP_MPMQ_STOPPING) { |
210 | 0 | return 0; |
211 | 0 | } |
212 | | |
213 | 0 | return 1; |
214 | 0 | } |
215 | | |
216 | | int ap_h1_set_keepalive(request_rec *r, ap_bucket_response *resp) |
217 | 0 | { |
218 | 0 | int ka_sent, left = 0, wimpy; |
219 | 0 | const char *conn; |
220 | |
|
221 | 0 | if (r->proto_num >= HTTP_VERSION(2,0)) { |
222 | 0 | goto update_keepalives; |
223 | 0 | } |
224 | | |
225 | 0 | ka_sent = 0; |
226 | 0 | left = r->server->keep_alive_max - r->connection->keepalives; |
227 | 0 | wimpy = ap_find_token(r->pool, |
228 | 0 | apr_table_get(resp->headers, "Connection"), |
229 | 0 | "close"); |
230 | 0 | conn = apr_table_get(r->headers_in, "Connection"); |
231 | | |
232 | | /* The following convoluted conditional determines whether or not |
233 | | * the current connection should remain persistent after this response |
234 | | * (a.k.a. HTTP Keep-Alive) and whether or not the output message |
235 | | * body should use the HTTP/1.1 chunked transfer-coding. In English, |
236 | | * |
237 | | * IF we have not marked this connection as errored; |
238 | | * and the client isn't expecting 100-continue (PR47087 - more |
239 | | * input here could be the client continuing when we're |
240 | | * closing the request). |
241 | | * and the response body has a defined length due to the status code |
242 | | * being 304 or 204, the request method being HEAD, already |
243 | | * having defined Content-Length or Transfer-Encoding: chunked, or |
244 | | * the request version being HTTP/1.1 and thus capable of being set |
245 | | * as chunked [we know the (r->chunked = 1) side-effect is ugly]; |
246 | | * and the server configuration enables keep-alive; |
247 | | * and the server configuration has a reasonable inter-request timeout; |
248 | | * and there is no maximum # requests or the max hasn't been reached; |
249 | | * and the response status does not require a close; |
250 | | * and the response generator has not already indicated close; |
251 | | * and the client did not request non-persistence (Connection: close); |
252 | | * and we haven't been configured to ignore the buggy twit |
253 | | * or they're a buggy twit coming through a HTTP/1.1 proxy |
254 | | * and the client is requesting an HTTP/1.0-style keep-alive |
255 | | * or the client claims to be HTTP/1.1 compliant (perhaps a proxy); |
256 | | * and this MPM process is not already exiting |
257 | | * THEN we can be persistent, which requires more headers be output. |
258 | | * |
259 | | * Note that the condition evaluation order is extremely important. |
260 | | */ |
261 | 0 | if ((r->connection->keepalive != AP_CONN_CLOSE) |
262 | 0 | && !r->expecting_100 |
263 | 0 | && (r->header_only |
264 | 0 | || AP_STATUS_IS_HEADER_ONLY(resp->status) |
265 | 0 | || apr_table_get(resp->headers, "Content-Length") |
266 | 0 | || ap_is_chunked(r->pool, |
267 | 0 | apr_table_get(resp->headers, "Transfer-Encoding")) |
268 | 0 | || ((r->proto_num >= HTTP_VERSION(1,1)) |
269 | 0 | && (r->chunked = 1))) /* THIS CODE IS CORRECT, see above. */ |
270 | 0 | && r->server->keep_alive |
271 | 0 | && (r->server->keep_alive_timeout > 0) |
272 | 0 | && ((r->server->keep_alive_max == 0) |
273 | 0 | || (left > 0)) |
274 | 0 | && !ap_status_drops_connection(resp->status) |
275 | 0 | && !wimpy |
276 | 0 | && !ap_find_token(r->pool, conn, "close") |
277 | 0 | && (!apr_table_get(r->subprocess_env, "nokeepalive") |
278 | 0 | || apr_table_get(r->headers_in, "Via")) |
279 | 0 | && ((ka_sent = ap_find_token(r->pool, conn, "keep-alive")) |
280 | 0 | || (r->proto_num >= HTTP_VERSION(1,1))) |
281 | 0 | && is_mpm_running()) { |
282 | |
|
283 | 0 | r->connection->keepalive = AP_CONN_KEEPALIVE; |
284 | 0 | r->connection->keepalives++; |
285 | | |
286 | | /* If they sent a Keep-Alive token, send one back */ |
287 | 0 | if (ka_sent) { |
288 | 0 | if (r->server->keep_alive_max) { |
289 | 0 | apr_table_setn(resp->headers, "Keep-Alive", |
290 | 0 | apr_psprintf(r->pool, "timeout=%d, max=%d", |
291 | 0 | (int)apr_time_sec(r->server->keep_alive_timeout), |
292 | 0 | left)); |
293 | 0 | } |
294 | 0 | else { |
295 | 0 | apr_table_setn(resp->headers, "Keep-Alive", |
296 | 0 | apr_psprintf(r->pool, "timeout=%d", |
297 | 0 | (int)apr_time_sec(r->server->keep_alive_timeout))); |
298 | 0 | } |
299 | 0 | apr_table_mergen(resp->headers, "Connection", "Keep-Alive"); |
300 | 0 | } |
301 | |
|
302 | 0 | return 1; |
303 | 0 | } |
304 | | |
305 | | /* Otherwise, we need to indicate that we will be closing this |
306 | | * connection immediately after the current response. |
307 | | * |
308 | | * We only really need to send "close" to HTTP/1.1 clients, but we |
309 | | * always send it anyway, because a broken proxy may identify itself |
310 | | * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag |
311 | | * to a HTTP/1.1 client. Better safe than sorry. |
312 | | */ |
313 | 0 | if (!wimpy) { |
314 | 0 | apr_table_mergen(resp->headers, "Connection", "close"); |
315 | 0 | } |
316 | |
|
317 | 0 | update_keepalives: |
318 | | /* |
319 | | * If we had previously been a keepalive connection and this |
320 | | * is the last one, then bump up the number of keepalives |
321 | | * we've had |
322 | | */ |
323 | 0 | if ((r->connection->keepalive != AP_CONN_CLOSE) |
324 | 0 | && r->server->keep_alive_max |
325 | 0 | && !left) { |
326 | 0 | r->connection->keepalives++; |
327 | 0 | } |
328 | 0 | r->connection->keepalive = AP_CONN_CLOSE; |
329 | |
|
330 | 0 | return 0; |
331 | 0 | } |
332 | | |
333 | | AP_DECLARE(int) ap_set_keepalive(request_rec *r) |
334 | 0 | { |
335 | 0 | ap_bucket_response resp; |
336 | |
|
337 | 0 | memset(&resp, 0, sizeof(resp)); |
338 | 0 | resp.status = r->status; |
339 | 0 | resp.headers = r->headers_out; |
340 | 0 | resp.notes = r->notes; |
341 | 0 | return ap_h1_set_keepalive(r, &resp); |
342 | 0 | } |
343 | | |
344 | | AP_DECLARE(ap_condition_e) ap_condition_if_match(request_rec *r, |
345 | | apr_table_t *headers) |
346 | 0 | { |
347 | 0 | const char *if_match, *etag; |
348 | | |
349 | | /* A server MUST use the strong comparison function (see section 13.3.3) |
350 | | * to compare the entity tags in If-Match. |
351 | | */ |
352 | 0 | if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) { |
353 | 0 | if (if_match[0] == '*' |
354 | 0 | || ((etag = apr_table_get(headers, "ETag")) != NULL |
355 | 0 | && ap_find_etag_strong(r->pool, if_match, etag))) { |
356 | 0 | return AP_CONDITION_STRONG; |
357 | 0 | } |
358 | 0 | else { |
359 | 0 | return AP_CONDITION_NOMATCH; |
360 | 0 | } |
361 | 0 | } |
362 | | |
363 | 0 | return AP_CONDITION_NONE; |
364 | 0 | } |
365 | | |
366 | | AP_DECLARE(ap_condition_e) ap_condition_if_unmodified_since(request_rec *r, |
367 | | apr_table_t *headers) |
368 | 0 | { |
369 | 0 | const char *if_unmodified; |
370 | |
|
371 | 0 | if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since"); |
372 | 0 | if (if_unmodified) { |
373 | 0 | apr_int64_t mtime, reqtime; |
374 | |
|
375 | 0 | apr_time_t ius = apr_time_sec(apr_date_parse_http(if_unmodified)); |
376 | | |
377 | | /* All of our comparisons must be in seconds, because that's the |
378 | | * highest time resolution the HTTP specification allows. |
379 | | */ |
380 | 0 | mtime = apr_time_sec(apr_date_parse_http( |
381 | 0 | apr_table_get(headers, "Last-Modified"))); |
382 | 0 | if (mtime == APR_DATE_BAD) { |
383 | 0 | mtime = apr_time_sec(r->mtime ? r->mtime : apr_time_now()); |
384 | 0 | } |
385 | |
|
386 | 0 | reqtime = apr_time_sec(apr_date_parse_http( |
387 | 0 | apr_table_get(headers, "Date"))); |
388 | 0 | if (!reqtime) { |
389 | 0 | reqtime = apr_time_sec(r->request_time); |
390 | 0 | } |
391 | |
|
392 | 0 | if ((ius != APR_DATE_BAD) && (mtime > ius)) { |
393 | 0 | if (reqtime < mtime + 60) { |
394 | 0 | if (apr_table_get(r->headers_in, "Range")) { |
395 | | /* weak matches not allowed with Range requests */ |
396 | 0 | return AP_CONDITION_NOMATCH; |
397 | 0 | } |
398 | 0 | else { |
399 | 0 | return AP_CONDITION_WEAK; |
400 | 0 | } |
401 | 0 | } |
402 | 0 | else { |
403 | 0 | return AP_CONDITION_STRONG; |
404 | 0 | } |
405 | 0 | } |
406 | 0 | else { |
407 | 0 | return AP_CONDITION_NOMATCH; |
408 | 0 | } |
409 | 0 | } |
410 | | |
411 | 0 | return AP_CONDITION_NONE; |
412 | 0 | } |
413 | | |
414 | | AP_DECLARE(ap_condition_e) ap_condition_if_none_match(request_rec *r, |
415 | | apr_table_t *headers) |
416 | 0 | { |
417 | 0 | const char *if_nonematch, *etag; |
418 | |
|
419 | 0 | if_nonematch = apr_table_get(r->headers_in, "If-None-Match"); |
420 | 0 | if (if_nonematch != NULL) { |
421 | |
|
422 | 0 | if (if_nonematch[0] == '*') { |
423 | 0 | return AP_CONDITION_STRONG; |
424 | 0 | } |
425 | | |
426 | | /* See section 13.3.3 for rules on how to determine if two entities tags |
427 | | * match. The weak comparison function can only be used with GET or HEAD |
428 | | * requests. |
429 | | */ |
430 | 0 | if (r->method_number == M_GET) { |
431 | 0 | if ((etag = apr_table_get(headers, "ETag")) != NULL) { |
432 | 0 | if (apr_table_get(r->headers_in, "Range")) { |
433 | 0 | if (ap_find_etag_strong(r->pool, if_nonematch, etag)) { |
434 | 0 | return AP_CONDITION_STRONG; |
435 | 0 | } |
436 | 0 | } |
437 | 0 | else { |
438 | 0 | if (ap_find_etag_weak(r->pool, if_nonematch, etag)) { |
439 | 0 | return AP_CONDITION_WEAK; |
440 | 0 | } |
441 | 0 | } |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | 0 | else if ((etag = apr_table_get(headers, "ETag")) != NULL |
446 | 0 | && ap_find_etag_strong(r->pool, if_nonematch, etag)) { |
447 | 0 | return AP_CONDITION_STRONG; |
448 | 0 | } |
449 | 0 | return AP_CONDITION_NOMATCH; |
450 | 0 | } |
451 | | |
452 | 0 | return AP_CONDITION_NONE; |
453 | 0 | } |
454 | | |
455 | | AP_DECLARE(ap_condition_e) ap_condition_if_modified_since(request_rec *r, |
456 | | apr_table_t *headers) |
457 | 0 | { |
458 | 0 | const char *if_modified_since; |
459 | |
|
460 | 0 | if ((if_modified_since = apr_table_get(r->headers_in, "If-Modified-Since")) |
461 | 0 | != NULL) { |
462 | 0 | apr_int64_t mtime; |
463 | 0 | apr_int64_t ims, reqtime; |
464 | | |
465 | | /* All of our comparisons must be in seconds, because that's the |
466 | | * highest time resolution the HTTP specification allows. |
467 | | */ |
468 | |
|
469 | 0 | mtime = apr_time_sec(apr_date_parse_http( |
470 | 0 | apr_table_get(headers, "Last-Modified"))); |
471 | 0 | if (mtime == APR_DATE_BAD) { |
472 | 0 | mtime = apr_time_sec(r->mtime ? r->mtime : apr_time_now()); |
473 | 0 | } |
474 | |
|
475 | 0 | reqtime = apr_time_sec(apr_date_parse_http( |
476 | 0 | apr_table_get(headers, "Date"))); |
477 | 0 | if (!reqtime) { |
478 | 0 | reqtime = apr_time_sec(r->request_time); |
479 | 0 | } |
480 | |
|
481 | 0 | ims = apr_time_sec(apr_date_parse_http(if_modified_since)); |
482 | |
|
483 | 0 | if (ims >= mtime && ims <= reqtime) { |
484 | 0 | if (reqtime < mtime + 60) { |
485 | 0 | if (apr_table_get(r->headers_in, "Range")) { |
486 | | /* weak matches not allowed with Range requests */ |
487 | 0 | return AP_CONDITION_NOMATCH; |
488 | 0 | } |
489 | 0 | else { |
490 | 0 | return AP_CONDITION_WEAK; |
491 | 0 | } |
492 | 0 | } |
493 | 0 | else { |
494 | 0 | return AP_CONDITION_STRONG; |
495 | 0 | } |
496 | 0 | } |
497 | 0 | else { |
498 | 0 | return AP_CONDITION_NOMATCH; |
499 | 0 | } |
500 | 0 | } |
501 | | |
502 | 0 | return AP_CONDITION_NONE; |
503 | 0 | } |
504 | | |
505 | | AP_DECLARE(ap_condition_e) ap_condition_if_range(request_rec *r, |
506 | | apr_table_t *headers) |
507 | 0 | { |
508 | 0 | const char *if_range, *etag; |
509 | |
|
510 | 0 | if ((if_range = apr_table_get(r->headers_in, "If-Range")) |
511 | 0 | && apr_table_get(r->headers_in, "Range")) { |
512 | 0 | if (if_range[0] == '"') { |
513 | |
|
514 | 0 | if ((etag = apr_table_get(headers, "ETag")) |
515 | 0 | && !strcmp(if_range, etag)) { |
516 | 0 | return AP_CONDITION_STRONG; |
517 | 0 | } |
518 | 0 | else { |
519 | 0 | return AP_CONDITION_NOMATCH; |
520 | 0 | } |
521 | |
|
522 | 0 | } |
523 | 0 | else { |
524 | 0 | apr_int64_t mtime; |
525 | 0 | apr_int64_t rtime, reqtime; |
526 | | |
527 | | /* All of our comparisons must be in seconds, because that's the |
528 | | * highest time resolution the HTTP specification allows. |
529 | | */ |
530 | |
|
531 | 0 | mtime = apr_time_sec(apr_date_parse_http( |
532 | 0 | apr_table_get(headers, "Last-Modified"))); |
533 | 0 | if (mtime == APR_DATE_BAD) { |
534 | 0 | mtime = apr_time_sec(r->mtime ? r->mtime : apr_time_now()); |
535 | 0 | } |
536 | |
|
537 | 0 | reqtime = apr_time_sec(apr_date_parse_http( |
538 | 0 | apr_table_get(headers, "Date"))); |
539 | 0 | if (!reqtime) { |
540 | 0 | reqtime = apr_time_sec(r->request_time); |
541 | 0 | } |
542 | |
|
543 | 0 | rtime = apr_time_sec(apr_date_parse_http(if_range)); |
544 | |
|
545 | 0 | if (rtime == mtime) { |
546 | 0 | if (reqtime < mtime + 60) { |
547 | | /* weak matches not allowed with Range requests */ |
548 | 0 | return AP_CONDITION_NOMATCH; |
549 | 0 | } |
550 | 0 | else { |
551 | 0 | return AP_CONDITION_STRONG; |
552 | 0 | } |
553 | 0 | } |
554 | 0 | else { |
555 | 0 | return AP_CONDITION_NOMATCH; |
556 | 0 | } |
557 | 0 | } |
558 | 0 | } |
559 | | |
560 | 0 | return AP_CONDITION_NONE; |
561 | 0 | } |
562 | | |
563 | | AP_DECLARE(int) ap_meets_conditions(request_rec *r) |
564 | 0 | { |
565 | 0 | int not_modified = -1; /* unset by default */ |
566 | 0 | ap_condition_e cond; |
567 | | |
568 | | /* Check for conditional requests --- note that we only want to do |
569 | | * this if we are successful so far and we are not processing a |
570 | | * subrequest or an ErrorDocument. |
571 | | * |
572 | | * The order of the checks is important, since ETag checks are supposed |
573 | | * to be more accurate than checks relative to the modification time. |
574 | | * However, not all documents are guaranteed to *have* ETags, and some |
575 | | * might have Last-Modified values w/o ETags, so this gets a little |
576 | | * complicated. |
577 | | */ |
578 | |
|
579 | 0 | if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) { |
580 | 0 | return OK; |
581 | 0 | } |
582 | | |
583 | | /* If an If-Match request-header field was given |
584 | | * AND the field value is not "*" (meaning match anything) |
585 | | * AND if our strong ETag does not match any entity tag in that field, |
586 | | * respond with a status of 412 (Precondition Failed). |
587 | | */ |
588 | 0 | cond = ap_condition_if_match(r, r->headers_out); |
589 | 0 | if (AP_CONDITION_NOMATCH == cond) { |
590 | 0 | return HTTP_PRECONDITION_FAILED; |
591 | 0 | } |
592 | | |
593 | | /* Else if a valid If-Unmodified-Since request-header field was given |
594 | | * AND the requested resource has been modified since the time |
595 | | * specified in this field, then the server MUST |
596 | | * respond with a status of 412 (Precondition Failed). |
597 | | */ |
598 | 0 | cond = ap_condition_if_unmodified_since(r, r->headers_out); |
599 | 0 | if (AP_CONDITION_NOMATCH == cond) { |
600 | 0 | not_modified = 0; |
601 | 0 | } |
602 | 0 | else if (cond >= AP_CONDITION_WEAK) { |
603 | 0 | return HTTP_PRECONDITION_FAILED; |
604 | 0 | } |
605 | | |
606 | | /* If an If-None-Match request-header field was given |
607 | | * AND the field value is "*" (meaning match anything) |
608 | | * OR our ETag matches any of the entity tags in that field, fail. |
609 | | * |
610 | | * If the request method was GET or HEAD, failure means the server |
611 | | * SHOULD respond with a 304 (Not Modified) response. |
612 | | * For all other request methods, failure means the server MUST |
613 | | * respond with a status of 412 (Precondition Failed). |
614 | | * |
615 | | * GET or HEAD allow weak etag comparison, all other methods require |
616 | | * strong comparison. We can only use weak if it's not a range request. |
617 | | */ |
618 | 0 | cond = ap_condition_if_none_match(r, r->headers_out); |
619 | 0 | if (AP_CONDITION_NOMATCH == cond) { |
620 | 0 | not_modified = 0; |
621 | 0 | } |
622 | 0 | else if (cond >= AP_CONDITION_WEAK) { |
623 | 0 | if (r->method_number == M_GET) { |
624 | 0 | if (not_modified) { |
625 | 0 | not_modified = 1; |
626 | 0 | } |
627 | 0 | } |
628 | 0 | else { |
629 | 0 | return HTTP_PRECONDITION_FAILED; |
630 | 0 | } |
631 | 0 | } |
632 | | |
633 | | /* If a valid If-Modified-Since request-header field was given |
634 | | * AND it is a GET or HEAD request |
635 | | * AND the requested resource has not been modified since the time |
636 | | * specified in this field, then the server MUST |
637 | | * respond with a status of 304 (Not Modified). |
638 | | * A date later than the server's current request time is invalid. |
639 | | */ |
640 | 0 | cond = ap_condition_if_modified_since(r, r->headers_out); |
641 | 0 | if (AP_CONDITION_NOMATCH == cond) { |
642 | 0 | not_modified = 0; |
643 | 0 | } |
644 | 0 | else if (cond >= AP_CONDITION_WEAK) { |
645 | 0 | if (r->method_number == M_GET) { |
646 | 0 | if (not_modified) { |
647 | 0 | not_modified = 1; |
648 | 0 | } |
649 | 0 | } |
650 | 0 | } |
651 | | |
652 | | /* If an If-Range and an Range header is present, we must return |
653 | | * 200 OK. The byterange filter will convert it to a range response. |
654 | | */ |
655 | 0 | cond = ap_condition_if_range(r, r->headers_out); |
656 | 0 | if (cond > AP_CONDITION_NONE) { |
657 | 0 | return OK; |
658 | 0 | } |
659 | | |
660 | 0 | if (not_modified == 1) { |
661 | 0 | return HTTP_NOT_MODIFIED; |
662 | 0 | } |
663 | | |
664 | 0 | return OK; |
665 | 0 | } |
666 | | |
667 | | /** |
668 | | * Singleton registry of additional methods. This maps new method names |
669 | | * such as "MYGET" to methnums, which are int offsets into bitmasks. |
670 | | * |
671 | | * This follows the same technique as standard M_GET, M_POST, etc. These |
672 | | * are dynamically assigned when modules are loaded and <Limit GET MYGET> |
673 | | * directives are processed. |
674 | | */ |
675 | | static apr_hash_t *methods_registry = NULL; |
676 | | static int cur_method_number = METHOD_NUMBER_FIRST; |
677 | | |
678 | | /* internal function to register one method/number pair */ |
679 | | static void register_one_method(apr_pool_t *p, const char *methname, |
680 | | int methnum) |
681 | 14.3k | { |
682 | 14.3k | int *pnum = apr_palloc(p, sizeof(*pnum)); |
683 | | |
684 | 14.3k | *pnum = methnum; |
685 | 14.3k | apr_hash_set(methods_registry, methname, APR_HASH_KEY_STRING, pnum); |
686 | 14.3k | } |
687 | | |
688 | | /* This internal function is used to clear the method registry |
689 | | * and reset the cur_method_number counter. |
690 | | */ |
691 | | static apr_status_t ap_method_registry_destroy(void *notused) |
692 | 531 | { |
693 | 531 | methods_registry = NULL; |
694 | 531 | cur_method_number = METHOD_NUMBER_FIRST; |
695 | 531 | return APR_SUCCESS; |
696 | 531 | } |
697 | | |
698 | | AP_DECLARE(void) ap_method_registry_init(apr_pool_t *p) |
699 | 531 | { |
700 | 531 | methods_registry = apr_hash_make(p); |
701 | 531 | apr_pool_cleanup_register(p, NULL, |
702 | 531 | ap_method_registry_destroy, |
703 | 531 | apr_pool_cleanup_null); |
704 | | |
705 | | /* put all the standard methods into the registry hash to ease the |
706 | | * mapping operations between name and number |
707 | | * HEAD is a special-instance of the GET method and shares the same ID |
708 | | */ |
709 | 531 | register_one_method(p, "GET", M_GET); |
710 | 531 | register_one_method(p, "HEAD", M_GET); |
711 | 531 | register_one_method(p, "PUT", M_PUT); |
712 | 531 | register_one_method(p, "POST", M_POST); |
713 | 531 | register_one_method(p, "DELETE", M_DELETE); |
714 | 531 | register_one_method(p, "CONNECT", M_CONNECT); |
715 | 531 | register_one_method(p, "OPTIONS", M_OPTIONS); |
716 | 531 | register_one_method(p, "TRACE", M_TRACE); |
717 | 531 | register_one_method(p, "PATCH", M_PATCH); |
718 | 531 | register_one_method(p, "PROPFIND", M_PROPFIND); |
719 | 531 | register_one_method(p, "PROPPATCH", M_PROPPATCH); |
720 | 531 | register_one_method(p, "MKCOL", M_MKCOL); |
721 | 531 | register_one_method(p, "COPY", M_COPY); |
722 | 531 | register_one_method(p, "MOVE", M_MOVE); |
723 | 531 | register_one_method(p, "LOCK", M_LOCK); |
724 | 531 | register_one_method(p, "UNLOCK", M_UNLOCK); |
725 | 531 | register_one_method(p, "VERSION-CONTROL", M_VERSION_CONTROL); |
726 | 531 | register_one_method(p, "CHECKOUT", M_CHECKOUT); |
727 | 531 | register_one_method(p, "UNCHECKOUT", M_UNCHECKOUT); |
728 | 531 | register_one_method(p, "CHECKIN", M_CHECKIN); |
729 | 531 | register_one_method(p, "UPDATE", M_UPDATE); |
730 | 531 | register_one_method(p, "LABEL", M_LABEL); |
731 | 531 | register_one_method(p, "REPORT", M_REPORT); |
732 | 531 | register_one_method(p, "MKWORKSPACE", M_MKWORKSPACE); |
733 | 531 | register_one_method(p, "MKACTIVITY", M_MKACTIVITY); |
734 | 531 | register_one_method(p, "BASELINE-CONTROL", M_BASELINE_CONTROL); |
735 | 531 | register_one_method(p, "MERGE", M_MERGE); |
736 | 531 | } |
737 | | |
738 | | AP_DECLARE(int) ap_method_register(apr_pool_t *p, const char *methname) |
739 | 0 | { |
740 | 0 | int *methnum; |
741 | |
|
742 | 0 | if (methname == NULL) { |
743 | 0 | return M_INVALID; |
744 | 0 | } |
745 | | |
746 | | /* Check if the method was previously registered. If it was |
747 | | * return the associated method number. |
748 | | */ |
749 | 0 | methnum = (int *)apr_hash_get(methods_registry, methname, |
750 | 0 | APR_HASH_KEY_STRING); |
751 | 0 | if (methnum != NULL) |
752 | 0 | return *methnum; |
753 | | |
754 | 0 | if (cur_method_number > METHOD_NUMBER_LAST) { |
755 | | /* The method registry has run out of dynamically |
756 | | * assignable method numbers. Log this and return M_INVALID. |
757 | | */ |
758 | 0 | ap_log_perror(APLOG_MARK, APLOG_ERR, 0, p, APLOGNO(01610) |
759 | 0 | "Maximum new request methods %d reached while " |
760 | 0 | "registering method %s.", |
761 | 0 | METHOD_NUMBER_LAST, methname); |
762 | 0 | return M_INVALID; |
763 | 0 | } |
764 | | |
765 | 0 | register_one_method(p, methname, cur_method_number); |
766 | 0 | return cur_method_number++; |
767 | 0 | } |
768 | | |
769 | | /* Get the method number associated with the given string, assumed to |
770 | | * contain an HTTP method. Returns M_INVALID if not recognized. |
771 | | * |
772 | | * This is the first step toward placing method names in a configurable |
773 | | * list. Hopefully it (and other routines) can eventually be moved to |
774 | | * something like a mod_http_methods.c, complete with config stuff. |
775 | | */ |
776 | | AP_DECLARE(int) ap_method_number_of(const char *method) |
777 | 373 | { |
778 | 373 | int len = strlen(method); |
779 | | |
780 | | /* check if the method has been dynamically registered */ |
781 | 373 | int *methnum = apr_hash_get(methods_registry, method, len); |
782 | | |
783 | 373 | if (methnum != NULL) { |
784 | 64 | return *methnum; |
785 | 64 | } |
786 | | |
787 | 309 | return M_INVALID; |
788 | 373 | } |
789 | | |
790 | | /* |
791 | | * Turn a known method number into a name. |
792 | | */ |
793 | | AP_DECLARE(const char *) ap_method_name_of(apr_pool_t *p, int methnum) |
794 | 0 | { |
795 | 0 | apr_hash_index_t *hi = apr_hash_first(p, methods_registry); |
796 | | |
797 | | /* scan through the hash table, looking for a value that matches |
798 | | the provided method number. */ |
799 | 0 | for (; hi; hi = apr_hash_next(hi)) { |
800 | 0 | const void *key; |
801 | 0 | void *val; |
802 | |
|
803 | 0 | apr_hash_this(hi, &key, NULL, &val); |
804 | 0 | if (*(int *)val == methnum) |
805 | 0 | return key; |
806 | 0 | } |
807 | | |
808 | | /* it wasn't found in the hash */ |
809 | 0 | return NULL; |
810 | 0 | } |
811 | | |
812 | | /* The index is found by its offset from the x00 code of each level. |
813 | | * Although this is fast, it will need to be replaced if some nutcase |
814 | | * decides to define a high-numbered code before the lower numbers. |
815 | | * If that sad event occurs, replace the code below with a linear search |
816 | | * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1]; |
817 | | * or use NULL to fill the gaps. |
818 | | */ |
819 | | static int index_of_response(int status) |
820 | 0 | { |
821 | 0 | static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400, LEVEL_500, |
822 | 0 | RESPONSE_CODES}; |
823 | 0 | int i, pos; |
824 | |
|
825 | 0 | if (status < 100) { /* Below 100 is illegal for HTTP status */ |
826 | 0 | return -1; |
827 | 0 | } |
828 | 0 | if (status > 999) { /* Above 999 is also illegal for HTTP status */ |
829 | 0 | return -1; |
830 | 0 | } |
831 | | |
832 | 0 | for (i = 0; i < 5; i++) { |
833 | 0 | status -= 100; |
834 | 0 | if (status < 100) { |
835 | 0 | pos = (status + shortcut[i]); |
836 | 0 | if (pos < shortcut[i + 1] && status_lines[pos] != NULL) { |
837 | 0 | return pos; |
838 | 0 | } |
839 | 0 | else { |
840 | 0 | break; |
841 | 0 | } |
842 | 0 | } |
843 | 0 | } |
844 | 0 | return -2; /* Status unknown (falls in gap) or above 600 */ |
845 | 0 | } |
846 | | |
847 | | AP_DECLARE(int) ap_index_of_response(int status) |
848 | 0 | { |
849 | 0 | int index = index_of_response(status); |
850 | 0 | return (index < 0) ? LEVEL_500 : index; |
851 | 0 | } |
852 | | |
853 | | AP_DECLARE(const char *) ap_get_status_line_ex(apr_pool_t *p, int status) |
854 | 0 | { |
855 | 0 | int index = index_of_response(status); |
856 | 0 | if (index >= 0) { |
857 | 0 | return status_lines[index]; |
858 | 0 | } |
859 | 0 | else if (index == -2) { |
860 | 0 | return apr_psprintf(p, "%i Status %i", status, status); |
861 | 0 | } |
862 | 0 | return status_lines[LEVEL_500]; |
863 | 0 | } |
864 | | |
865 | | AP_DECLARE(const char *) ap_get_status_line(int status) |
866 | 0 | { |
867 | 0 | return status_lines[ap_index_of_response(status)]; |
868 | 0 | } |
869 | | |
870 | | /* Build the Allow field-value from the request handler method mask. |
871 | | */ |
872 | | static char *make_allow(request_rec *r) |
873 | 0 | { |
874 | 0 | ap_method_mask_t mask; |
875 | 0 | apr_array_header_t *allow = apr_array_make(r->pool, 10, sizeof(char *)); |
876 | 0 | apr_hash_index_t *hi = apr_hash_first(r->pool, methods_registry); |
877 | | /* For TRACE below */ |
878 | 0 | core_server_config *conf = |
879 | 0 | ap_get_core_module_config(r->server->module_config); |
880 | |
|
881 | 0 | mask = r->allowed_methods->method_mask; |
882 | |
|
883 | 0 | for (; hi; hi = apr_hash_next(hi)) { |
884 | 0 | const void *key; |
885 | 0 | void *val; |
886 | |
|
887 | 0 | apr_hash_this(hi, &key, NULL, &val); |
888 | 0 | if ((mask & (AP_METHOD_BIT << *(int *)val)) != 0) { |
889 | 0 | APR_ARRAY_PUSH(allow, const char *) = key; |
890 | 0 | } |
891 | 0 | } |
892 | | |
893 | | /* TRACE is tested on a per-server basis */ |
894 | 0 | if (conf->trace_enable != AP_TRACE_DISABLE) |
895 | 0 | *(const char **)apr_array_push(allow) = "TRACE"; |
896 | | |
897 | | /* ### this is rather annoying. we should enforce registration of |
898 | | ### these methods */ |
899 | 0 | if ((mask & (AP_METHOD_BIT << M_INVALID)) |
900 | 0 | && (r->allowed_methods->method_list != NULL) |
901 | 0 | && (r->allowed_methods->method_list->nelts != 0)) { |
902 | 0 | apr_array_cat(allow, r->allowed_methods->method_list); |
903 | 0 | } |
904 | |
|
905 | 0 | return apr_array_pstrcat(r->pool, allow, ','); |
906 | 0 | } |
907 | | |
908 | | AP_DECLARE(int) ap_send_http_options(request_rec *r) |
909 | 0 | { |
910 | 0 | if (r->assbackwards) { |
911 | 0 | return DECLINED; |
912 | 0 | } |
913 | | |
914 | 0 | apr_table_setn(r->headers_out, "Allow", make_allow(r)); |
915 | | |
916 | | /* the request finalization will send an EOS, which will flush all |
917 | | * the headers out (including the Allow header) |
918 | | */ |
919 | |
|
920 | 0 | return OK; |
921 | 0 | } |
922 | | |
923 | | AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct) |
924 | 0 | { |
925 | 0 | if (!ct) { |
926 | 0 | r->content_type = NULL; |
927 | 0 | } |
928 | 0 | else if (!r->content_type || strcmp(r->content_type, ct)) { |
929 | 0 | r->content_type = ct; |
930 | 0 | } |
931 | 0 | } |
932 | | |
933 | | AP_DECLARE(void) ap_set_accept_ranges(request_rec *r) |
934 | 0 | { |
935 | 0 | core_dir_config *d = ap_get_core_module_config(r->per_dir_config); |
936 | 0 | apr_table_setn(r->headers_out, "Accept-Ranges", |
937 | 0 | (d->max_ranges == AP_MAXRANGES_NORANGES) ? "none" |
938 | 0 | : "bytes"); |
939 | 0 | } |
940 | | static const char *add_optional_notes(request_rec *r, |
941 | | const char *prefix, |
942 | | const char *key, |
943 | | const char *suffix) |
944 | 0 | { |
945 | 0 | const char *notes, *result; |
946 | |
|
947 | 0 | if ((notes = apr_table_get(r->notes, key)) == NULL) { |
948 | 0 | result = apr_pstrcat(r->pool, prefix, suffix, NULL); |
949 | 0 | } |
950 | 0 | else { |
951 | 0 | result = apr_pstrcat(r->pool, prefix, notes, suffix, NULL); |
952 | 0 | } |
953 | |
|
954 | 0 | return result; |
955 | 0 | } |
956 | | |
957 | | /* construct and return the default error message for a given |
958 | | * HTTP defined error code |
959 | | */ |
960 | | static const char *get_canned_error_string(int status, |
961 | | request_rec *r, |
962 | | const char *location) |
963 | 0 | { |
964 | 0 | apr_pool_t *p = r->pool; |
965 | 0 | const char *error_notes, *h1, *s1; |
966 | |
|
967 | 0 | switch (status) { |
968 | 0 | case HTTP_MOVED_PERMANENTLY: |
969 | 0 | case HTTP_MOVED_TEMPORARILY: |
970 | 0 | case HTTP_TEMPORARY_REDIRECT: |
971 | 0 | case HTTP_PERMANENT_REDIRECT: |
972 | 0 | return(apr_pstrcat(p, |
973 | 0 | "<p>The document has moved <a href=\"", |
974 | 0 | ap_escape_html(r->pool, location), |
975 | 0 | "\">here</a>.</p>\n", |
976 | 0 | NULL)); |
977 | 0 | case HTTP_SEE_OTHER: |
978 | 0 | return(apr_pstrcat(p, |
979 | 0 | "<p>The answer to your request is located " |
980 | 0 | "<a href=\"", |
981 | 0 | ap_escape_html(r->pool, location), |
982 | 0 | "\">here</a>.</p>\n", |
983 | 0 | NULL)); |
984 | 0 | case HTTP_USE_PROXY: |
985 | 0 | return("<p>This resource is only accessible " |
986 | 0 | "through the proxy\n" |
987 | 0 | "<br />\nYou will need to configure " |
988 | 0 | "your client to use that proxy.</p>\n"); |
989 | 0 | case HTTP_PROXY_AUTHENTICATION_REQUIRED: |
990 | 0 | case HTTP_UNAUTHORIZED: |
991 | 0 | return("<p>This server could not verify that you\n" |
992 | 0 | "are authorized to access the document\n" |
993 | 0 | "requested. Either you supplied the wrong\n" |
994 | 0 | "credentials (e.g., bad password), or your\n" |
995 | 0 | "browser doesn't understand how to supply\n" |
996 | 0 | "the credentials required.</p>\n"); |
997 | 0 | case HTTP_BAD_REQUEST: |
998 | 0 | return(add_optional_notes(r, |
999 | 0 | "<p>Your browser sent a request that " |
1000 | 0 | "this server could not understand.<br />\n", |
1001 | 0 | "error-notes", |
1002 | 0 | "</p>\n")); |
1003 | 0 | case HTTP_FORBIDDEN: |
1004 | 0 | return(add_optional_notes(r, "<p>You don't have permission to access this resource.", "error-notes", "</p>\n")); |
1005 | 0 | case HTTP_NOT_FOUND: |
1006 | 0 | return("<p>The requested URL was not found on this server.</p>\n"); |
1007 | 0 | case HTTP_METHOD_NOT_ALLOWED: |
1008 | 0 | return(apr_pstrcat(p, |
1009 | 0 | "<p>The requested method ", |
1010 | 0 | ap_escape_html(r->pool, r->method), |
1011 | 0 | " is not allowed for this URL.</p>\n", |
1012 | 0 | NULL)); |
1013 | 0 | case HTTP_NOT_ACCEPTABLE: |
1014 | 0 | return(add_optional_notes(r, |
1015 | 0 | "<p>An appropriate representation of the requested resource " |
1016 | 0 | "could not be found on this server.</p>\n", |
1017 | 0 | "variant-list", "")); |
1018 | 0 | case HTTP_MULTIPLE_CHOICES: |
1019 | 0 | return(add_optional_notes(r, "", "variant-list", "")); |
1020 | 0 | case HTTP_LENGTH_REQUIRED: |
1021 | 0 | s1 = apr_pstrcat(p, |
1022 | 0 | "<p>A request of the requested method ", |
1023 | 0 | ap_escape_html(r->pool, r->method), |
1024 | 0 | " requires a valid Content-length.<br />\n", |
1025 | 0 | NULL); |
1026 | 0 | return(add_optional_notes(r, s1, "error-notes", "</p>\n")); |
1027 | 0 | case HTTP_PRECONDITION_FAILED: |
1028 | 0 | return("<p>The precondition on the request " |
1029 | 0 | "for this URL evaluated to false.</p>\n"); |
1030 | 0 | case HTTP_NOT_IMPLEMENTED: |
1031 | 0 | s1 = apr_pstrcat(p, |
1032 | 0 | "<p>", |
1033 | 0 | ap_escape_html(r->pool, r->method), |
1034 | 0 | " not supported for current URL.<br />\n", |
1035 | 0 | NULL); |
1036 | 0 | return(add_optional_notes(r, s1, "error-notes", "</p>\n")); |
1037 | 0 | case HTTP_BAD_GATEWAY: |
1038 | 0 | s1 = "<p>The proxy server received an invalid" CRLF |
1039 | 0 | "response from an upstream server.<br />" CRLF; |
1040 | 0 | return(add_optional_notes(r, s1, "error-notes", "</p>\n")); |
1041 | 0 | case HTTP_VARIANT_ALSO_VARIES: |
1042 | 0 | return("<p>A variant for the requested " |
1043 | 0 | "resource\n<pre>\n" |
1044 | 0 | "\n</pre>\nis itself a negotiable resource. " |
1045 | 0 | "This indicates a configuration error.</p>\n"); |
1046 | 0 | case HTTP_REQUEST_TIME_OUT: |
1047 | 0 | return("<p>Server timeout waiting for the HTTP request from the client.</p>\n"); |
1048 | 0 | case HTTP_GONE: |
1049 | 0 | return("<p>The requested resource is no longer available on this server" |
1050 | 0 | " and there is no forwarding address.\n" |
1051 | 0 | "Please remove all references to this resource.</p>\n"); |
1052 | 0 | case HTTP_REQUEST_ENTITY_TOO_LARGE: |
1053 | 0 | return(apr_pstrcat(p, |
1054 | 0 | "The requested resource does not allow request data with ", |
1055 | 0 | ap_escape_html(r->pool, r->method), |
1056 | 0 | " requests, or the amount of data provided in\n" |
1057 | 0 | "the request exceeds the capacity limit.\n", |
1058 | 0 | NULL)); |
1059 | 0 | case HTTP_REQUEST_URI_TOO_LARGE: |
1060 | 0 | s1 = "<p>The requested URL's length exceeds the capacity\n" |
1061 | 0 | "limit for this server.<br />\n"; |
1062 | 0 | return(add_optional_notes(r, s1, "error-notes", "</p>\n")); |
1063 | 0 | case HTTP_UNSUPPORTED_MEDIA_TYPE: |
1064 | 0 | return("<p>The supplied request data is not in a format\n" |
1065 | 0 | "acceptable for processing by this resource.</p>\n"); |
1066 | 0 | case HTTP_RANGE_NOT_SATISFIABLE: |
1067 | 0 | return("<p>None of the range-specifier values in the Range\n" |
1068 | 0 | "request-header field overlap the current extent\n" |
1069 | 0 | "of the selected resource.</p>\n"); |
1070 | 0 | case HTTP_EXPECTATION_FAILED: |
1071 | 0 | s1 = apr_table_get(r->headers_in, "Expect"); |
1072 | 0 | if (s1) |
1073 | 0 | s1 = apr_pstrcat(p, |
1074 | 0 | "<p>The expectation given in the Expect request-header\n" |
1075 | 0 | "field could not be met by this server.\n" |
1076 | 0 | "The client sent<pre>\n Expect: ", |
1077 | 0 | ap_escape_html(r->pool, s1), "\n</pre>\n", |
1078 | 0 | NULL); |
1079 | 0 | else |
1080 | 0 | s1 = "<p>No expectation was seen, the Expect request-header \n" |
1081 | 0 | "field was not presented by the client.\n"; |
1082 | 0 | return add_optional_notes(r, s1, "error-notes", "</p>" |
1083 | 0 | "<p>Only the 100-continue expectation is supported.</p>\n"); |
1084 | 0 | case HTTP_UNPROCESSABLE_ENTITY: |
1085 | 0 | return("<p>The server understands the media type of the\n" |
1086 | 0 | "request entity, but was unable to process the\n" |
1087 | 0 | "contained instructions.</p>\n"); |
1088 | 0 | case HTTP_LOCKED: |
1089 | 0 | return("<p>The requested resource is currently locked.\n" |
1090 | 0 | "The lock must be released or proper identification\n" |
1091 | 0 | "given before the method can be applied.</p>\n"); |
1092 | 0 | case HTTP_FAILED_DEPENDENCY: |
1093 | 0 | return("<p>The method could not be performed on the resource\n" |
1094 | 0 | "because the requested action depended on another\n" |
1095 | 0 | "action and that other action failed.</p>\n"); |
1096 | 0 | case HTTP_TOO_EARLY: |
1097 | 0 | return("<p>The request could not be processed as TLS\n" |
1098 | 0 | "early data and should be retried.</p>\n"); |
1099 | 0 | case HTTP_UPGRADE_REQUIRED: |
1100 | 0 | return("<p>The requested resource can only be retrieved\n" |
1101 | 0 | "using SSL. The server is willing to upgrade the current\n" |
1102 | 0 | "connection to SSL, but your client doesn't support it.\n" |
1103 | 0 | "Either upgrade your client, or try requesting the page\n" |
1104 | 0 | "using https://\n"); |
1105 | 0 | case HTTP_PRECONDITION_REQUIRED: |
1106 | 0 | return("<p>The request is required to be conditional.</p>\n"); |
1107 | 0 | case HTTP_TOO_MANY_REQUESTS: |
1108 | 0 | return("<p>The user has sent too many requests\n" |
1109 | 0 | "in a given amount of time.</p>\n"); |
1110 | 0 | case HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE: |
1111 | 0 | return("<p>The server refused this request because\n" |
1112 | 0 | "the request header fields are too large.</p>\n"); |
1113 | 0 | case HTTP_INSUFFICIENT_STORAGE: |
1114 | 0 | return("<p>The method could not be performed on the resource\n" |
1115 | 0 | "because the server is unable to store the\n" |
1116 | 0 | "representation needed to successfully complete the\n" |
1117 | 0 | "request. There is insufficient free space left in\n" |
1118 | 0 | "your storage allocation.</p>\n"); |
1119 | 0 | case HTTP_SERVICE_UNAVAILABLE: |
1120 | 0 | return("<p>The server is temporarily unable to service your\n" |
1121 | 0 | "request due to maintenance downtime or capacity\n" |
1122 | 0 | "problems. Please try again later.</p>\n"); |
1123 | 0 | case HTTP_GATEWAY_TIME_OUT: |
1124 | 0 | return("<p>The gateway did not receive a timely response\n" |
1125 | 0 | "from the upstream server or application.</p>\n"); |
1126 | 0 | case HTTP_LOOP_DETECTED: |
1127 | 0 | return("<p>The server terminated an operation because\n" |
1128 | 0 | "it encountered an infinite loop.</p>\n"); |
1129 | 0 | case HTTP_NOT_EXTENDED: |
1130 | 0 | return("<p>A mandatory extension policy in the request is not\n" |
1131 | 0 | "accepted by the server for this resource.</p>\n"); |
1132 | 0 | case HTTP_NETWORK_AUTHENTICATION_REQUIRED: |
1133 | 0 | return("<p>The client needs to authenticate to gain\n" |
1134 | 0 | "network access.</p>\n"); |
1135 | 0 | case HTTP_IM_A_TEAPOT: |
1136 | 0 | return("<p>The resulting entity body MAY be short and\n" |
1137 | 0 | "stout.</p>\n"); |
1138 | 0 | case HTTP_MISDIRECTED_REQUEST: |
1139 | 0 | return("<p>The client needs a new connection for this\n" |
1140 | 0 | "request as the requested host name does not match\n" |
1141 | 0 | "the Server Name Indication (SNI) in use for this\n" |
1142 | 0 | "connection.</p>\n"); |
1143 | 0 | case HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: |
1144 | 0 | return(add_optional_notes(r, |
1145 | 0 | "<p>Access to this URL has been denied for legal reasons.<br />\n", |
1146 | 0 | "error-notes", "</p>\n")); |
1147 | 0 | default: /* HTTP_INTERNAL_SERVER_ERROR */ |
1148 | | /* |
1149 | | * This comparison to expose error-notes could be modified to |
1150 | | * use a configuration directive and export based on that |
1151 | | * directive. For now "*" is used to designate an error-notes |
1152 | | * that is totally safe for any user to see (ie lacks paths, |
1153 | | * database passwords, etc.) |
1154 | | */ |
1155 | 0 | if (((error_notes = apr_table_get(r->notes, |
1156 | 0 | "error-notes")) != NULL) |
1157 | 0 | && (h1 = apr_table_get(r->notes, "verbose-error-to")) != NULL |
1158 | 0 | && (strcmp(h1, "*") == 0)) { |
1159 | 0 | return(apr_pstrcat(p, error_notes, "<p />\n", NULL)); |
1160 | 0 | } |
1161 | 0 | else { |
1162 | 0 | return(apr_pstrcat(p, |
1163 | 0 | "<p>The server encountered an internal " |
1164 | 0 | "error or\n" |
1165 | 0 | "misconfiguration and was unable to complete\n" |
1166 | 0 | "your request.</p>\n" |
1167 | 0 | "<p>Please contact the server " |
1168 | 0 | "administrator at \n ", |
1169 | 0 | ap_escape_html(r->pool, |
1170 | 0 | r->server->server_admin), |
1171 | 0 | " to inform them of the time this " |
1172 | 0 | "error occurred,\n" |
1173 | 0 | " and the actions you performed just before " |
1174 | 0 | "this error.</p>\n" |
1175 | 0 | "<p>More information about this error " |
1176 | 0 | "may be available\n" |
1177 | 0 | "in the server error log.</p>\n", |
1178 | 0 | NULL)); |
1179 | 0 | } |
1180 | | /* |
1181 | | * It would be nice to give the user the information they need to |
1182 | | * fix the problem directly since many users don't have access to |
1183 | | * the error_log (think University sites) even though they can easily |
1184 | | * get this error by misconfiguring an htaccess file. However, the |
1185 | | * e error notes tend to include the real file pathname in this case, |
1186 | | * which some people consider to be a breach of privacy. Until we |
1187 | | * can figure out a way to remove the pathname, leave this commented. |
1188 | | * |
1189 | | * if ((error_notes = apr_table_get(r->notes, |
1190 | | * "error-notes")) != NULL) { |
1191 | | * return(apr_pstrcat(p, error_notes, "<p />\n", NULL); |
1192 | | * } |
1193 | | * else { |
1194 | | * return ""; |
1195 | | * } |
1196 | | */ |
1197 | 0 | } |
1198 | 0 | } |
1199 | | |
1200 | | /* We should have named this send_canned_response, since it is used for any |
1201 | | * response that can be generated by the server from the request record. |
1202 | | * This includes all 204 (no content), 3xx (redirect), 4xx (client error), |
1203 | | * and 5xx (server error) messages that have not been redirected to another |
1204 | | * handler via the ErrorDocument feature. |
1205 | | */ |
1206 | | AP_DECLARE(void) ap_send_error_response(request_rec *r, int recursive_error) |
1207 | 0 | { |
1208 | 0 | int status = r->status; |
1209 | 0 | int idx = ap_index_of_response(status); |
1210 | 0 | char *custom_response; |
1211 | 0 | const char *location = apr_table_get(r->headers_out, "Location"); |
1212 | | |
1213 | | /* At this point, we are starting the response over, so we have to reset |
1214 | | * this value. |
1215 | | */ |
1216 | 0 | r->eos_sent = 0; |
1217 | | |
1218 | | /* and we need to get rid of any RESOURCE filters that might be lurking |
1219 | | * around, thinking they are in the middle of the original request |
1220 | | */ |
1221 | |
|
1222 | 0 | r->output_filters = r->proto_output_filters; |
1223 | |
|
1224 | 0 | ap_run_insert_error_filter(r); |
1225 | | |
1226 | | /* We need to special-case the handling of 204 and 304 responses, |
1227 | | * since they have specific HTTP requirements and do not include a |
1228 | | * message body. Note that being assbackwards here is not an option. |
1229 | | */ |
1230 | 0 | if (AP_STATUS_IS_HEADER_ONLY(status)) { |
1231 | 0 | ap_finalize_request_protocol(r); |
1232 | 0 | return; |
1233 | 0 | } |
1234 | | |
1235 | | /* |
1236 | | * It's possible that the Location field might be in r->err_headers_out |
1237 | | * instead of r->headers_out; use the latter if possible, else the |
1238 | | * former. |
1239 | | */ |
1240 | 0 | if (location == NULL) { |
1241 | 0 | location = apr_table_get(r->err_headers_out, "Location"); |
1242 | 0 | } |
1243 | |
|
1244 | 0 | if (!r->assbackwards) { |
1245 | | |
1246 | | /* For all HTTP/1.x responses for which we generate the message, |
1247 | | * we need to avoid inheriting the "normal status" header fields |
1248 | | * that may have been set by the request handler before the |
1249 | | * error or redirect, except for Location on external redirects. |
1250 | | */ |
1251 | 0 | apr_table_clear(r->headers_out); |
1252 | |
|
1253 | 0 | if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) { |
1254 | 0 | if ((location != NULL) && *location) { |
1255 | 0 | apr_table_setn(r->headers_out, "Location", location); |
1256 | 0 | } |
1257 | 0 | else { |
1258 | 0 | location = ""; /* avoids coredump when printing, below */ |
1259 | 0 | } |
1260 | 0 | } |
1261 | |
|
1262 | 0 | r->content_languages = NULL; |
1263 | 0 | r->content_encoding = NULL; |
1264 | 0 | r->clength = 0; |
1265 | |
|
1266 | 0 | if (apr_table_get(r->subprocess_env, |
1267 | 0 | "suppress-error-charset") != NULL) { |
1268 | 0 | core_request_config *request_conf = |
1269 | 0 | ap_get_core_module_config(r->request_config); |
1270 | 0 | request_conf->suppress_charset = 1; /* avoid adding default |
1271 | | * charset later |
1272 | | */ |
1273 | 0 | ap_set_content_type(r, "text/html"); |
1274 | 0 | } |
1275 | 0 | else { |
1276 | 0 | ap_set_content_type(r, "text/html; charset=iso-8859-1"); |
1277 | 0 | } |
1278 | |
|
1279 | 0 | if ((status == HTTP_METHOD_NOT_ALLOWED) |
1280 | 0 | || (status == HTTP_NOT_IMPLEMENTED)) { |
1281 | 0 | apr_table_setn(r->headers_out, "Allow", make_allow(r)); |
1282 | 0 | } |
1283 | |
|
1284 | 0 | if (r->header_only) { |
1285 | 0 | ap_finalize_request_protocol(r); |
1286 | 0 | return; |
1287 | 0 | } |
1288 | 0 | } |
1289 | | |
1290 | 0 | if ((custom_response = ap_response_code_string(r, idx))) { |
1291 | | /* |
1292 | | * We have a custom response output. This should only be |
1293 | | * a text-string to write back. But if the ErrorDocument |
1294 | | * was a local redirect and the requested resource failed |
1295 | | * for any reason, the custom_response will still hold the |
1296 | | * redirect URL. We don't really want to output this URL |
1297 | | * as a text message, so first check the custom response |
1298 | | * string to ensure that it is a text-string (using the |
1299 | | * same test used in ap_die(), i.e. does it start with a "). |
1300 | | * |
1301 | | * If it's not a text string, we've got a recursive error or |
1302 | | * an external redirect. If it's a recursive error, ap_die passes |
1303 | | * us the second error code so we can write both, and has already |
1304 | | * backed up to the original error. If it's an external redirect, |
1305 | | * it hasn't happened yet; we may never know if it fails. |
1306 | | */ |
1307 | 0 | if (custom_response[0] == '\"') { |
1308 | 0 | ap_rvputs_proto_in_ascii(r, custom_response + 1, NULL); |
1309 | 0 | ap_finalize_request_protocol(r); |
1310 | 0 | return; |
1311 | 0 | } |
1312 | 0 | } |
1313 | 0 | { |
1314 | 0 | const char *title = status_lines[idx]; |
1315 | 0 | const char *h1; |
1316 | | |
1317 | | /* Accept a status_line set by a module, but only if it begins |
1318 | | * with the correct 3 digit status code |
1319 | | */ |
1320 | 0 | if (r->status_line) { |
1321 | 0 | char *end; |
1322 | 0 | int len = strlen(r->status_line); |
1323 | 0 | if (len >= 3 |
1324 | 0 | && apr_strtoi64(r->status_line, &end, 10) == r->status |
1325 | 0 | && (end - 3) == r->status_line |
1326 | 0 | && (len < 4 || apr_isspace(r->status_line[3])) |
1327 | 0 | && (len < 5 || apr_isalnum(r->status_line[4]))) { |
1328 | | /* Since we passed the above check, we know that length three |
1329 | | * is equivalent to only a 3 digit numeric http status. |
1330 | | * RFC2616 mandates a trailing space, let's add it. |
1331 | | * If we have an empty reason phrase, we also add "Unknown Reason". |
1332 | | */ |
1333 | 0 | if (len == 3) { |
1334 | 0 | r->status_line = apr_pstrcat(r->pool, r->status_line, " Unknown Reason", NULL); |
1335 | 0 | } else if (len == 4) { |
1336 | 0 | r->status_line = apr_pstrcat(r->pool, r->status_line, "Unknown Reason", NULL); |
1337 | 0 | } |
1338 | 0 | title = r->status_line; |
1339 | 0 | } |
1340 | 0 | } |
1341 | | |
1342 | | /* folks decided they didn't want the error code in the H1 text */ |
1343 | 0 | h1 = &title[4]; |
1344 | | |
1345 | | /* can't count on a charset filter being in place here, |
1346 | | * so do ebcdic->ascii translation explicitly (if needed) |
1347 | | */ |
1348 | |
|
1349 | 0 | ap_rvputs_proto_in_ascii(r, |
1350 | 0 | DOCTYPE_HTML_4_01 |
1351 | 0 | "<html><head>\n<title>", title, |
1352 | 0 | "</title>\n</head><body>\n<h1>", h1, "</h1>\n", |
1353 | 0 | NULL); |
1354 | |
|
1355 | 0 | ap_rvputs_proto_in_ascii(r, |
1356 | 0 | get_canned_error_string(status, r, location), |
1357 | 0 | NULL); |
1358 | |
|
1359 | 0 | if (recursive_error) { |
1360 | 0 | ap_rvputs_proto_in_ascii(r, "<p>Additionally, a ", |
1361 | 0 | status_lines[ap_index_of_response(recursive_error)], |
1362 | 0 | "\nerror was encountered while trying to use an " |
1363 | 0 | "ErrorDocument to handle the request.</p>\n", NULL); |
1364 | 0 | } |
1365 | 0 | ap_rvputs_proto_in_ascii(r, ap_psignature("<hr>\n", r), NULL); |
1366 | 0 | ap_rvputs_proto_in_ascii(r, "</body></html>\n", NULL); |
1367 | 0 | } |
1368 | 0 | ap_finalize_request_protocol(r); |
1369 | 0 | } |
1370 | | |
1371 | | /* |
1372 | | * Create a new method list with the specified number of preallocated |
1373 | | * extension slots. |
1374 | | */ |
1375 | | AP_DECLARE(ap_method_list_t *) ap_make_method_list(apr_pool_t *p, int nelts) |
1376 | 531 | { |
1377 | 531 | ap_method_list_t *ml; |
1378 | | |
1379 | 531 | ml = (ap_method_list_t *) apr_palloc(p, sizeof(ap_method_list_t)); |
1380 | 531 | ml->method_mask = 0; |
1381 | 531 | ml->method_list = apr_array_make(p, nelts, sizeof(char *)); |
1382 | 531 | return ml; |
1383 | 531 | } |
1384 | | |
1385 | | /* |
1386 | | * Make a copy of a method list (primarily for subrequests that may |
1387 | | * subsequently change it; don't want them changing the parent's, too!). |
1388 | | */ |
1389 | | AP_DECLARE(void) ap_copy_method_list(ap_method_list_t *dest, |
1390 | | ap_method_list_t *src) |
1391 | 0 | { |
1392 | 0 | int i; |
1393 | 0 | char **imethods; |
1394 | 0 | char **omethods; |
1395 | |
|
1396 | 0 | dest->method_mask = src->method_mask; |
1397 | 0 | imethods = (char **) src->method_list->elts; |
1398 | 0 | for (i = 0; i < src->method_list->nelts; ++i) { |
1399 | 0 | omethods = (char **) apr_array_push(dest->method_list); |
1400 | 0 | *omethods = apr_pstrdup(dest->method_list->pool, imethods[i]); |
1401 | 0 | } |
1402 | 0 | } |
1403 | | |
1404 | | /* |
1405 | | * Return true if the specified HTTP method is in the provided |
1406 | | * method list. |
1407 | | */ |
1408 | | AP_DECLARE(int) ap_method_in_list(ap_method_list_t *l, const char *method) |
1409 | 0 | { |
1410 | 0 | int methnum; |
1411 | | |
1412 | | /* |
1413 | | * If it's one of our known methods, use the shortcut and check the |
1414 | | * bitmask. |
1415 | | */ |
1416 | 0 | methnum = ap_method_number_of(method); |
1417 | 0 | if (methnum != M_INVALID) { |
1418 | 0 | return !!(l->method_mask & (AP_METHOD_BIT << methnum)); |
1419 | 0 | } |
1420 | | /* |
1421 | | * Otherwise, see if the method name is in the array of string names. |
1422 | | */ |
1423 | 0 | if ((l->method_list == NULL) || (l->method_list->nelts == 0)) { |
1424 | 0 | return 0; |
1425 | 0 | } |
1426 | | |
1427 | 0 | return ap_array_str_contains(l->method_list, method); |
1428 | 0 | } |
1429 | | |
1430 | | /* |
1431 | | * Add the specified method to a method list (if it isn't already there). |
1432 | | */ |
1433 | | AP_DECLARE(void) ap_method_list_add(ap_method_list_t *l, const char *method) |
1434 | 0 | { |
1435 | 0 | int methnum; |
1436 | 0 | const char **xmethod; |
1437 | | |
1438 | | /* |
1439 | | * If it's one of our known methods, use the shortcut and use the |
1440 | | * bitmask. |
1441 | | */ |
1442 | 0 | methnum = ap_method_number_of(method); |
1443 | 0 | if (methnum != M_INVALID) { |
1444 | 0 | l->method_mask |= (AP_METHOD_BIT << methnum); |
1445 | 0 | return; |
1446 | 0 | } |
1447 | | /* |
1448 | | * Otherwise, see if the method name is in the array of string names. |
1449 | | */ |
1450 | 0 | if (ap_array_str_contains(l->method_list, method)) { |
1451 | 0 | return; |
1452 | 0 | } |
1453 | | |
1454 | 0 | xmethod = (const char **) apr_array_push(l->method_list); |
1455 | 0 | *xmethod = method; |
1456 | 0 | } |
1457 | | |
1458 | | /* |
1459 | | * Remove the specified method from a method list. |
1460 | | */ |
1461 | | AP_DECLARE(void) ap_method_list_remove(ap_method_list_t *l, |
1462 | | const char *method) |
1463 | 0 | { |
1464 | 0 | int methnum; |
1465 | 0 | char **methods; |
1466 | | |
1467 | | /* |
1468 | | * If it's a known methods, either builtin or registered |
1469 | | * by a module, use the bitmask. |
1470 | | */ |
1471 | 0 | methnum = ap_method_number_of(method); |
1472 | 0 | if (methnum != M_INVALID) { |
1473 | 0 | l->method_mask &= ~(AP_METHOD_BIT << methnum); |
1474 | 0 | return; |
1475 | 0 | } |
1476 | | /* |
1477 | | * Otherwise, see if the method name is in the array of string names. |
1478 | | */ |
1479 | 0 | if (l->method_list->nelts != 0) { |
1480 | 0 | int i, j, k; |
1481 | 0 | methods = (char **)l->method_list->elts; |
1482 | 0 | for (i = 0; i < l->method_list->nelts; ) { |
1483 | 0 | if (strcmp(method, methods[i]) == 0) { |
1484 | 0 | for (j = i, k = i + 1; k < l->method_list->nelts; ++j, ++k) { |
1485 | 0 | methods[j] = methods[k]; |
1486 | 0 | } |
1487 | 0 | --l->method_list->nelts; |
1488 | 0 | } |
1489 | 0 | else { |
1490 | 0 | ++i; |
1491 | 0 | } |
1492 | 0 | } |
1493 | 0 | } |
1494 | 0 | } |
1495 | | |
1496 | | /* |
1497 | | * Reset a method list to be completely empty. |
1498 | | */ |
1499 | | AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l) |
1500 | 0 | { |
1501 | 0 | l->method_mask = 0; |
1502 | 0 | l->method_list->nelts = 0; |
1503 | 0 | } |
1504 | | |
1505 | | AP_DECLARE(apr_status_t) ap_h1_append_header(apr_bucket_brigade *bb, |
1506 | | apr_pool_t *pool, |
1507 | | const char *name, const char *value) |
1508 | 0 | { |
1509 | | #if APR_CHARSET_EBCDIC |
1510 | | char *headfield; |
1511 | | apr_size_t len; |
1512 | | |
1513 | | headfield = apr_pstrcat(pool, name, ": ", value, CRLF, NULL); |
1514 | | len = strlen(headfield); |
1515 | | |
1516 | | ap_xlate_proto_to_ascii(headfield, len); |
1517 | | return apr_brigade_write(bb, NULL, NULL, headfield, len); |
1518 | | #else |
1519 | 0 | struct iovec vec[4]; |
1520 | 0 | struct iovec *v = vec; |
1521 | 0 | v->iov_base = (void *)name; |
1522 | 0 | v->iov_len = strlen(name); |
1523 | 0 | v++; |
1524 | 0 | v->iov_base = ": "; |
1525 | 0 | v->iov_len = sizeof(": ") - 1; |
1526 | 0 | v++; |
1527 | 0 | v->iov_base = (void *)value; |
1528 | 0 | v->iov_len = strlen(value); |
1529 | 0 | v++; |
1530 | 0 | v->iov_base = CRLF; |
1531 | 0 | v->iov_len = sizeof(CRLF) - 1; |
1532 | 0 | return apr_brigade_writev(bb, NULL, NULL, vec, 4); |
1533 | 0 | #endif /* !APR_CHARSET_EBCDIC */ |
1534 | 0 | } |
1535 | | |
1536 | | AP_DECLARE(apr_status_t) ap_h1_append_headers(apr_bucket_brigade *bb, |
1537 | | request_rec *r, |
1538 | | apr_table_t *headers) |
1539 | 0 | { |
1540 | 0 | const apr_array_header_t *elts; |
1541 | 0 | const apr_table_entry_t *t_elt; |
1542 | 0 | const apr_table_entry_t *t_end; |
1543 | 0 | struct iovec *vec; |
1544 | 0 | struct iovec *vec_next; |
1545 | |
|
1546 | 0 | elts = apr_table_elts(headers); |
1547 | 0 | if (elts->nelts == 0) { |
1548 | 0 | return APR_SUCCESS; |
1549 | 0 | } |
1550 | 0 | t_elt = (const apr_table_entry_t *)(elts->elts); |
1551 | 0 | t_end = t_elt + elts->nelts; |
1552 | 0 | vec = (struct iovec *)apr_palloc(r->pool, 4 * elts->nelts * |
1553 | 0 | sizeof(struct iovec)); |
1554 | 0 | vec_next = vec; |
1555 | | |
1556 | | /* For each field, generate |
1557 | | * name ": " value CRLF |
1558 | | */ |
1559 | 0 | do { |
1560 | 0 | if (t_elt->key && t_elt->val) { |
1561 | 0 | vec_next->iov_base = (void*)(t_elt->key); |
1562 | 0 | vec_next->iov_len = strlen(t_elt->key); |
1563 | 0 | vec_next++; |
1564 | 0 | vec_next->iov_base = ": "; |
1565 | 0 | vec_next->iov_len = sizeof(": ") - 1; |
1566 | 0 | vec_next++; |
1567 | 0 | vec_next->iov_base = (void*)(t_elt->val); |
1568 | 0 | vec_next->iov_len = strlen(t_elt->val); |
1569 | 0 | vec_next++; |
1570 | 0 | vec_next->iov_base = CRLF; |
1571 | 0 | vec_next->iov_len = sizeof(CRLF) - 1; |
1572 | 0 | vec_next++; |
1573 | 0 | } |
1574 | 0 | t_elt++; |
1575 | 0 | } while (t_elt < t_end); |
1576 | |
|
1577 | 0 | if (APLOGrtrace4(r)) { |
1578 | 0 | t_elt = (const apr_table_entry_t *)(elts->elts); |
1579 | 0 | do { |
1580 | 0 | ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, " %s: %s", |
1581 | 0 | t_elt->key, t_elt->val); |
1582 | 0 | t_elt++; |
1583 | 0 | } while (t_elt < t_end); |
1584 | 0 | } |
1585 | |
|
1586 | | #if APR_CHARSET_EBCDIC |
1587 | | { |
1588 | | apr_size_t len; |
1589 | | char *tmp = apr_pstrcatv(r->pool, vec, vec_next - vec, &len); |
1590 | | ap_xlate_proto_to_ascii(tmp, len); |
1591 | | return apr_brigade_write(bb, NULL, NULL, tmp, len); |
1592 | | } |
1593 | | #else |
1594 | 0 | return apr_brigade_writev(bb, NULL, NULL, vec, vec_next - vec); |
1595 | 0 | #endif |
1596 | 0 | } |
1597 | | |
1598 | | AP_DECLARE(apr_status_t) ap_h1_terminate_header(apr_bucket_brigade *bb) |
1599 | 0 | { |
1600 | 0 | char crlf[] = CRLF; |
1601 | 0 | apr_size_t buflen; |
1602 | |
|
1603 | 0 | buflen = strlen(crlf); |
1604 | 0 | ap_xlate_proto_to_ascii(crlf, buflen); |
1605 | 0 | return apr_brigade_write(bb, NULL, NULL, crlf, buflen); |
1606 | 0 | } |
1607 | | |
1608 | | AP_DECLARE(void) ap_h1_add_end_chunk(apr_bucket_brigade *b, |
1609 | | apr_bucket *eos, |
1610 | | request_rec *r, |
1611 | | apr_table_t *trailers) |
1612 | 0 | { |
1613 | 0 | if (!trailers || apr_is_empty_table(trailers)) { |
1614 | 0 | apr_bucket *e; |
1615 | |
|
1616 | 0 | ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, |
1617 | 0 | "append empty end chunk"); |
1618 | 0 | e = apr_bucket_immortal_create(ZERO_ASCII CRLF_ASCII |
1619 | 0 | CRLF_ASCII, 5, b->bucket_alloc); |
1620 | 0 | if (eos) { |
1621 | 0 | APR_BUCKET_INSERT_BEFORE(eos, e); |
1622 | 0 | } |
1623 | 0 | else { |
1624 | 0 | APR_BRIGADE_INSERT_TAIL(b, e); |
1625 | 0 | } |
1626 | 0 | } |
1627 | 0 | else { |
1628 | 0 | apr_bucket_brigade *tmp; |
1629 | |
|
1630 | 0 | ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, |
1631 | 0 | "append end chunk with trailers"); |
1632 | 0 | tmp = eos? apr_brigade_split_ex(b, eos, NULL) : NULL; |
1633 | 0 | apr_brigade_write(b, NULL, NULL, ZERO_ASCII CRLF_ASCII, 3); |
1634 | 0 | ap_h1_append_headers(b, r, trailers); |
1635 | 0 | ap_h1_terminate_header(b); |
1636 | 0 | if (tmp) APR_BRIGADE_CONCAT(b, tmp); |
1637 | 0 | } |
1638 | 0 | } |
1639 | | |
1640 | | typedef enum { |
1641 | | rrl_none, rrl_badprotocol, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace, |
1642 | | rrl_missinguri, rrl_baduri, rrl_trailingtext, |
1643 | | } rrl_error; |
1644 | | |
1645 | | /* get the length of a name for logging, but no more than 80 bytes */ |
1646 | 0 | #define LOG_NAME_MAX_LEN 80 |
1647 | | static int log_name_len(const char *name) |
1648 | 0 | { |
1649 | 0 | apr_size_t len = strlen(name); |
1650 | 0 | return (len > LOG_NAME_MAX_LEN)? LOG_NAME_MAX_LEN : (int)len; |
1651 | 0 | } |
1652 | | |
1653 | | static void rrl_log_error(request_rec *r, rrl_error error, const char *etoken) |
1654 | 158 | { |
1655 | 158 | switch (error) { |
1656 | 0 | case rrl_none: |
1657 | 0 | break; |
1658 | 1 | case rrl_badprotocol: |
1659 | 1 | ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02418) |
1660 | 1 | "HTTP Request Line; Unrecognized protocol '%.*s' " |
1661 | 1 | "(perhaps whitespace was injected?)", |
1662 | 1 | log_name_len(etoken), etoken); |
1663 | 1 | break; |
1664 | 107 | case rrl_badmethod: |
1665 | 107 | ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03445) |
1666 | 107 | "HTTP Request Line; Invalid method token: '%.*s'", |
1667 | 107 | log_name_len(etoken), etoken); |
1668 | 107 | break; |
1669 | 34 | case rrl_badwhitespace: |
1670 | 34 | ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03447) |
1671 | 34 | "HTTP Request Line; Invalid whitespace"); |
1672 | 34 | break; |
1673 | 5 | case rrl_excesswhitespace: |
1674 | 5 | ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03448) |
1675 | 5 | "HTTP Request Line; Excess whitespace " |
1676 | 5 | "(disallowed by HttpProtocolOptions Strict)"); |
1677 | 5 | break; |
1678 | 6 | case rrl_missinguri: |
1679 | 6 | ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03446) |
1680 | 6 | "HTTP Request Line; Missing URI"); |
1681 | 6 | break; |
1682 | 3 | case rrl_baduri: |
1683 | 3 | ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03454) |
1684 | 3 | "HTTP Request Line; URI incorrectly encoded: '%.*s'", |
1685 | 3 | log_name_len(etoken), etoken); |
1686 | 3 | break; |
1687 | 2 | case rrl_trailingtext: |
1688 | 2 | ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03449) |
1689 | 2 | "HTTP Request Line; Extraneous text found '%.*s' " |
1690 | 2 | "(perhaps whitespace was injected?)", |
1691 | 2 | log_name_len(etoken), etoken); |
1692 | 2 | break; |
1693 | 158 | } |
1694 | 158 | } |
1695 | | |
1696 | | /* remember the first error we encountered during tokenization */ |
1697 | | #define RRL_ERROR(e, et, y, yt) \ |
1698 | 1.60k | do { \ |
1699 | 730 | if (e == rrl_none) {\ |
1700 | 158 | e = y; et = yt;\ |
1701 | 158 | }\ |
1702 | 730 | } while (0) |
1703 | | |
1704 | | static rrl_error tokenize_request_line( |
1705 | | char *line, int strict, |
1706 | | const char **pmethod, const char **puri, const char **pprotocol, |
1707 | | const char **perror_token) |
1708 | 531 | { |
1709 | 531 | char *method, *protocol, *uri, *ll; |
1710 | 531 | rrl_error e = rrl_none; |
1711 | 531 | char *etoken = NULL; |
1712 | 531 | apr_size_t len = 0; |
1713 | | |
1714 | 531 | method = line; |
1715 | | /* If there is whitespace before a method, skip it and mark in error */ |
1716 | 531 | if (apr_isspace(*method)) { |
1717 | 31 | RRL_ERROR(e, etoken, rrl_badwhitespace, method); |
1718 | 224 | for ( ; apr_isspace(*method); ++method) |
1719 | 193 | ; |
1720 | 31 | } |
1721 | | |
1722 | | /* Scan the method up to the next whitespace, ensure it contains only |
1723 | | * valid http-token characters, otherwise mark in error |
1724 | | */ |
1725 | 531 | if (strict) { |
1726 | 488 | ll = (char*) ap_scan_http_token(method); |
1727 | 488 | } |
1728 | 43 | else { |
1729 | 43 | ll = (char*) ap_scan_vchar_obstext(method); |
1730 | 43 | } |
1731 | | |
1732 | 531 | if ((ll == method) || (*ll && !apr_isspace(*ll))) { |
1733 | 133 | RRL_ERROR(e, etoken, rrl_badmethod, ll); |
1734 | 133 | ll = strpbrk(ll, "\t\n\v\f\r "); |
1735 | 133 | } |
1736 | | |
1737 | | /* Verify method terminated with a single SP, or mark as specific error */ |
1738 | 531 | if (!ll) { |
1739 | 81 | RRL_ERROR(e, etoken, rrl_missinguri, NULL); |
1740 | 81 | protocol = uri = ""; |
1741 | 81 | goto done; |
1742 | 81 | } |
1743 | 450 | else if (strict && ll[0] && apr_isspace(ll[1])) { |
1744 | 30 | RRL_ERROR(e, etoken, rrl_excesswhitespace, ll); |
1745 | 30 | } |
1746 | | |
1747 | | /* Advance uri pointer over leading whitespace, NUL terminate the method |
1748 | | * If non-SP whitespace is encountered, mark as specific error |
1749 | | */ |
1750 | 1.25k | for (uri = ll; apr_isspace(*uri); ++uri) |
1751 | 808 | if (*uri != ' ') |
1752 | 171 | RRL_ERROR(e, etoken, rrl_badwhitespace, uri); |
1753 | 450 | *ll = '\0'; |
1754 | | |
1755 | 450 | if (!*uri) |
1756 | 23 | RRL_ERROR(e, etoken, rrl_missinguri, NULL); |
1757 | | |
1758 | | /* Scan the URI up to the next whitespace, ensure it contains no raw |
1759 | | * control characters, otherwise mark in error |
1760 | | */ |
1761 | 450 | ll = (char*) ap_scan_vchar_obstext(uri); |
1762 | 450 | if (ll == uri || (*ll && !apr_isspace(*ll))) { |
1763 | 33 | RRL_ERROR(e, etoken, rrl_baduri, ll); |
1764 | 33 | ll = strpbrk(ll, "\t\n\v\f\r "); |
1765 | 33 | } |
1766 | | |
1767 | | /* Verify URI terminated with a single SP, or mark as specific error */ |
1768 | 450 | if (!ll) { |
1769 | 28 | protocol = ""; |
1770 | 28 | goto done; |
1771 | 28 | } |
1772 | 422 | else if (strict && ll[0] && apr_isspace(ll[1])) { |
1773 | 22 | RRL_ERROR(e, etoken, rrl_excesswhitespace, ll); |
1774 | 22 | } |
1775 | | |
1776 | | /* Advance protocol pointer over leading whitespace, NUL terminate the uri |
1777 | | * If non-SP whitespace is encountered, mark as specific error |
1778 | | */ |
1779 | 1.01k | for (protocol = ll; apr_isspace(*protocol); ++protocol) |
1780 | 594 | if (*protocol != ' ') |
1781 | 172 | RRL_ERROR(e, etoken, rrl_badwhitespace, protocol); |
1782 | 422 | *ll = '\0'; |
1783 | | |
1784 | | /* Scan the protocol up to the next whitespace, validation comes later */ |
1785 | 422 | if (!(ll = (char*) ap_scan_vchar_obstext(protocol))) { |
1786 | 0 | len = strlen(protocol); |
1787 | 0 | goto done; |
1788 | 0 | } |
1789 | 422 | len = ll - protocol; |
1790 | | |
1791 | | /* Advance over trailing whitespace, if found mark in error, |
1792 | | * determine if trailing text is found, unconditionally mark in error, |
1793 | | * finally NUL terminate the protocol string |
1794 | | */ |
1795 | 422 | if (*ll && !apr_isspace(*ll)) { |
1796 | 11 | RRL_ERROR(e, etoken, rrl_badprotocol, ll); |
1797 | 11 | } |
1798 | 411 | else if (strict && *ll) { |
1799 | 9 | RRL_ERROR(e, etoken, rrl_excesswhitespace, ll); |
1800 | 9 | } |
1801 | 402 | else { |
1802 | 556 | for ( ; apr_isspace(*ll); ++ll) |
1803 | 157 | if (*ll != ' ') { |
1804 | 3 | RRL_ERROR(e, etoken, rrl_badwhitespace, ll); |
1805 | 3 | break; |
1806 | 3 | } |
1807 | 402 | if (*ll) |
1808 | 11 | RRL_ERROR(e, etoken, rrl_trailingtext, ll); |
1809 | 402 | } |
1810 | 422 | *((char *)protocol + len) = '\0'; |
1811 | | |
1812 | 531 | done: |
1813 | 531 | *pmethod = method; |
1814 | 531 | *puri = uri; |
1815 | 531 | *pprotocol = protocol; |
1816 | 531 | *perror_token = etoken; |
1817 | 531 | return e; |
1818 | 422 | } |
1819 | | |
1820 | | AP_DECLARE(int) ap_h1_tokenize_request_line( |
1821 | | request_rec *r, const char *line, |
1822 | | const char **pmethod, const char **puri, const char **pprotocol) |
1823 | 531 | { |
1824 | 531 | core_server_config *conf = ap_get_core_module_config(r->server->module_config); |
1825 | 531 | int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE); |
1826 | 531 | rrl_error error; |
1827 | 531 | const char *error_token; |
1828 | | |
1829 | 531 | ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, |
1830 | 531 | "ap_tokenize_request_line: '%s'", line); |
1831 | 531 | error = tokenize_request_line(apr_pstrdup(r->pool, line), strict, pmethod, |
1832 | 531 | puri, pprotocol, &error_token); |
1833 | 531 | ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, |
1834 | 531 | "ap_tokenize_request: error=%d, method=%s, uri=%s, protocol=%s", |
1835 | 531 | error, *pmethod, *puri, *pprotocol); |
1836 | 531 | if (error != rrl_none) { |
1837 | 158 | rrl_log_error(r, error, error_token); |
1838 | 158 | return 0; |
1839 | 158 | } |
1840 | 373 | return 1; |
1841 | 531 | } |