1 /*
2  * Copyright (c) 2018 Linaro Limited
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdbool.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <ctype.h>
12 #include <errno.h>
13 
14 #include "mbedtls/md.h"
15 
16 #if !defined(__ZEPHYR__) || defined(CONFIG_POSIX_API)
17 
18 #include <netinet/in.h>
19 #include <sys/socket.h>
20 #include <arpa/inet.h>
21 #include <unistd.h>
22 #include <netdb.h>
23 
24 #endif
25 
26 #if defined(__ZEPHYR__)
27 
28 #include <zephyr/net/socket.h>
29 #include <zephyr/kernel.h>
30 
31 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
32 #include <zephyr/net/tls_credentials.h>
33 #include "ca_certificate.h"
34 #endif
35 
36 #define sleep(x) k_sleep(K_MSEC((x) * MSEC_PER_SEC))
37 
38 #endif
39 
40 #define bytes2KiB(Bytes)	(Bytes / (1024u))
41 #define bytes2MiB(Bytes)	(Bytes / (1024u * 1024u))
42 
43 #if defined(CONFIG_SAMPLE_BIG_HTTP_DL_MAX_URL_LENGTH)
44 #define MAX_URL_LENGTH CONFIG_SAMPLE_BIG_HTTP_DL_MAX_URL_LENGTH
45 #else
46 #define MAX_URL_LENGTH 256
47 #endif
48 
49 #if defined(CONFIG_SAMPLE_BIG_HTTP_DL_NUM_ITER)
50 #define NUM_ITER CONFIG_SAMPLE_BIG_HTTP_DL_NUM_ITER
51 #else
52 #define NUM_ITER 0
53 #endif
54 
55 /* This URL is parsed in-place, so buffer must be non-const. */
56 static char download_url[MAX_URL_LENGTH] =
57 #if defined(CONFIG_SAMPLE_BIG_HTTP_DL_URL)
58     CONFIG_SAMPLE_BIG_HTTP_DL_URL;
59 #else
60     "http://archive.ubuntu.com/ubuntu/dists/xenial/main/installer-amd64/current/images/hd-media/vmlinuz";
61 #endif /* defined(CONFIG_SAMPLE_BIG_HTTP_DL_URL) */
62 /* Quick testing. */
63 /*    "http://google.com/foo";*/
64 
65 /* print("".join(["\\x%02x" % x for x in list(binascii.unhexlify("hash"))])) */
66 static uint8_t download_hash[32] =
67 #if !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
68 "\x33\x7c\x37\xd7\xec\x00\x34\x84\x14\x22\x4b\xaa\x6b\xdb\x2d\x43\xf2\xa3\x4e\xf5\x67\x6b\xaf\xcd\xca\xd9\x16\xf1\x48\xb5\xb3\x17";
69 #else
70 "\x3a\x07\x55\xdd\x1c\xfa\xb7\x1a\x24\xdd\x96\xdf\x34\x98\xc2\x9c"
71 "\xd0\xac\xd1\x3b\x04\xf3\xd0\x8b\xf9\x33\xe8\x12\x86\xdb\x80\x2c";
72 #endif /* !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) */
73 
74 #define SSTRLEN(s) (sizeof(s) - 1)
75 #define CHECK(r) { if (r == -1) { printf("Error: " #r "\n"); exit(1); } }
76 
77 const char *host;
78 const char *port;
79 const char *uri_path = "";
80 static char response[1024];
81 static char response_hash[32];
82 mbedtls_md_context_t hash_ctx;
83 const mbedtls_md_info_t *hash_info;
84 unsigned int cur_bytes;
85 
dump_addrinfo(const struct addrinfo * ai)86 void dump_addrinfo(const struct addrinfo *ai)
87 {
88 	printf("addrinfo @%p: ai_family=%d, ai_socktype=%d, ai_protocol=%d, "
89 	       "sa_family=%d, sin_port=%x\n",
90 	       ai, ai->ai_family, ai->ai_socktype, ai->ai_protocol,
91 	       ai->ai_addr->sa_family,
92 	       ((struct sockaddr_in *)ai->ai_addr)->sin_port);
93 }
94 
fatal(const char * msg)95 void fatal(const char *msg)
96 {
97 	printf("Error: %s\n", msg);
98 	exit(1);
99 }
100 
sendall(int sock,const void * buf,size_t len)101 ssize_t sendall(int sock, const void *buf, size_t len)
102 {
103 	while (len) {
104 		ssize_t out_len = send(sock, buf, len, 0);
105 		if (out_len < 0) {
106 			return out_len;
107 		}
108 		buf = (const char *)buf + out_len;
109 		len -= out_len;
110 	}
111 
112 	return 0;
113 }
114 
parse_status(bool * redirect)115 static int parse_status(bool *redirect)
116 {
117 	char *ptr;
118 	int code;
119 
120 	ptr = strstr(response, "HTTP");
121 	if (ptr == NULL) {
122 		return -1;
123 	}
124 
125 	ptr = strstr(response, " ");
126 	if (ptr == NULL) {
127 		return -1;
128 	}
129 
130 	ptr++;
131 
132 	code = atoi(ptr);
133 	if (code >= 300 && code < 400) {
134 		*redirect = true;
135 	}
136 
137 	return 0;
138 }
139 
parse_header(bool * location_found)140 static int parse_header(bool *location_found)
141 {
142 	char *ptr;
143 
144 	ptr = strstr(response, ":");
145 	if (ptr == NULL) {
146 		return 0;
147 	}
148 
149 	*ptr = '\0';
150 	ptr = response;
151 
152 	while (*ptr != '\0') {
153 		*ptr = tolower(*ptr);
154 		ptr++;
155 	}
156 
157 	if (strcmp(response, "location") != 0) {
158 		return 0;
159 	}
160 
161 	/* Skip whitespace */
162 	while (*(++ptr) == ' ') {
163 		;
164 	}
165 
166 	strncpy(download_url, ptr, sizeof(download_url));
167 	download_url[sizeof(download_url) - 1] = '\0';
168 
169 	/* Trim LF. */
170 	ptr = strstr(download_url, "\n");
171 	if (ptr == NULL) {
172 		printf("Redirect URL too long or malformed\n");
173 		return -1;
174 	}
175 
176 	*ptr = '\0';
177 
178 	/* Trim CR if present. */
179 	ptr = strstr(download_url, "\r");
180 	if (ptr != NULL) {
181 		*ptr = '\0';
182 	}
183 
184 	*location_found = true;
185 
186 	return 0;
187 }
188 
skip_headers(int sock,bool * redirect)189 int skip_headers(int sock, bool *redirect)
190 {
191 	int state = 0;
192 	int i = 0;
193 	bool status_line = true;
194 	bool redirect_code = false;
195 	bool location_found = false;
196 
197 	while (1) {
198 		int st;
199 
200 		st = recv(sock, response + i, 1, 0);
201 		if (st <= 0) {
202 			return st;
203 		}
204 
205 		if (state == 0 && response[i] == '\r') {
206 			state++;
207 		} else if ((state == 0 || state == 1) && response[i] == '\n') {
208 			state = 2;
209 			response[i + 1] = '\0';
210 			i = 0;
211 
212 			if (status_line) {
213 				if (parse_status(&redirect_code) < 0) {
214 					return -1;
215 				}
216 
217 				status_line = false;
218 			} else {
219 				if (parse_header(&location_found) < 0) {
220 					return -1;
221 				}
222 			}
223 
224 			continue;
225 		} else if (state == 2 && response[i] == '\r') {
226 			state++;
227 		} else if ((state == 2 || state == 3) && response[i] == '\n') {
228 			break;
229 		} else {
230 			state = 0;
231 		}
232 
233 		i++;
234 		if (i >= sizeof(response) - 1) {
235 			i = 0;
236 		}
237 	}
238 
239 	if (redirect_code && location_found) {
240 		*redirect = true;
241 	}
242 
243 	return 1;
244 }
245 
print_hex(const unsigned char * p,int len)246 void print_hex(const unsigned char *p, int len)
247 {
248 	while (len--) {
249 		printf("%02x", *p++);
250 	}
251 }
252 
download(struct addrinfo * ai,bool is_tls,bool * redirect)253 bool download(struct addrinfo *ai, bool is_tls, bool *redirect)
254 {
255 	int sock;
256 	struct timeval timeout = {
257 		.tv_sec = 5
258 	};
259 
260 	cur_bytes = 0U;
261 	*redirect = false;
262 
263 	if (is_tls) {
264 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
265 		sock = socket(ai->ai_family, ai->ai_socktype, IPPROTO_TLS_1_2);
266 # else
267 		printf("TLS not supported\n");
268 		return false;
269 #endif
270 	} else {
271 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
272 	}
273 
274 	CHECK(sock);
275 	printf("sock = %d\n", sock);
276 
277 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
278 	if (is_tls) {
279 		sec_tag_t sec_tag_opt[ARRAY_SIZE(ca_certificates)];
280 
281 		for (int i = 0; i < ARRAY_SIZE(ca_certificates); i++) {
282 			sec_tag_opt[i] = CA_CERTIFICATE_TAG + i;
283 		};
284 
285 		CHECK(setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST,
286 				 sec_tag_opt, sizeof(sec_tag_opt)));
287 
288 		CHECK(setsockopt(sock, SOL_TLS, TLS_HOSTNAME,
289 				 host, strlen(host) + 1));
290 	}
291 #endif
292 
293 	CHECK(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout,
294 			 sizeof(timeout)));
295 	CHECK(setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout,
296 			 sizeof(timeout)));
297 
298 	CHECK(connect(sock, ai->ai_addr, ai->ai_addrlen));
299 	sendall(sock, "GET /", SSTRLEN("GET /"));
300 	sendall(sock, uri_path, strlen(uri_path));
301 	sendall(sock, " HTTP/1.0\r\n", SSTRLEN(" HTTP/1.0\r\n"));
302 	sendall(sock, "Host: ", SSTRLEN("Host: "));
303 	sendall(sock, host, strlen(host));
304 	sendall(sock, "\r\n\r\n", SSTRLEN("\r\n\r\n"));
305 
306 	if (skip_headers(sock, redirect) <= 0) {
307 		printf("EOF or error in response headers\n");
308 		goto error;
309 	}
310 
311 	if (*redirect) {
312 		printf("Server requested redirection to %s\n", download_url);
313 		goto error;
314 	}
315 
316 	mbedtls_md_starts(&hash_ctx);
317 
318 	while (1) {
319 		int len = recv(sock, response, sizeof(response) - 1, 0);
320 
321 		if (len < 0) {
322 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
323 				printf("Timeout on reading response\n");
324 			} else {
325 				printf("Error reading response\n");
326 			}
327 
328 			goto error;
329 		}
330 
331 		if (len == 0) {
332 			break;
333 		}
334 
335 		mbedtls_md_update(&hash_ctx, response, len);
336 
337 		cur_bytes += len;
338 		printf("Download progress: %u Bytes; %u KiB; %u MiB\r",
339 			cur_bytes, bytes2KiB(cur_bytes), bytes2MiB(cur_bytes));
340 
341 		response[len] = 0;
342 		/*printf("%s\n", response);*/
343 	}
344 
345 	printf("\n");
346 
347 	mbedtls_md_finish(&hash_ctx, response_hash);
348 
349 	printf("Hash: ");
350 	print_hex(response_hash, mbedtls_md_get_size(hash_info));
351 	printf("\n");
352 
353 	if (memcmp(response_hash, download_hash,
354 		   mbedtls_md_get_size(hash_info)) != 0) {
355 		printf("HASH MISMATCH!\n");
356 	}
357 
358 error:
359 	(void)close(sock);
360 
361 	return redirect;
362 }
363 
main(void)364 int main(void)
365 {
366 	static struct addrinfo hints;
367 	struct addrinfo *res;
368 	int st;
369 	char *p;
370 	unsigned int total_bytes = 0U;
371 	int resolve_attempts = 10;
372 	bool is_tls = false;
373 	unsigned int num_iterations = NUM_ITER;
374 	bool redirect = false;
375 
376 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
377 	for (int i = 0; i < ARRAY_SIZE(ca_certificates); i++) {
378 		tls_credential_add(CA_CERTIFICATE_TAG + i,
379 				   TLS_CREDENTIAL_CA_CERTIFICATE,
380 				   ca_certificates[i],
381 				   strlen(ca_certificates[i]) + 1);
382 	}
383 #endif
384 
385 	setbuf(stdout, NULL);
386 
387 redirect:
388 	if (strncmp(download_url, "http://", SSTRLEN("http://")) == 0) {
389 		port = "80";
390 		p = download_url + SSTRLEN("http://");
391 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
392 	} else if (strncmp(download_url, "https://",
393 		   SSTRLEN("https://")) == 0) {
394 		is_tls = true;
395 		port = "443";
396 		p = download_url + SSTRLEN("https://");
397 #endif /* defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) */
398 	} else {
399 		fatal("Only http: "
400 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
401 		      "and https: "
402 #endif
403 		      "URLs are supported");
404 	}
405 
406 	/* Parse host part */
407 	host = p;
408 	while (*p && *p != ':' && *p != '/') {
409 		p++;
410 	}
411 
412 	/* Store optional port part */
413 	if (*p == ':') {
414 		*p++ = 0;
415 		port = p;
416 	}
417 
418 	/* Parse path part */
419 	while (*p && *p != '/') {
420 		p++;
421 	}
422 
423 	if (*p == '/') {
424 		*p++ = 0;
425 		uri_path = p;
426 	}
427 
428 	printf("Preparing HTTP GET request for http%s://%s:%s/%s\n",
429 		       (is_tls ? "s" : ""), host, port, uri_path);
430 
431 	hints.ai_family = AF_INET;
432 	hints.ai_socktype = SOCK_STREAM;
433 
434 	while (resolve_attempts--) {
435 		st = getaddrinfo(host, port, &hints, &res);
436 
437 		if (st == 0) {
438 			break;
439 		}
440 
441 		printf("getaddrinfo status: %d, retrying\n", st);
442 		sleep(2);
443 	}
444 
445 	if (st != 0) {
446 		fatal("Unable to resolve address");
447 	}
448 
449 	dump_addrinfo(res);
450 
451 	hash_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
452 	if (!hash_info) {
453 		fatal("Unable to request hash type from mbedTLS");
454 	}
455 
456 	mbedtls_md_init(&hash_ctx);
457 	if (mbedtls_md_setup(&hash_ctx, hash_info, 0) < 0) {
458 		fatal("Can't setup mbedTLS hash engine");
459 	}
460 
461 	const uint32_t total_iterations = num_iterations;
462 	uint32_t current_iteration = 1;
463 	do {
464 		if (total_iterations == 0) {
465 			printf("\nIteration %u of INF\n", current_iteration);
466 		} else {
467 			printf("\nIteration %u of %u:\n",
468 				current_iteration, total_iterations);
469 		}
470 
471 		download(res, is_tls, &redirect);
472 		if (redirect) {
473 			goto redirect;
474 		}
475 
476 		total_bytes += cur_bytes;
477 		printf("Total downloaded so far: %u MiB\n",
478 			bytes2MiB(total_bytes));
479 
480 		sleep(3);
481 		current_iteration++;
482 	} while (--num_iterations != 0);
483 
484 	printf("Finished downloading.\n");
485 
486 	mbedtls_md_free(&hash_ctx);
487 	return 0;
488 }
489