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