mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
fix(usockets): safely handle socket reallocation during context adoption (#25361)
## Summary - Fix use-after-free vulnerability during socket adoption by properly tracking reallocated sockets - Add safety checks to prevent linking closed sockets to context lists - Properly track socket state with new `is_closed`, `adopted`, and `is_tls` flags ## What does this PR do? This PR improves event loop stability by addressing potential use-after-free issues that can occur when sockets are reallocated during adoption (e.g., when upgrading a TCP socket to TLS). ### Key Changes **Socket State Tracking ([internal.h](packages/bun-usockets/src/internal/internal.h))** - Added `is_closed` flag to explicitly track when a socket has been closed - Added `adopted` flag to mark sockets that were reallocated during context adoption - Added `is_tls` flag to track TLS socket state for proper low-priority queue handling **Safe Socket Adoption ([context.c](packages/bun-usockets/src/context.c))** - When `us_poll_resize()` returns a new pointer (reallocation occurred), the old socket is now: - Marked as closed (`is_closed = 1`) - Added to the closed socket cleanup list - Marked as adopted (`adopted = 1`) - Has its `prev` pointer set to the new socket for event redirection - Added guards to `us_internal_socket_context_link_socket/listen_socket/connecting_socket` to prevent linking already-closed sockets **Event Loop Handling ([loop.c](packages/bun-usockets/src/loop.c))** - After callbacks that can trigger socket adoption (`on_open`, `on_writable`, `on_data`), the event loop now checks if the socket was reallocated and redirects to the new socket - Low-priority socket handling now properly checks `is_closed` state and uses `is_tls` flag for correct SSL handling **Poll Resize Safety ([epoll_kqueue.c](packages/bun-usockets/src/eventing/epoll_kqueue.c))** - Changed `us_poll_resize()` to always allocate new memory with `us_calloc()` instead of `us_realloc()` to ensure the old pointer remains valid for cleanup - Now takes `old_ext_size` parameter to correctly calculate memory sizes - Re-enabled `us_internal_loop_update_pending_ready_polls()` call in `us_poll_change()` to ensure pending events are properly redirected ### How did you verify your code works? Run existing CI and existing socket upgrade tests under asan build
This commit is contained in:
@@ -54,8 +54,8 @@ void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) {
|
||||
s->next = loop->data.closed_head;
|
||||
loop->data.closed_head = s;
|
||||
|
||||
/* Any socket with prev = context is marked as closed */
|
||||
s->prev = (struct us_socket_t *) context;
|
||||
/* Mark the socket as closed */
|
||||
s->flags.is_closed = 1;
|
||||
}
|
||||
|
||||
/* We cannot immediately free a listen socket as we can be inside an accept loop */
|
||||
@@ -154,7 +154,9 @@ void us_internal_socket_context_unlink_connecting_socket(int ssl, struct us_sock
|
||||
|
||||
/* We always add in the top, so we don't modify any s.next */
|
||||
void us_internal_socket_context_link_listen_socket(int ssl, struct us_socket_context_t *context, struct us_listen_socket_t *ls) {
|
||||
|
||||
struct us_socket_t* s = &ls->s;
|
||||
if(us_socket_is_closed(ssl, s)) return;
|
||||
s->context = context;
|
||||
s->next = (struct us_socket_t *) context->head_listen_sockets;
|
||||
s->prev = 0;
|
||||
@@ -166,6 +168,8 @@ void us_internal_socket_context_link_listen_socket(int ssl, struct us_socket_con
|
||||
}
|
||||
|
||||
void us_internal_socket_context_link_connecting_socket(int ssl, struct us_socket_context_t *context, struct us_connecting_socket_t *c) {
|
||||
if(c->closed) return;
|
||||
|
||||
c->context = context;
|
||||
c->next_pending = context->head_connecting_sockets;
|
||||
c->prev_pending = 0;
|
||||
@@ -180,6 +184,8 @@ void us_internal_socket_context_link_connecting_socket(int ssl, struct us_socket
|
||||
|
||||
/* We always add in the top, so we don't modify any s.next */
|
||||
void us_internal_socket_context_link_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s) {
|
||||
if(us_socket_is_closed(ssl,s)) return;
|
||||
|
||||
s->context = context;
|
||||
s->next = context->head_sockets;
|
||||
s->prev = 0;
|
||||
@@ -386,6 +392,9 @@ struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_co
|
||||
s->flags.low_prio_state = 0;
|
||||
s->flags.is_paused = 0;
|
||||
s->flags.is_ipc = 0;
|
||||
s->flags.is_closed = 0;
|
||||
s->flags.adopted = 0;
|
||||
s->flags.is_tls = ssl;
|
||||
s->next = 0;
|
||||
s->flags.allow_half_open = (options & LIBUS_SOCKET_ALLOW_HALF_OPEN);
|
||||
us_internal_socket_context_link_listen_socket(ssl, context, ls);
|
||||
@@ -422,6 +431,9 @@ struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_sock
|
||||
s->flags.allow_half_open = (options & LIBUS_SOCKET_ALLOW_HALF_OPEN);
|
||||
s->flags.is_paused = 0;
|
||||
s->flags.is_ipc = 0;
|
||||
s->flags.is_closed = 0;
|
||||
s->flags.adopted = 0;
|
||||
s->flags.is_tls = ssl;
|
||||
s->next = 0;
|
||||
us_internal_socket_context_link_listen_socket(ssl, context, ls);
|
||||
|
||||
@@ -430,7 +442,7 @@ struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_sock
|
||||
return ls;
|
||||
}
|
||||
|
||||
struct us_socket_t* us_socket_context_connect_resolved_dns(struct us_socket_context_t *context, struct sockaddr_storage* addr, int options, int socket_ext_size) {
|
||||
struct us_socket_t* us_socket_context_connect_resolved_dns(int ssl, struct us_socket_context_t *context, struct sockaddr_storage* addr, int options, int socket_ext_size) {
|
||||
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket(addr, options);
|
||||
if (connect_socket_fd == LIBUS_SOCKET_ERROR) {
|
||||
return NULL;
|
||||
@@ -453,6 +465,9 @@ struct us_socket_t* us_socket_context_connect_resolved_dns(struct us_socket_cont
|
||||
socket->flags.allow_half_open = (options & LIBUS_SOCKET_ALLOW_HALF_OPEN);
|
||||
socket->flags.is_paused = 0;
|
||||
socket->flags.is_ipc = 0;
|
||||
socket->flags.is_closed = 0;
|
||||
socket->flags.adopted = 0;
|
||||
socket->flags.is_tls = ssl;
|
||||
socket->connect_state = NULL;
|
||||
socket->connect_next = NULL;
|
||||
|
||||
@@ -514,7 +529,7 @@ void *us_socket_context_connect(int ssl, struct us_socket_context_t *context, co
|
||||
struct sockaddr_storage addr;
|
||||
if (try_parse_ip(host, port, &addr)) {
|
||||
*has_dns_resolved = 1;
|
||||
return us_socket_context_connect_resolved_dns(context, &addr, options, socket_ext_size);
|
||||
return us_socket_context_connect_resolved_dns(ssl, context, &addr, options, socket_ext_size);
|
||||
}
|
||||
|
||||
struct addrinfo_request* ai_req;
|
||||
@@ -534,7 +549,7 @@ void *us_socket_context_connect(int ssl, struct us_socket_context_t *context, co
|
||||
struct sockaddr_storage addr;
|
||||
init_addr_with_port(&entries->info, port, &addr);
|
||||
*has_dns_resolved = 1;
|
||||
struct us_socket_t *s = us_socket_context_connect_resolved_dns(context, &addr, options, socket_ext_size);
|
||||
struct us_socket_t *s = us_socket_context_connect_resolved_dns(ssl, context, &addr, options, socket_ext_size);
|
||||
Bun__addrinfo_freeRequest(ai_req, s == NULL);
|
||||
return s;
|
||||
}
|
||||
@@ -583,6 +598,9 @@ int start_connections(struct us_connecting_socket_t *c, int count) {
|
||||
flags->allow_half_open = (c->options & LIBUS_SOCKET_ALLOW_HALF_OPEN);
|
||||
flags->is_paused = 0;
|
||||
flags->is_ipc = 0;
|
||||
flags->is_closed = 0;
|
||||
flags->adopted = 0;
|
||||
flags->is_tls = c->ssl;
|
||||
/* Link it into context so that timeout fires properly */
|
||||
us_internal_socket_context_link_socket(0, context, s);
|
||||
|
||||
@@ -760,6 +778,9 @@ struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_con
|
||||
connect_socket->flags.allow_half_open = (options & LIBUS_SOCKET_ALLOW_HALF_OPEN);
|
||||
connect_socket->flags.is_paused = 0;
|
||||
connect_socket->flags.is_ipc = 0;
|
||||
connect_socket->flags.is_closed = 0;
|
||||
connect_socket->flags.adopted = 0;
|
||||
connect_socket->flags.is_tls = ssl;
|
||||
connect_socket->connect_state = NULL;
|
||||
connect_socket->connect_next = NULL;
|
||||
us_internal_socket_context_link_socket(ssl, context, connect_socket);
|
||||
@@ -780,10 +801,10 @@ struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_so
|
||||
}
|
||||
|
||||
/* Note: This will set timeout to 0 */
|
||||
struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int ext_size) {
|
||||
struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int old_ext_size, int ext_size) {
|
||||
#ifndef LIBUS_NO_SSL
|
||||
if (ssl) {
|
||||
return (struct us_socket_t *) us_internal_ssl_socket_context_adopt_socket((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t *) s, ext_size);
|
||||
return (struct us_socket_t *) us_internal_ssl_socket_context_adopt_socket((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t *) s, old_ext_size, ext_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -807,7 +828,18 @@ struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_con
|
||||
struct us_socket_t *new_s = s;
|
||||
if (ext_size != -1) {
|
||||
struct us_poll_t *pool_ref = &s->p;
|
||||
new_s = (struct us_socket_t *) us_poll_resize(pool_ref, loop, sizeof(struct us_socket_t) + ext_size);
|
||||
new_s = (struct us_socket_t *) us_poll_resize(pool_ref, loop, sizeof(struct us_socket_t) + old_ext_size, sizeof(struct us_socket_t) + ext_size);
|
||||
if(new_s != s) {
|
||||
/* Mark the old socket as closed */
|
||||
s->flags.is_closed = 1;
|
||||
/* Link this socket to the close-list and let it be deleted after this iteration */
|
||||
s->next = s->context->loop->data.closed_head;
|
||||
s->context->loop->data.closed_head = s;
|
||||
/* Mark the old socket as adopted (reallocated) */
|
||||
s->flags.adopted = 1;
|
||||
/* Tell the event loop what is the new socket so we can process to send info to the right place and callbacks like more data and EOF*/
|
||||
s->prev = new_s;
|
||||
}
|
||||
if (c) {
|
||||
c->connecting_head = new_s;
|
||||
c->context = context;
|
||||
|
||||
@@ -396,7 +396,7 @@ void us_internal_update_handshake(struct us_internal_ssl_socket_t *s) {
|
||||
}
|
||||
|
||||
int result = SSL_do_handshake(s->ssl);
|
||||
|
||||
|
||||
if (SSL_get_shutdown(s->ssl) & SSL_RECEIVED_SHUTDOWN) {
|
||||
us_internal_ssl_socket_close(s, 0, NULL);
|
||||
return;
|
||||
@@ -417,6 +417,7 @@ void us_internal_update_handshake(struct us_internal_ssl_socket_t *s) {
|
||||
}
|
||||
s->handshake_state = HANDSHAKE_PENDING;
|
||||
s->ssl_write_wants_read = 1;
|
||||
s->s.context->loop->data.last_write_failed = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -434,6 +435,7 @@ ssl_on_close(struct us_internal_ssl_socket_t *s, int code, void *reason) {
|
||||
struct us_internal_ssl_socket_t * ret = context->on_close(s, code, reason);
|
||||
SSL_free(s->ssl); // free SSL after on_close
|
||||
s->ssl = NULL; // set to NULL
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1855,15 +1857,16 @@ void us_internal_ssl_socket_shutdown(struct us_internal_ssl_socket_t *s) {
|
||||
|
||||
struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_adopt_socket(
|
||||
struct us_internal_ssl_socket_context_t *context,
|
||||
struct us_internal_ssl_socket_t *s, int ext_size) {
|
||||
struct us_internal_ssl_socket_t *s, int old_ext_size, int ext_size) {
|
||||
// todo: this is completely untested
|
||||
int new_old_ext_size = sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + old_ext_size;
|
||||
int new_ext_size = ext_size;
|
||||
if (ext_size != -1) {
|
||||
new_ext_size = sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + ext_size;
|
||||
}
|
||||
return (struct us_internal_ssl_socket_t *)us_socket_context_adopt_socket(
|
||||
0, &context->sc, &s->s,
|
||||
new_ext_size);
|
||||
new_old_ext_size, new_ext_size);
|
||||
}
|
||||
|
||||
struct us_internal_ssl_socket_t *
|
||||
@@ -1920,10 +1923,11 @@ ssl_wrapped_context_on_data(struct us_internal_ssl_socket_t *s, char *data,
|
||||
struct us_wrapped_socket_context_t *wrapped_context =
|
||||
(struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(
|
||||
context);
|
||||
// raw data if needed
|
||||
// raw data if needed
|
||||
if (wrapped_context->old_events.on_data) {
|
||||
wrapped_context->old_events.on_data((struct us_socket_t *)s, data, length);
|
||||
}
|
||||
|
||||
// ssl wrapped data
|
||||
return ssl_on_data(s, data, length);
|
||||
}
|
||||
@@ -2028,7 +2032,7 @@ us_internal_ssl_socket_open(struct us_internal_ssl_socket_t *s, int is_client,
|
||||
// already opened
|
||||
if (s->ssl)
|
||||
return s;
|
||||
|
||||
|
||||
// start SSL open
|
||||
return ssl_on_open(s, is_client, ip, ip_length, NULL);
|
||||
}
|
||||
@@ -2040,6 +2044,7 @@ struct us_socket_t *us_socket_upgrade_to_tls(us_socket_r s, us_socket_context_r
|
||||
struct us_internal_ssl_socket_t *socket =
|
||||
(struct us_internal_ssl_socket_t *)us_socket_context_adopt_socket(
|
||||
0, new_context, s,
|
||||
sizeof(void*),
|
||||
(sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t)) + sizeof(void*));
|
||||
socket->ssl = NULL;
|
||||
socket->ssl_write_wants_read = 0;
|
||||
@@ -2058,7 +2063,7 @@ struct us_socket_t *us_socket_upgrade_to_tls(us_socket_r s, us_socket_context_r
|
||||
|
||||
struct us_internal_ssl_socket_t *us_internal_ssl_socket_wrap_with_tls(
|
||||
struct us_socket_t *s, struct us_bun_socket_context_options_t options,
|
||||
struct us_socket_events_t events, int socket_ext_size) {
|
||||
struct us_socket_events_t events, int old_socket_ext_size, int socket_ext_size) {
|
||||
/* Cannot wrap a closed socket */
|
||||
if (us_socket_is_closed(0, s)) {
|
||||
return NULL;
|
||||
@@ -2163,6 +2168,7 @@ us_socket_context_on_socket_connect_error(
|
||||
struct us_internal_ssl_socket_t *socket =
|
||||
(struct us_internal_ssl_socket_t *)us_socket_context_adopt_socket(
|
||||
0, context, s,
|
||||
old_socket_ext_size,
|
||||
sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) +
|
||||
socket_ext_size);
|
||||
socket->ssl = NULL;
|
||||
|
||||
@@ -325,7 +325,7 @@ void us_internal_loop_update_pending_ready_polls(struct us_loop_t *loop, struct
|
||||
int num_entries_possibly_remaining = 1;
|
||||
#else
|
||||
/* Ready polls may contain same poll twice under kqueue, as one poll may hold two filters */
|
||||
int num_entries_possibly_remaining = 2;//((old_events & LIBUS_SOCKET_READABLE) ? 1 : 0) + ((old_events & LIBUS_SOCKET_WRITABLE) ? 1 : 0);
|
||||
int num_entries_possibly_remaining = 2;
|
||||
#endif
|
||||
|
||||
/* Todo: for kqueue if we track things in us_change_poll it is possible to have a fast path with no seeking in cases of:
|
||||
@@ -377,22 +377,30 @@ int kqueue_change(int kqfd, int fd, int old_events, int new_events, void *user_d
|
||||
}
|
||||
#endif
|
||||
|
||||
struct us_poll_t *us_poll_resize(struct us_poll_t *p, struct us_loop_t *loop, unsigned int ext_size) {
|
||||
int events = us_poll_events(p);
|
||||
|
||||
struct us_poll_t *us_poll_resize(struct us_poll_t *p, struct us_loop_t *loop, unsigned int old_ext_size, unsigned int ext_size) {
|
||||
|
||||
struct us_poll_t *new_p = us_realloc(p, sizeof(struct us_poll_t) + ext_size);
|
||||
if (p != new_p) {
|
||||
unsigned int old_size = sizeof(struct us_poll_t) + old_ext_size;
|
||||
unsigned int new_size = sizeof(struct us_poll_t) + ext_size;
|
||||
if(new_size <= old_size) return p;
|
||||
|
||||
struct us_poll_t *new_p = us_calloc(1, new_size);
|
||||
memcpy(new_p, p, old_size);
|
||||
|
||||
/* Increment poll count for the new poll - the old poll will be freed separately
|
||||
* which decrements the count, keeping the total correct */
|
||||
loop->num_polls++;
|
||||
|
||||
int events = us_poll_events(p);
|
||||
#ifdef LIBUS_USE_EPOLL
|
||||
/* Hack: forcefully update poll by stripping away already set events */
|
||||
new_p->state.poll_type = us_internal_poll_type(new_p);
|
||||
us_poll_change(new_p, loop, events);
|
||||
/* Hack: forcefully update poll by stripping away already set events */
|
||||
new_p->state.poll_type = us_internal_poll_type(new_p);
|
||||
us_poll_change(new_p, loop, events);
|
||||
#else
|
||||
/* Forcefully update poll by resetting them with new_p as user data */
|
||||
kqueue_change(loop->fd, new_p->state.fd, 0, LIBUS_SOCKET_WRITABLE | LIBUS_SOCKET_READABLE, new_p);
|
||||
#endif /* This is needed for epoll also (us_change_poll doesn't update the old poll) */
|
||||
us_internal_loop_update_pending_ready_polls(loop, p, new_p, events, events);
|
||||
}
|
||||
/* Forcefully update poll by resetting them with new_p as user data */
|
||||
kqueue_change(loop->fd, new_p->state.fd, 0, LIBUS_SOCKET_WRITABLE | LIBUS_SOCKET_READABLE, new_p);
|
||||
#endif
|
||||
/* This is needed for epoll also (us_change_poll doesn't update the old poll) */
|
||||
us_internal_loop_update_pending_ready_polls(loop, p, new_p, events, events);
|
||||
|
||||
return new_p;
|
||||
}
|
||||
@@ -444,7 +452,7 @@ void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events) {
|
||||
kqueue_change(loop->fd, p->state.fd, old_events, events, p);
|
||||
#endif
|
||||
/* Set all removed events to null-polls in pending ready poll list */
|
||||
// us_internal_loop_update_pending_ready_polls(loop, p, p, old_events, events);
|
||||
us_internal_loop_update_pending_ready_polls(loop, p, p, old_events, events);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,11 @@ void us_poll_init(struct us_poll_t *p, LIBUS_SOCKET_DESCRIPTOR fd,
|
||||
}
|
||||
|
||||
void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop) {
|
||||
// poll was adopted and dont own uv_poll_t anymore
|
||||
if(!p->uv_p) {
|
||||
free(p);
|
||||
return;
|
||||
}
|
||||
/* The idea here is like so; in us_poll_stop we call uv_close after setting
|
||||
* data of uv-poll to 0. This means that in close_cb_free we call free on 0
|
||||
* with does nothing, since us_poll_stop should not really free the poll.
|
||||
@@ -86,6 +91,7 @@ void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop) {
|
||||
}
|
||||
|
||||
void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events) {
|
||||
if(!p->uv_p) return;
|
||||
p->poll_type = us_internal_poll_type(p) |
|
||||
((events & LIBUS_SOCKET_READABLE) ? POLL_TYPE_POLLING_IN : 0) |
|
||||
((events & LIBUS_SOCKET_WRITABLE) ? POLL_TYPE_POLLING_OUT : 0);
|
||||
@@ -99,6 +105,7 @@ void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events) {
|
||||
}
|
||||
|
||||
void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events) {
|
||||
if(!p->uv_p) return;
|
||||
if (us_poll_events(p) != events) {
|
||||
p->poll_type =
|
||||
us_internal_poll_type(p) |
|
||||
@@ -109,6 +116,7 @@ void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events) {
|
||||
}
|
||||
|
||||
void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop) {
|
||||
if(!p->uv_p) return;
|
||||
uv_poll_stop(p->uv_p);
|
||||
|
||||
/* We normally only want to close the poll here, not free it. But if we stop
|
||||
@@ -217,10 +225,20 @@ struct us_poll_t *us_create_poll(struct us_loop_t *loop, int fallthrough,
|
||||
/* If we update our block position we have to update the uv_poll data to point
|
||||
* to us */
|
||||
struct us_poll_t *us_poll_resize(struct us_poll_t *p, struct us_loop_t *loop,
|
||||
unsigned int ext_size) {
|
||||
unsigned int old_ext_size, unsigned int ext_size) {
|
||||
|
||||
// cannot resize if we dont own uv_poll_t
|
||||
if(!p->uv_p) return p;
|
||||
|
||||
unsigned int old_size = sizeof(struct us_poll_t) + old_ext_size;
|
||||
unsigned int new_size = sizeof(struct us_poll_t) + ext_size;
|
||||
if(new_size <= old_size) return p;
|
||||
|
||||
struct us_poll_t *new_p = calloc(1, new_size);
|
||||
memcpy(new_p, p, old_size);
|
||||
|
||||
struct us_poll_t *new_p = realloc(p, sizeof(struct us_poll_t) + ext_size);
|
||||
new_p->uv_p->data = new_p;
|
||||
p->uv_p = NULL;
|
||||
|
||||
return new_p;
|
||||
}
|
||||
|
||||
@@ -170,6 +170,12 @@ struct us_socket_flags {
|
||||
unsigned char low_prio_state: 2;
|
||||
/* If true, the socket should be read using readmsg to support receiving file descriptors */
|
||||
bool is_ipc: 1;
|
||||
/* If true, the socket has been closed */
|
||||
bool is_closed: 1;
|
||||
/* If true, the socket was reallocated during adoption */
|
||||
bool adopted: 1;
|
||||
/* If true, the socket is a TLS socket */
|
||||
bool is_tls: 1;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
@@ -435,11 +441,11 @@ void us_internal_ssl_socket_shutdown(us_internal_ssl_socket_r s);
|
||||
|
||||
struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_adopt_socket(
|
||||
us_internal_ssl_socket_context_r context,
|
||||
us_internal_ssl_socket_r s, int ext_size);
|
||||
us_internal_ssl_socket_r s, int old_ext_size, int ext_size);
|
||||
|
||||
struct us_internal_ssl_socket_t *us_internal_ssl_socket_wrap_with_tls(
|
||||
us_socket_r s, struct us_bun_socket_context_options_t options,
|
||||
struct us_socket_events_t events, int socket_ext_size);
|
||||
struct us_socket_events_t events, int old_socket_ext_size, int socket_ext_size);
|
||||
struct us_internal_ssl_socket_context_t *
|
||||
us_internal_create_child_ssl_socket_context(
|
||||
us_internal_ssl_socket_context_r context, int context_ext_size);
|
||||
|
||||
@@ -349,7 +349,7 @@ struct us_loop_t *us_socket_context_loop(int ssl, us_socket_context_r context) n
|
||||
|
||||
/* Invalidates passed socket, returning a new resized socket which belongs to a different socket context.
|
||||
* Used mainly for "socket upgrades" such as when transitioning from HTTP to WebSocket. */
|
||||
struct us_socket_t *us_socket_context_adopt_socket(int ssl, us_socket_context_r context, us_socket_r s, int ext_size);
|
||||
struct us_socket_t *us_socket_context_adopt_socket(int ssl, us_socket_context_r context, us_socket_r s, int old_ext_size, int ext_size);
|
||||
|
||||
struct us_socket_t *us_socket_upgrade_to_tls(us_socket_r s, us_socket_context_r new_context, const char *sni);
|
||||
|
||||
@@ -411,7 +411,7 @@ void *us_poll_ext(us_poll_r p) nonnull_fn_decl;
|
||||
LIBUS_SOCKET_DESCRIPTOR us_poll_fd(us_poll_r p) nonnull_fn_decl;
|
||||
|
||||
/* Resize an active poll */
|
||||
struct us_poll_t *us_poll_resize(us_poll_r p, us_loop_r loop, unsigned int ext_size) nonnull_fn_decl;
|
||||
struct us_poll_t *us_poll_resize(us_poll_r p, us_loop_r loop, unsigned int old_ext_size, unsigned int ext_size) nonnull_fn_decl;
|
||||
|
||||
/* Public interfaces for sockets */
|
||||
|
||||
@@ -470,7 +470,7 @@ void us_socket_local_address(int ssl, us_socket_r s, char *nonnull_arg buf, int
|
||||
/* Bun extras */
|
||||
struct us_socket_t *us_socket_pair(struct us_socket_context_t *ctx, int socket_ext_size, LIBUS_SOCKET_DESCRIPTOR* fds);
|
||||
struct us_socket_t *us_socket_from_fd(struct us_socket_context_t *ctx, int socket_ext_size, LIBUS_SOCKET_DESCRIPTOR fd, int ipc);
|
||||
struct us_socket_t *us_socket_wrap_with_tls(int ssl, us_socket_r s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size);
|
||||
struct us_socket_t *us_socket_wrap_with_tls(int ssl, us_socket_r s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int old_socket_ext_size, int socket_ext_size);
|
||||
int us_socket_raw_write(int ssl, us_socket_r s, const char *data, int length);
|
||||
struct us_socket_t* us_socket_open(int ssl, struct us_socket_t * s, int is_client, char* ip, int ip_length);
|
||||
int us_raw_root_certs(struct us_cert_string_t**out);
|
||||
|
||||
@@ -193,9 +193,17 @@ void us_internal_handle_low_priority_sockets(struct us_loop_t *loop) {
|
||||
loop_data->low_prio_head = s->next;
|
||||
if (s->next) s->next->prev = 0;
|
||||
s->next = 0;
|
||||
int ssl = s->flags.is_tls;
|
||||
|
||||
if(us_socket_is_closed(ssl, s)) {
|
||||
s->flags.low_prio_state = 2;
|
||||
us_socket_context_unref(ssl, s->context);
|
||||
continue;
|
||||
}
|
||||
|
||||
us_internal_socket_context_link_socket(0, s->context, s);
|
||||
us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) | LIBUS_SOCKET_READABLE);
|
||||
us_internal_socket_context_link_socket(ssl, s->context, s);
|
||||
us_socket_context_unref(ssl, s->context);
|
||||
us_poll_change(&s->p, us_socket_context(ssl, s)->loop, us_poll_events(&s->p) | LIBUS_SOCKET_READABLE);
|
||||
|
||||
s->flags.low_prio_state = 2;
|
||||
}
|
||||
@@ -243,6 +251,7 @@ void us_internal_free_closed_sockets(struct us_loop_t *loop) {
|
||||
/* Free all closed sockets (maybe it is better to reverse order?) */
|
||||
for (struct us_socket_t *s = loop->data.closed_head; s; ) {
|
||||
struct us_socket_t *next = s->next;
|
||||
s->prev = s->next = 0;
|
||||
us_poll_free((struct us_poll_t *) s, loop);
|
||||
s = next;
|
||||
}
|
||||
@@ -347,6 +356,9 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
|
||||
s->flags.allow_half_open = listen_socket->s.flags.allow_half_open;
|
||||
s->flags.is_paused = 0;
|
||||
s->flags.is_ipc = 0;
|
||||
s->flags.is_closed = 0;
|
||||
s->flags.adopted = 0;
|
||||
s->flags.is_tls = listen_socket->s.flags.is_tls;
|
||||
|
||||
/* We always use nodelay */
|
||||
bsd_socket_nodelay(client_fd, 1);
|
||||
@@ -354,7 +366,10 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
|
||||
us_internal_socket_context_link_socket(0, listen_socket->s.context, s);
|
||||
|
||||
listen_socket->s.context->on_open(s, 0, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr));
|
||||
|
||||
/* After socket adoption, track the new socket; the old one becomes invalid */
|
||||
if(s && s->flags.adopted && s->prev) {
|
||||
s = s->prev;
|
||||
}
|
||||
/* Exit accept loop if listen socket was closed in on_open handler */
|
||||
if (us_socket_is_closed(0, &listen_socket->s)) {
|
||||
break;
|
||||
@@ -369,6 +384,10 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
|
||||
case POLL_TYPE_SOCKET: {
|
||||
/* We should only use s, no p after this point */
|
||||
struct us_socket_t *s = (struct us_socket_t *) p;
|
||||
/* After socket adoption, track the new socket; the old one becomes invalid */
|
||||
if(s && s->flags.adopted && s->prev) {
|
||||
s = s->prev;
|
||||
}
|
||||
/* The context can change after calling a callback but the loop is always the same */
|
||||
struct us_loop_t* loop = s->context->loop;
|
||||
if (events & LIBUS_SOCKET_WRITABLE && !error) {
|
||||
@@ -381,6 +400,10 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
|
||||
#endif
|
||||
|
||||
s = s->context->on_writable(s);
|
||||
/* After socket adoption, track the new socket; the old one becomes invalid */
|
||||
if(s && s->flags.adopted && s->prev) {
|
||||
s = s->prev;
|
||||
}
|
||||
|
||||
if (!s || us_socket_is_closed(0, s)) {
|
||||
return;
|
||||
@@ -477,6 +500,10 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
|
||||
|
||||
if (length > 0) {
|
||||
s = s->context->on_data(s, loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, length);
|
||||
/* After socket adoption, track the new socket; the old one becomes invalid */
|
||||
if(s && s->flags.adopted && s->prev) {
|
||||
s = s->prev;
|
||||
}
|
||||
// loop->num_ready_polls isn't accessible on Windows.
|
||||
#ifndef WIN32
|
||||
// rare case: we're reading a lot of data, there's more to be read, and either:
|
||||
|
||||
@@ -125,7 +125,7 @@ int us_socket_is_closed(int ssl, struct us_socket_t *s) {
|
||||
if(ssl) {
|
||||
return us_internal_ssl_socket_is_closed((struct us_internal_ssl_socket_t *) s);
|
||||
}
|
||||
return s->prev == (struct us_socket_t *) s->context;
|
||||
return s->flags.is_closed;
|
||||
}
|
||||
|
||||
int us_connecting_socket_is_closed(int ssl, struct us_connecting_socket_t *c) {
|
||||
@@ -159,8 +159,8 @@ void us_connecting_socket_close(int ssl, struct us_connecting_socket_t *c) {
|
||||
s->next = s->context->loop->data.closed_head;
|
||||
s->context->loop->data.closed_head = s;
|
||||
|
||||
/* Any socket with prev = context is marked as closed */
|
||||
s->prev = (struct us_socket_t *) s->context;
|
||||
/* Mark the socket as closed */
|
||||
s->flags.is_closed = 1;
|
||||
}
|
||||
if(!c->error) {
|
||||
// if we have no error, we have to set that we were aborted aka we called close
|
||||
@@ -218,11 +218,10 @@ struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, vo
|
||||
|
||||
bsd_close_socket(us_poll_fd((struct us_poll_t *) s));
|
||||
|
||||
/* Mark the socket as closed */
|
||||
s->flags.is_closed = 1;
|
||||
|
||||
/* Any socket with prev = context is marked as closed */
|
||||
s->prev = (struct us_socket_t *) s->context;
|
||||
|
||||
/* mark it as closed and call the callback */
|
||||
/* call the callback */
|
||||
struct us_socket_t *res = s;
|
||||
if (!(us_internal_poll_type(&s->p) & POLL_TYPE_SEMI_SOCKET)) {
|
||||
res = s->context->on_close(s, code, reason);
|
||||
@@ -268,8 +267,8 @@ struct us_socket_t *us_socket_detach(int ssl, struct us_socket_t *s) {
|
||||
s->next = s->context->loop->data.closed_head;
|
||||
s->context->loop->data.closed_head = s;
|
||||
|
||||
/* Any socket with prev = context is marked as closed */
|
||||
s->prev = (struct us_socket_t *) s->context;
|
||||
/* Mark the socket as closed */
|
||||
s->flags.is_closed = 1;
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -321,8 +320,10 @@ struct us_socket_t *us_socket_from_fd(struct us_socket_context_t *ctx, int socke
|
||||
s->flags.low_prio_state = 0;
|
||||
s->flags.allow_half_open = 0;
|
||||
s->flags.is_paused = 0;
|
||||
s->flags.is_ipc = 0;
|
||||
s->flags.is_ipc = ipc;
|
||||
s->flags.is_closed = 0;
|
||||
s->flags.adopted = 0;
|
||||
s->flags.is_tls = 0;
|
||||
s->connect_state = NULL;
|
||||
|
||||
/* We always use nodelay */
|
||||
@@ -476,13 +477,13 @@ int us_connecting_socket_get_error(int ssl, struct us_connecting_socket_t *c) {
|
||||
Note: this assumes that the socket is non-TLS and will be adopted and wrapped with a new TLS context
|
||||
context ext will not be copied to the new context, new context will contain us_wrapped_socket_context_t on ext
|
||||
*/
|
||||
struct us_socket_t *us_socket_wrap_with_tls(int ssl, struct us_socket_t *s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size) {
|
||||
struct us_socket_t *us_socket_wrap_with_tls(int ssl, struct us_socket_t *s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int old_socket_ext_size, int socket_ext_size) {
|
||||
// only accepts non-TLS sockets
|
||||
if (ssl) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return(struct us_socket_t *) us_internal_ssl_socket_wrap_with_tls(s, options, events, socket_ext_size);
|
||||
return(struct us_socket_t *) us_internal_ssl_socket_wrap_with_tls(s, options, events, old_socket_ext_size, socket_ext_size);
|
||||
}
|
||||
|
||||
// if a TLS socket calls this, it will start SSL call open event and TLS handshake if required
|
||||
|
||||
@@ -331,7 +331,7 @@ public:
|
||||
|
||||
|
||||
/* Adopting a socket invalidates it, do not rely on it directly to carry any data */
|
||||
us_socket_t *usSocket = us_socket_context_adopt_socket(SSL, (us_socket_context_t *) webSocketContext, (us_socket_t *) this, sizeof(WebSocketData) + sizeof(UserData));
|
||||
us_socket_t *usSocket = us_socket_context_adopt_socket(SSL, (us_socket_context_t *) webSocketContext, (us_socket_t *) this, sizeof(HttpResponseData<SSL>), sizeof(WebSocketData) + sizeof(UserData));
|
||||
WebSocket<SSL, true, UserData> *webSocket = (WebSocket<SSL, true, UserData> *) usSocket;
|
||||
|
||||
/* For whatever reason we were corked, update cork to the new socket */
|
||||
|
||||
Reference in New Issue
Block a user