From bda1ad192d970c6beccb56b64aa4f686a7cd39ce Mon Sep 17 00:00:00 2001 From: Kai Tamkun <13513421+heimskr@users.noreply.github.com> Date: Fri, 21 Feb 2025 18:34:40 -0800 Subject: [PATCH] Fix EINVAL when setting IPv6 multicast membership (#17478) --- packages/bun-usockets/src/bsd.c | 2 +- src/bun.js/api/bun/udp_socket.zig | 4 +- test/js/node/dgram/node-dgram.test.js | 53 +++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 test/js/node/dgram/node-dgram.test.js diff --git a/packages/bun-usockets/src/bsd.c b/packages/bun-usockets/src/bsd.c index 5b4e24a1b4..1dfd4e230c 100644 --- a/packages/bun-usockets/src/bsd.c +++ b/packages/bun-usockets/src/bsd.c @@ -408,7 +408,7 @@ static int bsd_socket_set_membership6(LIBUS_SOCKET_DESCRIPTOR fd, const struct s mreq.ipv6mr_interface = iface->sin6_scope_id; } int option = drop ? IPV6_LEAVE_GROUP : IPV6_JOIN_GROUP; - return setsockopt(fd, IPPROTO_IP, option, &mreq, sizeof(mreq)); + return setsockopt(fd, IPPROTO_IPV6, option, &mreq, sizeof(mreq)); } int bsd_socket_set_membership(LIBUS_SOCKET_DESCRIPTOR fd, const struct sockaddr_storage *addr, const struct sockaddr_storage *iface, int drop) { diff --git a/src/bun.js/api/bun/udp_socket.zig b/src/bun.js/api/bun/udp_socket.zig index e29508f3a0..5729da9b9b 100644 --- a/src/bun.js/api/bun/udp_socket.zig +++ b/src/bun.js/api/bun/udp_socket.zig @@ -444,12 +444,12 @@ pub const UDPSocket = struct { return globalThis.throwInvalidArguments("Expected 1 argument, got {}", .{arguments.len}); } - var addr: std.posix.sockaddr.storage = undefined; + var addr = std.mem.zeroes(std.posix.sockaddr.storage); if (!parseAddr(this, globalThis, JSC.jsNumber(0), arguments[0], &addr)) { return globalThis.throwValue(bun.JSC.Maybe(void).errnoSys(@as(i32, @intCast(@intFromEnum(std.posix.E.INVAL))), .setsockopt).?.toJS(globalThis)); } - var interface: std.posix.sockaddr.storage = undefined; + var interface = std.mem.zeroes(std.posix.sockaddr.storage); const res = if (arguments.len > 1 and parseAddr(this, globalThis, JSC.jsNumber(0), arguments[1], &interface)) blk: { if (addr.family != interface.family) { diff --git a/test/js/node/dgram/node-dgram.test.js b/test/js/node/dgram/node-dgram.test.js new file mode 100644 index 0000000000..7f888e9376 --- /dev/null +++ b/test/js/node/dgram/node-dgram.test.js @@ -0,0 +1,53 @@ +import { describe, expect, it } from "bun:test"; +import * as dgram from "node:dgram"; +import { isWindows, isMacOS, isIPv6 } from "harness"; + +describe.skipIf(!isIPv6())("node:dgram", () => { + it("adds membership successfully (IPv6)", () => { + const socket = makeSocket6(); + socket.bind(0, () => { + socket.addMembership("ff01::1", getInterface()); + if (!isMacOS) { + // macOS seems to be iffy with automatically choosing an interface. + socket.addMembership("ff02::1"); + } + }); + }); + + it("doesn't add membership given invalid inputs (IPv6)", () => { + const { promise, resolve, reject } = Promise.withResolvers(); + const socket = makeSocket6(); + socket.bind(0, () => { + expect(() => { + // fe00:: is not a valid multicast address + socket.addMembership("fe00::", getInterface()); + reject(); + }).toThrow(); + expect(() => { + socket.addMembership("fe00::"); + reject(); + }).toThrow(); + resolve(); + }); + return promise; + }); +}); + +function makeSocket6() { + return dgram.createSocket({ + type: "udp6", + ipv6Only: true, + }); +} + +function getInterface() { + if (isWindows) { + return "::%1"; + } + + if (isMacOS) { + return "::%lo0"; + } + + return "::%lo"; +}