blob: c9ea70ea5786b496d734ea141fb7cf504f157a0a [file] [log] [blame]
/*
* Copyright (c) 2018 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mbedtls/md.h"
#if !defined(__ZEPHYR__) || defined(CONFIG_POSIX_API)
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#else
#include <zephyr/net/socket.h>
#include <zephyr/kernel.h>
#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
#include <zephyr/net/tls_credentials.h>
#include "ca_certificate.h"
#endif
#define sleep(x) k_sleep(K_MSEC((x) * MSEC_PER_SEC))
#endif
#define bytes2KiB(Bytes) (Bytes / (1024u))
#define bytes2MiB(Bytes) (Bytes / (1024u * 1024u))
/* This URL is parsed in-place, so buffer must be non-const. */
static char download_url[] =
#if defined(CONFIG_SAMPLE_BIG_HTTP_DL_URL)
CONFIG_SAMPLE_BIG_HTTP_DL_URL;
#else
#if !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
"http://archive.ubuntu.com/ubuntu/dists/xenial/main/installer-amd64/current/images/hd-media/vmlinuz";
#else
"https://www.7-zip.org/a/7z1805.exe";
#endif /* !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) */
#endif /* defined(CONFIG_SAMPLE_BIG_HTTP_DL_URL) */
/* Quick testing. */
/* "http://google.com/foo";*/
/* print("".join(["\\x%02x" % x for x in list(binascii.unhexlify("hash"))])) */
static uint8_t download_hash[32] =
#if !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
"\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";
#else
"\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";
#endif /* !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) */
#define SSTRLEN(s) (sizeof(s) - 1)
#define CHECK(r) { if (r == -1) { printf("Error: " #r "\n"); exit(1); } }
const char *host;
const char *port;
const char *uri_path = "";
static char response[1024];
static char response_hash[32];
mbedtls_md_context_t hash_ctx;
const mbedtls_md_info_t *hash_info;
unsigned int cur_bytes;
void dump_addrinfo(const struct addrinfo *ai)
{
printf("addrinfo @%p: ai_family=%d, ai_socktype=%d, ai_protocol=%d, "
"sa_family=%d, sin_port=%x\n",
ai, ai->ai_family, ai->ai_socktype, ai->ai_protocol,
ai->ai_addr->sa_family,
((struct sockaddr_in *)ai->ai_addr)->sin_port);
}
void fatal(const char *msg)
{
printf("Error: %s\n", msg);
exit(1);
}
ssize_t sendall(int sock, const void *buf, size_t len)
{
while (len) {
ssize_t out_len = send(sock, buf, len, 0);
if (out_len < 0) {
return out_len;
}
buf = (const char *)buf + out_len;
len -= out_len;
}
return 0;
}
int skip_headers(int sock)
{
int state = 0;
while (1) {
char c;
int st;
st = recv(sock, &c, 1, 0);
if (st <= 0) {
return st;
}
if (state == 0 && c == '\r') {
state++;
} else if (state == 1 && c == '\n') {
state++;
} else if (state == 2 && c == '\r') {
state++;
} else if (state == 3 && c == '\n') {
break;
} else {
state = 0;
}
}
return 1;
}
void print_hex(const unsigned char *p, int len)
{
while (len--) {
printf("%02x", *p++);
}
}
void download(struct addrinfo *ai, bool is_tls)
{
int sock;
struct timeval timeout = {
.tv_sec = 5
};
cur_bytes = 0U;
if (is_tls) {
#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
sock = socket(ai->ai_family, ai->ai_socktype, IPPROTO_TLS_1_2);
# else
printf("TLS not supported\n");
return;
#endif
} else {
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
}
CHECK(sock);
printf("sock = %d\n", sock);
#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
if (is_tls) {
sec_tag_t sec_tag_opt[] = {
CA_CERTIFICATE_TAG,
};
CHECK(setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST,
sec_tag_opt, sizeof(sec_tag_opt)));
CHECK(setsockopt(sock, SOL_TLS, TLS_HOSTNAME,
host, strlen(host) + 1));
}
#endif
CHECK(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout,
sizeof(timeout)));
CHECK(setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout,
sizeof(timeout)));
CHECK(connect(sock, ai->ai_addr, ai->ai_addrlen));
sendall(sock, "GET /", SSTRLEN("GET /"));
sendall(sock, uri_path, strlen(uri_path));
sendall(sock, " HTTP/1.0\r\n", SSTRLEN(" HTTP/1.0\r\n"));
sendall(sock, "Host: ", SSTRLEN("Host: "));
sendall(sock, host, strlen(host));
sendall(sock, "\r\n\r\n", SSTRLEN("\r\n\r\n"));
if (skip_headers(sock) <= 0) {
printf("EOF or error in response headers\n");
goto error;
}
mbedtls_md_starts(&hash_ctx);
while (1) {
int len = recv(sock, response, sizeof(response) - 1, 0);
if (len < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("Timeout on reading response\n");
} else {
printf("Error reading response\n");
}
goto error;
}
if (len == 0) {
break;
}
mbedtls_md_update(&hash_ctx, response, len);
cur_bytes += len;
printf("Download progress: %u Bytes; %u KiB; %u MiB\r",
cur_bytes, bytes2KiB(cur_bytes), bytes2MiB(cur_bytes));
response[len] = 0;
/*printf("%s\n", response);*/
}
printf("\n");
mbedtls_md_finish(&hash_ctx, response_hash);
printf("Hash: ");
print_hex(response_hash, mbedtls_md_get_size(hash_info));
printf("\n");
if (memcmp(response_hash, download_hash,
mbedtls_md_get_size(hash_info)) != 0) {
printf("HASH MISMATCH!\n");
}
error:
(void)close(sock);
}
void main(void)
{
static struct addrinfo hints;
struct addrinfo *res;
int st;
char *p;
unsigned int total_bytes = 0U;
int resolve_attempts = 10;
bool is_tls = false;
unsigned int num_iterations = CONFIG_SAMPLE_BIG_HTTP_DL_NUM_ITER;
#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
tls_credential_add(CA_CERTIFICATE_TAG, TLS_CREDENTIAL_CA_CERTIFICATE,
ca_certificate, sizeof(ca_certificate));
#endif
setbuf(stdout, NULL);
if (strncmp(download_url, "http://", SSTRLEN("http://")) == 0) {
port = "80";
p = download_url + SSTRLEN("http://");
#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
} else if (strncmp(download_url, "https://",
SSTRLEN("https://")) == 0) {
is_tls = true;
port = "443";
p = download_url + SSTRLEN("https://");
#endif /* defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) */
} else {
fatal("Only http: "
#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
"and https: "
#endif
"URLs are supported");
}
/* Parse host part */
host = p;
while (*p && *p != ':' && *p != '/') {
p++;
}
/* Store optional port part */
if (*p == ':') {
*p++ = 0;
port = p;
}
/* Parse path part */
while (*p && *p != '/') {
p++;
}
if (*p == '/') {
*p++ = 0;
uri_path = p;
}
printf("Preparing HTTP GET request for http%s://%s:%s/%s\n",
(is_tls ? "s" : ""), host, port, uri_path);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
while (resolve_attempts--) {
st = getaddrinfo(host, port, &hints, &res);
if (st == 0) {
break;
}
printf("getaddrinfo status: %d, retrying\n", st);
sleep(2);
}
if (st != 0) {
fatal("Unable to resolve address");
}
dump_addrinfo(res);
hash_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
if (!hash_info) {
fatal("Unable to request hash type from mbedTLS");
}
mbedtls_md_init(&hash_ctx);
if (mbedtls_md_setup(&hash_ctx, hash_info, 0) < 0) {
fatal("Can't setup mbedTLS hash engine");
}
const uint32_t total_iterations = num_iterations;
uint32_t current_iteration = 1;
do {
if (total_iterations == 0) {
printf("\nIteration %u of INF\n", current_iteration);
} else {
printf("\nIteration %u of %u:\n",
current_iteration, total_iterations);
}
download(res, is_tls);
total_bytes += cur_bytes;
printf("Total downloaded so far: %u MiB\n",
bytes2MiB(total_bytes));
sleep(3);
current_iteration++;
} while (--num_iterations != 0);
printf("Finished downloading.\n");
mbedtls_md_free(&hash_ctx);
}