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
Binary file not shown.
Binary file not shown.
+105 -38
View File
@@ -19,8 +19,8 @@ extern "C" {
namespace {
struct DerAccum { unsigned char* data; size_t len, cap; };
struct DnAccum { unsigned char* data; size_t len, cap; };
struct DerAccum { unsigned char* data; size_t len, cap; bool failed; };
struct DnAccum { unsigned char* data; size_t len, cap; bool failed; };
void der_append(void* ctx, const void* buf, size_t len) {
DerAccum* a = (DerAccum*)ctx;
@@ -28,7 +28,7 @@ void der_append(void* ctx, const void* buf, size_t len) {
size_t nc = a->cap * 2;
if (nc < a->len + len) nc = a->len + len + 4096;
unsigned char* nb = (unsigned char*)malloc(nc);
if (!nb) return;
if (!nb) { a->failed = true; return; }
if (a->data) { memcpy(nb, a->data, a->len); free(a->data); }
a->data = nb; a->cap = nc;
}
@@ -42,7 +42,7 @@ void dn_append(void* ctx, const void* buf, size_t len) {
size_t nc = a->cap * 2;
if (nc < a->len + len) nc = a->len + len + 256;
unsigned char* nb = (unsigned char*)malloc(nc);
if (!nb) return;
if (!nb) { a->failed = true; return; }
if (a->data) { memcpy(nb, a->data, a->len); free(a->data); }
a->data = nb; a->cap = nc;
}
@@ -50,53 +50,73 @@ void dn_append(void* ctx, const void* buf, size_t len) {
a->len += len;
}
void ta_add(tls::TrustAnchors* tas, const br_x509_trust_anchor* ta) {
bool ta_add(tls::TrustAnchors* tas, const br_x509_trust_anchor* ta) {
if (tas->count >= tas->capacity) {
size_t nc = tas->capacity == 0 ? 64 : tas->capacity * 2;
br_x509_trust_anchor* na = (br_x509_trust_anchor*)malloc(nc * sizeof(*na));
if (!na) return;
if (!na) return false;
if (tas->anchors) { memcpy(na, tas->anchors, tas->count * sizeof(*na)); free(tas->anchors); }
tas->anchors = na; tas->capacity = nc;
}
tas->anchors[tas->count++] = *ta;
return true;
}
bool process_cert_der(tls::TrustAnchors* tas, const unsigned char* der, size_t der_len) {
static br_x509_decoder_context dc; // ~2KB+, keep off stack
DnAccum dn = {nullptr, 0, 0};
br_x509_decoder_init(&dc, dn_append, &dn);
br_x509_decoder_push(&dc, der, der_len);
br_x509_pkey* pk = br_x509_decoder_get_pkey(&dc);
if (!pk) { if (dn.data) free(dn.data); return false; }
br_x509_decoder_context* dc =
(br_x509_decoder_context*)malloc(sizeof(br_x509_decoder_context));
if (!dc) return false;
DnAccum dn = {nullptr, 0, 0, false};
br_x509_decoder_init(dc, dn_append, &dn);
br_x509_decoder_push(dc, der, der_len);
br_x509_pkey* pk = br_x509_decoder_get_pkey(dc);
if (!pk || dn.failed) { free(dc); if (dn.data) free(dn.data); return false; }
br_x509_trust_anchor ta;
memset(&ta, 0, sizeof(ta));
ta.dn.data = dn.data; ta.dn.len = dn.len; ta.flags = 0;
if (br_x509_decoder_isCA(&dc)) ta.flags |= BR_X509_TA_CA;
if (br_x509_decoder_isCA(dc)) ta.flags |= BR_X509_TA_CA;
switch (pk->key_type) {
case BR_KEYTYPE_RSA:
ta.pkey.key_type = BR_KEYTYPE_RSA;
ta.pkey.key.rsa.nlen = pk->key.rsa.nlen;
ta.pkey.key.rsa.n = (unsigned char*)malloc(pk->key.rsa.nlen);
if (ta.pkey.key.rsa.n) memcpy(ta.pkey.key.rsa.n, pk->key.rsa.n, pk->key.rsa.nlen);
if (!ta.pkey.key.rsa.n) { free(dc); free(dn.data); return false; }
memcpy(ta.pkey.key.rsa.n, pk->key.rsa.n, pk->key.rsa.nlen);
ta.pkey.key.rsa.elen = pk->key.rsa.elen;
ta.pkey.key.rsa.e = (unsigned char*)malloc(pk->key.rsa.elen);
if (ta.pkey.key.rsa.e) memcpy(ta.pkey.key.rsa.e, pk->key.rsa.e, pk->key.rsa.elen);
if (!ta.pkey.key.rsa.e) {
free(ta.pkey.key.rsa.n);
free(dc);
free(dn.data);
return false;
}
memcpy(ta.pkey.key.rsa.e, pk->key.rsa.e, pk->key.rsa.elen);
break;
case BR_KEYTYPE_EC:
ta.pkey.key_type = BR_KEYTYPE_EC;
ta.pkey.key.ec.curve = pk->key.ec.curve;
ta.pkey.key.ec.qlen = pk->key.ec.qlen;
ta.pkey.key.ec.q = (unsigned char*)malloc(pk->key.ec.qlen);
if (ta.pkey.key.ec.q) memcpy(ta.pkey.key.ec.q, pk->key.ec.q, pk->key.ec.qlen);
if (!ta.pkey.key.ec.q) { free(dc); free(dn.data); return false; }
memcpy(ta.pkey.key.ec.q, pk->key.ec.q, pk->key.ec.qlen);
break;
default:
free(dc);
if (dn.data) free(dn.data);
return false;
}
ta_add(tas, &ta);
return true;
free(dc);
if (ta_add(tas, &ta)) return true;
free(ta.dn.data);
if (ta.pkey.key_type == BR_KEYTYPE_RSA) {
free(ta.pkey.key.rsa.n);
free(ta.pkey.key.rsa.e);
} else {
free(ta.pkey.key.ec.q);
}
return false;
}
} // anonymous namespace
@@ -116,36 +136,65 @@ TrustAnchors load_trust_anchors() {
unsigned char* pem = (unsigned char*)malloc(fsize + 1);
if (!pem) { montauk::close(fh); return tas; }
montauk::read(fh, pem, 0, fsize);
uint64_t readOffset = 0;
while (readOffset < fsize) {
int n = montauk::read(fh, pem + readOffset, readOffset, fsize - readOffset);
if (n <= 0) break;
readOffset += (uint64_t)n;
}
montauk::close(fh);
pem[fsize] = 0;
if (readOffset != fsize) { free(pem); return tas; }
pem[readOffset] = 0;
static br_pem_decoder_context pc; // keep off stack
br_pem_decoder_init(&pc);
DerAccum der = {nullptr, 0, 0};
br_pem_decoder_context* pc =
(br_pem_decoder_context*)malloc(sizeof(br_pem_decoder_context));
if (!pc) { free(pem); return tas; }
br_pem_decoder_init(pc);
DerAccum der = {nullptr, 0, 0, false};
bool inCert = false;
size_t offset = 0;
while (offset < fsize) {
size_t pushed = br_pem_decoder_push(&pc, pem + offset, fsize - offset);
size_t pushed = br_pem_decoder_push(pc, pem + offset, fsize - offset);
offset += pushed;
int ev = br_pem_decoder_event(&pc);
int ev = br_pem_decoder_event(pc);
if (ev == BR_PEM_BEGIN_OBJ) {
inCert = (strcmp(br_pem_decoder_name(&pc), "CERTIFICATE") == 0);
br_pem_decoder_setdest(&pc, inCert ? der_append : nullptr, inCert ? &der : nullptr);
if (inCert) der.len = 0;
inCert = (strcmp(br_pem_decoder_name(pc), "CERTIFICATE") == 0);
br_pem_decoder_setdest(pc, inCert ? der_append : nullptr, inCert ? &der : nullptr);
if (inCert) { der.len = 0; der.failed = false; }
} else if (ev == BR_PEM_END_OBJ) {
if (inCert && der.len > 0) process_cert_der(&tas, der.data, der.len);
inCert = false;
} else if (ev == BR_PEM_ERROR) {
break;
}
if (der.failed) break;
if (pushed == 0 && ev == 0) break;
}
if (der.data) free(der.data);
free(pc);
free(pem);
return tas;
}
void free_trust_anchors(TrustAnchors* tas) {
if (!tas) return;
for (size_t i = 0; i < tas->count; ++i) {
br_x509_trust_anchor& ta = tas->anchors[i];
free(ta.dn.data);
if (ta.pkey.key_type == BR_KEYTYPE_RSA) {
free(ta.pkey.key.rsa.n);
free(ta.pkey.key.rsa.e);
} else if (ta.pkey.key_type == BR_KEYTYPE_EC) {
free(ta.pkey.key.ec.q);
}
}
free(tas->anchors);
tas->anchors = nullptr;
tas->count = 0;
tas->capacity = 0;
}
void get_bearssl_time(uint32_t* days, uint32_t* seconds) {
montauk::abi::DateTime dt;
montauk::gettime(&dt);
@@ -167,7 +216,15 @@ int tls_send_all(int fd, const unsigned char* data, size_t len) {
int r = montauk::send(fd, data + sent, (uint32_t)(len - 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); }
else {
uint64_t now = montauk::get_milliseconds();
if (now >= deadline) return -1;
uint32_t signals = montauk::wait_handle(
fd, montauk::abi::IPC_SIGNAL_WRITABLE |
montauk::abi::IPC_SIGNAL_PEER_CLOSED, deadline - now);
if (signals == (uint32_t)-1 ||
(signals & montauk::abi::IPC_SIGNAL_PEER_CLOSED)) return -1;
}
}
return (int)sent;
}
@@ -178,8 +235,14 @@ int tls_recv_some(int fd, unsigned char* buf, size_t maxlen) {
int r = montauk::recv(fd, buf, (uint32_t)maxlen);
if (r > 0) return r;
if (r < 0) return -1;
if (montauk::get_milliseconds() >= deadline) return -1;
montauk::sleep_ms(1);
uint64_t now = montauk::get_milliseconds();
if (now >= deadline) return -1;
uint32_t signals = montauk::wait_handle(
fd, montauk::abi::IPC_SIGNAL_READABLE |
montauk::abi::IPC_SIGNAL_PEER_CLOSED, deadline - now);
if (signals == (uint32_t)-1) return -1;
if ((signals & montauk::abi::IPC_SIGNAL_PEER_CLOSED) &&
!(signals & montauk::abi::IPC_SIGNAL_READABLE)) return -1;
}
}
@@ -187,7 +250,7 @@ int tls_exchange(int fd, br_ssl_engine_context* eng,
const char* request, int reqLen,
char* respBuf, int respMax,
AbortCheckFn abort_check) {
bool requestSent = false;
int requestOffset = 0;
int respLen = 0;
uint64_t deadline = montauk::get_milliseconds() + 30000;
@@ -217,14 +280,14 @@ int tls_exchange(int fd, br_ssl_engine_context* eng,
br_ssl_engine_recvapp_ack(eng, len);
deadline = montauk::get_milliseconds() + 30000; continue;
}
if ((state & BR_SSL_SENDAPP) && !requestSent) {
if ((state & BR_SSL_SENDAPP) && requestOffset < reqLen) {
size_t len; unsigned char* buf = br_ssl_engine_sendapp_buf(eng, &len);
size_t toWrite = (size_t)reqLen;
size_t toWrite = (size_t)(reqLen - requestOffset);
if (toWrite > len) toWrite = len;
memcpy(buf, request, toWrite);
memcpy(buf, request + requestOffset, toWrite);
br_ssl_engine_sendapp_ack(eng, toWrite);
br_ssl_engine_flush(eng, 0);
requestSent = true;
requestOffset += (int)toWrite;
if (requestOffset == reqLen) br_ssl_engine_flush(eng, 0);
deadline = montauk::get_milliseconds() + 30000; continue;
}
if (state & BR_SSL_RECVREC) {
@@ -244,6 +307,8 @@ int https_fetch(const char* host, uint32_t ip, uint16_t port,
const TrustAnchors& tas,
char* respBuf, int respMax,
AbortCheckFn abort_check) {
if (!host || !*host || ip == 0 || port == 0 || !request || reqLen <= 0 ||
!respBuf || respMax < 2 || !tas.anchors || tas.count == 0) return -1;
int fd = montauk::socket(montauk::abi::SOCK_TCP);
if (fd < 0) return -1;
if (montauk::connect(fd, ip, port) < 0) { montauk::closesocket(fd); return -1; }
@@ -262,7 +327,9 @@ int https_fetch(const char* host, uint32_t ip, uint16_t port,
br_x509_minimal_set_time(xc, days, secs);
unsigned char seed[32];
montauk::getrandom(seed, sizeof(seed));
if (montauk::getrandom(seed, sizeof(seed)) != (int64_t)sizeof(seed)) {
montauk::closesocket(fd); free(cc); free(xc); free(iobuf); return -1;
}
br_ssl_engine_set_buffer(&cc->eng, iobuf, BR_SSL_BUFSIZE_BIDI, 1);
br_ssl_engine_inject_entropy(&cc->eng, seed, sizeof(seed));