1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24 #include <assert.h>
25
26 #include <apr_optional.h>
27 #include <apr_optional_hooks.h>
28 #include <apr_strings.h>
29 #include <apr_cstr.h>
30 #include <apr_time.h>
31 #include <apr_want.h>
32
33 #include <httpd.h>
34 #include <http_protocol.h>
35 #include <http_request.h>
36 #include <http_log.h>
37
38 static void curltest_hooks(apr_pool_t *pool);
39 static int curltest_echo_handler(request_rec *r);
40 static int curltest_put_handler(request_rec *r);
41 static int curltest_tweak_handler(request_rec *r);
42 static int curltest_1_1_required(request_rec *r);
43 static int curltest_sslinfo_handler(request_rec *r);
44
45 AP_DECLARE_MODULE(curltest) =
46 {
47 STANDARD20_MODULE_STUFF,
48 NULL, /* func to create per dir config */
49 NULL, /* func to merge per dir config */
50 NULL, /* func to create per server config */
51 NULL, /* func to merge per server config */
52 NULL, /* command handlers */
53 curltest_hooks,
54 #if defined(AP_MODULE_FLAG_NONE)
55 AP_MODULE_FLAG_ALWAYS_MERGE
56 #endif
57 };
58
curltest_post_config(apr_pool_t * p,apr_pool_t * plog,apr_pool_t * ptemp,server_rec * s)59 static int curltest_post_config(apr_pool_t *p, apr_pool_t *plog,
60 apr_pool_t *ptemp, server_rec *s)
61 {
62 void *data = NULL;
63 const char *key = "mod_curltest_init_counter";
64
65 (void)plog;(void)ptemp;
66
67 apr_pool_userdata_get(&data, key, s->process->pool);
68 if(!data) {
69 /* dry run */
70 apr_pool_userdata_set((const void *)1, key,
71 apr_pool_cleanup_null, s->process->pool);
72 return APR_SUCCESS;
73 }
74
75 /* mess with the overall server here */
76
77 return APR_SUCCESS;
78 }
79
curltest_hooks(apr_pool_t * pool)80 static void curltest_hooks(apr_pool_t *pool)
81 {
82 ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, "installing hooks");
83
84 /* Run once after configuration is set, but before mpm children initialize.
85 */
86 ap_hook_post_config(curltest_post_config, NULL, NULL, APR_HOOK_MIDDLE);
87
88 /* curl test handlers */
89 ap_hook_handler(curltest_echo_handler, NULL, NULL, APR_HOOK_MIDDLE);
90 ap_hook_handler(curltest_put_handler, NULL, NULL, APR_HOOK_MIDDLE);
91 ap_hook_handler(curltest_tweak_handler, NULL, NULL, APR_HOOK_MIDDLE);
92 ap_hook_handler(curltest_1_1_required, NULL, NULL, APR_HOOK_MIDDLE);
93 ap_hook_handler(curltest_sslinfo_handler, NULL, NULL, APR_HOOK_MIDDLE);
94 }
95
96 #define SECS_PER_HOUR (60*60)
97 #define SECS_PER_DAY (24*SECS_PER_HOUR)
98
duration_parse(apr_interval_time_t * ptimeout,const char * value,const char * def_unit)99 static apr_status_t duration_parse(apr_interval_time_t *ptimeout,
100 const char *value, const char *def_unit)
101 {
102 char *endp;
103 apr_int64_t n;
104
105 n = apr_strtoi64(value, &endp, 10);
106 if(errno) {
107 return errno;
108 }
109 if(!endp || !*endp) {
110 if(!def_unit)
111 def_unit = "s";
112 }
113 else if(endp == value) {
114 return APR_EINVAL;
115 }
116 else {
117 def_unit = endp;
118 }
119
120 switch(*def_unit) {
121 case 'D':
122 case 'd':
123 *ptimeout = apr_time_from_sec(n * SECS_PER_DAY);
124 break;
125 case 's':
126 case 'S':
127 *ptimeout = (apr_interval_time_t) apr_time_from_sec(n);
128 break;
129 case 'h':
130 case 'H':
131 /* Time is in hours */
132 *ptimeout = (apr_interval_time_t) apr_time_from_sec(n * SECS_PER_HOUR);
133 break;
134 case 'm':
135 case 'M':
136 switch(*(++def_unit)) {
137 /* Time is in milliseconds */
138 case 's':
139 case 'S':
140 *ptimeout = (apr_interval_time_t) n * 1000;
141 break;
142 /* Time is in minutes */
143 case 'i':
144 case 'I':
145 *ptimeout = (apr_interval_time_t) apr_time_from_sec(n * 60);
146 break;
147 default:
148 return APR_EGENERAL;
149 }
150 break;
151 case 'u':
152 case 'U':
153 switch(*(++def_unit)) {
154 /* Time is in microseconds */
155 case 's':
156 case 'S':
157 *ptimeout = (apr_interval_time_t) n;
158 break;
159 default:
160 return APR_EGENERAL;
161 }
162 break;
163 default:
164 return APR_EGENERAL;
165 }
166 return APR_SUCCESS;
167 }
168
status_from_str(const char * s,apr_status_t * pstatus)169 static int status_from_str(const char *s, apr_status_t *pstatus)
170 {
171 if(!strcmp("timeout", s)) {
172 *pstatus = APR_TIMEUP;
173 return 1;
174 }
175 else if(!strcmp("reset", s)) {
176 *pstatus = APR_ECONNRESET;
177 return 1;
178 }
179 return 0;
180 }
181
curltest_echo_handler(request_rec * r)182 static int curltest_echo_handler(request_rec *r)
183 {
184 conn_rec *c = r->connection;
185 apr_bucket_brigade *bb;
186 apr_bucket *b;
187 apr_status_t rv;
188 char buffer[8192];
189 const char *ct;
190 apr_off_t die_after_len = -1, total_read_len = 0;
191 int just_die = 0, die_after_100 = 0;
192 long l;
193
194 if(strcmp(r->handler, "curltest-echo")) {
195 return DECLINED;
196 }
197 if(r->method_number != M_GET && r->method_number != M_POST) {
198 return DECLINED;
199 }
200
201 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "echo_handler: processing");
202 if(r->args) {
203 apr_array_header_t *args = NULL;
204 int i;
205 args = apr_cstr_split(r->args, "&", 1, r->pool);
206 for(i = 0; i < args->nelts; ++i) {
207 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char *);
208 s = strchr(arg, '=');
209 if(s) {
210 *s = '\0';
211 val = s + 1;
212 if(!strcmp("die_after", arg)) {
213 die_after_len = (apr_off_t)apr_atoi64(val);
214 continue;
215 }
216 else if(!strcmp("just_die", arg)) {
217 just_die = 1;
218 continue;
219 }
220 else if(!strcmp("die_after_100", arg)) {
221 die_after_100 = 1;
222 continue;
223 }
224 }
225 }
226 }
227
228 if(just_die) {
229 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
230 "echo_handler: dying right away");
231 /* Generate no HTTP response at all. */
232 ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
233 r->connection->keepalive = AP_CONN_CLOSE;
234 return AP_FILTER_ERROR;
235 }
236
237 r->status = 200;
238 if(die_after_len >= 0) {
239 r->clength = die_after_len + 1;
240 r->chunked = 0;
241 apr_table_set(r->headers_out, "Content-Length",
242 apr_ltoa(r->pool, (long)r->clength));
243 }
244 else {
245 r->clength = -1;
246 r->chunked = 1;
247 apr_table_unset(r->headers_out, "Content-Length");
248 }
249 /* Discourage content-encodings */
250 apr_table_unset(r->headers_out, "Content-Encoding");
251 apr_table_setn(r->subprocess_env, "no-brotli", "1");
252 apr_table_setn(r->subprocess_env, "no-gzip", "1");
253
254 ct = apr_table_get(r->headers_in, "content-type");
255 ap_set_content_type(r, ct ? ct : "application/octet-stream");
256
257 bb = apr_brigade_create(r->pool, c->bucket_alloc);
258 /* copy any request body into the response */
259 rv = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
260 if(rv)
261 goto cleanup;
262 if(die_after_100) {
263 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
264 "echo_handler: dying after 100-continue");
265 /* Generate no HTTP response at all. */
266 ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
267 r->connection->keepalive = AP_CONN_CLOSE;
268 return AP_FILTER_ERROR;
269 }
270 if(ap_should_client_block(r)) {
271 while(0 < (l = ap_get_client_block(r, &buffer[0], sizeof(buffer)))) {
272 total_read_len += l;
273 if(die_after_len >= 0 && total_read_len >= die_after_len) {
274 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
275 "echo_handler: dying after %ld bytes as requested",
276 (long)total_read_len);
277 ap_pass_brigade(r->output_filters, bb);
278 ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
279 r->connection->keepalive = AP_CONN_CLOSE;
280 return DONE;
281 }
282 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
283 "echo_handler: copying %ld bytes from request body", l);
284 rv = apr_brigade_write(bb, NULL, NULL, buffer, l);
285 if(APR_SUCCESS != rv)
286 goto cleanup;
287 rv = ap_pass_brigade(r->output_filters, bb);
288 if(APR_SUCCESS != rv)
289 goto cleanup;
290 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
291 "echo_handler: passed %ld bytes from request body", l);
292 }
293 }
294 /* we are done */
295 b = apr_bucket_eos_create(c->bucket_alloc);
296 APR_BRIGADE_INSERT_TAIL(bb, b);
297 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "echo_handler: request read");
298
299 if(r->trailers_in && !apr_is_empty_table(r->trailers_in)) {
300 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
301 "echo_handler: seeing incoming trailers");
302 apr_table_setn(r->trailers_out, "h2test-trailers-in",
303 apr_itoa(r->pool, 1));
304 }
305
306 rv = ap_pass_brigade(r->output_filters, bb);
307
308 cleanup:
309 if(rv == APR_SUCCESS ||
310 r->status != HTTP_OK ||
311 c->aborted) {
312 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "echo_handler: done");
313 return OK;
314 }
315 else {
316 /* no way to know what type of error occurred */
317 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "echo_handler failed");
318 return AP_FILTER_ERROR;
319 }
320 return DECLINED;
321 }
322
curltest_tweak_handler(request_rec * r)323 static int curltest_tweak_handler(request_rec *r)
324 {
325 conn_rec *c = r->connection;
326 apr_bucket_brigade *bb;
327 apr_bucket *b;
328 apr_status_t rv;
329 char buffer[16*1024];
330 int i, chunks = 3, error_bucket = 1;
331 size_t chunk_size = sizeof(buffer);
332 const char *request_id = "none";
333 apr_time_t delay = 0, chunk_delay = 0, close_delay = 0;
334 apr_array_header_t *args = NULL;
335 int http_status = 200;
336 apr_status_t error = APR_SUCCESS, body_error = APR_SUCCESS;
337 int close_conn = 0, with_cl = 0;
338
339 if(strcmp(r->handler, "curltest-tweak")) {
340 return DECLINED;
341 }
342 if(r->method_number == M_DELETE) {
343 http_status = 204;
344 chunks = 0;
345 }
346 else if(r->method_number != M_GET && r->method_number != M_POST) {
347 return DECLINED;
348 }
349
350 if(r->args) {
351 args = apr_cstr_split(r->args, "&", 1, r->pool);
352 for(i = 0; i < args->nelts; ++i) {
353 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char *);
354 s = strchr(arg, '=');
355 if(s) {
356 *s = '\0';
357 val = s + 1;
358 if(!strcmp("status", arg)) {
359 http_status = (int)apr_atoi64(val);
360 if(http_status > 0) {
361 continue;
362 }
363 }
364 else if(!strcmp("chunks", arg)) {
365 chunks = (int)apr_atoi64(val);
366 if(chunks >= 0) {
367 continue;
368 }
369 }
370 else if(!strcmp("chunk_size", arg)) {
371 chunk_size = (int)apr_atoi64(val);
372 if(chunk_size >= 0) {
373 if(chunk_size > sizeof(buffer)) {
374 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
375 "chunk_size %zu too large", chunk_size);
376 ap_die(HTTP_BAD_REQUEST, r);
377 return OK;
378 }
379 continue;
380 }
381 }
382 else if(!strcmp("id", arg)) {
383 /* just an id for repeated requests with curl's url globbing */
384 request_id = val;
385 continue;
386 }
387 else if(!strcmp("error", arg)) {
388 if(status_from_str(val, &error)) {
389 continue;
390 }
391 }
392 else if(!strcmp("error_bucket", arg)) {
393 error_bucket = (int)apr_atoi64(val);
394 if(error_bucket >= 0) {
395 continue;
396 }
397 }
398 else if(!strcmp("body_error", arg)) {
399 if(status_from_str(val, &body_error)) {
400 continue;
401 }
402 }
403 else if(!strcmp("delay", arg)) {
404 rv = duration_parse(&delay, val, "s");
405 if(APR_SUCCESS == rv) {
406 continue;
407 }
408 }
409 else if(!strcmp("chunk_delay", arg)) {
410 rv = duration_parse(&chunk_delay, val, "s");
411 if(APR_SUCCESS == rv) {
412 continue;
413 }
414 }
415 else if(!strcmp("close_delay", arg)) {
416 rv = duration_parse(&close_delay, val, "s");
417 if(APR_SUCCESS == rv) {
418 continue;
419 }
420 }
421 }
422 else if(!strcmp("close", arg)) {
423 /* we are asked to close the connection */
424 close_conn = 1;
425 continue;
426 }
427 else if(!strcmp("with_cl", arg)) {
428 with_cl = 1;
429 continue;
430 }
431 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not "
432 "understood: '%s' in %s",
433 arg, r->args);
434 ap_die(HTTP_BAD_REQUEST, r);
435 return OK;
436 }
437 }
438
439 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "error_handler: processing "
440 "request, %s", r->args? r->args : "(no args)");
441 r->status = http_status;
442 r->clength = with_cl ? (chunks * chunk_size) : -1;
443 r->chunked = (r->proto_num >= HTTP_VERSION(1, 1)) && !with_cl;
444 apr_table_setn(r->headers_out, "request-id", request_id);
445 if(r->clength >= 0) {
446 apr_table_set(r->headers_out, "Content-Length",
447 apr_ltoa(r->pool, (long)r->clength));
448 }
449 else
450 apr_table_unset(r->headers_out, "Content-Length");
451 /* Discourage content-encodings */
452 apr_table_unset(r->headers_out, "Content-Encoding");
453 apr_table_setn(r->subprocess_env, "no-brotli", "1");
454 apr_table_setn(r->subprocess_env, "no-gzip", "1");
455
456 ap_set_content_type(r, "application/octet-stream");
457 bb = apr_brigade_create(r->pool, c->bucket_alloc);
458
459 if(delay) {
460 apr_sleep(delay);
461 }
462 if(error != APR_SUCCESS) {
463 return ap_map_http_request_error(error, HTTP_BAD_REQUEST);
464 }
465 /* flush response */
466 b = apr_bucket_flush_create(c->bucket_alloc);
467 APR_BRIGADE_INSERT_TAIL(bb, b);
468 rv = ap_pass_brigade(r->output_filters, bb);
469 if(APR_SUCCESS != rv)
470 goto cleanup;
471
472 memset(buffer, 'X', sizeof(buffer));
473 for(i = 0; i < chunks; ++i) {
474 if(chunk_delay) {
475 apr_sleep(chunk_delay);
476 }
477 rv = apr_brigade_write(bb, NULL, NULL, buffer, chunk_size);
478 if(APR_SUCCESS != rv)
479 goto cleanup;
480 rv = ap_pass_brigade(r->output_filters, bb);
481 if(APR_SUCCESS != rv)
482 goto cleanup;
483 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
484 "error_handler: passed %lu bytes as response body",
485 (unsigned long)chunk_size);
486 if(body_error != APR_SUCCESS) {
487 rv = body_error;
488 goto cleanup;
489 }
490 }
491 /* we are done */
492 b = apr_bucket_eos_create(c->bucket_alloc);
493 APR_BRIGADE_INSERT_TAIL(bb, b);
494 rv = ap_pass_brigade(r->output_filters, bb);
495 apr_brigade_cleanup(bb);
496 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r,
497 "error_handler: response passed");
498
499 cleanup:
500 if(close_conn) {
501 if(close_delay) {
502 b = apr_bucket_flush_create(c->bucket_alloc);
503 APR_BRIGADE_INSERT_TAIL(bb, b);
504 rv = ap_pass_brigade(r->output_filters, bb);
505 apr_brigade_cleanup(bb);
506 apr_sleep(close_delay);
507 }
508 r->connection->keepalive = AP_CONN_CLOSE;
509 }
510 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r,
511 "error_handler: request cleanup, r->status=%d, aborted=%d, "
512 "close=%d", r->status, c->aborted, close_conn);
513 if(rv == APR_SUCCESS) {
514 return OK;
515 }
516 if(error_bucket) {
517 http_status = ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
518 b = ap_bucket_error_create(http_status, NULL, r->pool, c->bucket_alloc);
519 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r,
520 "error_handler: passing error bucket, status=%d",
521 http_status);
522 APR_BRIGADE_INSERT_TAIL(bb, b);
523 ap_pass_brigade(r->output_filters, bb);
524 }
525 return AP_FILTER_ERROR;
526 }
527
curltest_put_handler(request_rec * r)528 static int curltest_put_handler(request_rec *r)
529 {
530 conn_rec *c = r->connection;
531 apr_bucket_brigade *bb;
532 apr_bucket *b;
533 apr_status_t rv;
534 char buffer[128*1024];
535 const char *ct;
536 apr_off_t rbody_len = 0;
537 const char *s_rbody_len;
538 const char *request_id = "none";
539 apr_time_t read_delay = 0, chunk_delay = 0;
540 apr_array_header_t *args = NULL;
541 long l;
542 int i;
543
544 if(strcmp(r->handler, "curltest-put")) {
545 return DECLINED;
546 }
547 if(r->method_number != M_PUT) {
548 return DECLINED;
549 }
550
551 if(r->args) {
552 args = apr_cstr_split(r->args, "&", 1, r->pool);
553 for(i = 0; i < args->nelts; ++i) {
554 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char *);
555 s = strchr(arg, '=');
556 if(s) {
557 *s = '\0';
558 val = s + 1;
559 if(!strcmp("id", arg)) {
560 /* just an id for repeated requests with curl's url globbing */
561 request_id = val;
562 continue;
563 }
564 else if(!strcmp("read_delay", arg)) {
565 rv = duration_parse(&read_delay, val, "s");
566 if(APR_SUCCESS == rv) {
567 continue;
568 }
569 }
570 else if(!strcmp("chunk_delay", arg)) {
571 rv = duration_parse(&chunk_delay, val, "s");
572 if(APR_SUCCESS == rv) {
573 continue;
574 }
575 }
576 }
577 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not "
578 "understood: '%s' in %s",
579 arg, r->args);
580 ap_die(HTTP_BAD_REQUEST, r);
581 return OK;
582 }
583 }
584
585 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "put_handler: processing");
586 r->status = 200;
587 r->clength = -1;
588 r->chunked = 1;
589 apr_table_unset(r->headers_out, "Content-Length");
590 /* Discourage content-encodings */
591 apr_table_unset(r->headers_out, "Content-Encoding");
592 apr_table_setn(r->subprocess_env, "no-brotli", "1");
593 apr_table_setn(r->subprocess_env, "no-gzip", "1");
594
595 ct = apr_table_get(r->headers_in, "content-type");
596 ap_set_content_type(r, ct ? ct : "text/plain");
597
598 if(read_delay) {
599 apr_sleep(read_delay);
600 }
601 bb = apr_brigade_create(r->pool, c->bucket_alloc);
602 /* copy any request body into the response */
603 rv = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
604 if(rv)
605 goto cleanup;
606 if(ap_should_client_block(r)) {
607 while(0 < (l = ap_get_client_block(r, &buffer[0], sizeof(buffer)))) {
608 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
609 "put_handler: read %ld bytes from request body", l);
610 if(chunk_delay) {
611 apr_sleep(chunk_delay);
612 }
613 rbody_len += l;
614 }
615 }
616 /* we are done */
617 s_rbody_len = apr_psprintf(r->pool, "%"APR_OFF_T_FMT, rbody_len);
618 apr_table_setn(r->headers_out, "Received-Length", s_rbody_len);
619 rv = apr_brigade_puts(bb, NULL, NULL, s_rbody_len);
620 if(APR_SUCCESS != rv)
621 goto cleanup;
622 b = apr_bucket_eos_create(c->bucket_alloc);
623 APR_BRIGADE_INSERT_TAIL(bb, b);
624 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "put_handler: request read");
625
626 rv = ap_pass_brigade(r->output_filters, bb);
627
628 cleanup:
629 if(rv == APR_SUCCESS
630 || r->status != HTTP_OK
631 || c->aborted) {
632 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "put_handler: done");
633 return OK;
634 }
635 else {
636 /* no way to know what type of error occurred */
637 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "put_handler failed");
638 return AP_FILTER_ERROR;
639 }
640 return DECLINED;
641 }
642
curltest_1_1_required(request_rec * r)643 static int curltest_1_1_required(request_rec *r)
644 {
645 conn_rec *c = r->connection;
646 apr_bucket_brigade *bb;
647 apr_bucket *b;
648 apr_status_t rv;
649 char buffer[16*1024];
650 const char *ct;
651 const char *request_id = "none";
652 apr_time_t chunk_delay = 0;
653 apr_array_header_t *args = NULL;
654 long l;
655 int i;
656
657 if(strcmp(r->handler, "curltest-1_1-required")) {
658 return DECLINED;
659 }
660
661 if(HTTP_VERSION_MAJOR(r->proto_num) > 1) {
662 apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "1");
663 ap_die(HTTP_FORBIDDEN, r);
664 return OK;
665 }
666
667 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: processing");
668 r->status = 200;
669 r->clength = -1;
670 r->chunked = 1;
671 apr_table_unset(r->headers_out, "Content-Length");
672 /* Discourage content-encodings */
673 apr_table_unset(r->headers_out, "Content-Encoding");
674 apr_table_setn(r->subprocess_env, "no-brotli", "1");
675 apr_table_setn(r->subprocess_env, "no-gzip", "1");
676
677 ct = apr_table_get(r->headers_in, "content-type");
678 ap_set_content_type(r, ct ? ct : "text/plain");
679
680 bb = apr_brigade_create(r->pool, c->bucket_alloc);
681 /* flush response */
682 b = apr_bucket_flush_create(c->bucket_alloc);
683 APR_BRIGADE_INSERT_TAIL(bb, b);
684 rv = ap_pass_brigade(r->output_filters, bb);
685 if(APR_SUCCESS != rv)
686 goto cleanup;
687
688 /* we are done */
689 rv = apr_brigade_printf(bb, NULL, NULL, "well done!");
690 if(APR_SUCCESS != rv)
691 goto cleanup;
692 b = apr_bucket_eos_create(c->bucket_alloc);
693 APR_BRIGADE_INSERT_TAIL(bb, b);
694 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: request read");
695
696 rv = ap_pass_brigade(r->output_filters, bb);
697
698 cleanup:
699 if(rv == APR_SUCCESS
700 || r->status != HTTP_OK
701 || c->aborted) {
702 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler: done");
703 return OK;
704 }
705 else {
706 /* no way to know what type of error occurred */
707 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler failed");
708 return AP_FILTER_ERROR;
709 }
710 return DECLINED;
711 }
712
brigade_env_var(request_rec * r,apr_bucket_brigade * bb,const char * name)713 static int brigade_env_var(request_rec *r, apr_bucket_brigade *bb,
714 const char *name)
715 {
716 const char *s;
717 s = apr_table_get(r->subprocess_env, name);
718 if(s)
719 return apr_brigade_printf(bb, NULL, NULL, ",\n \"%s\": \"%s\"", name, s);
720 return 0;
721 }
722
curltest_sslinfo_handler(request_rec * r)723 static int curltest_sslinfo_handler(request_rec *r)
724 {
725 conn_rec *c = r->connection;
726 apr_bucket_brigade *bb;
727 apr_bucket *b;
728 apr_status_t rv;
729 apr_array_header_t *args = NULL;
730 const char *request_id = NULL;
731 int close_conn = 0;
732 long l;
733 int i;
734
735 if(strcmp(r->handler, "curltest-sslinfo")) {
736 return DECLINED;
737 }
738 if(r->method_number != M_GET) {
739 return DECLINED;
740 }
741
742 if(r->args) {
743 apr_array_header_t *args = apr_cstr_split(r->args, "&", 1, r->pool);
744 for(i = 0; i < args->nelts; ++i) {
745 char *s, *val, *arg = APR_ARRAY_IDX(args, i, char *);
746 s = strchr(arg, '=');
747 if(s) {
748 *s = '\0';
749 val = s + 1;
750 if(!strcmp("id", arg)) {
751 /* just an id for repeated requests with curl's url globbing */
752 request_id = val;
753 continue;
754 }
755 }
756 else if(!strcmp("close", arg)) {
757 /* we are asked to close the connection */
758 close_conn = 1;
759 continue;
760 }
761 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not "
762 "understood: '%s' in %s",
763 arg, r->args);
764 ap_die(HTTP_BAD_REQUEST, r);
765 return OK;
766 }
767 }
768
769 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "sslinfo: processing");
770 r->status = 200;
771 r->clength = -1;
772 r->chunked = 1;
773 apr_table_unset(r->headers_out, "Content-Length");
774 /* Discourage content-encodings */
775 apr_table_unset(r->headers_out, "Content-Encoding");
776 apr_table_setn(r->subprocess_env, "no-brotli", "1");
777 apr_table_setn(r->subprocess_env, "no-gzip", "1");
778
779 ap_set_content_type(r, "application/json");
780
781 bb = apr_brigade_create(r->pool, c->bucket_alloc);
782
783 apr_brigade_puts(bb, NULL, NULL, "{\n \"Name\": \"SSL-Information\"");
784 brigade_env_var(r, bb, "HTTPS");
785 brigade_env_var(r, bb, "SSL_PROTOCOL");
786 brigade_env_var(r, bb, "SSL_CIPHER");
787 brigade_env_var(r, bb, "SSL_SESSION_ID");
788 brigade_env_var(r, bb, "SSL_SESSION_RESUMED");
789 brigade_env_var(r, bb, "SSL_SRP_USER");
790 brigade_env_var(r, bb, "SSL_SRP_USERINFO");
791 brigade_env_var(r, bb, "SSL_TLS_SNI");
792 apr_brigade_puts(bb, NULL, NULL, "}\n");
793
794 /* flush response */
795 b = apr_bucket_flush_create(c->bucket_alloc);
796 APR_BRIGADE_INSERT_TAIL(bb, b);
797 rv = ap_pass_brigade(r->output_filters, bb);
798 if(APR_SUCCESS != rv)
799 goto cleanup;
800
801 /* we are done */
802 b = apr_bucket_eos_create(c->bucket_alloc);
803 APR_BRIGADE_INSERT_TAIL(bb, b);
804 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: request read");
805
806 rv = ap_pass_brigade(r->output_filters, bb);
807
808 cleanup:
809 if(close_conn)
810 r->connection->keepalive = AP_CONN_CLOSE;
811 if(rv == APR_SUCCESS
812 || r->status != HTTP_OK
813 || c->aborted) {
814 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler: done");
815 return OK;
816 }
817 else {
818 /* no way to know what type of error occurred */
819 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler failed");
820 return AP_FILTER_ERROR;
821 }
822 return DECLINED;
823 }
824