refactor: network - harden TCP/IP and unify HTTP clients

This commit is contained in:
2026-07-29 15:35:17 +01:00
parent 75ae7ede56
commit a288dee7df
28 changed files with 1852 additions and 1361 deletions
+29 -179
View File
@@ -8,7 +8,7 @@
#include <montauk/syscall.h>
#include <montauk/string.h>
#include <tls/tls.hpp>
#include <http/http.hpp>
extern "C" {
#include <string.h>
@@ -56,11 +56,6 @@ static bool parse_uint16(const char* s, uint16_t* out) {
return true;
}
static void format_ip(char* buf, uint32_t ip) {
snprintf(buf, 32, "%u.%u.%u.%u",
ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF);
}
// ---- URL parser ----
struct ParsedUrl {
@@ -124,38 +119,6 @@ static ParsedUrl parse_url(const char* url) {
return u;
}
// ---- HTTP response parser ----
static int find_header_end(const char* buf, int len) {
for (int i = 0; i + 3 < len; i++) {
if (buf[i] == '\r' && buf[i+1] == '\n' && buf[i+2] == '\r' && buf[i+3] == '\n')
return i + 4;
}
return -1;
}
static int parse_status_code(const char* buf, int len) {
int i = 0;
while (i < len && buf[i] != ' ') i++;
if (i >= len) return -1;
i++;
if (i + 2 >= len) return -1;
if (buf[i] < '0' || buf[i] > '9') return -1;
return (buf[i] - '0') * 100 + (buf[i+1] - '0') * 10 + (buf[i+2] - '0');
}
static void parse_status_text(const char* buf, int len, char* out, int outMax) {
int i = 0;
while (i < len && buf[i] != ' ') i++;
i++;
while (i < len && buf[i] != ' ') i++;
i++;
int j = 0;
while (i < len && buf[i] != '\r' && buf[i] != '\n' && j < outMax - 1)
out[j++] = buf[i++];
out[j] = '\0';
}
// ---- Keyboard abort check for TLS ----
static bool check_keyboard_abort() {
@@ -167,95 +130,23 @@ static bool check_keyboard_abort() {
return false;
}
// ---- Plain HTTP exchange (no TLS) ----
static int plain_http_exchange(int fd, const char* request, int reqLen,
char* respBuf, int respMax) {
// Send request
int sent = 0;
uint64_t deadline = montauk::get_milliseconds() + 15000;
while (sent < reqLen) {
int r = montauk::send(fd, request + sent, reqLen - sent);
if (r > 0) { sent += r; deadline = montauk::get_milliseconds() + 15000; }
else if (r < 0) return -1;
else {
if (montauk::get_milliseconds() >= deadline) return -1;
montauk::sleep_ms(1);
}
}
// Receive response
int respLen = 0;
deadline = montauk::get_milliseconds() + 15000;
while (respLen < respMax - 1) {
if (montauk::is_key_available()) {
montauk::abi::KeyEvent ev;
montauk::getkey(&ev);
if (ev.pressed && ev.ctrl && ev.ascii == 'q') return -2; // aborted
}
int r = montauk::recv(fd, respBuf + respLen, respMax - 1 - respLen);
if (r > 0) { respLen += r; deadline = montauk::get_milliseconds() + 15000; }
else if (r < 0) break;
else {
uint64_t now = montauk::get_milliseconds();
if (now >= deadline) break;
uint32_t signals = montauk::wait_handle(
fd,
montauk::abi::IPC_SIGNAL_READABLE | montauk::abi::IPC_SIGNAL_PEER_CLOSED,
deadline - now
);
if (signals == 0 || signals == (uint32_t)-1) break;
}
}
return respLen;
}
// ---- Print response body ----
static void print_response(const char* respBuf, int respLen, bool verbose) {
if (respLen <= 0) {
montauk::print("Error: empty response\n");
return;
}
int headerEnd = find_header_end(respBuf, respLen);
if (headerEnd < 0) {
montauk::print("Warning: malformed response (no header boundary)\n\n");
// Print raw
char chunk[512];
int printed = 0;
while (printed < respLen) {
int n = respLen - printed;
if (n > 511) n = 511;
memcpy(chunk, respBuf + printed, n);
chunk[n] = '\0';
montauk::print(chunk);
printed += n;
}
montauk::putchar('\n');
return;
}
int statusCode = parse_status_code(respBuf, headerEnd);
char statusText[64];
parse_status_text(respBuf, headerEnd, statusText, sizeof(statusText));
int bodyLen = respLen - headerEnd;
static void print_response(const http::Response& response, bool verbose) {
if (verbose) {
char msg[256];
snprintf(msg, sizeof(msg), "HTTP %d %s (%d bytes)\n\n", statusCode, statusText, bodyLen);
char msg[128];
snprintf(msg, sizeof(msg), "HTTP %d (%d bytes)\n\n",
response.status, response.body_len);
montauk::print(msg);
}
if (bodyLen > 0) {
const char* body = respBuf + headerEnd;
if (response.body_len > 0) {
char chunk[512];
int printed = 0;
while (printed < bodyLen) {
int n = bodyLen - printed;
while (printed < response.body_len) {
int n = response.body_len - printed;
if (n > 511) n = 511;
memcpy(chunk, body + printed, n);
memcpy(chunk, response.body + printed, n);
chunk[n] = '\0';
montauk::print(chunk);
printed += n;
@@ -339,20 +230,8 @@ extern "C" void _start() {
}
}
// Resolve host to IP
uint32_t serverIp;
if (!parse_ip(hostStr, &serverIp)) {
serverIp = montauk::resolve(hostStr);
if (serverIp == 0) {
montauk::print("Error: could not resolve ");
montauk::print(hostStr);
montauk::putchar('\n');
montauk::exit(1);
}
}
char ipStr[32];
format_ip(ipStr, serverIp);
uint32_t numericIp = 0;
parse_ip(hostStr, &numericIp);
if (verbose) {
char msg[256];
@@ -361,16 +240,6 @@ extern "C" void _start() {
montauk::print(msg);
}
// Build HTTP request
char request[1024];
int reqLen = snprintf(request, sizeof(request),
"GET %s HTTP/1.0\r\n"
"Host: %s\r\n"
"User-Agent: MontaukOS/1.0\r\n"
"Connection: close\r\n"
"\r\n",
path, hostStr);
if (verbose) {
char msg[128];
snprintf(msg, sizeof(msg), "GET %s\n", path);
@@ -385,11 +254,9 @@ extern "C" void _start() {
montauk::exit(1);
}
int respLen;
tls::TrustAnchors tas = {};
if (useHttps) {
// ---- TLS handshake and exchange ----
tls::TrustAnchors tas = tls::load_trust_anchors();
tas = tls::load_trust_anchors();
if (verbose) {
char msg[64];
snprintf(msg, sizeof(msg), "Loaded %u trust anchors\n", (unsigned)tas.count);
@@ -415,43 +282,26 @@ extern "C" void _start() {
montauk::print("TLS handshake...\n");
}
respLen = tls::https_fetch(hostStr, serverIp, port,
request, reqLen, tas,
respBuf, RESP_MAX, check_keyboard_abort);
if (verbose && respLen > 0) {
montauk::print("TLS connection established\n");
}
} else {
// ---- Plain HTTP ----
int fd = montauk::socket(montauk::abi::SOCK_TCP);
if (fd < 0) {
montauk::print("Error: failed to create socket\n");
montauk::exit(1);
}
if (montauk::connect(fd, serverIp, port) < 0) {
montauk::print("Error: connection failed\n");
montauk::closesocket(fd);
montauk::exit(1);
}
respLen = plain_http_exchange(fd, request, reqLen, respBuf, RESP_MAX);
montauk::closesocket(fd);
if (respLen == -2) {
montauk::print("\nAborted.\n");
montauk::exit(0);
}
}
if (respLen <= 0) {
montauk::print("Error: no response received\n");
http::RequestOptions options;
options.secure = useHttps;
options.port = port;
options.resolved_ip = numericIp;
options.extra_headers = "User-Agent: MontaukOS/1.0\r\n";
options.abort_check = check_keyboard_abort;
options.timeout_ms = 15000;
http::Response response = http::request_into(
"GET", hostStr, path, nullptr, nullptr, 0, useHttps ? &tas : nullptr,
respBuf, RESP_MAX, options);
if (response.error != http::Error::NONE) {
montauk::print("Error: ");
montauk::print(http::error_string(response.error));
montauk::putchar('\n');
montauk::exit(1);
}
respBuf[respLen] = '\0';
print_response(respBuf, respLen, verbose);
if (verbose && useHttps) montauk::print("TLS connection established\n");
print_response(response, verbose);
montauk::exit(0);
}