From 462e463da6a9b3ad613ca234d62806da1a9321cc Mon Sep 17 00:00:00 2001 From: Greg Cotten Date: Tue, 25 Mar 2025 09:58:31 -0700 Subject: [PATCH 1/2] getWindows to actually compile and pass some tests --- FlyingSocks/Sources/Mutex.swift | 2 +- FlyingSocks/Sources/Socket+WinSock2.swift | 136 ++++++++++++++++-- FlyingSocks/Sources/Socket.swift | 14 +- .../Tests/FileManager+TemporaryFile.swift | 9 +- FlyingSocks/Tests/SocketPool+PollTests.swift | 2 +- FlyingSocks/Tests/SocketTests.swift | 40 ++++-- 6 files changed, 173 insertions(+), 30 deletions(-) diff --git a/FlyingSocks/Sources/Mutex.swift b/FlyingSocks/Sources/Mutex.swift index 9847c346..d4d79e54 100644 --- a/FlyingSocks/Sources/Mutex.swift +++ b/FlyingSocks/Sources/Mutex.swift @@ -204,7 +204,7 @@ extension Mutex { } func tryLock() -> Bool { - TryAcquireSRWLockExclusive(_lock) + TryAcquireSRWLockExclusive(_lock) != 0 } } } diff --git a/FlyingSocks/Sources/Socket+WinSock2.swift b/FlyingSocks/Sources/Socket+WinSock2.swift index 918cbc90..eb75dd1b 100755 --- a/FlyingSocks/Sources/Socket+WinSock2.swift +++ b/FlyingSocks/Sources/Socket+WinSock2.swift @@ -31,16 +31,17 @@ #if canImport(WinSDK) import WinSDK.WinSock2 +import Foundation let O_NONBLOCK = Int32(1) let F_SETFL = Int32(1) let F_GETFL = Int32(1) var errno: Int32 { WSAGetLastError() } let EWOULDBLOCK = WSAEWOULDBLOCK -let EBADF = WSA_INVALID_HANDLE +let EBADF = WSAENOTSOCK let EINPROGRESS = WSAEINPROGRESS let EISCONN = WSAEISCONN -public typealias sa_family_t = UInt8 +public typealias sa_family_t = ADDRESS_FAMILY public extension Socket { typealias FileDescriptorType = UInt64 @@ -59,10 +60,9 @@ extension Socket { static let datagram = Int32(SOCK_DGRAM) static let in_addr_any = WinSDK.in_addr() static let ipproto_ip = Int32(IPPROTO_IP) - static let ipproto_ipv6 = Int32(IPPROTO_IPV6) + static let ipproto_ipv6 = Int32(IPPROTO_IPV6.rawValue) static let ip_pktinfo = Int32(IP_PKTINFO) static let ipv6_pktinfo = Int32(IPV6_PKTINFO) - static let ipv6_recvpktinfo = Int32(IPV6_RECVPKTINFO) static func makeAddressINET(port: UInt16) -> WinSDK.sockaddr_in { WinSDK.sockaddr_in( @@ -107,6 +107,7 @@ extension Socket { } static func fcntl(_ fd: FileDescriptorType, _ cmd: Int32) -> Int32 { + guard fd != INVALID_SOCKET else { return -1 } return 0 } @@ -123,7 +124,110 @@ extension Socket { } static func socketpair(_ domain: Int32, _ type: Int32, _ protocol: Int32) -> (FileDescriptorType, FileDescriptorType) { - (-1, -1) // no supported + guard domain == AF_UNIX else { return (INVALID_SOCKET, INVALID_SOCKET) } + func makeTempUnixPath() -> URL { + let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("socketpair_\(UUID().uuidString.prefix(8)).sock", isDirectory: false) + try? FileManager.default.removeItem(at: tempURL) + return tempURL + } + + if type == SOCK_STREAM { + let tempURL = makeTempUnixPath() + defer { try? FileManager.default.removeItem(at: tempURL) } + + let listener = socket(domain, type, `protocol`) + + guard listener != INVALID_SOCKET else { return (INVALID_SOCKET, INVALID_SOCKET) } + + let addr = makeAddressUnix(path: tempURL.path) + + let bindListenerResult = addr.withSockAddr { + bind(listener, $0, addr.size) + } + + guard bindListenerResult == 0 else { return (INVALID_SOCKET, INVALID_SOCKET) } + + guard listen(listener, 1) == 0 else { + _ = close(listener) + return (INVALID_SOCKET, INVALID_SOCKET) + } + + let connector = socket(domain, type, `protocol`) + + guard connector != INVALID_SOCKET else { + _ = close(listener) + return (INVALID_SOCKET, INVALID_SOCKET) + } + + let connectResult = addr.withSockAddr { connect(connector, $0, addr.size) == 0 } + + guard connectResult else { + _ = close(listener) + _ = close(connector) + return (INVALID_SOCKET, INVALID_SOCKET) + } + + let acceptor = accept(listener, nil, nil) + guard acceptor != INVALID_SOCKET else { + _ = close(listener) + _ = close(connector) + return (INVALID_SOCKET, INVALID_SOCKET) + } + + _ = close(listener) + + return (connector, acceptor) + } else if type == SOCK_DGRAM { + return (INVALID_SOCKET, INVALID_SOCKET) + // unsupported at this time: https://github.com/microsoft/WSL/issues/5272 + // let tempURL1 = makeTempUnixPath() + // let tempURL2 = makeTempUnixPath() + // guard FileManager.default.createFile(atPath: tempURL1.path, contents: nil) else { return (INVALID_SOCKET, INVALID_SOCKET) } + // guard FileManager.default.createFile(atPath: tempURL2.path, contents: nil) else { return (INVALID_SOCKET, INVALID_SOCKET) } + + // defer { try? FileManager.default.removeItem(at: tempURL1) } + // defer { try? FileManager.default.removeItem(at: tempURL2) } + + // let socket1 = socket(domain, type, `protocol`) + // let socket2 = socket(domain, type, `protocol`) + + // guard socket1 != INVALID_SOCKET, socket2 != INVALID_SOCKET else { + // if socket1 != INVALID_SOCKET { _ = close(socket1) } + // if socket2 != INVALID_SOCKET { _ = close(socket2) } + // return (INVALID_SOCKET, INVALID_SOCKET) + // } + + // let addr1 = makeAddressUnix(path: tempURL1.path) + // let addr2 = makeAddressUnix(path: tempURL2.path) + + // guard addr1.withSockAddr({ bind(socket1, $0, addr1.size) }) == 0 else { + // _ = close(socket1) + // _ = close(socket2) + // return (INVALID_SOCKET, INVALID_SOCKET) + // } + + // guard addr2.withSockAddr({ bind(socket2, $0, addr2.size) }) == 0 else { + // _ = close(socket1) + // _ = close(socket2) + // return (INVALID_SOCKET, INVALID_SOCKET) + // } + + // guard addr2.withSockAddr({ connect(socket1, $0, addr2.size) }) == 0 else { + // _ = close(socket1) + // _ = close(socket2) + // return (INVALID_SOCKET, INVALID_SOCKET) + // } + + // guard addr1.withSockAddr({ connect(socket2, $0, addr1.size) }) == 0 else { + // _ = close(socket1) + // _ = close(socket2) + // return (INVALID_SOCKET, INVALID_SOCKET) + // } + + // return (socket1, socket2) + } else { + return (INVALID_SOCKET, INVALID_SOCKET) + } } static func setsockopt(_ fd: FileDescriptorType, _ level: Int32, _ name: Int32, @@ -196,19 +300,29 @@ extension Socket { } static func recvfrom(_ fd: FileDescriptorType, _ buffer: UnsafeMutableRawPointer!, _ nbyte: Int, _ flags: Int32, _ addr: UnsafeMutablePointer!, _ len: UnsafeMutablePointer!) -> Int { - WinSDK.recvfrom(fd, buffer, nbyte, flags, addr, len) + Int(WinSDK.recvfrom(fd, buffer, Int32(nbyte), flags, addr, len)) } static func sendto(_ fd: FileDescriptorType, _ buffer: UnsafeRawPointer!, _ nbyte: Int, _ flags: Int32, _ destaddr: UnsafePointer!, _ destlen: socklen_t) -> Int { - WinSDK.sendto(fd, buffer, nbyte, flags, destaddr, destlen) + Int(WinSDK.sendto(fd, buffer, Int32(nbyte), flags, destaddr, destlen)) } +} - static func recvmsg(_ fd: FileDescriptorType, _ message: UnsafeMutablePointer, _ flags: Int32) -> Int { - WinSDK.recvmsg(fd, message, flags) +public extension in_addr { + var s_addr: UInt32 { + get { + S_un.S_addr + } set { + S_un.S_addr = newValue + } } +} - static func sendmsg(_ fd: FileDescriptorType, _ message: UnsafePointer, _ flags: Int32) -> Int { - WinSDK.sendmsg(fd, message, flags) +private extension URL { + var fileSystemRepresentation: String { + withUnsafeFileSystemRepresentation { + String(cString: $0!) + } } } diff --git a/FlyingSocks/Sources/Socket.swift b/FlyingSocks/Sources/Socket.swift index 7b6669d0..877b6732 100644 --- a/FlyingSocks/Sources/Socket.swift +++ b/FlyingSocks/Sources/Socket.swift @@ -30,7 +30,7 @@ // #if canImport(WinSDK) -import WinSDK.WinSock2 +@_exported import WinSDK.WinSock2 #elseif canImport(Android) @_exported import Android #endif @@ -126,8 +126,10 @@ public struct Socket: Sendable, Hashable { switch domain { case AF_INET: try setValue(true, for: .packetInfoIP) + #if !canImport(WinSDK) case AF_INET6: try setValue(true, for: .packetInfoIPv6) + #endif default: return } @@ -566,9 +568,11 @@ public extension SocketOption where Self == BoolSocketOption { BoolSocketOption(level: Socket.ipproto_ip, name: Socket.ip_pktinfo) } + #if !canImport(WinSDK) static var packetInfoIPv6: Self { BoolSocketOption(level: Socket.ipproto_ipv6, name: Socket.ipv6_recvpktinfo) } + #endif #if canImport(Darwin) // Prevents SIG_TRAP when app is paused / running in background. @@ -597,9 +601,17 @@ package extension Socket { static func makePair(flags: Flags? = nil, type: SocketType = .stream) throws -> (Socket, Socket) { let (file1, file2) = Socket.socketpair(AF_UNIX, type.rawValue, 0) + + #if canImport(WinSDK) + guard file1 != INVALID_SOCKET, file2 != INVALID_SOCKET else { + throw SocketError.makeFailed("SocketPair") + } + #else guard file1 > -1, file2 > -1 else { throw SocketError.makeFailed("SocketPair") } + #endif + let s1 = Socket(file: .init(rawValue: file1)) let s2 = Socket(file: .init(rawValue: file2)) diff --git a/FlyingSocks/Tests/FileManager+TemporaryFile.swift b/FlyingSocks/Tests/FileManager+TemporaryFile.swift index 3d9837b2..407e8023 100644 --- a/FlyingSocks/Tests/FileManager+TemporaryFile.swift +++ b/FlyingSocks/Tests/FileManager+TemporaryFile.swift @@ -20,7 +20,14 @@ extension FileManager { let dirPath = temporaryDirectory.appendingPathComponent("FlyingSocks.XXXXXX") return dirPath.withUnsafeFileSystemRepresentation { maybePath in guard let path = maybePath else { return nil } - var mutablePath = Array(repeating: Int8(0), count: Int(PATH_MAX)) + + #if canImport(WinSDK) + let pathMax = Int(MAX_PATH) + #else + let pathMax = Int(PATH_MAX) + #endif + + var mutablePath = Array(repeating: Int8(0), count: pathMax) mutablePath.withUnsafeMutableBytes { mutablePathBufferPtr in mutablePathBufferPtr.baseAddress!.copyMemory( from: path, byteCount: Int(strlen(path)) + 1) diff --git a/FlyingSocks/Tests/SocketPool+PollTests.swift b/FlyingSocks/Tests/SocketPool+PollTests.swift index 8999d5e6..694d5c26 100644 --- a/FlyingSocks/Tests/SocketPool+PollTests.swift +++ b/FlyingSocks/Tests/SocketPool+PollTests.swift @@ -254,7 +254,7 @@ private extension Poll { } private extension pollfd { - static func make(fd: Int32 = 0, + static func make(fd: Socket.FileDescriptorType = 0, events: Int32 = POLLIN, revents: Int32 = POLLIN) -> Self { .init(fd: fd, events: Int16(events), revents: Int16(revents)) diff --git a/FlyingSocks/Tests/SocketTests.swift b/FlyingSocks/Tests/SocketTests.swift index 218ab923..46ded162 100644 --- a/FlyingSocks/Tests/SocketTests.swift +++ b/FlyingSocks/Tests/SocketTests.swift @@ -161,7 +161,7 @@ struct SocketTests { let socket = try Socket(domain: AF_UNIX, type: .stream) try socket.setValue(2048, for: .receiveBufferSize) -#if canImport(Darwin) +#if canImport(Darwin) || canImport(WinSDK) #expect(try socket.getValue(for: .receiveBufferSize) == Int32(2048)) #else // Linux kernel doubles this value (to allow space for bookkeeping overhead) @@ -174,7 +174,7 @@ struct SocketTests { let socket = try Socket(domain: AF_UNIX, type: .stream) try socket.setValue(2048, for: .sendBufferSize) -#if canImport(Darwin) +#if canImport(Darwin) || canImport(WinSDK) #expect(try socket.getValue(for: .sendBufferSize) == Int32(2048)) #else // Linux kernel doubles this value (to allow space for bookkeeping overhead) @@ -204,7 +204,7 @@ struct SocketTests { @Test func socketAccept_ThrowsError_WhenInvalid() { - let socket = Socket(file: -1) + let socket = Socket.invalid() #expect(throws: SocketError.self) { try socket.accept() } @@ -212,7 +212,7 @@ struct SocketTests { @Test func socketConnect_ThrowsError_WhenInvalid() { - let socket = Socket(file: -1) + let socket = Socket.invalid() #expect(throws: SocketError.self) { try socket.connect(to: .unix(path: "test")) } @@ -220,7 +220,7 @@ struct SocketTests { @Test func socketClose_ThrowsError_WhenInvalid() { - let socket = Socket(file: -1) + let socket = Socket.invalid() #expect(throws: SocketError.self) { try socket.close() } @@ -228,7 +228,7 @@ struct SocketTests { @Test func socketListen_ThrowsError_WhenInvalid() { - let socket = Socket(file: -1) + let socket = Socket.invalid() #expect(throws: SocketError.self) { try socket.listen() } @@ -248,7 +248,7 @@ struct SocketTests { @Test func socketBind_ToINET6_ThrowsError_WhenInvalid() { - let socket = Socket(file: -1) + let socket = Socket.invalid() let address = Socket.makeAddressINET6(port: 8080) #expect(throws: SocketError.self) { try socket.bind(to: address) @@ -257,7 +257,7 @@ struct SocketTests { @Test func socketBind_ToStorage_ThrowsError_WhenInvalid() { - let socket = Socket(file: -1) + let socket = Socket.invalid() let address = Socket.makeAddressINET6(port: 8080) #expect(throws: SocketError.self) { try socket.bind(to: address) @@ -266,7 +266,7 @@ struct SocketTests { @Test func socketGetOption_ThrowsError_WhenInvalid() { - let socket = Socket(file: -1) + let socket = Socket.invalid() #expect(throws: SocketError.self) { _ = try socket.getValue(for: .localAddressReuse) } @@ -274,7 +274,7 @@ struct SocketTests { @Test func socketSetOption_ThrowsError_WhenInvalid() { - let socket = Socket(file: -1) + let socket = Socket.invalid() #expect(throws: SocketError.self) { try socket.setValue(true, for: .localAddressReuse) } @@ -282,7 +282,7 @@ struct SocketTests { @Test func socketGetFlags_ThrowsError_WhenInvalid() { - let socket = Socket(file: -1) + let socket = Socket.invalid() #expect(throws: SocketError.self) { _ = try socket.flags } @@ -290,7 +290,7 @@ struct SocketTests { @Test func socketSetFlags_ThrowsError_WhenInvalid() { - let socket = Socket(file: -1) + let socket = Socket.invalid() #expect(throws: SocketError.self) { try socket.setFlags(.nonBlocking) } @@ -298,7 +298,7 @@ struct SocketTests { @Test func socketRemotePeer_ThrowsError_WhenInvalid() { - let socket = Socket(file: -1) + let socket = Socket.invalid() #expect(throws: SocketError.self) { try socket.remotePeer() } @@ -306,7 +306,7 @@ struct SocketTests { @Test func socket_sockname_ThrowsError_WhenInvalid() { - let socket = Socket(file: -1) + let socket = Socket.invalid() #expect(throws: SocketError.self) { try socket.sockname() } @@ -331,6 +331,7 @@ struct SocketTests { ) } + #if !canImport(WinSDK) @Test func makes_datagram_ip6() throws { let socket = try Socket(domain: Int32(sa_family_t(AF_INET6)), type: .datagram) @@ -339,6 +340,7 @@ struct SocketTests { try socket.getValue(for: .packetInfoIPv6) == true ) } + #endif } extension Socket.Flags { @@ -346,7 +348,15 @@ extension Socket.Flags { } private extension Socket { - init(file: Int32) { + init(file: FileDescriptorType) { self.init(file: .init(rawValue: file)) } + + static func invalid() -> Socket { + #if canImport(WinSDK) + self.init(file: .init(rawValue: INVALID_SOCKET)) + #else + self.init(file: .init(rawValue: -1)) + #endif + } } From 70fb1ec6657e97522892fa386cbd28bd48caaa46 Mon Sep 17 00:00:00 2001 From: Greg Cotten Date: Tue, 25 Mar 2025 10:02:24 -0700 Subject: [PATCH 2/2] setting errno doesn't work correctly on windows? --- FlyingSocks/Tests/SocketErrorTests.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/FlyingSocks/Tests/SocketErrorTests.swift b/FlyingSocks/Tests/SocketErrorTests.swift index a0f2c780..d96e6585 100644 --- a/FlyingSocks/Tests/SocketErrorTests.swift +++ b/FlyingSocks/Tests/SocketErrorTests.swift @@ -52,7 +52,12 @@ struct SocketErrorTests { @Test func socketError_makeFailed() { + #if canImport(WinSDK) + WSASetLastError(EIO) + #else errno = EIO + #endif + let socketError = SocketError.makeFailed("unit-test") switch socketError { case let .failed(type: type, errno: socketErrno, message: message):