From 35b0b0a41335a4e086c6226f3e3859283358bee1 Mon Sep 17 00:00:00 2001 From: Ian Gordon Date: Tue, 28 Jul 2026 11:18:48 -0400 Subject: [PATCH] Darwin: correct ipv6_pktinfo to IPV6_3542PKTINFO (46) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hardcoded value 50 is IPV6_3542DSTOPTS in , not IPV6_PKTINFO — so the cmsg_type the kernel delivers for IPV6_RECVPKTINFO (61) never matched and received IPv6 datagram Messages always carried nil interfaceIndex/localAddress. The RFC 3542 value paired with IPV6_RECVPKTINFO is IPV6_3542PKTINFO (46). Swift cannot define __APPLE_USE_RFC_3542, hence the hardcoded constants. Closes TVT-1131 Co-Authored-By: Claude Fable 5 --- FlyingSocks/Sources/Socket+Darwin.swift | 7 ++++-- FlyingSocks/Tests/AsyncSocketTests.swift | 27 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/FlyingSocks/Sources/Socket+Darwin.swift b/FlyingSocks/Sources/Socket+Darwin.swift index 2524411..88aca68 100644 --- a/FlyingSocks/Sources/Socket+Darwin.swift +++ b/FlyingSocks/Sources/Socket+Darwin.swift @@ -51,8 +51,11 @@ extension Socket { static let ipproto_ip = Int32(IPPROTO_IP) static let ipproto_ipv6 = Int32(IPPROTO_IPV6) static let ip_pktinfo = Int32(IP_PKTINFO) - static let ipv6_pktinfo = Int32(50) // __APPLE_USE_RFC_2292 - static let ipv6_recvpktinfo = Int32(61) // __APPLE_USE_RFC_2292 + // Swift cannot define __APPLE_USE_RFC_3542 before importing Darwin, so + // the RFC 3542 values from are hardcoded: + // IPV6_PKTINFO = IPV6_3542PKTINFO (46), IPV6_RECVPKTINFO (61). + static let ipv6_pktinfo = Int32(46) // __APPLE_USE_RFC_3542 + static let ipv6_recvpktinfo = Int32(61) // __APPLE_USE_RFC_3542 static func makeAddressINET(port: UInt16) -> Darwin.sockaddr_in { Darwin.sockaddr_in( diff --git a/FlyingSocks/Tests/AsyncSocketTests.swift b/FlyingSocks/Tests/AsyncSocketTests.swift index 1237457..20f82c6 100644 --- a/FlyingSocks/Tests/AsyncSocketTests.swift +++ b/FlyingSocks/Tests/AsyncSocketTests.swift @@ -297,6 +297,33 @@ struct AsyncSocketTests { ) } #endif + + @Test + func receiveMessage_ParsesIPv6PacketInfo() async throws { + // TVT-1131 regression: Socket.ipv6_pktinfo must match the cmsg_type + // the kernel delivers for IPV6_RECVPKTINFO — on Darwin the RFC 3542 + // IPV6_PKTINFO (46), per . Datagram sockets enable + // pktinfo delivery in Socket.init, so a received Message carries the + // parsed interface index and local (destination) address. + let (server, port) = try await AsyncSocket.makeLoopbackDatagram() + + async let received: AsyncSocket.Message = server.receive(atMost: 100) + + let client = try await AsyncSocket.makeLoopbackDatagram().0 + try await client.send(Data("Peas 🫛".utf8), to: sockaddr_in6.loopback(port: port)) + + let message = try await received + #expect(try message.payloadString == "Peas 🫛") + #expect(message.interfaceIndex != nil) + let storage = try #require(message.localAddress).makeStorage() + let sin6 = withUnsafeBytes(of: storage) { $0.load(as: sockaddr_in6.self) } + let loopback = sockaddr_in6.loopback(port: 0).sin6_addr + #expect( + withUnsafeBytes(of: sin6.sin6_addr) { received in + withUnsafeBytes(of: loopback) { received.elementsEqual($0) } + } + ) + } #endif }