diff --git a/packages/posix/src/driver.ts b/packages/posix/src/driver.ts index 3128f3308..9a98fbbb3 100644 --- a/packages/posix/src/driver.ts +++ b/packages/posix/src/driver.ts @@ -50,7 +50,7 @@ import { ModuleCache } from './module-cache.js'; import { readdir, stat } from 'node:fs/promises'; import { existsSync, statSync } from 'node:fs'; import { basename, isAbsolute, join } from 'node:path'; -import { type Socket } from 'node:net'; +import { type Socket, isIP } from 'node:net'; import { connect as tlsConnect, type TLSSocket } from 'node:tls'; import { lookup } from 'node:dns/promises'; @@ -1325,8 +1325,11 @@ class WasmVmRuntimeDriver implements RuntimeDriver { const hostname = msg.args.hostname as string; const tlsOpts: Record = { socket: realSock, - servername: hostname, // SNI }; + // SNI is not allowed for IP addresses in Node.js + if (!isIP(hostname)) { + tlsOpts.servername = hostname; + } if (msg.args.verifyPeer === false) { tlsOpts.rejectUnauthorized = false; } diff --git a/packages/posix/src/wasi-constants.ts b/packages/posix/src/wasi-constants.ts index 8dd062bdd..17fbf7ebc 100644 --- a/packages/posix/src/wasi-constants.ts +++ b/packages/posix/src/wasi-constants.ts @@ -100,12 +100,16 @@ export const ERRNO_EBADF = 8; export const ERRNO_ECHILD = 10; export const ERRNO_ECONNREFUSED = 14; export const ERRNO_EEXIST = 20; +export const ERRNO_EINPROGRESS = 26; export const ERRNO_EINVAL = 28; export const ERRNO_EIO = 76; export const ERRNO_EISDIR = 31; +export const ERRNO_EMFILE = 33; +export const ERRNO_EMSGSIZE = 40; export const ERRNO_ENOENT = 44; export const ERRNO_ENOSPC = 51; export const ERRNO_ENOSYS = 52; +export const ERRNO_ENOTCONN = 53; export const ERRNO_ENOTDIR = 54; export const ERRNO_ENOTEMPTY = 55; export const ERRNO_EPERM = 63; @@ -123,12 +127,16 @@ export const ERRNO_MAP: Record = { ECHILD: ERRNO_ECHILD, ECONNREFUSED: ERRNO_ECONNREFUSED, EEXIST: ERRNO_EEXIST, + EINPROGRESS: ERRNO_EINPROGRESS, EINVAL: ERRNO_EINVAL, EIO: ERRNO_EIO, EISDIR: ERRNO_EISDIR, + EMFILE: ERRNO_EMFILE, + EMSGSIZE: ERRNO_EMSGSIZE, ENOENT: ERRNO_ENOENT, ENOSPC: ERRNO_ENOSPC, ENOSYS: ERRNO_ENOSYS, + ENOTCONN: ERRNO_ENOTCONN, ENOTDIR: ERRNO_ENOTDIR, ENOTEMPTY: ERRNO_ENOTEMPTY, EPERM: ERRNO_EPERM, diff --git a/registry/native/c/curl-upstream-overlay/lib/wasi_getsockopt.c b/registry/native/c/curl-upstream-overlay/lib/wasi_getsockopt.c new file mode 100644 index 000000000..e737b27bb --- /dev/null +++ b/registry/native/c/curl-upstream-overlay/lib/wasi_getsockopt.c @@ -0,0 +1,36 @@ +/* getsockopt shim — routes through host_net.net_getsockopt WASM import. + * + * The WASI socket patch provides setsockopt but not getsockopt. curl calls + * getsockopt(SOL_SOCKET, SO_ERROR) in verifyconnect() to check the result + * of a non-blocking connect. Without this shim, getsockopt returns -1 and + * curl treats every connection as failed (exit code 7). + */ + +#include +#include +#include + +#define WASM_IMPORT(mod, fn) \ + __attribute__((__import_module__(mod), __import_name__(fn))) + +WASM_IMPORT("host_net", "net_getsockopt") +uint32_t __host_net_getsockopt(uint32_t fd, uint32_t level, uint32_t optname, + uint8_t *optval_ptr, uint32_t *optval_len_ptr); + +int getsockopt(int sockfd, int level, int optname, void *restrict optval, + socklen_t *restrict optlen) { + if (optval == NULL || optlen == NULL) { + errno = EINVAL; + return -1; + } + uint32_t len = (uint32_t)*optlen; + uint32_t err = __host_net_getsockopt( + (uint32_t)sockfd, (uint32_t)level, (uint32_t)optname, + (uint8_t *)optval, &len); + if (err != 0) { + errno = (int)err; + return -1; + } + *optlen = (socklen_t)len; + return 0; +} diff --git a/registry/native/c/scripts/build-curl-upstream.sh b/registry/native/c/scripts/build-curl-upstream.sh index e958afc34..4c1797ca9 100644 --- a/registry/native/c/scripts/build-curl-upstream.sh +++ b/registry/native/c/scripts/build-curl-upstream.sh @@ -207,16 +207,20 @@ if "#define CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG 1" not in text: config.write_text(text) PY -echo "Patching generated lib/Makefile to compile wasi_tls.c..." +echo "Patching generated lib/Makefile to compile wasi_tls.c and wasi_getsockopt.c..." cat >> lib/Makefile <<'EOF' -am_libcurl_la_OBJECTS += vtls/libcurl_la-wasi_tls.lo -libcurl_la_LIBADD += vtls/libcurl_la-wasi_tls.lo -libcurl.la: vtls/libcurl_la-wasi_tls.lo +am_libcurl_la_OBJECTS += vtls/libcurl_la-wasi_tls.lo libcurl_la-wasi_getsockopt.lo +libcurl_la_LIBADD += vtls/libcurl_la-wasi_tls.lo libcurl_la-wasi_getsockopt.lo +libcurl.la: vtls/libcurl_la-wasi_tls.lo libcurl_la-wasi_getsockopt.lo vtls/libcurl_la-wasi_tls.lo: vtls/$(am__dirstamp) vtls/$(DEPDIR)/$(am__dirstamp) vtls/wasi_tls.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-wasi_tls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-wasi_tls.Tpo -c -o vtls/libcurl_la-wasi_tls.lo `test -f 'vtls/wasi_tls.c' || echo '$(srcdir)/'`vtls/wasi_tls.c $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-wasi_tls.Tpo vtls/$(DEPDIR)/libcurl_la-wasi_tls.Plo + +libcurl_la-wasi_getsockopt.lo: wasi_getsockopt.c + $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-wasi_getsockopt.lo -MD -MP -MF $(DEPDIR)/libcurl_la-wasi_getsockopt.Tpo -c -o libcurl_la-wasi_getsockopt.lo `test -f 'wasi_getsockopt.c' || echo '$(srcdir)/'`wasi_getsockopt.c + $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-wasi_getsockopt.Tpo $(DEPDIR)/libcurl_la-wasi_getsockopt.Plo EOF echo "Building upstream libcurl..."