Fix Windows networking bugs and connection metadata mismatch
- Use closesocket() instead of close() for sockets on Windows (lib/tcp.c) - Call WSAStartup in tcp_init so outbound connections work (lib/tcp.c) - Check WSAGetLastError() instead of errno for recv/send on Windows (lib/tcp.c) - Use WSAEWOULDBLOCK instead of EINPROGRESS for non-blocking connect (lib/tcp.c) - Add NULL guard in tcp_write for stale connection handles (lib/tcp.c) - Fix ConnMetadata/TCP_Conn index mismatch in get_next_message: use event.handle.idx directly instead of searching for a free metadata slot, which could diverge when stale entries remain from failed outbound connections (lib/message.c) - Initialize num_senders to 0 and skip unused metadata in find_conn_by_target (lib/message.c) - Clear ready flag in http_server_next_request to prevent infinite loop (lib/http_server.c) - Add CRT invalid parameter handler for the HTTP proxy (src/main.c) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -245,6 +245,7 @@ bool http_server_next_request(HTTP_Server *server,
|
||||
continue;
|
||||
|
||||
if (server->conns[i].ready) {
|
||||
server->conns[i].ready = false;
|
||||
*request = &server->conns[i].request;
|
||||
*builder = (HTTP_ResponseBuilder) {
|
||||
.server = server,
|
||||
|
||||
+11
-8
@@ -26,6 +26,7 @@ int message_system_init(MessageSystem *msys,
|
||||
for (int i = 0; i < max_conns; i++) {
|
||||
msys->conns[i].used = false;
|
||||
msys->conns[i].gen = 0;
|
||||
msys->conns[i].num_senders = 0;
|
||||
}
|
||||
|
||||
int ret = tcp_init(&msys->tcp, max_conns);
|
||||
@@ -81,17 +82,17 @@ void *get_next_message(MessageSystem *msys)
|
||||
|
||||
// Skip if already set up by ensure_conn (outbound connection)
|
||||
if (tcp_get_user_ptr(event.handle) == NULL) {
|
||||
int i = 0;
|
||||
while (i < msys->max_conns && msys->conns[i].used)
|
||||
i++;
|
||||
|
||||
if (i == msys->max_conns) {
|
||||
tcp_close(event.handle);
|
||||
continue;
|
||||
}
|
||||
// Use the TCP connection's slot index so that the
|
||||
// ConnMetadata index always matches the TCP_Conn index.
|
||||
// Searching for a "free" metadata slot could assign a
|
||||
// different index when stale metadata entries remain
|
||||
// from failed outbound connections.
|
||||
int i = event.handle.idx;
|
||||
assert(i >= 0 && i < msys->max_conns);
|
||||
|
||||
ConnMetadata *meta = &msys->conns[i];
|
||||
meta->used = true;
|
||||
meta->gen = event.handle.gen;
|
||||
meta->num_senders = 0;
|
||||
meta->message = NULL;
|
||||
tcp_set_user_ptr(event.handle, meta);
|
||||
@@ -168,6 +169,8 @@ static TCP_Handle find_conn_by_target(MessageSystem *msys, int target)
|
||||
{
|
||||
for (int i = 0; i < msys->max_conns; i++) {
|
||||
ConnMetadata *meta = &msys->conns[i];
|
||||
if (!meta->used)
|
||||
continue;
|
||||
for (int j = 0; j < meta->num_senders; j++) {
|
||||
if (meta->senders[j] == target) {
|
||||
return (TCP_Handle) { &msys->tcp, meta->gen, i };
|
||||
|
||||
@@ -15,6 +15,18 @@ typedef SOCKET NATIVE_SOCKET;
|
||||
typedef int NATIVE_SOCKET;
|
||||
#endif
|
||||
|
||||
// On Windows, sockets must be closed with closesocket(), not close().
|
||||
// close() is a CRT function for file descriptors and triggers an
|
||||
// "Invalid parameter" abort when called on a socket handle.
|
||||
static void close_socket(int fd)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(QUAKEY_ENABLE_MOCKS)
|
||||
closesocket(fd);
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void tcp_conn_free(TCP_Conn *conn);
|
||||
static bool tcp_conn_free_maybe(TCP_Conn *conn);
|
||||
|
||||
@@ -57,7 +69,7 @@ static int create_listen_socket(string addr, uint16_t port,
|
||||
return -1;
|
||||
|
||||
if (set_socket_blocking(fd, false) < 0) {
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -73,14 +85,14 @@ static int create_listen_socket(string addr, uint16_t port,
|
||||
|
||||
char copy[100];
|
||||
if (addr.len >= (int) sizeof(copy)) {
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return -1;
|
||||
}
|
||||
memcpy(copy, addr.ptr, addr.len);
|
||||
copy[addr.len] = '\0';
|
||||
|
||||
if (inet_pton(AF_INET, copy, &addr_buf) < 0) {
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -90,12 +102,12 @@ static int create_listen_socket(string addr, uint16_t port,
|
||||
bind_buf.sin_addr = addr_buf;
|
||||
bind_buf.sin_port = htons(port);
|
||||
if (bind(fd, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) {
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(fd, backlog) < 0) {
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -104,6 +116,13 @@ static int create_listen_socket(string addr, uint16_t port,
|
||||
|
||||
int tcp_init(TCP *tcp, int max_conns)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(QUAKEY_ENABLE_MOCKS)
|
||||
{
|
||||
WSADATA wsa;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsa);
|
||||
}
|
||||
#endif
|
||||
|
||||
TCP_Conn *conns = malloc(max_conns * sizeof(TCP_Conn));
|
||||
if (conns == NULL)
|
||||
return -1;
|
||||
@@ -131,11 +150,11 @@ void tcp_free(TCP *tcp)
|
||||
free(tcp->conns);
|
||||
|
||||
if (tcp->tcp_listen_fd != -1)
|
||||
close(tcp->tcp_listen_fd);
|
||||
close_socket(tcp->tcp_listen_fd);
|
||||
|
||||
#ifdef TLS_ENABLED
|
||||
if (tcp->tls_listen_fd != -1) {
|
||||
close(tcp->tls_listen_fd);
|
||||
close_socket(tcp->tls_listen_fd);
|
||||
tls_server_free(&tcp->tls);
|
||||
}
|
||||
#endif
|
||||
@@ -211,7 +230,7 @@ static void tcp_conn_init(TCP *tcp, TCP_Conn *conn, bool secure, TCP_ConnState s
|
||||
static void tcp_conn_free(TCP_Conn *conn)
|
||||
{
|
||||
if (conn->fd >= 0) {
|
||||
close(conn->fd);
|
||||
close_socket(conn->fd);
|
||||
conn->fd = -1;
|
||||
}
|
||||
|
||||
@@ -396,13 +415,13 @@ int tcp_connect(TCP *tcp, bool secure, Address *addrs, int num_addrs)
|
||||
}
|
||||
|
||||
if (set_socket_blocking(fd, false) < 0) {
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in sa;
|
||||
if (build_sockaddr(&addrs[0], &sa) < 0) {
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -416,10 +435,15 @@ int tcp_connect(TCP *tcp, bool secure, Address *addrs, int num_addrs)
|
||||
}
|
||||
} else {
|
||||
assert(ret < 0);
|
||||
if (errno == EINPROGRESS) {
|
||||
#if defined(_WIN32) && !defined(QUAKEY_ENABLE_MOCKS)
|
||||
int connect_in_progress = (WSAGetLastError() == WSAEWOULDBLOCK);
|
||||
#else
|
||||
int connect_in_progress = (errno == EINPROGRESS);
|
||||
#endif
|
||||
if (connect_in_progress) {
|
||||
state = TCP_CONN_STATE_CONNECTING;
|
||||
} else {
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -432,7 +456,7 @@ int tcp_connect(TCP *tcp, bool secure, Address *addrs, int num_addrs)
|
||||
|
||||
static int restart_connect(TCP_Conn *conn)
|
||||
{
|
||||
close(conn->fd);
|
||||
close_socket(conn->fd);
|
||||
conn->fd = -1;
|
||||
|
||||
conn->addr_idx++;
|
||||
@@ -446,13 +470,13 @@ static int restart_connect(TCP_Conn *conn)
|
||||
}
|
||||
|
||||
if (set_socket_blocking(fd, false) < 0) {
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in sa;
|
||||
if (build_sockaddr(&conn->addrs[conn->addr_idx], &sa) < 0) {
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -466,10 +490,15 @@ static int restart_connect(TCP_Conn *conn)
|
||||
}
|
||||
} else {
|
||||
assert(ret < 0);
|
||||
if (errno == EINPROGRESS) {
|
||||
#if defined(_WIN32) && !defined(QUAKEY_ENABLE_MOCKS)
|
||||
int connect_in_progress = (WSAGetLastError() == WSAEWOULDBLOCK);
|
||||
#else
|
||||
int connect_in_progress = (errno == EINPROGRESS);
|
||||
#endif
|
||||
if (connect_in_progress) {
|
||||
state = TCP_CONN_STATE_CONNECTING;
|
||||
} else {
|
||||
close(fd);
|
||||
close_socket(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -501,7 +530,7 @@ void tcp_process_events(TCP *tcp, void **ptrs, struct pollfd *arr, int num)
|
||||
continue;
|
||||
|
||||
if (set_socket_blocking(new_fd, false) < 0) {
|
||||
close(new_fd);
|
||||
close_socket(new_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -510,7 +539,7 @@ void tcp_process_events(TCP *tcp, void **ptrs, struct pollfd *arr, int num)
|
||||
while (slot < tcp->max_conns && tcp->conns[slot].state != TCP_CONN_STATE_FREE)
|
||||
slot++;
|
||||
if (slot == tcp->max_conns) {
|
||||
close(new_fd);
|
||||
close_socket(new_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -539,7 +568,8 @@ void tcp_process_events(TCP *tcp, void **ptrs, struct pollfd *arr, int num)
|
||||
{
|
||||
int err = 0;
|
||||
socklen_t len = sizeof(err);
|
||||
if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, (void*) &err, &len) < 0) {
|
||||
int gsret = getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, (void*) &err, &len);
|
||||
if (gsret < 0) {
|
||||
defer_connect = true;
|
||||
break;
|
||||
}
|
||||
@@ -568,8 +598,13 @@ void tcp_process_events(TCP *tcp, void **ptrs, struct pollfd *arr, int num)
|
||||
int n = recv(conn->fd, buf, cap, 0);
|
||||
if (n == 0) { defer_close = true; break; }
|
||||
if (n < 0) {
|
||||
#if defined(_WIN32) && !defined(QUAKEY_ENABLE_MOCKS)
|
||||
if (WSAGetLastError() != WSAEWOULDBLOCK)
|
||||
{ defer_close = true; break; }
|
||||
#else
|
||||
if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN)
|
||||
{ defer_close = true; break; }
|
||||
#endif
|
||||
n = 0;
|
||||
}
|
||||
tls_conn_net_write_ack(&conn->tls, n);
|
||||
@@ -588,8 +623,13 @@ void tcp_process_events(TCP *tcp, void **ptrs, struct pollfd *arr, int num)
|
||||
if (buf) {
|
||||
int n = send(conn->fd, buf, num, 0);
|
||||
if (n < 0) {
|
||||
#if defined(_WIN32) && !defined(QUAKEY_ENABLE_MOCKS)
|
||||
if (WSAGetLastError() != WSAEWOULDBLOCK)
|
||||
{ defer_close = true; break; }
|
||||
#else
|
||||
if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN)
|
||||
{ defer_close = true; break; }
|
||||
#endif
|
||||
n = 0;
|
||||
}
|
||||
tls_conn_net_read_ack(&conn->tls, n);
|
||||
@@ -621,12 +661,20 @@ void tcp_process_events(TCP *tcp, void **ptrs, struct pollfd *arr, int num)
|
||||
if (arr[i].revents & POLLIN) {
|
||||
ByteView buf = tcp_conn_write_buf(conn);
|
||||
int n = recv(conn->fd, (char*) buf.ptr, buf.len, 0);
|
||||
#if defined(_WIN32) && !defined(QUAKEY_ENABLE_MOCKS)
|
||||
int last_err = WSAGetLastError();
|
||||
#endif
|
||||
if (n == 0) {
|
||||
defer_close = true;
|
||||
} else {
|
||||
if (n < 0) {
|
||||
#if defined(_WIN32) && !defined(QUAKEY_ENABLE_MOCKS)
|
||||
if (last_err != WSAEWOULDBLOCK)
|
||||
defer_close = true;
|
||||
#else
|
||||
if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN)
|
||||
defer_close = true;
|
||||
#endif
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
@@ -640,8 +688,13 @@ void tcp_process_events(TCP *tcp, void **ptrs, struct pollfd *arr, int num)
|
||||
ByteView buf = tcp_conn_read_buf(conn);
|
||||
int n = send(conn->fd, (char*) buf.ptr, buf.len, 0);
|
||||
if (n < 0) {
|
||||
#if defined(_WIN32) && !defined(QUAKEY_ENABLE_MOCKS)
|
||||
if (WSAGetLastError() != WSAEWOULDBLOCK)
|
||||
defer_close = true;
|
||||
#else
|
||||
if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN)
|
||||
defer_close = true;
|
||||
#endif
|
||||
n = 0;
|
||||
}
|
||||
tcp_conn_read_ack(conn, n);
|
||||
@@ -673,7 +726,7 @@ void tcp_process_events(TCP *tcp, void **ptrs, struct pollfd *arr, int num)
|
||||
|
||||
if (defer_close) {
|
||||
|
||||
close(conn->fd);
|
||||
close_socket(conn->fd);
|
||||
conn->fd = -1;
|
||||
conn->events |= TCP_EVENT_HUP;
|
||||
|
||||
@@ -846,7 +899,10 @@ TCP_Offset tcp_write_off(TCP_Handle handle)
|
||||
void tcp_write(TCP_Handle handle, string str)
|
||||
{
|
||||
while (str.len > 0) {
|
||||
byte_queue_write_setmincap(&handle_to_conn(handle)->output, str.len);
|
||||
TCP_Conn *conn = handle_to_conn(handle);
|
||||
if (conn == NULL)
|
||||
return;
|
||||
byte_queue_write_setmincap(&conn->output, str.len);
|
||||
ByteView buf = tcp_write_buf(handle);
|
||||
int num = MIN(buf.len, str.len);
|
||||
memcpy(buf.ptr, str.ptr, num);
|
||||
|
||||
+1
-1
@@ -272,4 +272,4 @@ int http_proxy_free(void *state)
|
||||
http_server_free(&proxy->http_server);
|
||||
free(proxy->opers);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -319,6 +319,16 @@ int main(int argc, char **argv)
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
static void invalid_param_handler(const wchar_t *expr, const wchar_t *func,
|
||||
const wchar_t *file, unsigned int line, uintptr_t reserved)
|
||||
{
|
||||
(void)expr; (void)func; (void)file; (void)line; (void)reserved;
|
||||
fprintf(stderr, "[CRT] Invalid parameter in %ls (%ls:%u)\n",
|
||||
func ? func : L"?", file ? file : L"?", line);
|
||||
fflush(stderr);
|
||||
}
|
||||
#else
|
||||
#include <poll.h>
|
||||
#endif
|
||||
@@ -329,6 +339,9 @@ int main(int argc, char **argv)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
_set_invalid_parameter_handler(invalid_param_handler);
|
||||
#endif
|
||||
int ret;
|
||||
|
||||
HTTPProxy state;
|
||||
|
||||
Reference in New Issue
Block a user