2025-03-02 16:15:54 -07:00
|
|
|
#include "client.h"
|
|
|
|
|
#include "url_utility.h"
|
|
|
|
|
#include "debug_utility.h"
|
|
|
|
|
#include "network_utility.h"
|
|
|
|
|
#include "string_utility.h"
|
|
|
|
|
#include "socket_utility.h"
|
|
|
|
|
#include "certificates.h"
|
2025-03-02 17:52:32 -07:00
|
|
|
#include "data_store.h"
|
2025-03-02 16:15:54 -07:00
|
|
|
|
|
|
|
|
#include <xtl.h>
|
|
|
|
|
#include <stdio.h>
|
2025-03-02 17:52:32 -07:00
|
|
|
#include <stdint.h>
|
2026-01-30 20:01:50 -05:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2025-03-02 16:15:54 -07:00
|
|
|
#include <bearssl.h>
|
|
|
|
|
|
2025-03-02 17:52:32 -07:00
|
|
|
#define READ_BUFFER_SIZE 65536
|
2025-03-02 16:15:54 -07:00
|
|
|
|
2025-03-02 17:52:32 -07:00
|
|
|
int client::download_file(const char* url, uint16_t port, const char* file_path)
|
|
|
|
|
{
|
2025-03-02 16:15:54 -07:00
|
|
|
char *host = NULL;
|
|
|
|
|
char *path = NULL;
|
|
|
|
|
if (url_utility::parse_url(url, &host, &path) == false)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t host_ip = network_utility::resolve_host(host);
|
|
|
|
|
|
|
|
|
|
int socket = socket_utility::connect_to_host(host_ip, port);
|
|
|
|
|
|
|
|
|
|
certificates::initialize_trust_anchors();
|
|
|
|
|
|
|
|
|
|
br_ssl_client_context client_context;
|
|
|
|
|
br_x509_minimal_context cert_context;
|
|
|
|
|
br_ssl_client_init_full(&client_context, &cert_context, certificates::get_trust_anchors(), certificates::get_num_trust_anchors());
|
|
|
|
|
|
|
|
|
|
unsigned char io_buffer[BR_SSL_BUFSIZE_BIDI];
|
|
|
|
|
br_ssl_engine_set_buffer(&client_context.eng, io_buffer, BR_SSL_BUFSIZE_BIDI, 1);
|
|
|
|
|
br_ssl_client_reset(&client_context, host, 0);
|
|
|
|
|
|
2025-03-02 17:52:32 -07:00
|
|
|
br_sslio_context io_context;
|
|
|
|
|
br_sslio_init(&io_context, &client_context.eng, socket_utility::socket_read, &socket, socket_utility::socket_write, &socket);
|
2025-03-02 16:15:54 -07:00
|
|
|
|
|
|
|
|
char* request = string_utility::format_string("GET %s HTTP/1.1\r\nHost: %s\r\nAccept-Encoding: identity\r\nConnection: close\r\n\r\n", path, host);
|
2025-03-02 17:52:32 -07:00
|
|
|
br_sslio_write_all(&io_context, request, strlen(request));
|
|
|
|
|
br_sslio_flush(&io_context);
|
2025-03-02 16:15:54 -07:00
|
|
|
|
2025-03-02 17:52:32 -07:00
|
|
|
data_store* data = new data_store();
|
|
|
|
|
pointer_vector<char*>* headers = new pointer_vector<char*>(false);
|
2026-01-30 20:01:50 -05:00
|
|
|
populate_headers(&io_context, data, headers);
|
2025-03-02 17:52:32 -07:00
|
|
|
|
|
|
|
|
int64_t content_length = -1;
|
|
|
|
|
char* redirect_url = NULL;
|
|
|
|
|
|
|
|
|
|
bool has_redirect = false;
|
|
|
|
|
for (uint32_t i = 0; i < headers->count(); i++)
|
2025-03-02 16:15:54 -07:00
|
|
|
{
|
2025-03-02 17:52:32 -07:00
|
|
|
pointer_vector<char*>* parts = string_utility::split_first(headers->get(i), ":", true);
|
|
|
|
|
if (parts->count() != 2)
|
2025-03-02 16:15:54 -07:00
|
|
|
{
|
2025-03-02 17:52:32 -07:00
|
|
|
continue;
|
2025-03-02 16:15:54 -07:00
|
|
|
}
|
2025-03-02 17:52:32 -07:00
|
|
|
if (strcmp(parts->get(0), "Content-Length") == 0)
|
|
|
|
|
{
|
|
|
|
|
content_length = string_utility::string_to_int64(parts->get(1));
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(parts->get(0), "Location") == 0)
|
|
|
|
|
{
|
|
|
|
|
has_redirect = true;
|
|
|
|
|
redirect_url = strdup(parts->get(1));
|
|
|
|
|
}
|
|
|
|
|
delete parts;
|
2025-03-02 16:15:54 -07:00
|
|
|
}
|
2025-03-02 17:52:32 -07:00
|
|
|
|
|
|
|
|
delete headers;
|
|
|
|
|
|
|
|
|
|
if (has_redirect == true)
|
2025-03-02 16:15:54 -07:00
|
|
|
{
|
2025-03-02 17:52:32 -07:00
|
|
|
closesocket(socket);
|
|
|
|
|
certificates::close_trust_anchors();
|
|
|
|
|
delete data;
|
|
|
|
|
|
|
|
|
|
int result;
|
|
|
|
|
result = download_file(redirect_url, port, file_path);
|
|
|
|
|
if (redirect_url != NULL)
|
|
|
|
|
{
|
|
|
|
|
free(redirect_url);
|
|
|
|
|
}
|
|
|
|
|
return result;;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (redirect_url != NULL)
|
|
|
|
|
{
|
|
|
|
|
free(redirect_url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (content_length < 0)
|
|
|
|
|
{
|
2026-01-30 20:01:50 -05:00
|
|
|
chunk_download(&io_context, data, file_path);
|
2025-03-02 17:52:32 -07:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-01-30 20:01:50 -05:00
|
|
|
download(&io_context, data, file_path, content_length);
|
2025-03-02 16:15:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
closesocket(socket);
|
2025-03-02 17:52:32 -07:00
|
|
|
certificates::close_trust_anchors();
|
|
|
|
|
delete data;
|
2025-03-02 16:15:54 -07:00
|
|
|
|
2025-03-02 17:52:32 -07:00
|
|
|
if (br_ssl_engine_current_state(&client_context.eng) == BR_SSL_CLOSED)
|
|
|
|
|
{
|
|
|
|
|
int error = br_ssl_engine_last_error(&client_context.eng);
|
|
|
|
|
if (error == 0)
|
|
|
|
|
{
|
|
|
|
|
debug_utility::debug_print("Closed.\n");
|
2025-03-02 16:15:54 -07:00
|
|
|
return EXIT_SUCCESS;
|
2025-03-02 17:52:32 -07:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
debug_utility::debug_print("SSL error %d\n", error);
|
2025-03-02 16:15:54 -07:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
2025-03-02 17:52:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
debug_utility::debug_print("Socket closed without proper SSL termination\n");
|
|
|
|
|
return EXIT_FAILURE;
|
2025-03-02 16:15:54 -07:00
|
|
|
}
|
2025-03-02 17:52:32 -07:00
|
|
|
|
2025-03-02 17:54:21 -07:00
|
|
|
// Private
|
2025-03-02 17:52:32 -07:00
|
|
|
|
2026-01-30 20:01:50 -05:00
|
|
|
void client::populate_headers(br_sslio_context* io_context, data_store* data, pointer_vector<char*>* headers)
|
2025-03-02 17:52:32 -07:00
|
|
|
{
|
|
|
|
|
unsigned char* read_buffer = (unsigned char*)malloc(READ_BUFFER_SIZE);
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
2026-01-30 20:01:50 -05:00
|
|
|
int bytes_read = br_sslio_read(io_context, read_buffer, READ_BUFFER_SIZE);
|
2025-03-02 17:52:32 -07:00
|
|
|
if (bytes_read < 0)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data->add_range(read_buffer, 0, bytes_read);
|
|
|
|
|
|
|
|
|
|
bool eof_headers = false;
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
unsigned char* data_buffer = data->get_all();
|
|
|
|
|
int offset = string_utility::find_crlf(data_buffer, data->count());
|
|
|
|
|
if (offset < 0)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
char* hearder_line = (char*)malloc(offset + 1);
|
|
|
|
|
memcpy(hearder_line, data_buffer, offset);
|
|
|
|
|
hearder_line[offset] = 0;
|
|
|
|
|
if (hearder_line[0] == 0)
|
|
|
|
|
{
|
|
|
|
|
free(hearder_line);
|
|
|
|
|
data->remove_range(0, offset + 2);
|
|
|
|
|
eof_headers = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
debug_utility::debug_print("Header Line: %s\n", hearder_line);
|
|
|
|
|
headers->add(hearder_line);
|
|
|
|
|
data->remove_range(0, offset + 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (eof_headers == true)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(read_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 20:01:50 -05:00
|
|
|
void client::chunk_download(br_sslio_context* io_context, data_store* data, const char* file_path)
|
2025-03-02 17:52:32 -07:00
|
|
|
{
|
|
|
|
|
unsigned char* read_buffer = (unsigned char*)malloc(READ_BUFFER_SIZE);
|
|
|
|
|
|
|
|
|
|
FILE* file = fopen(file_path, "wb");
|
|
|
|
|
|
|
|
|
|
int chunk_size = 0;
|
|
|
|
|
bool looking_for_chunk_header = true;
|
|
|
|
|
|
|
|
|
|
int64_t bytes_written = 0;
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
2026-01-30 20:01:50 -05:00
|
|
|
int bytes_read = br_sslio_read(io_context, read_buffer, READ_BUFFER_SIZE);
|
2025-03-02 17:52:32 -07:00
|
|
|
if (bytes_read < 0)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data->add_range(read_buffer, 0, bytes_read);
|
|
|
|
|
|
|
|
|
|
if (looking_for_chunk_header == true)
|
|
|
|
|
{
|
|
|
|
|
unsigned char* data_buffer = data->get_all();
|
|
|
|
|
int offset = string_utility::find_crlf(data_buffer, data->count());
|
|
|
|
|
if (offset < 0)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char* chunk_hex = (char*)malloc(offset + 1);
|
|
|
|
|
memcpy(chunk_hex, data_buffer, offset);
|
|
|
|
|
chunk_hex[offset] = 0;
|
|
|
|
|
|
|
|
|
|
if (chunk_hex[0] != 0)
|
|
|
|
|
{
|
|
|
|
|
chunk_size = (int)string_utility::hex_to_int64(chunk_hex);
|
|
|
|
|
looking_for_chunk_header = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(chunk_hex);
|
|
|
|
|
data->remove_range(0, offset + 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (looking_for_chunk_header == false)
|
|
|
|
|
{
|
|
|
|
|
int bytes_to_get = min(chunk_size, (int)data->count());
|
|
|
|
|
if (bytes_to_get != chunk_size)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (file != NULL)
|
|
|
|
|
{
|
|
|
|
|
fwrite(data->get_all(), 1, bytes_to_get, file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bytes_written += data->count();
|
|
|
|
|
debug_utility::debug_print("Written %lld bytes of unknown bytes\n", bytes_written);
|
|
|
|
|
|
|
|
|
|
data->remove_range(0, bytes_to_get);
|
|
|
|
|
looking_for_chunk_header = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
|
free(read_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 20:01:50 -05:00
|
|
|
void client::download(br_sslio_context* io_context, data_store* data, const char* file_path, int64_t content_length)
|
2025-03-02 17:52:32 -07:00
|
|
|
{
|
|
|
|
|
unsigned char* read_buffer = (unsigned char*)malloc(READ_BUFFER_SIZE);
|
|
|
|
|
|
|
|
|
|
FILE* file = fopen(file_path, "wb");
|
|
|
|
|
|
|
|
|
|
int64_t bytes_written = 0;
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
2026-01-30 20:01:50 -05:00
|
|
|
int bytes_read = br_sslio_read(io_context, read_buffer, READ_BUFFER_SIZE);
|
2025-03-02 17:52:32 -07:00
|
|
|
if (bytes_read < 0)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data->add_range(read_buffer, 0, bytes_read);
|
|
|
|
|
if (file != NULL)
|
|
|
|
|
{
|
|
|
|
|
fwrite(data->get_all(), 1, data->count(), file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bytes_written += data->count();
|
|
|
|
|
debug_utility::debug_print("Written %lld bytes of ", bytes_written);
|
|
|
|
|
debug_utility::debug_print("%lld bytes\n", content_length);
|
|
|
|
|
data->remove_range(0, data->count());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
|
free(read_buffer);
|
|
|
|
|
}
|