1 /*
2 * Copyright (c) 2013-2014 Tim Ruehsen
3 * Copyright (c) 2015-2021 Free Software Foundation, Inc.
4 *
5 * This file is part of Wget
6 *
7 * Wget is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Wget is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Wget If not, see <https://www.gnu.org/licenses/>.
19 *
20 *
21 * Test suite function library
22 *
23 * Changelog
24 * 16.01.2013 Tim Ruehsen created
25 *
26 * To create the X.509 stuff, I followed the instructions at
27 * gnutls.org/manual/html_node/gnutls_002dserv-Invocation.html
28 *
29 */
30
31 #include <config.h>
32
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <utime.h>
39 #include <dirent.h>
40 #include <sys/stat.h>
41 #include <sys/wait.h>
42
43 #include <wget.h>
44 #include "../src/wget_utils.h"
45 #include "libtest.h"
46
47 #include <microhttpd.h>
48 #ifdef HAVE_MICROHTTPD_HTTP2_H
49 # include <microhttpd_http2.h>
50 #endif
51 #ifndef HAVE_MHD_FREE
52 # define MHD_free wget_free
53 #endif
54 #ifndef MHD_HTTP_RANGE_NOT_SATISFIABLE
55 # define MHD_HTTP_RANGE_NOT_SATISFIABLE MHD_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE
56 #endif
57 #ifndef MHD_USE_TLS
58 # define MHD_USE_TLS MHD_USE_SSL
59 #endif
60 #if MHD_VERSION <= 0x00097000
61 #undef MHD_NO
62 #undef MHD_YES
63 enum MHD_Result {
64 MHD_NO = 0,
65 MHD_YES = 1
66 };
67 #endif
68
69 #include <sys/types.h>
70 #include <sys/select.h>
71 #include <sys/socket.h>
72 #include <netdb.h>
73
74 #ifdef WITH_GNUTLS_IN_TESTSUITE
75 #ifdef WITH_GNUTLS_OCSP
76 # include <gnutls/ocsp.h>
77 # include <gnutls/x509.h>
78 # include <gnutls/abstract.h>
79 #endif
80
81 # include <gnutls/gnutls.h>
82 # define file_load_err(fname, msg) wget_error_printf_exit("Couldn't load '%s' : %s\n", fname, msg)
83 #endif
84
85 static int
86 http_server_port,
87 https_server_port,
88 ocsp_server_port,
89 h2_server_port,
90 keep_tmpfiles,
91 clean_directory,
92 reject_http_connection,
93 reject_https_connection;
94 static wget_vector
95 *request_urls;
96 static wget_test_url_t
97 *urls;
98 static size_t
99 nurls;
100 static char
101 tmpdir[128];
102 static char
103 server_send_content_length = 1;
104
105 #if MHD_VERSION >= 0x00096302 && GNUTLS_VERSION_NUMBER >= 0x030603
106 static enum CHECK_POST_HANDSHAKE_AUTH {
107 CHECK_ENABLED,
108 CHECK_PASSED,
109 CHECK_FAILED
110 } *post_handshake_auth;
111 #endif
112
113 // MHD_Daemon instance
114 static struct MHD_Daemon
115 *httpdaemon,
116 *httpsdaemon,
117 *ocspdaemon,
118 *h2daemon;
119
120 #ifdef WITH_GNUTLS_OCSP
121 static gnutls_pcert_st *pcrt;
122 static gnutls_privkey_t *privkey;
123
124 static struct ocsp_resp_t {
125 char
126 *data;
127 size_t
128 size;
129 } *ocsp_resp;
130 #endif
131
132 #ifdef WITH_GNUTLS_OCSP
133 #if MHD_VERSION >= 0x00096502 && GNUTLS_VERSION_NUMBER >= 0x030603
134 static gnutls_ocsp_data_st *ocsp_stap_resp;
135 #endif
136 #endif
137
138 // for passing URL query string
139 struct query_string {
140 wget_buffer
141 *params;
142 int
143 it;
144 };
145
146 static char
147 *key_pem,
148 *cert_pem;
149
150 enum SERVER_MODE {
151 HTTP_MODE,
152 HTTPS_MODE,
153 OCSP_MODE,
154 OCSP_STAP_MODE,
155 H2_MODE
156 };
157
158 static enum PASS {
159 HTTP_1_1_PASS,
160 H2_PASS,
161 END_PASS
162 } proto_pass;
163
_parse_hostname(const char * data)164 static const char *_parse_hostname(const char* data)
165 {
166 if (data) {
167 if (!wget_strncasecmp_ascii(data, "http://", 7)) {
168 return strchr(data + 7, '/');
169 }
170 if (!wget_strncasecmp_ascii(data, "https://", 8)) {
171 return strchr(data + 8, '/');
172 }
173 }
174
175 return data;
176 }
177
_replace_space_with_plus(wget_buffer * buf,const char * data)178 static void _replace_space_with_plus(wget_buffer *buf, const char *data)
179 {
180 for (; *data; data++)
181 wget_buffer_memcat(buf, *data == ' ' ? "+" : data, 1);
182 }
183
_print_query_string(void * cls,enum MHD_ValueKind kind WGET_GCC_UNUSED,const char * key,const char * value)184 static enum MHD_Result _print_query_string(
185 void *cls,
186 enum MHD_ValueKind kind WGET_GCC_UNUSED,
187 const char *key,
188 const char *value)
189 {
190 struct query_string *query = cls;
191
192 if (key && !query->it) {
193 wget_buffer_strcpy(query->params, "?");
194 _replace_space_with_plus(query->params, key);
195 if (value) {
196 wget_buffer_strcat(query->params, "=");
197 _replace_space_with_plus(query->params, value);
198 }
199 }
200 if (key && query->it) {
201 wget_buffer_strcat(query->params, "&");
202 _replace_space_with_plus(query->params, key);
203 if (value) {
204 wget_buffer_strcat(query->params, "=");
205 _replace_space_with_plus(query->params, value);
206 }
207 }
208
209 query->it++;
210 return MHD_YES;
211 }
212
_print_header_range(void * cls,enum MHD_ValueKind kind WGET_GCC_UNUSED,const char * key,const char * value)213 static enum MHD_Result _print_header_range(
214 void *cls,
215 enum MHD_ValueKind kind WGET_GCC_UNUSED,
216 const char *key,
217 const char *value)
218 {
219 wget_buffer *header_range = cls;
220
221 if (!strcasecmp(key, MHD_HTTP_HEADER_RANGE)) {
222 wget_buffer_strcpy(header_range, key);
223 if (value) {
224 wget_buffer_strcat(header_range, value);
225 }
226 }
227
228 return MHD_YES;
229 }
230
231 struct ResponseContentCallbackParam
232 {
233 const char *response_data;
234 size_t response_size;
235 interrupt_response_mode_t interrupt_response_mode;
236 size_t interrupt_response_after_nbytes;
237 };
238
_callback(void * cls,uint64_t pos,char * buf,size_t buf_size)239 static ssize_t _callback (void *cls, uint64_t pos, char *buf, size_t buf_size)
240 {
241 size_t size_to_copy;
242 struct ResponseContentCallbackParam *const param =
243 (struct ResponseContentCallbackParam *)cls;
244
245 if (pos >= param->response_size)
246 return (ssize_t) MHD_CONTENT_READER_END_OF_STREAM;
247
248 // divide data into two chunks
249 buf_size = (param->response_size / 2) + 1;
250 if (buf_size < (param->response_size - pos))
251 size_to_copy = buf_size;
252 else
253 size_to_copy = param->response_size - pos;
254
255 memcpy (buf, param->response_data + pos, size_to_copy);
256
257 return size_to_copy;
258 }
259
_callback_interruptable(void * cls,uint64_t pos,char * buf,size_t buf_size)260 static ssize_t _callback_interruptable (void *cls, uint64_t pos, char *buf, size_t buf_size)
261 {
262 size_t size_to_copy;
263 struct ResponseContentCallbackParam *const param =
264 (struct ResponseContentCallbackParam *)cls;
265
266 if (pos >= param->response_size)
267 return (ssize_t) MHD_CONTENT_READER_END_OF_STREAM;
268
269 if (buf_size <= (param->response_size - pos)) {
270 size_to_copy = buf_size;
271 } else {
272 size_to_copy = param->response_size - pos;
273 }
274
275 if (param->interrupt_response_mode != INTERRUPT_RESPONSE_DISABLED) {
276 if (pos >= param->interrupt_response_after_nbytes) {
277 return (ssize_t) MHD_CONTENT_READER_END_WITH_ERROR;
278 }
279
280 if (size_to_copy > (param->interrupt_response_after_nbytes - pos)) {
281 size_to_copy = param->interrupt_response_after_nbytes - pos;
282 }
283 }
284
285 memcpy (buf, param->response_data + pos, size_to_copy);
286 return size_to_copy;
287 }
288
_free_callback_param(void * cls)289 static void _free_callback_param(void *cls)
290 {
291 wget_free(cls);
292 }
293
294 #ifdef WITH_GNUTLS_OCSP
_ocsp_ahc(void * cls WGET_GCC_UNUSED,struct MHD_Connection * connection,const char * url WGET_GCC_UNUSED,const char * method WGET_GCC_UNUSED,const char * version WGET_GCC_UNUSED,const char * upload_data,size_t * upload_data_size,void ** con_cls WGET_GCC_UNUSED)295 static enum MHD_Result _ocsp_ahc(
296 void *cls WGET_GCC_UNUSED,
297 struct MHD_Connection *connection,
298 const char *url WGET_GCC_UNUSED,
299 const char *method WGET_GCC_UNUSED,
300 const char *version WGET_GCC_UNUSED,
301 const char *upload_data,
302 size_t *upload_data_size,
303 void **con_cls WGET_GCC_UNUSED)
304 {
305 static bool first = true;
306
307 if (first && upload_data == NULL) {
308 first = false;
309
310 return MHD_YES;
311 } else if (!first && upload_data == NULL) {
312 int ret = 0;
313
314 if (ocsp_resp->data) {
315 struct MHD_Response *response = MHD_create_response_from_buffer (ocsp_resp->size, ocsp_resp->data, MHD_RESPMEM_MUST_COPY);
316
317 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
318
319 MHD_destroy_response (response);
320
321 wget_xfree(ocsp_resp->data);
322 }
323
324 return ret;
325 }
326
327 *upload_data_size = 0;
328
329 return MHD_YES;
330 }
331
_ocsp_cert_callback(gnutls_session_t session WGET_GCC_UNUSED,const gnutls_datum_t * req_ca_dn WGET_GCC_UNUSED,int nreqs WGET_GCC_UNUSED,const gnutls_pk_algorithm_t * pk_algos WGET_GCC_UNUSED,int pk_algos_length WGET_GCC_UNUSED,gnutls_pcert_st ** pcert,unsigned int * pcert_length,gnutls_privkey_t * pkey)332 static int _ocsp_cert_callback(
333 gnutls_session_t session WGET_GCC_UNUSED,
334 const gnutls_datum_t* req_ca_dn WGET_GCC_UNUSED,
335 int nreqs WGET_GCC_UNUSED,
336 const gnutls_pk_algorithm_t* pk_algos WGET_GCC_UNUSED,
337 int pk_algos_length WGET_GCC_UNUSED,
338 gnutls_pcert_st** pcert,
339 unsigned int *pcert_length,
340 gnutls_privkey_t *pkey)
341 {
342 *pcert = pcrt;
343 *(pcert+1) = pcrt+1;
344 *pkey = *privkey;
345 *pcert_length = 2;
346
347 return 0;
348 }
349
350 #if MHD_VERSION >= 0x00096502 && GNUTLS_VERSION_NUMBER >= 0x030603
_ocsp_stap_cert_callback(gnutls_session_t session WGET_GCC_UNUSED,const struct gnutls_cert_retr_st * info WGET_GCC_UNUSED,gnutls_pcert_st ** certs,unsigned int * pcert_length,gnutls_ocsp_data_st ** ocsp,unsigned int * ocsp_length,gnutls_privkey_t * pkey,unsigned int * flags WGET_GCC_UNUSED)351 static int _ocsp_stap_cert_callback(
352 gnutls_session_t session WGET_GCC_UNUSED,
353 const struct gnutls_cert_retr_st *info WGET_GCC_UNUSED,
354 gnutls_pcert_st **certs,
355 unsigned int *pcert_length,
356 gnutls_ocsp_data_st **ocsp,
357 unsigned int *ocsp_length,
358 gnutls_privkey_t *pkey,
359 unsigned int *flags WGET_GCC_UNUSED)
360 {
361 *certs = pcrt;
362 *(certs+1) = pcrt+1;
363 *pcert_length = 2;
364
365 *pkey = *privkey;
366
367 *ocsp = ocsp_stap_resp;
368 *ocsp_length = 1;
369
370 return 0;
371 }
372 #endif
373 #endif
374
_answer_to_connection(void * cls WGET_GCC_UNUSED,struct MHD_Connection * connection,const char * url,const char * method,const char * version WGET_GCC_UNUSED,const char * upload_data WGET_GCC_UNUSED,size_t * upload_data_size WGET_GCC_UNUSED,void ** con_cls WGET_GCC_UNUSED)375 static enum MHD_Result _answer_to_connection(
376 void *cls WGET_GCC_UNUSED,
377 struct MHD_Connection *connection,
378 const char *url,
379 const char *method,
380 const char *version WGET_GCC_UNUSED,
381 const char *upload_data WGET_GCC_UNUSED,
382 size_t *upload_data_size WGET_GCC_UNUSED,
383 void **con_cls WGET_GCC_UNUSED)
384 {
385 #if MHD_VERSION >= 0x00096302 && GNUTLS_VERSION_NUMBER >= 0x030603
386 if (post_handshake_auth) {
387 gnutls_session_t tls_sess;
388 const union MHD_ConnectionInfo *conn_info = MHD_get_connection_info (connection, MHD_CONNECTION_INFO_GNUTLS_SESSION);
389
390 if (conn_info) {
391 int check_auth;
392 tls_sess = conn_info->tls_session;
393 gnutls_certificate_server_set_request(tls_sess, GNUTLS_CERT_REQUEST);
394 do
395 check_auth = gnutls_reauth(tls_sess, 0);
396 while (check_auth == GNUTLS_E_AGAIN);
397
398 *post_handshake_auth = (check_auth == GNUTLS_E_SUCCESS) ? CHECK_PASSED : CHECK_FAILED;
399 }
400 }
401 #endif
402
403 struct MHD_Response *response = NULL;
404 struct query_string query;
405 int ret = 0;
406 int64_t modified;
407 const char *modified_val, *to_bytes_string = "";
408 ssize_t from_bytes, to_bytes;
409 char content_len[100], content_range[100];
410
411 // whether or not this connection is HTTPS
412 bool https = !!MHD_get_connection_info(connection, MHD_CONNECTION_INFO_PROTOCOL);
413
414 // get query string
415 query.params = wget_buffer_alloc(1024);
416 query.it = 0;
417 MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, (MHD_KeyValueIterator)_print_query_string, &query);
418
419 // get if-modified-since header
420 modified_val = MHD_lookup_connection_value(connection, MHD_HEADER_KIND,
421 MHD_HTTP_HEADER_IF_MODIFIED_SINCE);
422 modified = 0;
423 if (modified_val)
424 modified = wget_http_parse_full_date(modified_val);
425
426 // get header range
427 wget_buffer *header_range = wget_buffer_alloc(1024);
428 if (!strcmp(method, "GET"))
429 MHD_get_connection_values(connection, MHD_HEADER_KIND, (MHD_KeyValueIterator)_print_header_range, header_range);
430
431 from_bytes = to_bytes = 0;
432 if (*header_range->data) {
433 const char *from_bytes_string;
434 const char *range_string = strchr(header_range->data, '=');
435
436 to_bytes_string = strchr(range_string, '-');
437 if (strcmp(to_bytes_string, "-"))
438 to_bytes = (ssize_t) atoi(to_bytes_string + 1);
439 from_bytes_string = wget_strmemdup(range_string, to_bytes_string - range_string);
440 from_bytes = (ssize_t) atoi(from_bytes_string + 1);
441 wget_xfree(from_bytes_string);
442 }
443
444 // append 'index.html' to directory and append query string
445 const char *url_full, *p;
446 if ((p = strrchr(url, '/')) && p[1] == 0) {
447 url_full = wget_aprintf("%sindex.html%s", url, query.params->data ? query.params->data : "");
448 } else {
449 url_full = wget_aprintf("%s%s", url, query.params->data ? query.params->data : "");
450 }
451 wget_buffer_free(&query.params);
452
453 // iterate over test urls array
454 bool found = false, chunked = false;
455 char *url_iri = NULL;
456
457 for (wget_test_url_t *request_url = urls; request_url < urls + nurls; request_url++) {
458 if (request_url->http_only && https)
459 continue;
460 if (request_url->https_only && !https)
461 continue;
462
463 // convert remote url into escaped char for iri encoding
464 wget_xfree(url_iri);
465 url_iri = wget_strdup(request_url->name);
466 MHD_http_unescape(url_iri);
467
468 if (!strcmp(_parse_hostname(url_full), _parse_hostname(url_iri))) {
469 size_t body_length =
470 request_url->body_len ? request_url->body_len
471 : (request_url->body ? strlen(request_url->body) : 0);
472
473 // check request headers
474 bool bad_request = false;
475
476 if (request_url->expected_method && strcmp(method, request_url->expected_method)) {
477 wget_debug_printf("%s: Expected request method '%s', but got '%s'\n",
478 __func__, request_url->expected_method, method);
479 bad_request = true;
480 }
481
482 for (const char **header = request_url->expected_req_headers; *header; header++) {
483 const char *header_value = strchr(*header, ':');
484 const char *header_key = wget_strmemdup(*header, header_value - *header);
485 const char *got_val = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, header_key);
486 wget_xfree(header_key);
487
488 // 400 Bad Request
489 if (!got_val || strcmp(got_val, header_value + 2)) {
490 wget_debug_printf("%s: Missing expected header '%s'\n", __func__, *header);
491 bad_request = true;
492 break;
493 }
494 }
495
496 // check unexpected headers
497 for (const char **header_key = request_url->unexpected_req_headers; *header_key; header_key++) {
498 const char *got_val = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, *header_key);
499
500 // 400 Bad Request
501 if (got_val) {
502 wget_debug_printf("%s: Got unexpected header '%s'\n", __func__, *header_key);
503 bad_request = true;
504 break;
505 }
506 }
507
508 // return with "400 Bad Request"
509 if (bad_request) {
510 response = MHD_create_response_from_buffer(0, (void *) "", MHD_RESPMEM_PERSISTENT);
511 ret = MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, response);
512 found = true;
513 break;
514 }
515
516 // chunked encoding
517 if (!wget_strcmp(request_url->name + 3, "bad.txt")) {
518 response = MHD_create_response_from_buffer(body_length,
519 (void *) request_url->body, MHD_RESPMEM_MUST_COPY);
520 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
521 MHD_add_response_header(response, "Transfer-Encoding", "chunked");
522 MHD_add_response_header(response, "Connection", "close");
523 found = true;
524 break;
525 }
526 for (const char **header = request_url->headers; *header; header++) {
527 const char *header_value = strchr(*header, ':');
528 const char *header_key = wget_strmemdup(*header, header_value - *header);
529 if (!strcmp(header_key, "Transfer-Encoding") && !strcmp(header_value + 2, "chunked"))
530 chunked = true;
531 wget_xfree(header_key);
532 }
533 if (chunked) {
534 struct ResponseContentCallbackParam *callback_param = wget_malloc(sizeof(struct ResponseContentCallbackParam));
535
536 callback_param->response_data = request_url->body;
537 callback_param->response_size = body_length;
538
539 response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN,
540 1024, _callback, callback_param, _free_callback_param);
541 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
542 found = true;
543 break;
544 }
545
546 // redirection
547 if (atoi(request_url->code)/100 == 3) {
548 response = MHD_create_response_from_buffer(0, (void *) "", MHD_RESPMEM_PERSISTENT);
549
550 // add available headers
551 for (const char **header = request_url->headers; *header; header++) {
552 const char *header_value = strchr(*header, ':');
553 const char *header_key = wget_strmemdup(*header, header_value - *header);
554 MHD_add_response_header(response, header_key, header_value + 2);
555 wget_xfree(header_key);
556 }
557 ret = MHD_queue_response(connection, atoi(request_url->code), response);
558 found = true;
559 break;
560 }
561
562 // 404 with non-empty "body"
563 if (atoi(request_url->code) != 200) {
564 response = MHD_create_response_from_buffer(body_length,
565 (void *) request_url->body, MHD_RESPMEM_MUST_COPY);
566 ret = MHD_queue_response(connection, atoi(request_url->code), response);
567 found = true;
568 break;
569 }
570
571 // basic authentication
572 if (!wget_strcmp(request_url->auth_method, "Basic")) {
573 char *pass = NULL;
574 char *user = MHD_basic_auth_get_username_password(connection, &pass);
575 if ((user == NULL && pass == NULL) ||
576 wget_strcmp(user, request_url->auth_username) ||
577 wget_strcmp(pass, request_url->auth_password))
578 {
579 response = MHD_create_response_from_buffer(strlen ("DENIED"),
580 (void *) "DENIED", MHD_RESPMEM_PERSISTENT);
581 ret = MHD_queue_basic_auth_fail_response(connection, "basic@example.com", response);
582 MHD_free(user);
583 MHD_free(pass);
584 found = true;
585 break;
586 }
587 MHD_free(user);
588 MHD_free(pass);
589 }
590
591 // digest authentication
592 if (!wget_strcmp(request_url->auth_method, "Digest")) {
593 const char *realm = "digest@example.com";
594 char *user = MHD_digest_auth_get_username(connection);
595 if (wget_strcmp(user, request_url->auth_username)) {
596 response = MHD_create_response_from_buffer(strlen ("DENIED"),
597 (void *) "DENIED", MHD_RESPMEM_PERSISTENT);
598 ret = MHD_queue_auth_fail_response(connection, realm, TEST_OPAQUE_STR, response, MHD_NO);
599 MHD_free(user);
600 found = true;
601 break;
602 }
603 ret = MHD_digest_auth_check(connection, realm, user, request_url->auth_password, 300);
604 MHD_free(user);
605 if ((ret == MHD_INVALID_NONCE) || (ret == MHD_NO)) {
606 response = MHD_create_response_from_buffer(strlen ("DENIED"),
607 (void *) "DENIED", MHD_RESPMEM_PERSISTENT);
608
609 if (response) {
610 ret = MHD_queue_auth_fail_response(connection, realm, TEST_OPAQUE_STR, response,
611 (ret == MHD_INVALID_NONCE) ? MHD_YES : MHD_NO);
612 found = true;
613 } else
614 ret = MHD_NO;
615
616 break;
617 }
618 }
619
620 if (modified && request_url->modified <= modified) {
621 response = MHD_create_response_from_buffer(0, (void *) "", MHD_RESPMEM_PERSISTENT);
622 ret = MHD_queue_response(connection, MHD_HTTP_NOT_MODIFIED, response);
623 }
624 else if (*header_range->data) {
625 if (!strcmp(to_bytes_string, "-"))
626 to_bytes = body_length - 1;
627
628 size_t body_len = to_bytes - from_bytes + 1;
629
630 if (from_bytes > to_bytes || from_bytes >= (int) body_length) {
631 response = MHD_create_response_from_buffer(0, (void *) "", MHD_RESPMEM_PERSISTENT);
632 ret = MHD_queue_response(connection, MHD_HTTP_RANGE_NOT_SATISFIABLE, response);
633 } else {
634 if (request_url->interrupt_response_mode != INTERRUPT_RESPONSE_DISABLED) {
635 struct ResponseContentCallbackParam *callback_param = wget_malloc(sizeof(struct ResponseContentCallbackParam));
636 callback_param->response_data = (void *) (request_url->body + from_bytes);
637 callback_param->response_size = body_len;
638 callback_param->interrupt_response_mode = request_url->interrupt_response_mode;
639 callback_param->interrupt_response_after_nbytes = request_url->interrupt_response_after_nbytes;
640
641 response = MHD_create_response_from_callback(body_len,
642 1024, _callback_interruptable, callback_param, _free_callback_param);
643 } else {
644 response = MHD_create_response_from_buffer(body_len,
645 (void *) (request_url->body + from_bytes), MHD_RESPMEM_MUST_COPY);
646 }
647 MHD_add_response_header(response, MHD_HTTP_HEADER_ACCEPT_RANGES, "bytes");
648 wget_snprintf(content_range, sizeof(content_range), "%zd-%zd/%zu", from_bytes, to_bytes, body_len);
649 MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_RANGE, content_range);
650 wget_snprintf(content_len, sizeof(content_len), "%zu", body_len);
651 MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_LENGTH, content_len);
652 ret = MHD_queue_response(connection, MHD_HTTP_PARTIAL_CONTENT, response);
653 }
654 } else {
655 if (request_url->interrupt_response_mode != INTERRUPT_RESPONSE_DISABLED) {
656 struct ResponseContentCallbackParam *callback_param = wget_malloc(sizeof(struct ResponseContentCallbackParam));
657 callback_param->response_data = request_url->body;
658 callback_param->response_size = body_length;
659 callback_param->interrupt_response_mode = request_url->interrupt_response_mode;
660 callback_param->interrupt_response_after_nbytes = request_url->interrupt_response_after_nbytes;
661
662 response = MHD_create_response_from_callback(body_length,
663 1024, _callback_interruptable, callback_param, _free_callback_param);
664 } else {
665 response = MHD_create_response_from_buffer(body_length, (void *) request_url->body, MHD_RESPMEM_MUST_COPY);
666 }
667
668 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
669 }
670
671 // switch off Content-Length sanity checks
672 #if MHD_VERSION >= 0x00096800
673 MHD_set_response_options(response,
674 MHD_RF_INSANITY_HEADER_CONTENT_LENGTH,
675 MHD_RO_END);
676 #endif
677
678 // add available headers
679 for (const char **header = request_url->headers; *header; header++) {
680 const char *header_value = strchr(*header, ':');
681 const char *header_key = wget_strmemdup(*header, header_value - *header);
682 MHD_add_response_header(response, header_key, header_value + 2);
683 wget_xfree(header_key);
684 }
685
686 found = true;
687 }
688 }
689
690 // 404 with empty "body"
691 if (!found) {
692 response = MHD_create_response_from_buffer(0, (void *) "", MHD_RESPMEM_PERSISTENT);
693 ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
694 }
695
696 wget_xfree(url_iri);
697 wget_xfree(url_full);
698 wget_buffer_free(&header_range);
699 char server_version[50];
700 wget_snprintf(server_version, sizeof(server_version), "Libmicrohttpd/%08x", (unsigned int) MHD_VERSION);
701 MHD_add_response_header(response, "Server", server_version);
702 MHD_destroy_response(response);
703 return ret;
704 }
705
_http_server_stop(void)706 static void _http_server_stop(void)
707 {
708 MHD_stop_daemon(httpdaemon);
709 MHD_stop_daemon(httpsdaemon);
710 MHD_stop_daemon(ocspdaemon);
711 MHD_stop_daemon(h2daemon);
712
713 wget_xfree(key_pem);
714 wget_xfree(cert_pem);
715
716 #ifdef WITH_GNUTLS_OCSP
717 gnutls_global_deinit();
718
719 if(ocsp_resp)
720 wget_free(ocsp_resp->data);
721
722 wget_xfree(ocsp_resp);
723 #endif
724 }
725
_check_to_accept(void * cls,WGET_GCC_UNUSED const struct sockaddr * addr,WGET_GCC_UNUSED socklen_t addrlen)726 static enum MHD_Result _check_to_accept(
727 void *cls,
728 WGET_GCC_UNUSED const struct sockaddr *addr,
729 WGET_GCC_UNUSED socklen_t addrlen)
730 {
731 int server_mode = (int) (ptrdiff_t) cls;
732
733 if (server_mode == HTTP_MODE)
734 return reject_http_connection ? MHD_NO : MHD_YES;
735
736 return reject_https_connection ? MHD_NO : MHD_YES;
737 }
738
_http_server_start(int SERVER_MODE)739 static int _http_server_start(int SERVER_MODE)
740 {
741 uint16_t port_num = 0;
742
743 if (SERVER_MODE == HTTP_MODE) {
744 static char rnd[8] = "realrnd"; // fixed 'random' value
745
746 httpdaemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY,
747 port_num, (MHD_AcceptPolicyCallback)_check_to_accept,
748 (void *) (ptrdiff_t) SERVER_MODE, (MHD_AccessHandlerCallback)_answer_to_connection, NULL,
749 MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof(rnd), rnd,
750 MHD_OPTION_NONCE_NC_SIZE, 300,
751 #if MHD_VERSION >= 0x00095400
752 MHD_OPTION_STRICT_FOR_CLIENT, 1,
753 #endif
754 #if MHD_VERSION >= 0x00096800
755 MHD_OPTION_SERVER_INSANITY, 1,
756 #endif
757 MHD_OPTION_END);
758
759 if (!httpdaemon)
760 return 1;
761 } else if (SERVER_MODE == HTTPS_MODE || SERVER_MODE == H2_MODE) {
762 size_t size;
763
764 if (!ocspdaemon) {
765 key_pem = wget_read_file(SRCDIR "/certs/x509-server-key.pem", &size);
766 cert_pem = wget_read_file(SRCDIR "/certs/x509-server-cert.pem", &size);
767
768 if ((key_pem == NULL) || (cert_pem == NULL))
769 {
770 wget_error_printf("The key/certificate files could not be read.\n");
771 return 1;
772 }
773
774 if (SERVER_MODE == HTTPS_MODE) {
775 httpsdaemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY | MHD_USE_TLS
776 #if MHD_VERSION >= 0x00096302
777 | MHD_USE_POST_HANDSHAKE_AUTH_SUPPORT
778 #endif
779 ,
780 port_num, (MHD_AcceptPolicyCallback)_check_to_accept,
781 (void *) (ptrdiff_t) SERVER_MODE, (MHD_AccessHandlerCallback)_answer_to_connection, NULL,
782 MHD_OPTION_HTTPS_MEM_KEY, key_pem,
783 MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
784 #if MHD_VERSION >= 0x00095400
785 MHD_OPTION_STRICT_FOR_CLIENT, 1,
786 #endif
787 #if MHD_VERSION >= 0x00096800
788 MHD_OPTION_SERVER_INSANITY, 1,
789 #endif
790 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) 1*1024*1024,
791 MHD_OPTION_END);
792
793 if (!httpsdaemon) {
794 wget_error_printf("Cannot start the HTTPS server.\n");
795 return 1;
796 }
797 }
798 else {
799 #ifdef HAVE_MICROHTTPD_HTTP2_H
800 h2daemon = MHD_start_daemon(MHD_USE_HTTP2 | MHD_USE_SELECT_INTERNALLY | MHD_USE_TLS
801 #if MHD_VERSION >= 0x00096302
802 | MHD_USE_POST_HANDSHAKE_AUTH_SUPPORT
803 #endif
804 ,
805 port_num, (MHD_AcceptPolicyCallback)_check_to_accept,
806 (void *) (ptrdiff_t) SERVER_MODE, (MHD_AccessHandlerCallback)_answer_to_connection, NULL,
807 MHD_OPTION_HTTPS_MEM_KEY, key_pem,
808 MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
809 #if MHD_VERSION >= 0x00095400
810 MHD_OPTION_STRICT_FOR_CLIENT, 1,
811 #endif
812 #if MHD_VERSION >= 0x00096800
813 MHD_OPTION_SERVER_INSANITY, 1,
814 #endif
815 //Enough to send 1MB files through
816 MHD_OPTION_CONNECTION_MEMORY_LIMIT, 1*1024*1024,
817 MHD_OPTION_END);
818 #endif
819
820 if (!h2daemon) {
821 wget_error_printf("Cannot start the h2 server.\n");
822 wget_error_printf("HTTP/2 support for MHD not found.\n");
823 return 1;
824 }
825 }
826 }
827 #ifdef WITH_GNUTLS_OCSP
828 else {
829 httpsdaemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY | MHD_USE_TLS
830 #if MHD_VERSION >= 0x00096302
831 | MHD_USE_POST_HANDSHAKE_AUTH_SUPPORT
832 #endif
833 ,
834 port_num, (MHD_AcceptPolicyCallback)_check_to_accept,
835 (void *) (ptrdiff_t) SERVER_MODE, (MHD_AccessHandlerCallback)_answer_to_connection, NULL,
836 MHD_OPTION_HTTPS_CERT_CALLBACK, _ocsp_cert_callback,
837 #if MHD_VERSION >= 0x00095400
838 MHD_OPTION_STRICT_FOR_CLIENT, 1,
839 #endif
840 #if MHD_VERSION >= 0x00096800
841 MHD_OPTION_SERVER_INSANITY, 1,
842 #endif
843 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) 1*1024*1024,
844 MHD_OPTION_END);
845
846 int rc;
847 gnutls_datum_t data;
848
849 privkey = wget_malloc(sizeof(gnutls_privkey_t));
850 gnutls_privkey_init(privkey);
851
852 if ((rc = gnutls_load_file(SRCDIR "/certs/ocsp/x509-server-key.pem", &data)) < 0)
853 file_load_err(SRCDIR "/certs/ocsp/x509-server-key.pem", gnutls_strerror(rc));
854
855 gnutls_privkey_import_x509_raw(*privkey, &data, GNUTLS_X509_FMT_PEM, NULL, 0);
856 gnutls_free(data.data);
857
858 pcrt = wget_malloc(sizeof(gnutls_pcert_st)*2);
859
860 if ((rc = gnutls_load_file(SRCDIR "/certs/ocsp/x509-server-cert.pem", &data)) < 0)
861 file_load_err(SRCDIR "/certs/ocsp/x509-server-cert.pem", gnutls_strerror(rc));
862
863 gnutls_pcert_import_x509_raw(pcrt, &data, GNUTLS_X509_FMT_PEM, 0);
864 gnutls_free(data.data);
865
866 if ((rc = gnutls_load_file(SRCDIR "/certs/ocsp/x509-interm-cert.pem", &data)) < 0)
867 file_load_err(SRCDIR "/certs/ocsp/x509-interm-cert.pem", gnutls_strerror(rc));
868
869 gnutls_pcert_import_x509_raw(pcrt+1, &data, GNUTLS_X509_FMT_PEM, 0);
870 gnutls_free(data.data);
871
872 if (!httpsdaemon) {
873 wget_error_printf("Cannot start the HTTPS server.\n");
874 return 1;
875 }
876
877 }
878 #endif
879 } else if (SERVER_MODE == OCSP_MODE) {
880 #ifdef WITH_GNUTLS_OCSP
881 static char rnd[8] = "realrnd"; // fixed 'random' value
882
883 ocspdaemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY,
884 port_num, NULL, NULL, (MHD_AccessHandlerCallback)_ocsp_ahc, NULL,
885 MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof(rnd), rnd,
886 MHD_OPTION_NONCE_NC_SIZE, 300,
887 #if MHD_VERSION >= 0x00095400
888 MHD_OPTION_STRICT_FOR_CLIENT, 1,
889 #endif
890 #if MHD_VERSION >= 0x00096800
891 MHD_OPTION_SERVER_INSANITY, 1,
892 #endif
893 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) 1*1024*1024,
894 MHD_OPTION_END);
895
896 ocsp_resp = wget_malloc(sizeof(struct ocsp_resp_t));
897 #endif
898
899 if (!ocspdaemon)
900 return 1;
901 }
902 #ifdef WITH_GNUTLS_OCSP
903 #if MHD_VERSION >= 0x00096502 && GNUTLS_VERSION_NUMBER >= 0x030603
904 else if (SERVER_MODE == OCSP_STAP_MODE) {
905 int rc;
906
907 gnutls_datum_t data;
908
909 /* Load private key */
910 privkey = wget_malloc(sizeof(gnutls_privkey_t));
911
912 gnutls_privkey_init(privkey);
913
914 if ((rc = gnutls_load_file(SRCDIR "/certs/ocsp/x509-server-key.pem", &data)) < 0)
915 file_load_err(SRCDIR "/certs/ocsp/x509-server-key.pem", gnutls_strerror(rc));
916
917 gnutls_privkey_import_x509_raw(*privkey, &data, GNUTLS_X509_FMT_PEM, NULL, 0);
918 gnutls_free(data.data);
919
920 /* Load certificate chain */
921 pcrt = wget_malloc(sizeof(gnutls_pcert_st) * 2);
922
923 if ((rc = gnutls_load_file(SRCDIR "/certs/ocsp/x509-server-cert.pem", &data)) < 0)
924 file_load_err(SRCDIR "/certs/ocsp/x509-server-cert.pem", gnutls_strerror(rc));
925
926 gnutls_pcert_import_x509_raw(pcrt, &data, GNUTLS_X509_FMT_PEM, 0);
927 gnutls_free(data.data);
928
929 if ((rc = gnutls_load_file(SRCDIR "/certs/ocsp/x509-interm-cert.pem", &data)) < 0)
930 file_load_err(SRCDIR "/certs/ocsp/x509-interm-cert.pem", gnutls_strerror(rc));
931
932 gnutls_pcert_import_x509_raw(pcrt+1, &data, GNUTLS_X509_FMT_PEM, 0);
933 gnutls_free(data.data);
934
935 /* Load stapled OCSP response */
936 ocsp_stap_resp = wget_malloc(sizeof(gnutls_ocsp_data_st));
937
938 if ((rc = gnutls_load_file(SRCDIR "/certs/ocsp/ocsp_stapled_resp.der", &data)) < 0)
939 file_load_err(SRCDIR "/certs/ocsp/ocsp_stapled_resp.der", gnutls_strerror(rc));
940
941 ocsp_stap_resp->response.data = data.data;
942 ocsp_stap_resp->response.size = data.size;
943 ocsp_stap_resp->exptime = 0;
944
945 /* Start HTTPS daemon with stapled OCSP responses */
946 httpsdaemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY | MHD_USE_TLS
947 | MHD_USE_POST_HANDSHAKE_AUTH_SUPPORT
948 ,
949 port_num, (MHD_AcceptPolicyCallback)_check_to_accept,
950 (void *) (ptrdiff_t) SERVER_MODE, (MHD_AccessHandlerCallback)_answer_to_connection, NULL,
951 MHD_OPTION_HTTPS_CERT_CALLBACK2, _ocsp_stap_cert_callback,
952 #if MHD_VERSION >= 0x00095400
953 MHD_OPTION_STRICT_FOR_CLIENT, 1,
954 #endif
955 #if MHD_VERSION >= 0x00096800
956 MHD_OPTION_SERVER_INSANITY, 1,
957 #endif
958 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) 1*1024*1024,
959 MHD_OPTION_END);
960 }
961 #endif
962 #endif
963
964 // get open random port number
965 if (0) {}
966 #if MHD_VERSION >= 0x00095501
967 else if (MHD_NO != MHD_is_feature_supported(MHD_FEATURE_AUTODETECT_BIND_PORT))
968 {
969 const union MHD_DaemonInfo *dinfo = NULL;
970 if (SERVER_MODE == HTTP_MODE)
971 dinfo = MHD_get_daemon_info(httpdaemon, MHD_DAEMON_INFO_BIND_PORT);
972 else if (SERVER_MODE == HTTPS_MODE || SERVER_MODE == OCSP_STAP_MODE)
973 dinfo = MHD_get_daemon_info(httpsdaemon, MHD_DAEMON_INFO_BIND_PORT);
974 #ifdef WITH_GNUTLS_OCSP
975 else if (SERVER_MODE == OCSP_MODE)
976 dinfo = MHD_get_daemon_info(ocspdaemon, MHD_DAEMON_INFO_BIND_PORT);
977 #endif
978 #ifdef HAVE_MICROHTTPD_HTTP2_H
979 else if (SERVER_MODE == H2_MODE)
980 dinfo = MHD_get_daemon_info(h2daemon, MHD_DAEMON_INFO_BIND_PORT);
981 #endif
982
983 if (!dinfo || dinfo->port == 0)
984 return 1;
985
986 port_num = dinfo->port;
987 if (SERVER_MODE == HTTP_MODE)
988 http_server_port = port_num;
989 else if (SERVER_MODE == HTTPS_MODE || SERVER_MODE == OCSP_STAP_MODE)
990 https_server_port = port_num;
991 #ifdef WITH_GNUTLS_OCSP
992 else if (SERVER_MODE == OCSP_MODE)
993 ocsp_server_port = port_num;
994 #endif
995 #ifdef HAVE_MICROHTTPD_HTTP2_H
996 else if (SERVER_MODE == H2_MODE) {
997 h2_server_port = port_num;
998 }
999 #endif
1000 }
1001 #endif /* MHD_VERSION >= 0x00095501 */
1002 else
1003 {
1004 const union MHD_DaemonInfo *dinfo = NULL;
1005 int sock_fd;
1006
1007 if (SERVER_MODE == HTTP_MODE)
1008 dinfo = MHD_get_daemon_info(httpdaemon, MHD_DAEMON_INFO_LISTEN_FD);
1009 else if (SERVER_MODE == HTTPS_MODE || SERVER_MODE == OCSP_STAP_MODE)
1010 dinfo = MHD_get_daemon_info(httpsdaemon, MHD_DAEMON_INFO_LISTEN_FD);
1011 #ifdef WITH_GNUTLS_OCSP
1012 else if (SERVER_MODE == OCSP_MODE)
1013 dinfo = MHD_get_daemon_info(ocspdaemon, MHD_DAEMON_INFO_LISTEN_FD);
1014 #endif
1015 #ifdef HAVE_MICROHTTPD_HTTP2_H
1016 else if (SERVER_MODE == H2_MODE)
1017 dinfo = MHD_get_daemon_info(h2daemon, MHD_DAEMON_INFO_LISTEN_FD);
1018 #endif
1019
1020 if (!dinfo)
1021 return 1;
1022 #ifdef _WIN32
1023 sock_fd = _open_osfhandle(dinfo->listen_fd, O_RDWR | O_BINARY);
1024 #else
1025 sock_fd = dinfo->listen_fd;
1026 #endif
1027
1028 struct sockaddr_storage addr_store;
1029 struct sockaddr *addr = (struct sockaddr *)&addr_store;
1030 socklen_t addr_len = sizeof(addr_store);
1031
1032 // get automatic retrieved port number
1033 if (getsockname(sock_fd, addr, &addr_len) == 0) {
1034 char s_port[NI_MAXSERV];
1035
1036 if (getnameinfo(addr, addr_len, NULL, 0, s_port, sizeof(s_port), NI_NUMERICSERV) == 0) {
1037 port_num = (uint16_t)atoi(s_port);
1038 if (SERVER_MODE == HTTP_MODE)
1039 http_server_port = port_num;
1040 else if (SERVER_MODE == HTTPS_MODE || SERVER_MODE == OCSP_STAP_MODE)
1041 https_server_port = port_num;
1042 #ifdef WITH_GNUTLS_OCSP
1043 else if (SERVER_MODE == OCSP_MODE)
1044 ocsp_server_port = port_num;
1045 #endif
1046
1047 #ifdef HAVE_MICROHTTPD_HTTP2_H
1048 else if (SERVER_MODE == H2_MODE)
1049 h2_server_port = port_num;
1050 #endif
1051 }
1052 }
1053 }
1054
1055 return 0;
1056 }
1057
1058 #if defined __CYGWIN__
1059 // Using opendir/readdir loop plus unlink() has a race condition
1060 // with CygWin. Not sure if this also happens on other systems as well.
1061 // Since we don't have valgrind, we can use system() without issues.
_remove_directory(const char * dirname)1062 static void _remove_directory(const char *dirname)
1063 {
1064 char cmd[strlen(dirname) + 16];
1065
1066 wget_snprintf(cmd, sizeof(cmd), "rm -rf %s", dirname);
1067 system(cmd);
1068 }
_empty_directory(const char * dirname)1069 static void _empty_directory(const char *dirname)
1070 {
1071 _remove_directory(dirname);
1072
1073 if (mkdir(dirname, 0755) != 0)
1074 wget_error_printf_exit("Failed to re-create directory (%d)\n", errno);
1075 }
1076 #else
1077 // To reduce the verbosity of 'valgrind --trace-children=yes' output,
1078 // we avoid system("rm -rf ...") calls.
1079 static void _remove_directory(const char *dirname);
_empty_directory(const char * dirname)1080 static void _empty_directory(const char *dirname)
1081 {
1082 DIR *dir;
1083 size_t dirlen = strlen(dirname);
1084
1085 if ((dir = opendir(dirname))) {
1086 struct dirent *dp;
1087
1088 while ((dp = readdir(dir))) {
1089 if (*dp->d_name == '.' && (dp->d_name[1] == 0 || (dp->d_name[1] == '.' && dp->d_name[2] == 0)))
1090 continue;
1091
1092 char fname[dirlen + 1 + strlen(dp->d_name) + 1];
1093 wget_snprintf(fname, sizeof(fname), "%s/%s", dirname, dp->d_name);
1094
1095 if (unlink(fname) == -1) {
1096 // in case fname is a directory glibc returns EISDIR but correct POSIX value would be EPERM.
1097 // MinGW + Wine returns EACCESS here.
1098 if (errno == EISDIR || errno == EPERM || errno == EACCES)
1099 _remove_directory(fname);
1100 else
1101 wget_error_printf("Failed to unlink %s (%d)\n", fname, errno);
1102 }
1103 }
1104
1105 closedir(dir);
1106
1107 wget_debug_printf("Removed test directory '%s'\n", dirname);
1108 } else if (errno != ENOENT)
1109 wget_error_printf("Failed to opendir %s (%d)\n", dirname, errno);
1110 }
1111
_remove_directory(const char * dirname)1112 static void _remove_directory(const char *dirname)
1113 {
1114 _empty_directory(dirname);
1115 if (rmdir(dirname) == -1 && errno != ENOENT)
1116 wget_error_printf("Failed to rmdir %s (%d)\n", dirname, errno);
1117 }
1118 #endif
1119
wget_test_stop_server(void)1120 void wget_test_stop_server(void)
1121 {
1122 // wget_vector_free(&response_headers);
1123 wget_vector_free(&request_urls);
1124
1125 for (wget_test_url_t *url = urls; url < urls + nurls; url++) {
1126 if (url->body_original) {
1127 wget_xfree(url->body);
1128 url->body_original = NULL;
1129 }
1130
1131 for (size_t it = 0; it < countof(url->headers); it++) {
1132 if (url->headers_original[it]) {
1133 wget_xfree(url->headers[it]);
1134 url->headers_original[it] = NULL;
1135 }
1136 }
1137 }
1138
1139 if (chdir("..") != 0)
1140 wget_error_printf("Failed to chdir ..\n");
1141
1142 if (!keep_tmpfiles)
1143 _remove_directory(tmpdir);
1144
1145 wget_global_deinit();
1146 _http_server_stop();
1147 }
1148
_insert_ports(const char * src)1149 static char *_insert_ports(const char *src)
1150 {
1151 if (!src || (!strstr(src, "{{port}}") && !strstr(src, "{{sslport}}") && !strstr(src, "{{ocspport}}")))
1152 return NULL;
1153
1154 size_t srclen = strlen(src) + 1;
1155 char *ret = wget_malloc(srclen);
1156 char *dst = ret;
1157
1158 while (*src) {
1159 if (*src == '{') {
1160 if (!strncmp(src, "{{port}}", 8)) {
1161 if (proto_pass == HTTP_1_1_PASS) {
1162 dst += wget_snprintf(dst, srclen - (dst - ret), "%d", http_server_port);
1163 }
1164 #ifdef HAVE_MICROHTTPD_HTTP2_H
1165 else {
1166 dst += wget_snprintf(dst, srclen - (dst - ret), "%d", reject_https_connection ? http_server_port : h2_server_port);
1167 }
1168 #endif
1169 src += 8;
1170 continue;
1171 }
1172 else if (!strncmp(src, "{{sslport}}", 11)) {
1173 if (proto_pass == HTTP_1_1_PASS) {
1174 dst += wget_snprintf(dst, srclen - (dst - ret), "%d", https_server_port);
1175 }
1176 #ifdef HAVE_MICROHTTPD_HTTP2_H
1177 else {
1178 dst += wget_snprintf(dst, srclen - (dst - ret), "%d", h2_server_port);
1179 }
1180 #endif
1181 src += 11;
1182 continue;
1183 }
1184 else if (!strncmp(src, "{{ocspport}}", 12)) {
1185 dst += wget_snprintf(dst, srclen - (dst - ret), "%d", ocsp_server_port);
1186 src += 12;
1187 continue;
1188 }
1189 }
1190
1191 *dst++ = *src++;
1192 }
1193 *dst = 0;
1194
1195 return ret;
1196 }
1197
_write_msg(const char * msg,size_t len)1198 static void _write_msg(const char *msg, size_t len)
1199 {
1200 #ifdef _WIN32
1201 fwrite(msg, 1, len, stderr);
1202 #else
1203 if (isatty(fileno(stderr))) {
1204 if (len && msg[len - 1] == '\n')
1205 len--;
1206
1207 wget_fprintf(stderr, "\033[33m%.*s\033[m\n", (int) len, msg);
1208 } else
1209 fwrite(msg, 1, len, stderr);
1210 #endif
1211 }
1212
wget_test_start_server(int first_key,...)1213 void wget_test_start_server(int first_key, ...)
1214 {
1215 int rc, key;
1216 va_list args;
1217 bool start_http = 1;
1218 #ifdef WITH_TLS
1219 bool start_https = 1;
1220 #ifdef WITH_GNUTLS_OCSP
1221 bool ocsp_stap = 0;
1222 bool start_ocsp = 0;
1223 #endif
1224 #ifdef HAVE_MICROHTTPD_HTTP2_H
1225 bool start_h2 = 1;
1226 #endif
1227 #endif
1228
1229 wget_global_init(
1230 WGET_DEBUG_FUNC, _write_msg,
1231 WGET_ERROR_FUNC, _write_msg,
1232 WGET_INFO_FUNC, _write_msg,
1233 0);
1234
1235 wget_debug_printf("MHD compiled with 0x%08x, linked with %s\n", (unsigned) MHD_VERSION, MHD_get_version());
1236 #if MHD_VERSION >= 0x00095400
1237 wget_debug_printf("MHD_OPTION_STRICT_FOR_CLIENT: yes\n");
1238 #else
1239 wget_debug_printf("MHD_OPTION_STRICT_FOR_CLIENT: no\n");
1240 #endif
1241 #if MHD_VERSION >= 0x00096800
1242 wget_debug_printf("MHD_OPTION_SERVER_INSANITY: yes\n");
1243 #else
1244 wget_debug_printf("MHD_OPTION_SERVER_INSANITY: no\n");
1245 #endif
1246 #ifdef HAVE_MICROHTTPD_HTTP2_H
1247 wget_debug_printf("HAVE_MICROHTTPD_HTTP2_H: yes\n");
1248 #else
1249 wget_debug_printf("HAVE_MICROHTTPD_HTTP2_H: no\n");
1250 #endif
1251 #ifdef HAVE_GNUTLS_OCSP_H
1252 wget_debug_printf("HAVE_GNUTLS_OCSP_H: yes\n");
1253 #else
1254 wget_debug_printf("HAVE_GNUTLS_OCSP_H: no\n");
1255 #endif
1256 wget_debug_printf("\n");
1257
1258 va_start(args, first_key);
1259 for (key = first_key; key; key = va_arg(args, int)) {
1260 switch (key) {
1261 /* case WGET_TEST_RESPONSE_BODY:
1262 response_body = va_arg(args, const char *);
1263 break;
1264 case WGET_TEST_RESPONSE_HEADER:
1265 if (!response_headers)
1266 response_headers = wget_vector_create(4,4,NULL);
1267 wget_vector_add_str(response_headers, va_arg(args, const char *));
1268 break;
1269 case WGET_TEST_RESPONSE_CODE:
1270 response_code = va_arg(args, const char *);
1271 break;
1272 */ case WGET_TEST_RESPONSE_URLS:
1273 urls = va_arg(args, wget_test_url_t *);
1274 nurls = va_arg(args, size_t);
1275 break;
1276 case WGET_TEST_SERVER_SEND_CONTENT_LENGTH:
1277 server_send_content_length = !!va_arg(args, int);
1278 break;
1279 case WGET_TEST_HTTPS_ONLY:
1280 start_http = 0;
1281 break;
1282 case WGET_TEST_HTTP_ONLY:
1283 #ifdef WITH_TLS
1284 start_https = 0;
1285 #ifdef HAVE_MICROHTTPD_HTTP2_H
1286 start_h2 = 0;
1287 #endif
1288 #endif
1289 break;
1290 case WGET_TEST_H2_ONLY:
1291 start_http = 0;
1292 #ifdef WITH_TLS
1293 start_https = 0;
1294 #endif
1295 break;
1296 case WGET_TEST_HTTP_REJECT_CONNECTIONS:
1297 reject_http_connection = 1;
1298 break;
1299 case WGET_TEST_HTTPS_REJECT_CONNECTIONS:
1300 reject_https_connection = 1;
1301 break;
1302 case WGET_TEST_FEATURE_MHD:
1303 break;
1304 case WGET_TEST_FEATURE_TLS:
1305 #if !defined WITH_TLS
1306 wget_error_printf("Test requires TLS. Skipping\n");
1307 exit(WGET_TEST_EXIT_SKIP);
1308 #endif
1309 break;
1310 case WGET_TEST_FEATURE_IDN:
1311 #if !defined WITH_LIBIDN && !defined WITH_LIBIDN2
1312 wget_error_printf("Support for LibIDN not found. Skipping\n");
1313 exit(WGET_TEST_EXIT_SKIP);
1314 #endif
1315 break;
1316 case WGET_TEST_FEATURE_PLUGIN:
1317 #ifndef PLUGIN_SUPPORT
1318 wget_error_printf("Plugin Support Disabled. Skipping\n");
1319 exit(WGET_TEST_EXIT_SKIP);
1320 #endif
1321 break;
1322 case WGET_TEST_FEATURE_OCSP:
1323 #if !defined WITH_GNUTLS_OCSP
1324 wget_error_printf("Test requires GnuTLS with OCSP support. Skipping\n");
1325 exit(WGET_TEST_EXIT_SKIP);
1326 #else
1327 start_http = 0;
1328 #ifdef HAVE_MICROHTTPD_HTTP2_H
1329 start_h2 = 0;
1330 #endif
1331 #ifdef WITH_TLS
1332 #ifdef WITH_GNUTLS_OCSP
1333 start_ocsp = 1;
1334 #endif
1335 #endif
1336 break;
1337 #endif
1338 case WGET_TEST_FEATURE_OCSP_STAPLING:
1339 #if !defined WITH_GNUTLS_OCSP || MHD_VERSION < 0x00096502 || GNUTLS_VERSION_NUMBER < 0x030603
1340 wget_error_printf("MHD or GnuTLS version insufficient. Skipping\n");
1341 exit(WGET_TEST_EXIT_SKIP);
1342 #else
1343 start_http = 0;
1344 #ifdef WITH_TLS
1345 start_https = 0;
1346 #endif
1347 #ifdef HAVE_MICROHTTPD_HTTP2_H
1348 start_h2 = 0;
1349 #endif
1350 #ifdef WITH_TLS
1351 #ifdef WITH_GNUTLS_OCSP
1352 ocsp_stap = 1;
1353 #endif
1354 #endif
1355 break;
1356 #endif
1357 case WGET_TEST_SKIP_H2:
1358 #ifdef HAVE_MICROHTTPD_HTTP2_H
1359 start_h2 = 0;
1360 #endif
1361 break;
1362 default:
1363 wget_error_printf("Unknown option %d\n", key);
1364 }
1365 }
1366 va_end(args);
1367
1368 atexit(wget_test_stop_server);
1369
1370 wget_snprintf(tmpdir, sizeof(tmpdir), ".test_%d", (int) getpid());
1371
1372 // remove tmpdir if exists from previous tests
1373 _remove_directory(tmpdir);
1374
1375 if (mkdir(tmpdir, 0755) != 0)
1376 wget_error_printf_exit("Failed to create tmpdir (%d)\n", errno);
1377
1378 if (chdir(tmpdir) != 0)
1379 wget_error_printf_exit("Failed to change to tmpdir (%d)\n", errno);
1380
1381 // start HTTP server
1382 if (start_http) {
1383 if ((rc = _http_server_start(HTTP_MODE)) != 0)
1384 wget_error_printf_exit("Failed to start HTTP server, error %d\n", rc);
1385 }
1386
1387 #ifdef WITH_TLS
1388 #ifdef WITH_GNUTLS_OCSP
1389 // start OCSP responder
1390 if (start_ocsp) {
1391 if ((rc = _http_server_start(OCSP_MODE)) != 0)
1392 wget_error_printf_exit("Failed to start OCSP server, error %d\n", rc);
1393 }
1394
1395 // start OCSP server (stapling)
1396 if (ocsp_stap) {
1397 if ((rc = _http_server_start(OCSP_STAP_MODE)) != 0)
1398 wget_error_printf_exit("Failed to start OCSP Stapling server, error %d\n", rc);
1399 }
1400 #endif
1401
1402 // start HTTPS server
1403 if (start_https) {
1404 if ((rc = _http_server_start(HTTPS_MODE)) != 0)
1405 wget_error_printf_exit("Failed to start HTTPS server, error %d\n", rc);
1406 }
1407
1408 #ifdef HAVE_MICROHTTPD_HTTP2_H
1409 // start h2 server
1410 if (start_h2) {
1411 if ((rc = _http_server_start(H2_MODE)) != 0)
1412 wget_error_printf_exit("Failed to start h2 server, error %d\n", rc);
1413 }
1414 #endif
1415 #endif
1416 }
1417
_scan_for_unexpected(const char * dirname,const wget_test_file_t * expected_files)1418 static void _scan_for_unexpected(const char *dirname, const wget_test_file_t *expected_files)
1419 {
1420 DIR *dir;
1421 struct stat st;
1422 size_t dirlen = strlen(dirname);
1423
1424 wget_info_printf("Entering %s\n", dirname);
1425
1426 if ((dir = opendir(dirname))) {
1427 struct dirent *dp;
1428
1429 while ((dp = readdir(dir))) {
1430 char fname[dirlen + 1 + strlen(dp->d_name) + 1];
1431
1432 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1433 continue;
1434
1435 if (*dirname == '.' && dirname[1] == 0)
1436 wget_snprintf(fname, sizeof(fname), "%s", dp->d_name);
1437 else
1438 wget_snprintf(fname, sizeof(fname), "%s/%s", dirname, dp->d_name);
1439
1440 wget_info_printf(" - %s/%s\n", dirname, dp->d_name);
1441 if (stat(fname, &st) == 0 && S_ISDIR(st.st_mode)) {
1442 _scan_for_unexpected(fname, expected_files);
1443 continue;
1444 }
1445
1446 if (expected_files) {
1447 // Mac OS X converts to NFD, so we might find an unexpected file name, e.g. when using accents.
1448 // Example: cedilla (%C3%A7) will be converted to c+composed_cedilla (%63%CC%A7)
1449 // Since there are a few pitfalls with Apple's NFD, just skip the check here.
1450 #if !(defined __APPLE__ && defined __MACH__)
1451 size_t it;
1452
1453 wget_info_printf("search %s\n", fname);
1454
1455 for (it = 0; expected_files[it].name; it++) {
1456 #ifdef _WIN32
1457 char buf[strlen(expected_files[it].name) * 3 + 1];
1458 const char *restricted_fname = wget_restrict_file_name(expected_files[it].name, buf,
1459 expected_files[it].restricted_mode ? expected_files[it].restricted_mode : WGET_RESTRICT_NAMES_WINDOWS);
1460 #else
1461 const char *restricted_fname = expected_files[it].name;
1462 #endif
1463 /*
1464 {
1465 char b[256];
1466 if (it==0) {
1467 wget_memtohex(fname, strlen(fname), b, sizeof(b));
1468 wget_debug_printf("f %s\n", b);
1469 }
1470 wget_memtohex(restricted_fname, strlen(restricted_fname), b, sizeof(b));
1471 wget_debug_printf("r %s\n", b);
1472 }
1473 */
1474 if (!strcmp(restricted_fname, fname))
1475 break;
1476 }
1477
1478 if (!expected_files[it].name)
1479 wget_error_printf_exit("Unexpected file %s/%s found\n", tmpdir, fname);
1480 #endif
1481 } else
1482 wget_error_printf_exit("Unexpected file %s/%s found\n", tmpdir, fname);
1483 }
1484
1485 closedir(dir);
1486 } else
1487 wget_error_printf_exit("Failed to diropen %s\n", dirname);
1488 }
1489
1490 static const char *global_executable;
wget_test_set_executable(const char * program)1491 void wget_test_set_executable(const char *program)
1492 {
1493 global_executable = program;
1494 }
1495
wget_test(int first_key,...)1496 void wget_test(int first_key, ...)
1497 {
1498 #if !defined WITH_LIBNGHTTP2 || !defined HAVE_MICROHTTPD_HTTP2_H
1499 if (!httpdaemon && !httpsdaemon)
1500 exit(WGET_TEST_EXIT_SKIP);
1501 #endif
1502
1503 for (proto_pass = 0; proto_pass < END_PASS; proto_pass++) {
1504 if (proto_pass == HTTP_1_1_PASS && !httpdaemon && !httpsdaemon)
1505 continue;
1506
1507 if (proto_pass == H2_PASS) {
1508 #ifndef WITH_LIBNGHTTP2
1509 continue;
1510 #endif
1511 if (!h2daemon)
1512 continue;
1513 }
1514
1515 // now replace {{port}} in the body by the actual server port
1516 for (wget_test_url_t *url = urls; url < urls + nurls; url++) {
1517 char *p = _insert_ports(url->body);
1518
1519 if (p) {
1520 url->body_original = url->body;
1521 url->body = p;
1522 }
1523
1524 for (unsigned it = 0; it < countof(url->headers) && url->headers[it]; it++) {
1525 p = _insert_ports(url->headers[it]);
1526
1527 if (p) {
1528 url->headers_original[it] = url->headers[it];
1529 url->headers[it] = p;
1530 }
1531 }
1532 }
1533
1534 const char
1535 *request_url,
1536 *options = "",
1537 #ifdef WITH_GNUTLS_OCSP
1538 *ocsp_resp_file = NULL,
1539 #endif
1540 *executable = global_executable;
1541 const wget_test_file_t
1542 *expected_files = NULL,
1543 *existing_files = NULL;
1544 wget_buffer
1545 *cmd = wget_buffer_alloc(1024);
1546 unsigned
1547 it;
1548 int
1549 key,
1550 fd,
1551 rc,
1552 expected_error_code2 = -1,
1553 expected_error_code = 0;
1554 va_list
1555 args;
1556 char
1557 server_send_content_length_old = server_send_content_length;
1558 bool
1559 options_alloc = 0;
1560
1561 if (!executable) {
1562 #ifdef _WIN32
1563 if (proto_pass == H2_PASS)
1564 executable = BUILDDIR "\\..\\src\\wget2_noinstall" EXEEXT " -d --no-config --no-local-db --max-threads=1 --prefer-family=ipv4 --no-proxy --timeout 3 --tries=1 --https-enforce=hard --ca-certificate=" SRCDIR "/certs/x509-ca-cert.pem --no-ocsp";
1565 else
1566 executable = BUILDDIR "\\..\\src\\wget2_noinstall" EXEEXT " -d --no-config --no-local-db --max-threads=1 --prefer-family=ipv4 --no-proxy --timeout 3 --tries=1";
1567 #else
1568 if (proto_pass == H2_PASS)
1569 executable = BUILDDIR "/../src/wget2_noinstall" EXEEXT " -d --no-config --no-local-db --max-threads=1 --prefer-family=ipv4 --no-proxy --timeout 3 --tries=1 --https-enforce=hard --ca-certificate=" SRCDIR "/certs/x509-ca-cert.pem --no-ocsp";
1570 else
1571 executable = BUILDDIR "/../src/wget2_noinstall" EXEEXT " -d --no-config --no-local-db --max-threads=1 --prefer-family=ipv4 --no-proxy --timeout 3 --tries=1";
1572 #endif
1573 }
1574
1575 keep_tmpfiles = 0;
1576 clean_directory = 1;
1577
1578 if (!request_urls) {
1579 request_urls = wget_vector_create(8, NULL);
1580 wget_vector_set_destructor(request_urls, NULL);
1581 }
1582
1583 va_start (args, first_key);
1584 for (key = first_key; key; key = va_arg(args, int)) {
1585 switch (key) {
1586 case WGET_TEST_REQUEST_URL:
1587 if ((request_url = va_arg(args, const char *)))
1588 wget_vector_add(request_urls, request_url);
1589 break;
1590 case WGET_TEST_REQUEST_URLS:
1591 while ((request_url = va_arg(args, const char *)))
1592 wget_vector_add(request_urls, request_url);
1593 break;
1594 case WGET_TEST_EXPECTED_ERROR_CODE:
1595 expected_error_code = va_arg(args, int);
1596 break;
1597 case WGET_TEST_EXPECTED_ERROR_CODE2:
1598 expected_error_code2 = va_arg(args, int);
1599 break;
1600 case WGET_TEST_EXPECTED_FILES:
1601 expected_files = va_arg(args, const wget_test_file_t *);
1602 break;
1603 case WGET_TEST_EXISTING_FILES:
1604 existing_files = va_arg(args, const wget_test_file_t *);
1605 break;
1606 case WGET_TEST_OPTIONS:
1607 {
1608 options = va_arg(args, const char *);
1609 const char *tmp = _insert_ports(options);
1610 if (tmp) {
1611 options = tmp;
1612 options_alloc = 1;
1613 }
1614 break;
1615 }
1616 case WGET_TEST_KEEP_TMPFILES:
1617 keep_tmpfiles = va_arg(args, int);
1618 break;
1619 case WGET_TEST_CLEAN_DIRECTORY:
1620 clean_directory = va_arg(args, int);
1621 break;
1622 case WGET_TEST_EXECUTABLE:
1623 executable = va_arg(args, const char *);
1624 break;
1625 case WGET_TEST_SERVER_SEND_CONTENT_LENGTH:
1626 server_send_content_length = !!va_arg(args, int);
1627 break;
1628 case WGET_TEST_POST_HANDSHAKE_AUTH:
1629 if (va_arg(args, int)) {
1630 #if MHD_VERSION >= 0x00096302 && GNUTLS_VERSION_NUMBER >= 0x030603
1631 post_handshake_auth = wget_malloc(sizeof(enum CHECK_POST_HANDSHAKE_AUTH));
1632 #endif
1633 }
1634 break;
1635 case WGET_TEST_OCSP_RESP_FILE:
1636 #ifdef WITH_GNUTLS_OCSP
1637 ocsp_resp_file = va_arg(args, const char *);
1638 #endif
1639 break;
1640 default:
1641 wget_error_printf_exit("Unknown option %d [%s]\n", key, options);
1642 }
1643 }
1644 va_end(args);
1645
1646 if (clean_directory) {
1647 // clean directory
1648 wget_buffer_printf(cmd, "../%s", tmpdir);
1649 _empty_directory(cmd->data);
1650 }
1651
1652 #ifdef WITH_GNUTLS_OCSP
1653 if (ocspdaemon) {
1654 if (ocsp_resp_file) {
1655 ocsp_resp->data = wget_read_file(ocsp_resp_file, &(ocsp_resp->size));
1656 if (ocsp_resp->data == NULL) {
1657 wget_error_printf_exit("Couldn't read the response.\n");
1658 }
1659 } else {
1660 wget_error_printf_exit("Need value for option WGET_TEST_OCSP_RESP_FILE.\n");
1661 }
1662 }
1663 #endif
1664
1665 // create files
1666 if (existing_files) {
1667 for (it = 0; existing_files[it].name; it++) {
1668 mkdir_path(existing_files[it].name, 1);
1669
1670 if (existing_files[it].hardlink) {
1671 if (link(existing_files[it].hardlink, existing_files[it].name) != 0) {
1672 wget_error_printf_exit("Failed to link %s/%s -> %s/%s [%s]\n",
1673 tmpdir, existing_files[it].hardlink,
1674 tmpdir, existing_files[it].name, options);
1675 }
1676 }
1677 else if ((fd = open(existing_files[it].name, O_CREAT|O_WRONLY|O_TRUNC|O_BINARY, 0644)) != -1) {
1678 const char *existing_content = _insert_ports(existing_files[it].content);
1679 if (!existing_content)
1680 existing_content = existing_files[it].content;
1681
1682 ssize_t nbytes = write(fd, existing_content, strlen(existing_content));
1683 close(fd);
1684
1685 if (nbytes != (ssize_t)strlen(existing_content))
1686 wget_error_printf_exit("Failed to write %zu bytes to file %s/%s [%s]\n",
1687 strlen(existing_content), tmpdir, existing_files[it].name, options);
1688
1689 if (existing_files[it].timestamp) {
1690 // take the old utime() instead of utimes()
1691 if (utime(existing_files[it].name, &(struct utimbuf){ 0, existing_files[it].timestamp }))
1692 wget_error_printf_exit("Failed to set mtime of %s/%s [%s]\n",
1693 tmpdir, existing_files[it].name, options);
1694 }
1695
1696 if (existing_content != existing_files[it].content)
1697 wget_xfree(existing_content);
1698
1699 } else {
1700 wget_error_printf_exit("Failed to write open file %s/%s [%s] (%d,%s)\n",
1701 tmpdir, *existing_files[it].name == '/' ? existing_files[it].name + 1 : existing_files[it].name , options,
1702 errno, strerror(errno));
1703 }
1704 }
1705 }
1706
1707 const char *valgrind = getenv("VALGRIND_TESTS");
1708 if (!valgrind || !*valgrind || !strcmp(valgrind, "0")) {
1709 // On some system we get random IP order (v4, v6) for localhost, so we need --prefer-family for testing since
1710 // the test servers will listen only on the first IP and also prefers IPv4
1711 const char *emulator = getenv("EMULATOR");
1712 if (emulator && *emulator)
1713 wget_buffer_printf(cmd, "%s %s %s", emulator, executable, options);
1714 else
1715 wget_buffer_printf(cmd, "%s %s", executable, options);
1716 } else if (!strcmp(valgrind, "1")) {
1717 wget_buffer_printf(cmd, "valgrind --error-exitcode=301 --leak-check=yes --show-reachable=yes --track-origins=yes --child-silent-after-fork=yes --suppressions=" SRCDIR "/valgrind-suppressions %s %s", executable, options);
1718 } else
1719 wget_buffer_printf(cmd, "%s %s %s", valgrind, executable, options);
1720
1721 for (it = 0; it < (size_t)wget_vector_size(request_urls); it++) {
1722 request_url = wget_vector_get(request_urls, it);
1723
1724 if (!wget_strncasecmp_ascii(request_url, "http://", 7)
1725 || !wget_strncasecmp_ascii(request_url, "https://", 8))
1726 {
1727 char *tmp = _insert_ports(request_url);
1728 wget_buffer_printf_append(cmd, " \"%s\"", tmp ? tmp : request_url);
1729 wget_xfree(tmp);
1730 } else {
1731 if (proto_pass == HTTP_1_1_PASS) {
1732 wget_buffer_printf_append(cmd, " \"http://localhost:%d/%s\"",
1733 http_server_port, request_url);
1734 }
1735 #ifdef HAVE_MICROHTTPD_HTTP2_H
1736 else {
1737 wget_buffer_printf_append(cmd, " \"https://localhost:%d/%s\"",
1738 h2_server_port, request_url);
1739 }
1740 #endif
1741 }
1742 }
1743
1744 wget_buffer_strcat(cmd, " 2>&1");
1745
1746 wget_error_printf("\n##### Testing '%s'\n", cmd->data);
1747
1748 // catch stdout and write to stderr so all output is in sync
1749 FILE *pp;
1750 if ((pp = popen(cmd->data, "r"))) {
1751 char buf[4096];
1752
1753 while (fgets(buf, sizeof(buf), pp)) {
1754 fputs(buf, stderr);
1755 fflush(stderr);
1756 }
1757
1758 rc = pclose(pp);
1759 } else
1760 wget_error_printf_exit("Failed to execute test (%d) [%s]\n", errno, options);
1761 /*
1762 rc = system(cmd->data);
1763 */
1764 if (!WIFEXITED(rc)) {
1765 wget_error_printf_exit("Unexpected error code %d, expected %d [%s]\n", rc, expected_error_code, options);
1766 }
1767 else if (WEXITSTATUS(rc) != expected_error_code) {
1768 if (expected_error_code2 >= 0) {
1769 if (WEXITSTATUS(rc) != expected_error_code2)
1770 wget_error_printf_exit("Unexpected error code %d, expected %d or %d [%s]\n",
1771 WEXITSTATUS(rc), expected_error_code, expected_error_code2, options);
1772 }
1773 else
1774 wget_error_printf_exit("Unexpected error code %d, expected %d [%s]\n",
1775 WEXITSTATUS(rc), expected_error_code, options);
1776 }
1777
1778 if (expected_files) {
1779 for (it = 0; expected_files[it].name; it++) {
1780 struct stat st;
1781 #ifdef _WIN32
1782 char buf[strlen(expected_files[it].name) * 3 + 1];
1783 const char *fname = wget_restrict_file_name(expected_files[it].name, buf,
1784 expected_files[it].restricted_mode ? expected_files[it].restricted_mode : WGET_RESTRICT_NAMES_WINDOWS);
1785 #else
1786 const char *fname = expected_files[it].name;
1787 #endif
1788
1789 if (stat(fname, &st) != 0)
1790 wget_error_printf_exit("Missing expected file '%s/%s' [%s]\n", tmpdir, fname, options);
1791
1792 if (expected_files[it].content) {
1793 size_t nbytes;
1794 char *content = wget_read_file(fname, &nbytes);
1795
1796 if (content) {
1797 const char *expected_content = _insert_ports(expected_files[it].content);
1798 bool expected_content_alloc = 0;
1799
1800 if (!expected_content)
1801 expected_content = expected_files[it].content;
1802 else
1803 expected_content_alloc = 1;
1804
1805 size_t content_length = expected_files[it].content_length ? expected_files[it].content_length : strlen(expected_content);
1806
1807 if (content_length != nbytes || memcmp(expected_content, content, nbytes) != 0) {
1808 wget_error_printf("Unexpected content in %s [%s]\n", fname, options);
1809 wget_error_printf(" Expected %zu bytes:\n%s\n", content_length, expected_content);
1810 wget_error_printf(" Got %zu bytes:\n%s\n", nbytes, content);
1811 exit(EXIT_FAILURE);
1812 }
1813
1814 if (expected_content_alloc)
1815 wget_xfree(expected_content);
1816 }
1817
1818 wget_xfree(content);
1819 }
1820
1821 if (expected_files[it].timestamp && st.st_mtime != expected_files[it].timestamp)
1822 wget_error_printf_exit("Unexpected timestamp '%s/%s' (%ld) [%s]\n", tmpdir, fname, st.st_mtime, options);
1823 }
1824 }
1825
1826 // look if there are unexpected files in our working dir
1827 _scan_for_unexpected(".", expected_files);
1828
1829 #if MHD_VERSION >= 0x00096302 && GNUTLS_VERSION_NUMBER >= 0x030603
1830 if (post_handshake_auth && *post_handshake_auth == CHECK_FAILED) {
1831 wget_free(post_handshake_auth);
1832 wget_error_printf_exit("Post-handshake authentication failed\n");
1833 } else if (post_handshake_auth)
1834 wget_free(post_handshake_auth);
1835 #endif
1836
1837 wget_vector_clear(request_urls);
1838 wget_buffer_free(&cmd);
1839
1840 if (options_alloc)
1841 wget_xfree(options);
1842
1843 server_send_content_length = server_send_content_length_old;
1844
1845 // system("ls -la");
1846
1847 // cleanup for next iteration
1848 for (wget_test_url_t *url = urls; url < urls + nurls; url++) {
1849 if (url->body_original) {
1850 wget_xfree(url->body);
1851 url->body = url->body_original;
1852 url->body_original = NULL;
1853 }
1854
1855 for (it = 0; it < countof(url->headers) && url->headers[it]; it++) {
1856 if (url->headers_original[it]) {
1857 wget_xfree(url->headers[it]);
1858 url->headers[it] = url->headers_original[it];
1859 url->headers_original[it] = NULL;
1860 }
1861 }
1862 }
1863 }
1864 }
1865
wget_test_get_http_server_port(void)1866 int wget_test_get_http_server_port(void)
1867 {
1868 return proto_pass == H2_PASS ? h2_server_port : http_server_port;
1869 }
1870
wget_test_get_https_server_port(void)1871 int wget_test_get_https_server_port(void)
1872 {
1873 return proto_pass == H2_PASS ? h2_server_port : https_server_port;
1874 }
1875
wget_test_get_h2_server_port(void)1876 int wget_test_get_h2_server_port(void)
1877 {
1878 #ifndef HAVE_MICROHTTPD_HTTP2_H
1879 return -1;
1880 #else
1881 return h2_server_port;
1882 #endif
1883 }
1884
wget_test_get_ocsp_server_port(void)1885 int wget_test_get_ocsp_server_port(void)
1886 {
1887 return ocsp_server_port;
1888 }
1889
1890 // assume that we are in 'tmpdir'
wget_test_check_file_system(void)1891 int wget_test_check_file_system(void)
1892 {
1893 static char fname[3][3] = { "Ab", "ab", "AB" };
1894 char buf[sizeof(fname[0])];
1895 int flags = 0, fd;
1896 ssize_t rc;
1897
1898 _empty_directory(tmpdir);
1899
1900 // Create 3 files with differently cased names with different content.
1901 // On a case-mangling file system like HFS+ there will be just one file with the contents of the last write.
1902 for (unsigned it = 0; it < countof(fname); it++) {
1903 if ((fd = open(fname[it], O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0644)) != -1) {
1904 rc = write(fd, fname[it], sizeof(fname[0]));
1905 close(fd);
1906
1907 if (rc != sizeof(fname[0])) {
1908 wget_debug_printf("%s: Failed to write to '%s/%s' (%d) %zd %zu\n", __func__, tmpdir, fname[it], errno, rc, sizeof(fname[0]));
1909 goto out;
1910 }
1911 } else {
1912 wget_debug_printf("%s: Failed to write open '%s/%s'\n", __func__, tmpdir, fname[it]);
1913 goto out;
1914 }
1915 }
1916
1917 // Check file content to see if FS is case-mangling
1918 for (unsigned it = 0; it < countof(fname); it++) {
1919 if ((fd = open(fname[it], O_RDONLY | O_BINARY, 0644)) != -1) {
1920 rc = read(fd, buf, sizeof(fname[0]));
1921 close(fd);
1922
1923 if (rc != sizeof(fname[0])) {
1924 wget_debug_printf("%s: Failed to read from '%s/%s'\n", __func__, tmpdir, fname[it]);
1925 goto out;
1926 }
1927
1928 if (memcmp(buf, fname[it], sizeof(fname[0]))) {
1929 wget_debug_printf("%s: Found case-mangling file system\n", __func__);
1930 flags = WGET_TEST_FS_CASEMATTERS;
1931 goto out; // we can stop here
1932 }
1933 } else {
1934 wget_debug_printf("%s: Failed to read open '%s/%s'\n", __func__, tmpdir, fname[it]);
1935 goto out;
1936 }
1937 }
1938
1939 wget_debug_printf("%s: Found case-respecting file system\n", __func__);
1940
1941 out:
1942 _empty_directory(tmpdir);
1943
1944 return flags;
1945 }
1946