Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 4 additions & 2 deletions packages/bun-usockets/src/bsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1216,7 +1216,7 @@ int bsd_set_defer_accept(LIBUS_SOCKET_DESCRIPTOR listenFd) {

// return LIBUS_SOCKET_ERROR or the fd that represents listen socket
// listen both on ipv6 and ipv4
LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options, int* error) {
LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options, int* error, int* dns_error) {
struct addrinfo hints, *result;
memset(&hints, 0, sizeof(struct addrinfo));

Expand All @@ -1227,7 +1227,9 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int
char port_string[16];
snprintf(port_string, 16, "%d", port);

if (getaddrinfo(host, port_string, &hints, &result)) {
int gai_error = getaddrinfo(host, port_string, &hints, &result);
if (gai_error != 0) {
*dns_error = gai_error;
return LIBUS_SOCKET_ERROR;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/bun-usockets/src/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -388,8 +388,8 @@ static void us_internal_init_listen_socket(struct us_listen_socket_t *ls,

struct us_listen_socket_t *us_socket_group_listen(struct us_socket_group_t *group,
unsigned char kind, struct ssl_ctx_st *ssl_ctx,
const char *host, int port, int options, int socket_ext_size, int *error) {
LIBUS_SOCKET_DESCRIPTOR listen_socket_fd = bsd_create_listen_socket(host, port, options, error);
const char *host, int port, int options, int socket_ext_size, int *error, int *dns_error) {
LIBUS_SOCKET_DESCRIPTOR listen_socket_fd = bsd_create_listen_socket(host, port, options, error, dns_error);
if (listen_socket_fd == LIBUS_SOCKET_ERROR) {
return 0;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/bun-usockets/src/internal/networking/bsd.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ int bsd_send_is_transient_error();

// return LIBUS_SOCKET_ERROR or the fd that represents listen socket
// listen both on ipv6 and ipv4
LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options, int* error);
// error / dns_error: see us_socket_group_listen() in libusockets.h
LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options, int* error, int* dns_error);

LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket_unix(const char *path, size_t pathlen, int options, int* error);

Expand Down
13 changes: 10 additions & 3 deletions packages/bun-usockets/src/libusockets.h
Original file line number Diff line number Diff line change
Expand Up @@ -386,11 +386,18 @@ void us_socket_start_tls_handshake(us_socket_r s) nonnull_fn_decl;
/* ── Listen ───────────────────────────────────────────────────────────────
* The listener owns: an embedded group for accepted sockets, the SSL_CTX
* (borrowed ref, optional), the SNI tree (optional), and the kind to stamp on
* accepted sockets. */
* accepted sockets.
*
* The out-params are only meaningful when NULL is returned. A `host` that did
* not resolve leaves the raw getaddrinfo(3) return code in *dns_error and does
* not touch *error; failures after resolution report errno (WSAGetLastError()
* on Windows) through *error. The two number spaces overlap, so they are kept
* apart the same way us_connecting_socket_t tags error_is_dns. Callers zero
* both before the call. */
struct us_listen_socket_t *us_socket_group_listen(us_socket_group_r group,
unsigned char kind, struct ssl_ctx_st *ssl_ctx,
const char *host, int port, int options, int socket_ext_size, int *error)
__attribute__((nonnull(1, 8))); /* ssl_ctx, host nullable */
const char *host, int port, int options, int socket_ext_size, int *error, int *dns_error)
__attribute__((nonnull(1, 8, 9))); /* ssl_ctx, host nullable */
struct us_listen_socket_t *us_socket_group_listen_unix(us_socket_group_r group,
unsigned char kind, struct ssl_ctx_st *ssl_ctx,
const char *path, size_t pathlen, int options, int socket_ext_size, int *error)
Expand Down
36 changes: 18 additions & 18 deletions packages/bun-uws/src/App.h
Original file line number Diff line number Diff line change
Expand Up @@ -713,35 +713,35 @@ struct TemplatedApp {

struct ssl_ctx_st *sslCtxOrNull() { return SSL ? sslCtx : nullptr; }

/* (listen socket or nullptr, dns_error as documented on us_socket_group_listen) */
using ListenHandler = MoveOnlyFunction<void(us_listen_socket_t *, int)>;

TemplatedApp &&listenTcp(const char *host, int port, int options, ListenHandler &&handler) {
int dnsError = 0;
us_listen_socket_t *listenSocket = httpContext ? trackListenSocket(httpContext->listen(sslCtxOrNull(), host, port, options, &dnsError)) : nullptr;
handler(listenSocket, dnsError);
return std::move(*this);
}

public:
/* Host, port, callback */
TemplatedApp &&listen(const std::string &host, int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
if (host.empty()) {
return listen(port, std::move(handler));
}
handler(httpContext ? trackListenSocket(httpContext->listen(sslCtxOrNull(), host.c_str(), port, 0)) : nullptr);
return std::move(*this);
TemplatedApp &&listen(const std::string &host, int port, ListenHandler &&handler) {
return listenTcp(host.empty() ? nullptr : host.c_str(), port, 0, std::move(handler));
}

/* Host, port, options, callback */
TemplatedApp &&listen(const std::string &host, int port, int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
if (host.empty()) {
return listen(port, options, std::move(handler));
}
handler(httpContext ? trackListenSocket(httpContext->listen(sslCtxOrNull(), host.c_str(), port, options)) : nullptr);
return std::move(*this);
TemplatedApp &&listen(const std::string &host, int port, int options, ListenHandler &&handler) {
return listenTcp(host.empty() ? nullptr : host.c_str(), port, options, std::move(handler));
}

/* Port, callback */
TemplatedApp &&listen(int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
handler(httpContext ? trackListenSocket(httpContext->listen(sslCtxOrNull(), nullptr, port, 0)) : nullptr);
return std::move(*this);
TemplatedApp &&listen(int port, ListenHandler &&handler) {
return listenTcp(nullptr, port, 0, std::move(handler));
}

/* Port, options, callback */
TemplatedApp &&listen(int port, int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
handler(httpContext ? trackListenSocket(httpContext->listen(sslCtxOrNull(), nullptr, port, options)) : nullptr);
return std::move(*this);
TemplatedApp &&listen(int port, int options, ListenHandler &&handler) {
return listenTcp(nullptr, port, options, std::move(handler));
}

/* options, callback, path to unix domain socket */
Expand Down
4 changes: 2 additions & 2 deletions packages/bun-uws/src/HttpContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -1047,11 +1047,11 @@ struct HttpContext {
}

/* Listen to port using this HttpContext. ssl_ctx may be nullptr for plain HTTP. */
us_listen_socket_t *listen(struct ssl_ctx_st *sslCtx, const char *host, int port, int options) {
us_listen_socket_t *listen(struct ssl_ctx_st *sslCtx, const char *host, int port, int options, int *dnsError) {
int error = 0;
/* HTTP clients always send first (the request, or ClientHello for TLS), so defer
* accept() until data arrives and dispatch the read immediately after accept. */
auto socket = us_socket_group_listen(&group, socketKind(), sslCtx, host, port, options | LIBUS_LISTEN_DEFER_ACCEPT, socketExtSize(), &error);
auto socket = us_socket_group_listen(&group, socketKind(), sslCtx, host, port, options | LIBUS_LISTEN_DEFER_ACCEPT, socketExtSize(), &error, dnsError);
// we dont depend on libuv ref for keeping it alive
if (socket) {
us_socket_unref(&socket->s);
Expand Down
35 changes: 25 additions & 10 deletions src/runtime/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fn server_js_create(
}
}

use bun_cares_sys::c_ares_draft as c_ares;
use bun_io::KeepAlive;
use bun_uws as uws;
use bun_uws_sys as uws_sys;
Expand Down Expand Up @@ -1961,9 +1962,13 @@ impl<const SSL: bool, const DEBUG: bool> NewServer<SSL, DEBUG> {
}
}

pub(crate) fn on_listen(&mut self, socket: Option<*mut uws_sys::app::ListenSocket<SSL>>) {
pub(crate) fn on_listen(
&mut self,
socket: Option<*mut uws_sys::app::ListenSocket<SSL>>,
dns_error: c_int,
) {
let Some(socket) = socket else {
return self.on_listen_failed();
return self.on_listen_failed(dns_error);
};
self.listener = Some(socket);
// SAFETY: `vm_mut()` is the process-static `*mut VirtualMachine` (non-null
Expand All @@ -1983,22 +1988,30 @@ impl<const SSL: bool, const DEBUG: bool> NewServer<SSL, DEBUG> {
/// error-stack drain is still TODO; the EADDRINUSE/
/// EACCES paths below cover the node:http `server.listen` error contract.
#[cold]
pub(crate) fn on_listen_failed(&mut self) {
pub(crate) fn on_listen_failed(&mut self, dns_error: c_int) {
self.listener = None;
let global = self.global_this();

let error_instance = match &self.config.address {
server_config::Address::Tcp {
port,
hostname: _hostname,
} => {
server_config::Address::Tcp { port, hostname } => {
// The hostname did not resolve: no socket call ran, so errno below is meaningless.
if let Some(dns_err) = c_ares::Error::init_eai(dns_error) {
let host = hostname.as_ref().map(|h| h.as_bytes()).unwrap_or(b"");
let err = crate::dns_jsc::cares_jsc::system_error_with_syscall_and_hostname(
dns_err,
b"getaddrinfo",
host,
);
let _ = global.throw_value(err.to_error_instance(global));
return;
}
// Rust's `target_os = "linux"` excludes
// Android, so match both explicitly.
#[cfg(any(target_os = "linux", target_os = "android"))]
{
let errno = bun_sys::get_errno(-1i32);
if errno == bun_sys::E::EACCES {
let host = _hostname
let host = hostname
.as_ref()
.map(|h| h.as_bytes())
.unwrap_or(b"0.0.0.0");
Expand Down Expand Up @@ -3298,6 +3311,7 @@ mod trampoline {

pub(super) extern "C" fn on_listen<const SSL: bool, const DEBUG: bool>(
socket: *mut UwsListenSocket,
dns_error: c_int,
user_data: *mut c_void,
) {
// SAFETY: user_data is the `*mut NewServer<..>` passed to listen_with_config.
Expand All @@ -3307,7 +3321,7 @@ mod trampoline {
} else {
Some(socket.cast::<uws_sys::app::ListenSocket<SSL>>())
};
server.on_listen(socket);
server.on_listen(socket, dns_error);
}

pub(super) extern "C" fn on_listen_unix<const SSL: bool, const DEBUG: bool>(
Expand All @@ -3316,7 +3330,8 @@ mod trampoline {
_flags: i32,
user_data: *mut c_void,
) {
on_listen::<SSL, DEBUG>(socket, user_data);
// A unix path is never resolved, so there is no dns_error to forward.
on_listen::<SSL, DEBUG>(socket, 0, user_data);
}

pub(super) extern "C" fn on_404<const SSL: bool, const DEBUG: bool>(
Expand Down
12 changes: 12 additions & 0 deletions src/runtime/socket/Listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use core::ptr::NonNull;
use std::rc::Rc;

use bun_boringssl_sys as boring_sys;
use bun_cares_sys::c_ares_draft as c_ares;
use bun_io::KeepAlive;
use bun_jsc::ZigStringJsc as _;
use bun_jsc::strong::Optional as Strong;
Expand Down Expand Up @@ -439,6 +440,7 @@ impl Listener {
.map(|p| p.as_ptr().cast::<uws::SslCtx>());

let mut errno: c_int = 0;
let mut dns_error: c_int = 0;
let listen_socket: *mut uws_sys::ListenSocket = match &mut connection {
UnixOrHost::Host { host, port } => {
let hostz = bun_core::ZBox::from_bytes(&host[..]);
Expand All @@ -452,6 +454,7 @@ impl Listener {
socket_flags,
size_of::<*mut c_void>() as c_int,
&mut errno,
&mut dns_error,
)
});
if !ls.is_null() {
Expand Down Expand Up @@ -494,6 +497,15 @@ impl Listener {
UnixOrHost::Unix(u) => u,
UnixOrHost::Fd(_) => b"",
};
if let Some(dns_err) = c_ares::Error::init_eai(dns_error) {
log!("Failed to resolve listen hostname {}", dns_error);
let err = crate::dns_jsc::cares_jsc::system_error_with_syscall_and_hostname(
dns_err,
b"getaddrinfo",
hostname_bytes,
);
return Err(global.throw_value(err.to_error_instance(global)));
}
let err = global.create_error_instance(format_args!(
"Failed to listen at {}",
bstr::BStr::new(hostname_bytes)
Expand Down
6 changes: 4 additions & 2 deletions src/uws_sys/App.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ impl<const SSL: bool> App<SSL> {
pub fn listen(
&mut self,
port: i32,
handler: extern "C" fn(*mut UwsListenSocket, *mut c_void),
handler: extern "C" fn(*mut UwsListenSocket, c_int, *mut c_void),
user_data: *mut c_void,
) {
// Callers supply the C-ABI shim directly (see the RouteHandler note above).
Expand Down Expand Up @@ -462,7 +462,9 @@ pub(crate) type uws_app_t = uws_app_s;
pub mod c {
use super::*;

pub(crate) type uws_listen_handler = Option<extern "C" fn(*mut UwsListenSocket, *mut c_void)>;
/// `(listen_socket, dns_error, user_data)`; `dns_error` as in `SocketGroup::listen`.
pub(crate) type uws_listen_handler =
Option<extern "C" fn(*mut UwsListenSocket, c_int, *mut c_void)>;
pub(crate) type uws_method_handler =
Option<extern "C" fn(*mut uws_res, *mut Request, *mut c_void)>;
// The C++ shim hands the filter the uws_res_t*, which for HTTP server
Expand Down
4 changes: 4 additions & 0 deletions src/uws_sys/SocketGroup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ impl SocketGroup {
&& self.low_prio_count == 0
}

/// On null: `dns_err` (getaddrinfo code) is set if `host` did not resolve, else `err` (errno).
pub fn listen(
&mut self,
kind: SocketKind,
Expand All @@ -162,6 +163,7 @@ impl SocketGroup {
options: c_int,
socket_ext_size: c_int,
err: &mut c_int,
dns_err: &mut c_int,
) -> *mut ListenSocket {
// SAFETY: forwarding to C; all pointers are valid or null as documented.
unsafe {
Expand All @@ -174,6 +176,7 @@ impl SocketGroup {
options,
socket_ext_size,
err,
dns_err,
)
}
}
Expand Down Expand Up @@ -318,6 +321,7 @@ unsafe extern "C" {
options: c_int,
socket_ext_size: c_int,
err: *mut c_int,
dns_err: *mut c_int,
) -> *mut ListenSocket;
fn us_socket_group_listen_unix(
group: *mut SocketGroup,
Expand Down
3 changes: 2 additions & 1 deletion src/uws_sys/_libusockets.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,9 @@ typedef struct {
uws_websocket_close_handler close;
} uws_socket_behavior_t;

/* dns_error: getaddrinfo(3) return code if the host did not resolve, else 0. */
typedef void (*uws_listen_handler)(struct us_listen_socket_t* listen_socket,
void* user_data);
int dns_error, void* user_data);
typedef void (*uws_listen_domain_handler)(
struct us_listen_socket_t* listen_socket, const char* domain, int options,
void* user_data);
Expand Down
16 changes: 8 additions & 8 deletions src/uws_sys/libuwsockets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,16 +439,16 @@ extern "C"
{
uWS::SSLApp *uwsApp = (uWS::SSLApp *)app;
uwsApp->listen(port, [handler,
user_data](struct us_listen_socket_t *listen_socket)
{ handler((struct us_listen_socket_t *)listen_socket, user_data); });
user_data](struct us_listen_socket_t *listen_socket, int dns_error)
{ handler(listen_socket, dns_error, user_data); });
}
else
{
uWS::App *uwsApp = (uWS::App *)app;

uwsApp->listen(port, [handler,
user_data](struct us_listen_socket_t *listen_socket)
{ handler((struct us_listen_socket_t *)listen_socket, user_data); });
user_data](struct us_listen_socket_t *listen_socket, int dns_error)
{ handler(listen_socket, dns_error, user_data); });
}
}

Expand All @@ -462,19 +462,19 @@ extern "C"
uWS::SSLApp *uwsApp = (uWS::SSLApp *)app;
uwsApp->listen(
hostname, port, options,
[handler, user_data](struct us_listen_socket_t *listen_socket)
[handler, user_data](struct us_listen_socket_t *listen_socket, int dns_error)
{
handler((struct us_listen_socket_t *)listen_socket, user_data);
handler(listen_socket, dns_error, user_data);
});
}
else
{
uWS::App *uwsApp = (uWS::App *)app;
uwsApp->listen(
hostname, port, options,
[handler, user_data](struct us_listen_socket_t *listen_socket)
[handler, user_data](struct us_listen_socket_t *listen_socket, int dns_error)
{
handler((struct us_listen_socket_t *)listen_socket, user_data);
handler(listen_socket, dns_error, user_data);
});
}
}
Expand Down
Loading
Loading