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