Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 25 additions & 24 deletions packages/bun-usockets/src/bsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,12 @@ void bsd_close_socket(LIBUS_SOCKET_DESCRIPTOR fd) {
#else
close(fd);
#endif
/* After the real close (no fd leak): an armed "close" rule leaves errno,
* and WSAGetLastError on Windows, set the way a failed close would, so a
* caller that reads them after its cleanup is caught. */
ssize_t injected = 0; int unused = 0;
(void)US_FAULT_CHECK(US_FAULT_CLOSE, fd, injected, unused);
(void)injected; (void)unused;
}

void bsd_shutdown_socket(LIBUS_SOCKET_DESCRIPTOR fd) {
Expand Down Expand Up @@ -1997,9 +2003,15 @@ static int is_loopback(struct sockaddr_storage *sockaddr) {
}
#endif

LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr, struct sockaddr_storage *local_addr, int options) {
int bsd_last_error_or_refused(void) {
int err = LIBUS_ERR;
return err ? err : LIBUS_ECONNREFUSED;
}

LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr, struct sockaddr_storage *local_addr, int options, int *error) {
LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(addr->ss_family, SOCK_STREAM, 0, NULL);
if (fd == LIBUS_SOCKET_ERROR) {
*error = bsd_last_error_or_refused();
Comment thread
robobun marked this conversation as resolved.
Outdated
return LIBUS_SOCKET_ERROR;
}

Expand All @@ -2017,15 +2029,8 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr,
#endif
socklen_t local_len = local_addr->ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
if (bind(fd, (struct sockaddr *) local_addr, local_len)) {
#ifdef _WIN32
int bind_err = WSAGetLastError();
bsd_close_socket(fd);
WSASetLastError(bind_err);
#else
int bind_err = errno;
*error = bsd_last_error_or_refused();
bsd_close_socket(fd);
errno = bind_err;
#endif
return LIBUS_SOCKET_ERROR;
}
}
Expand Down Expand Up @@ -2065,65 +2070,61 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr,
int rc = bsd_do_connect_raw(fd, (struct sockaddr*) addr, addr->ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));

if (rc != 0) {
/* The connect error itself, not what closing the socket leaves behind. */
*error = rc;
bsd_close_socket(fd);
#ifdef _WIN32
/* bsd_do_connect_raw returned the WSA error; re-arm it so the Rust
* caller's WSAGetLastError() observes the connect failure rather than
* whatever closesocket() left behind. */
WSASetLastError(rc);
#endif
return LIBUS_SOCKET_ERROR;
}
return fd;
}

static LIBUS_SOCKET_DESCRIPTOR internal_bsd_create_connect_socket_unix(const char *server_path, size_t len, int options, struct sockaddr_un* server_address, const size_t addrlen) {
static LIBUS_SOCKET_DESCRIPTOR internal_bsd_create_connect_socket_unix(const char *server_path, size_t len, int options, struct sockaddr_un* server_address, const size_t addrlen, int *error) {
LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0, NULL);

if (fd == LIBUS_SOCKET_ERROR) {
*error = bsd_last_error_or_refused();
return LIBUS_SOCKET_ERROR;
}

win32_set_nonblocking(fd);

int rc = bsd_do_connect_raw(fd, (struct sockaddr *)server_address, addrlen);
if (rc != 0) {
*error = rc;
bsd_close_socket(fd);
#ifdef _WIN32
WSASetLastError(rc);
#endif
return LIBUS_SOCKET_ERROR;
}

return fd;
}

LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, size_t len, int options) {
LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, size_t len, int options, int *error) {
struct sockaddr_un server_address;
size_t addrlen = 0;
int dirfd_workaround_for_unix_path_len = -1;
if (bsd_create_unix_socket_address(server_path, len, &dirfd_workaround_for_unix_path_len, &server_address, &addrlen)) {
/* ENOENT for an empty path (ERROR_PATH_NOT_FOUND on Windows),
* ENAMETOOLONG for one sun_path cannot hold. */
*error = bsd_last_error_or_refused();
return LIBUS_SOCKET_ERROR;
}

#if defined(__APPLE__)
if (dirfd_workaround_for_unix_path_len != -1) {
if (__pthread_fchdir(dirfd_workaround_for_unix_path_len) != 0) {
close(dirfd_workaround_for_unix_path_len);
errno = ENAMETOOLONG;
*error = ENAMETOOLONG;
return LIBUS_SOCKET_ERROR;
}
}
#endif

LIBUS_SOCKET_DESCRIPTOR fd = internal_bsd_create_connect_socket_unix(server_path, len, options, &server_address, addrlen);
LIBUS_SOCKET_DESCRIPTOR fd = internal_bsd_create_connect_socket_unix(server_path, len, options, &server_address, addrlen, error);

#if defined(__APPLE__)
if (dirfd_workaround_for_unix_path_len != -1) {
int saved_errno = errno;
__pthread_fchdir(-1);
close(dirfd_workaround_for_unix_path_len);
errno = saved_errno;
}
#elif defined(__linux__)
if (dirfd_workaround_for_unix_path_len != -1) {
Expand Down
55 changes: 37 additions & 18 deletions packages/bun-usockets/src/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ void us_socket_group_close_all_ex(struct us_socket_group_t *group, int also_list
* the natural failure path would have, which detaches the
* wrapper. The handler then closes; if it doesn't, the
* force-drain below catches it. */
us_dispatch_connect_error(s, ECONNABORTED);
us_dispatch_connect_error(s, LIBUS_ECONNABORTED);
if (!us_socket_is_closed(s)) {
us_internal_socket_close_raw(s, LIBUS_SOCKET_CLOSE_CODE_CONNECTION_RESET, 0);
}
Expand Down Expand Up @@ -552,8 +552,8 @@ static inline void us_internal_init_connect_socket(struct us_socket_t *s,

struct us_socket_t *us_socket_group_connect_resolved_dns(struct us_socket_group_t *group,
unsigned char kind, struct ssl_ctx_st *ssl_ctx,
struct sockaddr_storage *addr, struct sockaddr_storage *local_addr, int options, int socket_ext_size) {
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket(addr, local_addr, options);
struct sockaddr_storage *addr, struct sockaddr_storage *local_addr, int options, int socket_ext_size, int *error) {
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket(addr, local_addr, options, error);
if (connect_socket_fd == LIBUS_SOCKET_ERROR) {
return NULL;
}
Expand All @@ -563,10 +563,9 @@ struct us_socket_t *us_socket_group_connect_resolved_dns(struct us_socket_group_
struct us_poll_t *p = us_create_poll(group->loop, 0, sizeof(struct us_socket_t) + socket_ext_size);
us_poll_init(p, connect_socket_fd, POLL_TYPE_SEMI_SOCKET);
if (us_poll_start_rc(p, group->loop, LIBUS_SOCKET_WRITABLE) != 0) {
int saved_errno = errno;
*error = bsd_last_error_or_refused();
bsd_close_socket(connect_socket_fd);
us_poll_free(p, group->loop);
errno = saved_errno;
return NULL;
}

Expand Down Expand Up @@ -603,7 +602,7 @@ static bool try_parse_ip(const char *ip_str, int port, struct sockaddr_storage *
void *us_socket_group_connect(struct us_socket_group_t *group, unsigned char kind,
struct ssl_ctx_st *ssl_ctx, const char *host, int port,
const char *local_host, int local_port, int options,
int socket_ext_size, int *has_dns_resolved) {
int socket_ext_size, int *has_dns_resolved, int *error) {
struct us_loop_t *loop = group->loop;

/* The local address is always a literal IP (Node validates it as one). */
Expand All @@ -616,7 +615,7 @@ void *us_socket_group_connect(struct us_socket_group_t *group, unsigned char kin
struct sockaddr_storage addr;
if (try_parse_ip(host, port, &addr)) {
*has_dns_resolved = 1;
return us_socket_group_connect_resolved_dns(group, kind, ssl_ctx, &addr, local_addr, options, socket_ext_size);
return us_socket_group_connect_resolved_dns(group, kind, ssl_ctx, &addr, local_addr, options, socket_ext_size, error);
}

struct addrinfo_request *ai_req;
Expand All @@ -633,7 +632,7 @@ void *us_socket_group_connect(struct us_socket_group_t *group, unsigned char kin
struct sockaddr_storage a;
init_addr_with_port(&entries->info, port, &a);
*has_dns_resolved = 1;
struct us_socket_t *s = us_socket_group_connect_resolved_dns(group, kind, ssl_ctx, &a, local_addr, options, socket_ext_size);
struct us_socket_t *s = us_socket_group_connect_resolved_dns(group, kind, ssl_ctx, &a, local_addr, options, socket_ext_size, error);
Bun__addrinfo_freeRequest(ai_req, s == NULL);
return s;
}
Expand Down Expand Up @@ -671,19 +670,18 @@ void *us_socket_group_connect(struct us_socket_group_t *group, unsigned char kin

struct us_socket_t *us_socket_group_connect_unix(struct us_socket_group_t *group,
unsigned char kind, struct ssl_ctx_st *ssl_ctx,
const char *server_path, size_t pathlen, int options, int socket_ext_size) {
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket_unix(server_path, pathlen, options);
const char *server_path, size_t pathlen, int options, int socket_ext_size, int *error) {
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket_unix(server_path, pathlen, options, error);
if (connect_socket_fd == LIBUS_SOCKET_ERROR) {
return 0;
}

struct us_poll_t *p = us_create_poll(group->loop, 0, sizeof(struct us_socket_t) + socket_ext_size);
us_poll_init(p, connect_socket_fd, POLL_TYPE_SEMI_SOCKET);
if (us_poll_start_rc(p, group->loop, LIBUS_SOCKET_WRITABLE) != 0) {
int saved_errno = errno;
*error = bsd_last_error_or_refused();
bsd_close_socket(connect_socket_fd);
us_poll_free(p, group->loop);
errno = saved_errno;
return 0;
}

Expand All @@ -705,16 +703,22 @@ int start_connections(struct us_connecting_socket_t *c, int count) {
for (; c->addrinfo_head != NULL && opened < count; c->addrinfo_head = c->addrinfo_head->ai_next) {
struct sockaddr_storage addr;
init_addr_with_port(c->addrinfo_head, c->port, &addr);
/* The deferred-DNS path does not carry a local binding. */
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket(&addr, NULL, c->options);
/* The deferred-DNS path does not carry a local binding. The dial goes
* on with the next address; the error is kept for the case none
* connects. Read from the out-param, not LIBUS_ERR after the call: the
* failed socket has been closed by then. */
int candidate_error = 0;
LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket(&addr, NULL, c->options, &candidate_error);
if (connect_socket_fd == LIBUS_SOCKET_ERROR) {
c->last_candidate_error = candidate_error;
continue;
}
bsd_socket_nodelay(connect_socket_fd, 1);
struct us_socket_t *s = (struct us_socket_t *)us_create_poll(loop, 0, sizeof(struct us_socket_t) + c->socket_ext_size);
struct us_poll_t *poll = &s->p;
us_poll_init(poll, connect_socket_fd, POLL_TYPE_SEMI_SOCKET);
if (us_poll_start_rc(poll, loop, LIBUS_SOCKET_WRITABLE) != 0) {
c->last_candidate_error = bsd_last_error_or_refused();
bsd_close_socket(connect_socket_fd);
us_poll_free(poll, loop);
continue;
Expand Down Expand Up @@ -774,8 +778,9 @@ void us_internal_socket_after_resolve(struct us_connecting_socket_t *c) {
int opened = start_connections(c, CONCURRENT_CONNECTIONS);
if (opened == 0) {
/* Same as the exhausted path in us_internal_socket_after_open: a
* real connect failure must not be reported as a caller abort. */
c->error = ECONNREFUSED;
* real connect failure must not be reported as a caller abort, and
* the last address's own error beats a blanket LIBUS_ECONNREFUSED. */
c->error = c->last_candidate_error ? c->last_candidate_error : LIBUS_ECONNREFUSED;
us_connecting_socket_close(c);
}
}
Expand All @@ -793,6 +798,17 @@ void us_internal_socket_after_open(struct us_socket_t *s, int error) {
break;
}
default: {
/* The probe only says the socket never connected
* (WSAENOTCONN); why the connect failed (WSAECONNREFUSED,
* WSAETIMEDOUT, ...) is in SO_ERROR. A probe error other
* than WSAENOTCONN (WSAECONNRESET, WSAEADDRINUSE) is the
* reason itself. */
int connect_error = us_socket_get_error(s);
if (connect_error != 0) {
error = connect_error;
} else if (error == WSAENOTCONN) {
error = LIBUS_ECONNREFUSED;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
break;
}
}
Expand All @@ -801,6 +817,7 @@ void us_internal_socket_after_open(struct us_socket_t *s, int error) {
#endif
if (error) {
if (c) {
c->last_candidate_error = error;
for (struct us_socket_t **next = &c->connecting_head; *next; next = &(*next)->connect_next) {
if (*next == s) {
*next = s->connect_next;
Expand All @@ -815,8 +832,10 @@ void us_internal_socket_after_open(struct us_socket_t *s, int error) {
/* Every resolved address failed to connect. Without this,
* us_connecting_socket_close defaults c->error to
* ECONNABORTED (caller abort) and never invalidates the
* DNS cache entry for the dead host. */
c->error = ECONNREFUSED;
* DNS cache entry for the dead host. The last address's
* own error is reported; several addresses failing
* differently is last-wins. */
c->error = c->last_candidate_error ? c->last_candidate_error : LIBUS_ECONNREFUSED;
us_connecting_socket_close(c);
}
}
Expand Down
3 changes: 3 additions & 0 deletions packages/bun-usockets/src/internal/fault_inject.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ enum us_fault_syscall {
US_FAULT_ACCEPT,
/* Reserved: no bsd.c hooks yet, so the JS setter does not accept them. */
US_FAULT_SOCKET,
/* Runs after the real close in bsd_close_socket. Only US_FAULT_ERRNO
* applies: the close still happens, the rule only sets errno afterwards. */
US_FAULT_CLOSE,
/* Reserved, as US_FAULT_SOCKET. */
Comment thread
coderabbitai[bot] marked this conversation as resolved.
US_FAULT_SHUTDOWN,
/* Not a syscall: the per-loop TLS plaintext buffer allocated once by
* us_internal_init_loop_ssl_data. Only US_FAULT_ERRNO applies — there is
Expand Down
12 changes: 12 additions & 0 deletions packages/bun-usockets/src/internal/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,18 @@ extern void __attribute__((__noreturn__)) Bun__outOfMemory(void);
#define IS_EINTR(rc) (rc == SOCKET_ERROR && WSAGetLastError() == WSAEINTR)
#define LIBUS_ERR WSAGetLastError()
#define LIBUS_ECONNRESET WSAECONNRESET
/* The codes uSockets fills in itself for a connect it ended or gave up on,
* in that same numbering, so a caller can treat every connect error code
* alike. */
#define LIBUS_ECONNABORTED WSAECONNABORTED
#define LIBUS_ECONNREFUSED WSAECONNREFUSED
#else
#include <errno.h>
#define IS_EINTR(rc) (rc == -1 && errno == EINTR)
#define LIBUS_ERR errno
#define LIBUS_ECONNRESET ECONNRESET
#define LIBUS_ECONNABORTED ECONNABORTED
#define LIBUS_ECONNREFUSED ECONNREFUSED
#endif
#include <stdbool.h>
/* Poll type and what it polls for */
Expand Down Expand Up @@ -384,6 +391,11 @@ struct us_connecting_socket_t {
unsigned char kind;
uint16_t port;
int error;
/* The error of the last address that failed (the out-param of a dial
* that failed outright, or the SO_ERROR reported through after_open).
* When every address is exhausted this is what `error` becomes, in
* place of a blanket LIBUS_ECONNREFUSED. */
int last_candidate_error;
struct addrinfo *addrinfo_head;
// this is used to track pending connecting sockets in the context
struct us_connecting_socket_t* next_pending;
Expand Down
9 changes: 7 additions & 2 deletions packages/bun-usockets/src/internal/networking/bsd.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ ssize_t bsd_writev(LIBUS_SOCKET_DESCRIPTOR fd, const struct us_iovec_t *iov, int
ssize_t bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length);
int bsd_would_block();
int bsd_send_is_transient_error();
/* The OS error of the call that just failed; never 0, so a caller that got
* LIBUS_SOCKET_ERROR always has a code to report. */
int bsd_last_error_or_refused(void);

// return LIBUS_SOCKET_ERROR or the fd that represents listen socket
// listen both on ipv6 and ipv4
Expand All @@ -239,9 +242,11 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port, int op
int bsd_connect_udp_socket(LIBUS_SOCKET_DESCRIPTOR fd, const char *host, int port);
int bsd_disconnect_udp_socket(LIBUS_SOCKET_DESCRIPTOR fd);

LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr, struct sockaddr_storage *local_addr, int options);
/* On LIBUS_SOCKET_ERROR, *error is the OS error of the call that failed
* (socket, bind, connect, or building the unix address); never 0. */
LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr, struct sockaddr_storage *local_addr, int options, int *error);

LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, size_t pathlen, int options);
LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, size_t pathlen, int options, int *error);

int bsd_socket_export_size(void);
int bsd_socket_export(LIBUS_SOCKET_DESCRIPTOR fd, unsigned int target_pid, void *info_out);
Expand Down
13 changes: 8 additions & 5 deletions packages/bun-usockets/src/libusockets.h
Original file line number Diff line number Diff line change
Expand Up @@ -447,16 +447,19 @@ void us_socket_on_server_name(us_socket_r s, us_socket_server_name_cb cb);
/* ── Connect ──────────────────────────────────────────────────────────────
* Returns either us_socket_t* (fast path, *is_connecting=1) or
* us_connecting_socket_t* (DNS / happy-eyeballs in flight, *is_connecting=0).
* ssl_ctx may be NULL for plain TCP. */
* ssl_ctx may be NULL for plain TCP. NULL when the dial failed outright, with
* the OS error (errno, or the WSA/Win32 code on Windows) in *error; a dial
* that fails later reports through the connect error callback instead. */
void *us_socket_group_connect(us_socket_group_r group, unsigned char kind,
struct ssl_ctx_st *ssl_ctx, const char *host, int port,
const char *local_host, int local_port, int options,
int socket_ext_size, int *is_connecting)
__attribute__((nonnull(1, 4, 10))); /* ssl_ctx, local_host nullable */
int socket_ext_size, int *is_connecting, int *error)
__attribute__((nonnull(1, 4, 10, 11))); /* ssl_ctx, local_host nullable */
struct us_socket_t *us_socket_group_connect_unix(us_socket_group_r group,
unsigned char kind, struct ssl_ctx_st *ssl_ctx,
const char *server_path, size_t pathlen, int options, int socket_ext_size)
__attribute__((nonnull(1, 4))); /* ssl_ctx nullable */
const char *server_path, size_t pathlen, int options, int socket_ext_size,
int *error)
__attribute__((nonnull(1, 4, 8))); /* ssl_ctx nullable */

int us_socket_is_established(us_socket_r s) nonnull_fn_decl;
void us_connecting_socket_free(struct us_connecting_socket_t *c) nonnull_fn_decl;
Expand Down
Loading
Loading