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