fix: stop parliament accept starvation on outbound dial (0.9.22)
Peer dial used per-step read timeouts so a stuck handshake could block the single-threaded daemon for tens of seconds; inbound hellos piled up unread (CLOSE-WAIT). Use a wall-clock dial budget, drain accepts before each dial, skip self, and only one side initiates idle polls. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+72
-21
@@ -13,6 +13,33 @@
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
/* Wall-clock deadline so a multi-step handshake cannot stretch to
|
||||
* N_steps * timeout_ms (that starved the daemon accept loop). */
|
||||
static int64_t peer_now_ms(void) {
|
||||
#ifdef _WIN32
|
||||
return (int64_t)GetTickCount64();
|
||||
#else
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (int64_t)tv.tv_sec * 1000 + (int64_t)tv.tv_usec / 1000;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int peer_remain_ms(int64_t deadline_ms) {
|
||||
int64_t left = deadline_ms - peer_now_ms();
|
||||
if (left <= 0)
|
||||
return 0;
|
||||
if (left > 600000)
|
||||
return 600000;
|
||||
return (int)left;
|
||||
}
|
||||
|
||||
static void hex_encode(const uint8_t *in, size_t n, char *out) {
|
||||
static const char *H = "0123456789abcdef";
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
@@ -379,48 +406,60 @@ static int recv_sealed(cst_peer_session_t *sess, uint8_t **out, size_t *out_n,
|
||||
|
||||
static int handshake_as_client(os_socket_t s, const cst_realm_cert_t *self,
|
||||
const uint8_t self_sk[64],
|
||||
const uint8_t realm_pk[32], int timeout_ms,
|
||||
const uint8_t realm_pk[32], int64_t deadline_ms,
|
||||
cst_realm_cert_t *remote) {
|
||||
if (send_hello(s, self, timeout_ms) != 0)
|
||||
int t;
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 || send_hello(s, self, t) != 0)
|
||||
return -1;
|
||||
uint8_t nonce[32];
|
||||
if (read_chal(s, timeout_ms, nonce) != 0)
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 || read_chal(s, t, nonce) != 0)
|
||||
return -1;
|
||||
if (send_auth(s, self_sk, nonce, timeout_ms) != 0)
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 ||
|
||||
send_auth(s, self_sk, nonce, t) != 0)
|
||||
return -1;
|
||||
if (read_hello(s, realm_pk, timeout_ms, remote) != 0)
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 ||
|
||||
read_hello(s, realm_pk, t, remote) != 0)
|
||||
return -1;
|
||||
if (fill_random(nonce, 32) != 0)
|
||||
return -1;
|
||||
if (send_chal(s, nonce, timeout_ms) != 0)
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 || send_chal(s, nonce, t) != 0)
|
||||
return -1;
|
||||
if (read_auth(s, remote, nonce, timeout_ms) != 0)
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 ||
|
||||
read_auth(s, remote, nonce, t) != 0)
|
||||
return -1;
|
||||
if (send_ok(s, timeout_ms) != 0 || read_ok(s, timeout_ms) != 0)
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 || send_ok(s, t) != 0)
|
||||
return -1;
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 || read_ok(s, t) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handshake_as_server(os_socket_t s, const cst_realm_cert_t *self,
|
||||
const uint8_t self_sk[64],
|
||||
const uint8_t realm_pk[32], int timeout_ms,
|
||||
const uint8_t realm_pk[32], int64_t deadline_ms,
|
||||
cst_realm_cert_t *remote) {
|
||||
if (read_hello(s, realm_pk, timeout_ms, remote) != 0)
|
||||
int t;
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 ||
|
||||
read_hello(s, realm_pk, t, remote) != 0)
|
||||
return -1;
|
||||
uint8_t nonce[32];
|
||||
if (fill_random(nonce, 32) != 0)
|
||||
return -1;
|
||||
if (send_chal(s, nonce, timeout_ms) != 0)
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 || send_chal(s, nonce, t) != 0)
|
||||
return -1;
|
||||
if (read_auth(s, remote, nonce, timeout_ms) != 0)
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 ||
|
||||
read_auth(s, remote, nonce, t) != 0)
|
||||
return -1;
|
||||
if (send_hello(s, self, timeout_ms) != 0)
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 || send_hello(s, self, t) != 0)
|
||||
return -1;
|
||||
if (read_chal(s, timeout_ms, nonce) != 0)
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 || read_chal(s, t, nonce) != 0)
|
||||
return -1;
|
||||
if (send_auth(s, self_sk, nonce, timeout_ms) != 0)
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 ||
|
||||
send_auth(s, self_sk, nonce, t) != 0)
|
||||
return -1;
|
||||
if (read_ok(s, timeout_ms) != 0 || send_ok(s, timeout_ms) != 0)
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 || read_ok(s, t) != 0)
|
||||
return -1;
|
||||
if ((t = peer_remain_ms(deadline_ms)) <= 0 || send_ok(s, t) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
@@ -433,15 +472,19 @@ int cst_peer_dial(const char *host, int port, const cst_realm_cert_t *self,
|
||||
err[0] = 0;
|
||||
if (!host || !self || !self_sk || !realm_pk || !out)
|
||||
return -1;
|
||||
if (timeout_ms <= 0)
|
||||
timeout_ms = 3000;
|
||||
int64_t deadline = peer_now_ms() + timeout_ms;
|
||||
os_net_init();
|
||||
os_socket_t s = os_net_connect(host, port, timeout_ms);
|
||||
int t = peer_remain_ms(deadline);
|
||||
os_socket_t s = os_net_connect(host, port, t);
|
||||
if (s == OS_SOCKET_BAD) {
|
||||
if (err && err_sz)
|
||||
snprintf(err, err_sz, "connect failed");
|
||||
return -1;
|
||||
}
|
||||
cst_realm_cert_t remote;
|
||||
if (handshake_as_client(s, self, self_sk, realm_pk, timeout_ms, &remote) !=
|
||||
if (handshake_as_client(s, self, self_sk, realm_pk, deadline, &remote) !=
|
||||
0) {
|
||||
if (err && err_sz)
|
||||
snprintf(err, err_sz, "peer handshake failed");
|
||||
@@ -453,7 +496,8 @@ int cst_peer_dial(const char *host, int port, const cst_realm_cert_t *self,
|
||||
snprintf(out->host, sizeof(out->host), "%s", host);
|
||||
out->port = port;
|
||||
out->sock = (intptr_t)s;
|
||||
if (peer_kx(s, 1, out->aead_key, timeout_ms) != 0) {
|
||||
t = peer_remain_ms(deadline);
|
||||
if (t <= 0 || peer_kx(s, 1, out->aead_key, t) != 0) {
|
||||
if (err && err_sz)
|
||||
snprintf(err, err_sz, "peer key exchange failed");
|
||||
cst_peer_close(out);
|
||||
@@ -471,15 +515,21 @@ int cst_peer_accept(intptr_t listen_sock, const cst_realm_cert_t *self,
|
||||
err[0] = 0;
|
||||
if (!self || !self_sk || !realm_pk || !out)
|
||||
return -1;
|
||||
if (timeout_ms <= 0)
|
||||
timeout_ms = 3000;
|
||||
int timed = 0;
|
||||
/* Accept wait uses the caller budget; handshake+kx share a fresh deadline
|
||||
* after the connection is taken, so a long accept wait does not shrink the
|
||||
* crypto exchange to zero. */
|
||||
os_socket_t s = os_net_accept((os_socket_t)listen_sock, timeout_ms, &timed);
|
||||
if (s == OS_SOCKET_BAD) {
|
||||
if (err && err_sz)
|
||||
snprintf(err, err_sz, timed ? "accept timeout" : "accept failed");
|
||||
return -1;
|
||||
}
|
||||
int64_t deadline = peer_now_ms() + (timeout_ms < 3000 ? 3000 : timeout_ms);
|
||||
cst_realm_cert_t remote;
|
||||
if (handshake_as_server(s, self, self_sk, realm_pk, timeout_ms, &remote) !=
|
||||
if (handshake_as_server(s, self, self_sk, realm_pk, deadline, &remote) !=
|
||||
0) {
|
||||
if (err && err_sz)
|
||||
snprintf(err, err_sz, "peer handshake failed");
|
||||
@@ -491,7 +541,8 @@ int cst_peer_accept(intptr_t listen_sock, const cst_realm_cert_t *self,
|
||||
os_net_peer(s, out->host, sizeof(out->host));
|
||||
out->port = 0;
|
||||
out->sock = (intptr_t)s;
|
||||
if (peer_kx(s, 0, out->aead_key, timeout_ms) != 0) {
|
||||
int t = peer_remain_ms(deadline);
|
||||
if (t <= 0 || peer_kx(s, 0, out->aead_key, t) != 0) {
|
||||
if (err && err_sz)
|
||||
snprintf(err, err_sz, "peer key exchange failed");
|
||||
cst_peer_close(out);
|
||||
|
||||
Reference in New Issue
Block a user