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 
12 #include "mbedtls/md.h"
13 
14 #if !defined(__ZEPHYR__) || defined(CONFIG_POSIX_API)
15 
16 #include <netinet/in.h>
17 #include <sys/socket.h>
18 #include <arpa/inet.h>
19 #include <unistd.h>
20 #include <netdb.h>
21 
22 #else
23 
24 #include <net/socket.h>
25 #include <kernel.h>
26 
27 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
28 #include <net/tls_credentials.h>
29 #include "ca_certificate.h"
30 #endif
31 
32 #define sleep(x) k_sleep(K_MSEC((x) * MSEC_PER_SEC))
33 
34 #endif
35 
36 #define bytes2KiB(Bytes)	(Bytes / (1024u))
37 #define bytes2MiB(Bytes)	(Bytes / (1024u * 1024u))
38 
39 /* This URL is parsed in-place, so buffer must be non-const. */
40 static char download_url[] =
41 #if defined(CONFIG_SAMPLE_BIG_HTTP_DL_URL)
42     CONFIG_SAMPLE_BIG_HTTP_DL_URL;
43 #else
44 #if !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
45     "http://archive.ubuntu.com/ubuntu/dists/xenial/main/installer-amd64/current/images/hd-media/vmlinuz";
46 #else
47     "https://www.7-zip.org/a/7z1805.exe";
48 #endif /* !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) */
49 #endif /* defined(CONFIG_SAMPLE_BIG_HTTP_DL_URL) */
50 /* Quick testing. */
51 /*    "http://google.com/foo";*/
52 
53 /* print("".join(["\\x%02x" % x for x in list(binascii.unhexlify("hash"))])) */
54 static uint8_t download_hash[32] =
55 #if !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
56 "\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";
57 #else
58 "\x64\x7a\x9a\x62\x11\x62\xcd\x7a\x50\x08\x93\x4a\x08\xe2\x3f\xf7\xc1\x13\x5d\x6f\x12\x61\x68\x9f\xd9\x54\xaa\x17\xd5\x0f\x97\x29";
59 #endif /* !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) */
60 
61 #define SSTRLEN(s) (sizeof(s) - 1)
62 #define CHECK(r) { if (r == -1) { printf("Error: " #r "\n"); exit(1); } }
63 
64 const char *host;
65 const char *port;
66 const char *uri_path = "";
67 static char response[1024];
68 static char response_hash[32];
69 mbedtls_md_context_t hash_ctx;
70 const mbedtls_md_info_t *hash_info;
71 unsigned int cur_bytes;
72 
dump_addrinfo(const struct addrinfo * ai)73 void dump_addrinfo(const struct addrinfo *ai)
74 {
75 	printf("addrinfo @%p: ai_family=%d, ai_socktype=%d, ai_protocol=%d, "
76 	       "sa_family=%d, sin_port=%x\n",
77 	       ai, ai->ai_family, ai->ai_socktype, ai->ai_protocol,
78 	       ai->ai_addr->sa_family,
79 	       ((struct sockaddr_in *)ai->ai_addr)->sin_port);
80 }
81 
fatal(const char * msg)82 void fatal(const char *msg)
83 {
84 	printf("Error: %s\n", msg);
85 	exit(1);
86 }
87 
sendall(int sock,const void * buf,size_t len)88 ssize_t sendall(int sock, const void *buf, size_t len)
89 {
90 	while (len) {
91 		ssize_t out_len = send(sock, buf, len, 0);
92 		if (out_len < 0) {
93 			return out_len;
94 		}
95 		buf = (const char *)buf + out_len;
96 		len -= out_len;
97 	}
98 
99 	return 0;
100 }
101 
skip_headers(int sock)102 int skip_headers(int sock)
103 {
104 	int state = 0;
105 
106 	while (1) {
107 		char c;
108 		int st;
109 
110 		st = recv(sock, &c, 1, 0);
111 		if (st <= 0) {
112 			return st;
113 		}
114 
115 		if (state == 0 && c == '\r') {
116 			state++;
117 		} else if (state == 1 && c == '\n') {
118 			state++;
119 		} else if (state == 2 && c == '\r') {
120 			state++;
121 		} else if (state == 3 && c == '\n') {
122 			break;
123 		} else {
124 			state = 0;
125 		}
126 	}
127 
128 	return 1;
129 }
130 
print_hex(const unsigned char * p,int len)131 void print_hex(const unsigned char *p, int len)
132 {
133 	while (len--) {
134 		printf("%02x", *p++);
135 	}
136 }
137 
download(struct addrinfo * ai,bool is_tls)138 void download(struct addrinfo *ai, bool is_tls)
139 {
140 	int sock;
141 	struct timeval timeout = {
142 		.tv_sec = 5
143 	};
144 
145 	cur_bytes = 0U;
146 
147 	if (is_tls) {
148 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
149 		sock = socket(ai->ai_family, ai->ai_socktype, IPPROTO_TLS_1_2);
150 # else
151 		printf("TLS not supported\n");
152 		return;
153 #endif
154 	} else {
155 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
156 	}
157 
158 	CHECK(sock);
159 	printf("sock = %d\n", sock);
160 
161 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
162 	if (is_tls) {
163 		sec_tag_t sec_tag_opt[] = {
164 			CA_CERTIFICATE_TAG,
165 		};
166 		CHECK(setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST,
167 				 sec_tag_opt, sizeof(sec_tag_opt)));
168 
169 		CHECK(setsockopt(sock, SOL_TLS, TLS_HOSTNAME,
170 				 host, strlen(host) + 1));
171 	}
172 #endif
173 
174 	CHECK(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout,
175 			 sizeof(timeout)));
176 	CHECK(setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout,
177 			 sizeof(timeout)));
178 
179 	CHECK(connect(sock, ai->ai_addr, ai->ai_addrlen));
180 	sendall(sock, "GET /", SSTRLEN("GET /"));
181 	sendall(sock, uri_path, strlen(uri_path));
182 	sendall(sock, " HTTP/1.0\r\n", SSTRLEN(" HTTP/1.0\r\n"));
183 	sendall(sock, "Host: ", SSTRLEN("Host: "));
184 	sendall(sock, host, strlen(host));
185 	sendall(sock, "\r\n\r\n", SSTRLEN("\r\n\r\n"));
186 
187 	if (skip_headers(sock) <= 0) {
188 		printf("EOF or error in response headers\n");
189 		goto error;
190 	}
191 
192 	mbedtls_md_starts(&hash_ctx);
193 
194 	while (1) {
195 		int len = recv(sock, response, sizeof(response) - 1, 0);
196 
197 		if (len < 0) {
198 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
199 				printf("Timeout on reading response\n");
200 			} else {
201 				printf("Error reading response\n");
202 			}
203 
204 			goto error;
205 		}
206 
207 		if (len == 0) {
208 			break;
209 		}
210 
211 		mbedtls_md_update(&hash_ctx, response, len);
212 
213 		cur_bytes += len;
214 		printf("Download progress: %u Bytes; %u KiB; %u MiB\r",
215 			cur_bytes, bytes2KiB(cur_bytes), bytes2MiB(cur_bytes));
216 
217 		response[len] = 0;
218 		/*printf("%s\n", response);*/
219 	}
220 
221 	printf("\n");
222 
223 	mbedtls_md_finish(&hash_ctx, response_hash);
224 
225 	printf("Hash: ");
226 	print_hex(response_hash, mbedtls_md_get_size(hash_info));
227 	printf("\n");
228 
229 	if (memcmp(response_hash, download_hash,
230 		   mbedtls_md_get_size(hash_info)) != 0) {
231 		printf("HASH MISMATCH!\n");
232 	}
233 
234 error:
235 	(void)close(sock);
236 }
237 
main(void)238 void main(void)
239 {
240 	static struct addrinfo hints;
241 	struct addrinfo *res;
242 	int st;
243 	char *p;
244 	unsigned int total_bytes = 0U;
245 	int resolve_attempts = 10;
246 	bool is_tls = false;
247 	int num_iterations = CONFIG_SAMPLE_BIG_HTTP_DL_NUM_ITER;
248 
249 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
250 	tls_credential_add(CA_CERTIFICATE_TAG, TLS_CREDENTIAL_CA_CERTIFICATE,
251 			   ca_certificate, sizeof(ca_certificate));
252 #endif
253 
254 	setbuf(stdout, NULL);
255 
256 	if (strncmp(download_url, "http://", SSTRLEN("http://")) == 0) {
257 		port = "80";
258 		p = download_url + SSTRLEN("http://");
259 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
260 	} else if (strncmp(download_url, "https://",
261 		   SSTRLEN("https://")) == 0) {
262 		is_tls = true;
263 		port = "443";
264 		p = download_url + SSTRLEN("https://");
265 #endif /* defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) */
266 	} else {
267 		fatal("Only http: "
268 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
269 		      "and https: "
270 #endif
271 		      "URLs are supported");
272 	}
273 
274 	/* Parse host part */
275 	host = p;
276 	while (*p && *p != ':' && *p != '/') {
277 		p++;
278 	}
279 
280 	/* Store optional port part */
281 	if (*p == ':') {
282 		*p++ = 0;
283 		port = p;
284 	}
285 
286 	/* Parse path part */
287 	while (*p && *p != '/') {
288 		p++;
289 	}
290 
291 	if (*p == '/') {
292 		*p++ = 0;
293 		uri_path = p;
294 	}
295 
296 	printf("Preparing HTTP GET request for http%s://%s:%s/%s\n",
297 		       (is_tls ? "s" : ""), host, port, uri_path);
298 
299 	hints.ai_family = AF_INET;
300 	hints.ai_socktype = SOCK_STREAM;
301 
302 	while (resolve_attempts--) {
303 		st = getaddrinfo(host, port, &hints, &res);
304 
305 		if (st == 0) {
306 			break;
307 		}
308 
309 		printf("getaddrinfo status: %d, retrying\n", st);
310 		sleep(2);
311 	}
312 
313 	if (st != 0) {
314 		fatal("Unable to resolve address");
315 	}
316 
317 	dump_addrinfo(res);
318 
319 	hash_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
320 	if (!hash_info) {
321 		fatal("Unable to request hash type from mbedTLS");
322 	}
323 
324 	mbedtls_md_init(&hash_ctx);
325 	if (mbedtls_md_setup(&hash_ctx, hash_info, 0) < 0) {
326 		fatal("Can't setup mbedTLS hash engine");
327 	}
328 
329 	const uint32_t total_iterations = num_iterations;
330 	uint32_t current_iteration = 1;
331 	do {
332 		if (total_iterations == 0) {
333 			printf("\nIteration %u of INF\n", current_iteration);
334 		} else {
335 			printf("\nIteration %u of %u:\n",
336 				current_iteration, total_iterations);
337 		}
338 		download(res, is_tls);
339 
340 		total_bytes += cur_bytes;
341 		printf("Total downloaded so far: %u MiB\n",
342 			bytes2MiB(total_bytes));
343 
344 		sleep(3);
345 		current_iteration++;
346 	} while (--num_iterations != 0);
347 
348 	printf("Finished downloading.\n");
349 
350 	mbedtls_md_free(&hash_ctx);
351 }
352