From 5d68f1c73fb5306a30cd892564603a379594f8fc Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Thu, 28 Oct 2021 19:09:03 -0500 Subject: [PATCH 1/5] Added socket support --- Sources/System/FileDescriptor.swift | 14 + Sources/System/FileDescriptorSet.swift | 179 +++++ Sources/System/FileEvent.swift | 76 +++ Sources/System/FileHelpers.swift | 44 +- .../System/Internals/CInternetAddress.swift | 76 +++ Sources/System/Internals/CInterop.swift | 53 +- Sources/System/Internals/CSocketAddress.swift | 55 ++ Sources/System/Internals/Compatibility.swift | 48 ++ Sources/System/Internals/Constants.swift | 332 +++++++++ Sources/System/Internals/NetworkOrder.swift | 21 + Sources/System/Internals/Syscalls.swift | 237 +++++++ Sources/System/InternetProtocol.swift | 208 ++++++ Sources/System/Socket/MessageFlags.swift | 59 ++ Sources/System/Socket/SocketAddress.swift | 151 +++++ .../System/Socket/SocketAddressFamily.swift | 224 ++++++ Sources/System/Socket/SocketFlags.swift | 56 ++ Sources/System/Socket/SocketOperations.swift | 636 ++++++++++++++++++ Sources/System/Socket/SocketOption.swift | 84 +++ Sources/System/Socket/SocketOptionID.swift | 45 ++ Sources/System/Socket/SocketOptionLevel.swift | 41 ++ Sources/System/Socket/SocketProtocol.swift | 94 +++ Sources/System/Socket/SocketType.swift | 69 ++ Sources/System/Util.swift | 13 + 23 files changed, 2813 insertions(+), 2 deletions(-) create mode 100644 Sources/System/FileDescriptorSet.swift create mode 100644 Sources/System/FileEvent.swift create mode 100644 Sources/System/Internals/CInternetAddress.swift create mode 100644 Sources/System/Internals/CSocketAddress.swift create mode 100644 Sources/System/Internals/Compatibility.swift create mode 100644 Sources/System/Internals/NetworkOrder.swift create mode 100644 Sources/System/InternetProtocol.swift create mode 100644 Sources/System/Socket/MessageFlags.swift create mode 100644 Sources/System/Socket/SocketAddress.swift create mode 100644 Sources/System/Socket/SocketAddressFamily.swift create mode 100644 Sources/System/Socket/SocketFlags.swift create mode 100644 Sources/System/Socket/SocketOperations.swift create mode 100644 Sources/System/Socket/SocketOption.swift create mode 100644 Sources/System/Socket/SocketOptionID.swift create mode 100644 Sources/System/Socket/SocketOptionLevel.swift create mode 100644 Sources/System/Socket/SocketProtocol.swift create mode 100644 Sources/System/Socket/SocketType.swift diff --git a/Sources/System/FileDescriptor.swift b/Sources/System/FileDescriptor.swift index 43ef3b1e..329f1c23 100644 --- a/Sources/System/FileDescriptor.swift +++ b/Sources/System/FileDescriptor.swift @@ -25,6 +25,20 @@ public struct FileDescriptor: RawRepresentable, Hashable, Codable { public init(rawValue: CInt) { self.rawValue = rawValue } } +internal extension FileDescriptor { + + init(socket: CInterop.SocketDescriptor) { + // On Unix a file descriptor can be a file, pipe, socket, etc + // On windows file descriptors and sockets are different types + #if os(Windows) + #warning("Windows sockets not implemented") + fatalError() + #else + self.init(rawValue: socket) + #endif + } +} + // Standard file descriptors extension FileDescriptor { /// The standard input file descriptor, with a numeric value of 0. diff --git a/Sources/System/FileDescriptorSet.swift b/Sources/System/FileDescriptorSet.swift new file mode 100644 index 00000000..c74da0e1 --- /dev/null +++ b/Sources/System/FileDescriptorSet.swift @@ -0,0 +1,179 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 - 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +public extension FileDescriptor { + + /// Set of file descriptors + @frozen + struct Set { + + @_alwaysEmitIntoClient + internal private(set) var bytes: CInterop.FileDescriptorSet + + @_alwaysEmitIntoClient + public init(_ bytes: CInterop.FileDescriptorSet) { + self.bytes = bytes + } + + @_alwaysEmitIntoClient + public init() { + self.init(CInterop.FileDescriptorSet()) + } + } +} + +public extension FileDescriptor.Set { + + @_alwaysEmitIntoClient + init(_ sequence: S) where S: Sequence, S.Element == FileDescriptor { + self.init() + for element in sequence { + bytes.set(element.rawValue) + } + } + + @_alwaysEmitIntoClient + mutating func append(_ element: FileDescriptor) { + bytes.set(element.rawValue) + } + + @_alwaysEmitIntoClient + mutating func remove(_ element: FileDescriptor) { + bytes.clear(element.rawValue) + } + + @_alwaysEmitIntoClient + mutating func contains(_ element: FileDescriptor) -> Bool { + bytes.isSet(element.rawValue) + } + + @_alwaysEmitIntoClient + mutating func removeAll() { + self.bytes.zero() + } + + #if os(Windows) + @_alwaysEmitIntoClient + var count: Int { + return numericCast(bytes.fd_count) + } + #endif +} + +extension FileDescriptor.Set: ExpressibleByArrayLiteral { + + public init(arrayLiteral elements: FileDescriptor...) { + assert(elements.count <= _fd_set_count, + "FileDescriptor.Set can only contain \(_fd_set_count) elements") + self.init(elements) + } +} + +extension FileDescriptor.Set: CustomStringConvertible, CustomDebugStringConvertible { + + @inline(never) + public var description: String { + return "FileDescriptor.Set()" + } + + public var debugDescription: String { + return description + } +} + +public extension FileDescriptor.Set { + + @_alwaysEmitIntoClient + mutating func withUnsafeMutablePointer(_ body: (UnsafeMutablePointer) throws -> T) rethrows -> T { + return try bytes.withUnsafeMutablePointer(body) + } +} + +internal extension CInterop.FileDescriptorSet { + + @usableFromInline + mutating func zero() { + withUnsafeMutablePointer { + $0.initialize(repeating: 0, count: _fd_set_count) + } + } + + /// + /// Set an fd in an fd_set + /// + /// - Parameter fd: The fd to add to the fd_set + /// + @usableFromInline + mutating func set(_ fd: Int32) { + let (index, mask) = Self.address(for: fd) + withUnsafeMutablePointer { $0[index] |= mask } + } + + /// + /// Clear an fd from an fd_set + /// + /// - Parameter fd: The fd to clear from the fd_set + /// + @usableFromInline + mutating func clear(_ fd: Int32) { + let (index, mask) = Self.address(for: fd) + withUnsafeMutablePointer { $0[index] &= ~mask } + } + + /// + /// Check if an fd is present in an fd_set + /// + /// - Parameter fd: The fd to check + /// + /// - Returns: `True` if present, `false` otherwise. + /// + @usableFromInline + mutating func isSet(_ fd: Int32) -> Bool { + let (index, mask) = Self.address(for: fd) + return withUnsafeMutablePointer { $0[index] & mask != 0 } + } + + @usableFromInline + static func address(for fd: Int32) -> (Int, Int32) { + var intOffset = Int(fd) / _fd_set_count + #if _endian(big) + if intOffset % 2 == 0 { + intOffset += 1 + } else { + intOffset -= 1 + } + #endif + let bitOffset = Int(fd) % _fd_set_count + let mask = Int32(bitPattern: UInt32(1 << bitOffset)) + return (intOffset, mask) + } + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + @usableFromInline + mutating func withUnsafeMutablePointer(_ body: (UnsafeMutablePointer) throws -> T) rethrows -> T { + return try Swift.withUnsafeMutablePointer(to: &fds_bits) { + try body(UnsafeMutableRawPointer($0).assumingMemoryBound(to: Int32.self)) + } + } +#elseif os(Linux) || os(FreeBSD) || os(Android) + @usableFromInline + mutating func withUnsafeMutablePointer(_ body: (UnsafeMutablePointer) throws -> T) rethrows -> T { + return try Swift.withUnsafeMutablePointer(to: &__fds_bits) { + try body(UnsafeMutableRawPointer($0).assumingMemoryBound(to: Int32.self)) + } + } +#elseif os(Windows) + @usableFromInline + mutating func withUnsafeMutablePointer(_ body: (UnsafeMutablePointer) throws -> T) rethrows -> T { + return try Swift.withUnsafeMutablePointer(to: &fds_bits) { + try body(UnsafeMutableRawPointer($0).assumingMemoryBound(to: Int32.self)) + } + } +#endif +} diff --git a/Sources/System/FileEvent.swift b/Sources/System/FileEvent.swift new file mode 100644 index 00000000..5f3abc65 --- /dev/null +++ b/Sources/System/FileEvent.swift @@ -0,0 +1,76 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 - 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +/// File Events bitmask +@frozen +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +public struct FileEvents: OptionSet, Hashable, Codable { + + /// The raw C file events. + @_alwaysEmitIntoClient + public let rawValue: CInterop.FileEvent + + /// Create a strongly-typed file events from a raw C value. + @_alwaysEmitIntoClient + public init(rawValue: CInterop.FileEvent) { self.rawValue = rawValue } + + @_alwaysEmitIntoClient + private init(_ raw: CInt) { self.init(rawValue: numericCast(raw)) } +} + +public extension FileEvents { + + /// There is data to read. + @_alwaysEmitIntoClient + static var read: FileEvents { FileEvents(_POLLIN) } + + /// There is urgent data to read (e.g., out-of-band data on TCP socket; + /// pseudoterminal master in packet mode has seen state change in slave). + @_alwaysEmitIntoClient + static var readUrgent: FileEvents { FileEvents(_POLLPRI) } + + /// Writing now will not block. + @_alwaysEmitIntoClient + static var write: FileEvents { FileEvents(_POLLOUT) } + + /// Error condition. + @_alwaysEmitIntoClient + static var error: FileEvents { FileEvents(_POLLERR) } + + /// Hang up. + @_alwaysEmitIntoClient + static var hangup: FileEvents { FileEvents(_POLLHUP) } + + /// Error condition. + @_alwaysEmitIntoClient + static var invalidRequest: FileEvents { FileEvents(_POLLNVAL) } +} + + +extension FileEvents + : CustomStringConvertible, CustomDebugStringConvertible +{ + /// A textual representation of the file permissions. + @inline(never) + public var description: String { + let descriptions: [(Element, StaticString)] = [ + (.read, ".read"), + (.readUrgent, ".readUrgent"), + (.write, ".write"), + (.error, ".error"), + (.hangup, ".hangup"), + (.invalidRequest, ".invalidRequest") + ] + + return _buildDescription(descriptions) + } + + /// A textual representation of the file permissions, suitable for debugging. + public var debugDescription: String { self.description } +} diff --git a/Sources/System/FileHelpers.swift b/Sources/System/FileHelpers.swift index d083c101..5f80e06a 100644 --- a/Sources/System/FileHelpers.swift +++ b/Sources/System/FileHelpers.swift @@ -9,6 +9,7 @@ // @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) extension FileDescriptor { + /// Runs a closure and then closes the file descriptor, even if an error occurs. /// /// - Parameter body: The closure to run. @@ -32,7 +33,36 @@ extension FileDescriptor { try self.close() return result } - + + /// Runs a closure and then closes the file descriptor if an error occurs. + /// + /// - Parameter body: The closure to run. + /// If the closure throws an error, + /// this method closes the file descriptor before it rethrows that error. + /// + /// - Returns: The value returned by the closure. + /// + /// If `body` throws an error + /// this method rethrows that error. + @_alwaysEmitIntoClient + public func closeIfThrows(_ body: () throws -> R) throws -> R { + do { + return try body() + } catch { + _ = self._close() // Squash close error and throw closure's + throw error + } + } + + @usableFromInline + internal func _closeIfThrows(_ body: () -> Result) -> Result { + return body().mapError { + // close if error is thrown + let _ = _close() + return $0 + } + } + /// Writes a sequence of bytes to the current offset /// and then updates the offset. /// @@ -123,3 +153,15 @@ extension FileDescriptor { } } } + +internal extension Result where Success == FileDescriptor, Failure == Errno { + + @usableFromInline + func _closeIfThrows(_ body: (FileDescriptor) -> Result) -> Result { + return flatMap { fileDescriptor in + fileDescriptor._closeIfThrows { + body(fileDescriptor) + } + } + } +} diff --git a/Sources/System/Internals/CInternetAddress.swift b/Sources/System/Internals/CInternetAddress.swift new file mode 100644 index 00000000..7a54f138 --- /dev/null +++ b/Sources/System/Internals/CInternetAddress.swift @@ -0,0 +1,76 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +@usableFromInline +internal protocol CInternetAddress { + + static var stringLength: Int { get } + + static var family: SocketAddressFamily { get } + + init() +} + +internal extension CInternetAddress { + + @usableFromInline + init?(_ string: String) { + self.init() + /** + inet_pton() returns 1 on success (network address was successfully converted). 0 is returned if src does not contain a character string representing a valid network address in the specified address family. If af does not contain a valid address family, -1 is returned and errno is set to EAFNOSUPPORT. + */ + let result = string.withCString { + system_inet_pton(Self.family.rawValue, $0, &self) + } + guard result == 1 else { + assert(result != -1, "Invalid address family") + return nil + } + } +} + +internal extension String { + + @usableFromInline + init(_ cInternetAddress: T) throws { + let cString = UnsafeMutablePointer.allocate(capacity: T.stringLength) + defer { cString.deallocate() } + let success = withUnsafePointer(to: cInternetAddress) { + system_inet_ntop( + T.family.rawValue, + $0, + cString, + numericCast(T.stringLength) + ) != nil + } + guard success else { + throw Errno.current + } + + self.init(cString: cString) + } +} + +extension CInterop.IPv4Address: CInternetAddress { + + @usableFromInline + static var stringLength: Int { return numericCast(_INET_ADDRSTRLEN) } + + @usableFromInline + static var family: SocketAddressFamily { .ipv4 } +} + +extension CInterop.IPv6Address: CInternetAddress { + + @usableFromInline + static var stringLength: Int { return numericCast(_INET6_ADDRSTRLEN) } + + @usableFromInline + static var family: SocketAddressFamily { .ipv6 } +} diff --git a/Sources/System/Internals/CInterop.swift b/Sources/System/Internals/CInterop.swift index 7f3b96d7..8e6254d4 100644 --- a/Sources/System/Internals/CInterop.swift +++ b/Sources/System/Internals/CInterop.swift @@ -49,7 +49,7 @@ public enum CInterop { /// Windows, this is `UInt16` (a "wide" character). public typealias PlatformChar = CInterop.Char #endif - + #if os(Windows) /// The platform's preferred Unicode encoding. On Unix this is UTF-8 and on /// Windows it is UTF-16. Native strings may contain invalid Unicode, @@ -63,4 +63,55 @@ public enum CInterop { /// on API. public typealias PlatformUnicodeEncoding = UTF8 #endif + + /// The platform file descriptor set. + public typealias FileDescriptorSet = fd_set + + public typealias PollFileDescriptor = pollfd + + public typealias FileDescriptorCount = nfds_t + + public typealias FileEvent = Int16 + + #if os(Windows) + /// The platform socket descriptor. + public typealias SocketDescriptor = SOCKET + #else + /// The platform socket descriptor, which is the same as a file desciptor on Unix systems. + public typealias SocketDescriptor = CInt + #endif + + /// The C `msghdr` type + public typealias MessageHeader = msghdr + + /// The C `sa_family_t` type + public typealias SocketAddressFamily = sa_family_t + + /// Socket Type + #if os(Linux) + public typealias SocketType = __socket_type + #else + public typealias SocketType = CInt + #endif + + /// The C `addrinfo` type + public typealias AddressInfo = addrinfo + + /// The C `in_addr` type + public typealias IPv4Address = in_addr + + /// The C `in6_addr` type + public typealias IPv6Address = in6_addr + + /// The C `sockaddr_in` type + public typealias SocketAddress = sockaddr + + /// The C `sockaddr_in` type + public typealias UnixSocketAddress = sockaddr_un + + /// The C `sockaddr_in` type + public typealias IPv4SocketAddress = sockaddr_in + + /// The C `sockaddr_in6` type + public typealias IPv6SocketAddress = sockaddr_in6 } diff --git a/Sources/System/Internals/CSocketAddress.swift b/Sources/System/Internals/CSocketAddress.swift new file mode 100644 index 00000000..a1c06eb9 --- /dev/null +++ b/Sources/System/Internals/CSocketAddress.swift @@ -0,0 +1,55 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +@usableFromInline +internal protocol CSocketAddress { + + static var family: SocketAddressFamily { get } + + init() +} + +internal extension CSocketAddress { + + @usableFromInline + func withUnsafePointer( + _ body: (UnsafePointer, UInt32) throws -> Result + ) rethrows -> Result { + return try Swift.withUnsafeBytes(of: self) { + return try body($0.baseAddress!.assumingMemoryBound(to: CInterop.SocketAddress.self), UInt32(MemoryLayout.size)) + } + } + + @usableFromInline + mutating func withUnsafeMutablePointer( + _ body: (UnsafeMutablePointer, UInt32) throws -> Result + ) rethrows -> Result { + return try Swift.withUnsafeMutableBytes(of: &self) { + return try body($0.baseAddress!.assumingMemoryBound(to: CInterop.SocketAddress.self), UInt32(MemoryLayout.size)) + } + } +} + +extension CInterop.UnixSocketAddress: CSocketAddress { + + @_alwaysEmitIntoClient + static var family: SocketAddressFamily { .unix } +} + +extension CInterop.IPv4SocketAddress: CSocketAddress { + + @_alwaysEmitIntoClient + static var family: SocketAddressFamily { .ipv4 } +} + +extension CInterop.IPv6SocketAddress: CSocketAddress { + + @_alwaysEmitIntoClient + static var family: SocketAddressFamily { .ipv6 } +} diff --git a/Sources/System/Internals/Compatibility.swift b/Sources/System/Internals/Compatibility.swift new file mode 100644 index 00000000..e219787e --- /dev/null +++ b/Sources/System/Internals/Compatibility.swift @@ -0,0 +1,48 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +internal extension String { + + @usableFromInline + init( + _unsafeUninitializedCapacity capacity: Int, + initializingUTF8With body: (UnsafeMutableBufferPointer) throws -> Int + ) rethrows { +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + if #available(macOS 11, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { + self = try String( + unsafeUninitializedCapacity: capacity, + initializingUTF8With: body) + return + } else { + self = try Self.withUnsafeUninitialized(capacity: capacity, initializing: body) + } +#elseif swift(>=5.5) + self = try String( + unsafeUninitializedCapacity: capacity, + initializingUTF8With: body + ) +#else // older Swift + self = try Self.withUnsafeUninitialized(capacity: capacity, initializing: body) +#endif + } + + static func withUnsafeUninitialized( + capacity: Int, + initializing body: (UnsafeMutableBufferPointer) throws -> Int + ) rethrows -> String { + let array = try Array( + unsafeUninitializedCapacity: capacity + ) { buffer, count in + count = try body(buffer) + } + return String(decoding: array, as: UTF8.self) + } +} + diff --git a/Sources/System/Internals/Constants.swift b/Sources/System/Internals/Constants.swift index a4dbb89c..8ed5872b 100644 --- a/Sources/System/Internals/Constants.swift +++ b/Sources/System/Internals/Constants.swift @@ -529,3 +529,335 @@ internal var _SEEK_HOLE: CInt { SEEK_HOLE } internal var _SEEK_DATA: CInt { SEEK_DATA } #endif +@_alwaysEmitIntoClient +internal var _POLLIN: CInt { POLLIN } + +@_alwaysEmitIntoClient +internal var _POLLPRI: CInt { POLLPRI } + +@_alwaysEmitIntoClient +internal var _POLLOUT: CInt { POLLOUT } + +@_alwaysEmitIntoClient +internal var _POLLRDNORM: CInt { POLLRDNORM } + +@_alwaysEmitIntoClient +internal var _POLLWRNORM: CInt { POLLWRNORM } + +@_alwaysEmitIntoClient +internal var _POLLRDBAND: CInt { POLLRDBAND } + +@_alwaysEmitIntoClient +internal var _POLLWRBAND: CInt { POLLWRBAND } + +@_alwaysEmitIntoClient +internal var _POLLERR: CInt { POLLERR } + +@_alwaysEmitIntoClient +internal var _POLLHUP: CInt { POLLHUP } + +@_alwaysEmitIntoClient +internal var _POLLNVAL: CInt { POLLNVAL } + +@_alwaysEmitIntoClient +internal var _INET_ADDRSTRLEN: CInt { INET_ADDRSTRLEN } + +@_alwaysEmitIntoClient +internal var _INET6_ADDRSTRLEN: CInt { INET6_ADDRSTRLEN } + +@_alwaysEmitIntoClient +internal var _INADDR_ANY: CInterop.IPv4Address { CInterop.IPv4Address(s_addr: INADDR_ANY) } + +@_alwaysEmitIntoClient +internal var _INADDR_LOOPBACK: CInterop.IPv4Address { CInterop.IPv4Address(s_addr: INADDR_LOOPBACK) } + +@_alwaysEmitIntoClient +internal var _INADDR6_ANY: CInterop.IPv6Address { in6addr_any } + +@_alwaysEmitIntoClient +internal var _INADDR6_LOOPBACK: CInterop.IPv6Address { in6addr_loopback } + +@_alwaysEmitIntoClient +internal var _AF_UNIX: CInt { AF_UNIX } + +@_alwaysEmitIntoClient +internal var _AF_INET: CInt { AF_INET } + +@_alwaysEmitIntoClient +internal var _AF_INET6: CInt { AF_INET6 } + +@_alwaysEmitIntoClient +internal var _AF_IPX: CInt { AF_IPX } + +@_alwaysEmitIntoClient +internal var _AF_APPLETALK: CInt { AF_APPLETALK } + +#if !os(Windows) +@_alwaysEmitIntoClient +internal var _AF_DECnet: CInt { AF_DECnet } + +@_alwaysEmitIntoClient +internal var _AF_VSOCK: CInt { AF_VSOCK } + +@_alwaysEmitIntoClient +internal var _AF_ISDN: CInt { AF_ISDN } +#endif + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_alwaysEmitIntoClient +internal var _AF_IMPLINK: CInt { AF_IMPLINK } + +@_alwaysEmitIntoClient +internal var _AF_PUP: CInt { AF_PUP } + +@_alwaysEmitIntoClient +internal var _AF_CHAOS: CInt { AF_CHAOS } + +@_alwaysEmitIntoClient +internal var _AF_NS: CInt { AF_NS } + +@_alwaysEmitIntoClient +internal var _AF_ISO: CInt { AF_ISO } + +@_alwaysEmitIntoClient +internal var _AF_PPP: CInt { AF_PPP } + +@_alwaysEmitIntoClient +internal var _AF_LINK: CInt { AF_LINK } + +@_alwaysEmitIntoClient +internal var _AF_NETBIOS: CInt { AF_NETBIOS } +#endif + +#if os(Linux) +@_alwaysEmitIntoClient +internal var _AF_AX25: CInt { AF_AX25 } + +@_alwaysEmitIntoClient +internal var _AF_X25: CInt { AF_X25 } + +@_alwaysEmitIntoClient +internal var _AF_KEY: CInt { AF_KEY } + +@_alwaysEmitIntoClient +internal var _AF_NETLINK: CInt { AF_NETLINK } + +@_alwaysEmitIntoClient +internal var _AF_PACKET: CInt { AF_PACKET } + +@_alwaysEmitIntoClient +internal var _AF_ATMSVC: CInt { AF_ATMSVC } + +@_alwaysEmitIntoClient +internal var _AF_RDS: CInt { AF_RDS } + +@_alwaysEmitIntoClient +internal var _AF_PPPOX: CInt { AF_PPPOX } + +@_alwaysEmitIntoClient +internal var _AF_WANPIPE: CInt { AF_WANPIPE } + +@_alwaysEmitIntoClient +internal var _AF_LLC: CInt { AF_LLC } + +@_alwaysEmitIntoClient +internal var _AF_IB: CInt { AF_IB } + +@_alwaysEmitIntoClient +internal var _AF_MPLS: CInt { AF_MPLS } + +@_alwaysEmitIntoClient +internal var _AF_CAN: CInt { AF_CAN } + +@_alwaysEmitIntoClient +internal var _AF_TIPC: CInt { AF_TIPC } + +@_alwaysEmitIntoClient +internal var _AF_BLUETOOTH: CInt { AF_BLUETOOTH } + +@_alwaysEmitIntoClient +internal var _AF_IUCV: CInt { AF_IUCV } + +@_alwaysEmitIntoClient +internal var _AF_RXRPC: CInt { AF_RXRPC } + +@_alwaysEmitIntoClient +internal var _AF_PHONET: CInt { AF_PHONET } + +@_alwaysEmitIntoClient +internal var _AF_IEEE802154: CInt { AF_IEEE802154 } + +@_alwaysEmitIntoClient +internal var _AF_CAIF: CInt { AF_CAIF } + +@_alwaysEmitIntoClient +internal var _AF_ALG: CInt { AF_ALG } + +@_alwaysEmitIntoClient +internal var _AF_KCM: CInt { AF_KCM } + +@_alwaysEmitIntoClient +internal var _AF_QIPCRTR: CInt { AF_QIPCRTR } + +@_alwaysEmitIntoClient +internal var _AF_SMC: CInt { AF_SMC } + +@_alwaysEmitIntoClient +internal var _AF_XDP: CInt { AF_XDP } +#endif + +#if os(Windows) +@_alwaysEmitIntoClient +internal var _AF_IRDA: CInt { AF_IRDA } + +@_alwaysEmitIntoClient +internal var _AF_BTH: CInt { AF_BTH } +#endif + +@_alwaysEmitIntoClient +internal var _SOCK_STREAM: CInterop.SocketType { SOCK_STREAM } + +@_alwaysEmitIntoClient +internal var _SOCK_DGRAM: CInterop.SocketType { SOCK_DGRAM } + +@_alwaysEmitIntoClient +internal var _SOCK_RAW: CInterop.SocketType { SOCK_RAW } + +@_alwaysEmitIntoClient +internal var _SOCK_RDM: CInterop.SocketType { SOCK_RDM } + +@_alwaysEmitIntoClient +internal var _SOCK_SEQPACKET: CInterop.SocketType { SOCK_SEQPACKET } + +#if os(Linux) +@_alwaysEmitIntoClient +internal var _SOCK_DCCP: CInterop.SocketType { SOCK_DCCP } + +@_alwaysEmitIntoClient +internal var _SOCK_CLOEXEC: CInterop.SocketType { SOCK_CLOEXEC } + +@_alwaysEmitIntoClient +internal var _SOCK_NONBLOCK: CInterop.SocketType { SOCK_NONBLOCK } +#endif + +@_alwaysEmitIntoClient +internal var _IPPROTO_RAW: CInt { numericCast(IPPROTO_RAW) } + +@_alwaysEmitIntoClient +internal var _IPPROTO_TCP: CInt { numericCast(IPPROTO_TCP) } + +@_alwaysEmitIntoClient +internal var _IPPROTO_UDP: CInt { numericCast(IPPROTO_UDP) } + +@_alwaysEmitIntoClient +internal var _SOL_SOCKET: CInt { SOL_SOCKET } + +@_alwaysEmitIntoClient +internal var _SO_DEBUG: CInt { SO_DEBUG } + +@_alwaysEmitIntoClient +internal var _SO_ACCEPTCONN: CInt { SO_ACCEPTCONN } + +@_alwaysEmitIntoClient +internal var _SO_REUSEADDR: CInt { SO_REUSEADDR } + +@_alwaysEmitIntoClient +internal var _SO_KEEPALIVE: CInt { SO_KEEPALIVE } + +@_alwaysEmitIntoClient +internal var _SO_DONTROUTE: CInt { SO_DONTROUTE } + +@_alwaysEmitIntoClient +internal var _SO_BROADCAST: CInt { SO_BROADCAST } + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_alwaysEmitIntoClient +internal var _SO_USELOOPBACK: CInt { SO_USELOOPBACK } +#endif + +@_alwaysEmitIntoClient +internal var _SO_LINGER: CInt { SO_LINGER } + +#if os(Linux) +@_alwaysEmitIntoClient +internal var _SOL_NETLINK: CInt { SOL_NETLINK } + +@_alwaysEmitIntoClient +internal var _SOL_BLUETOOTH: CInt { SOL_BLUETOOTH } + +@_alwaysEmitIntoClient +internal var _SOL_L2CAP: CInt { 6 } +#endif + +@_alwaysEmitIntoClient +internal var _MSG_DONTROUTE: CInt { numericCast(MSG_DONTROUTE) } /* send without using routing tables */ + +@_alwaysEmitIntoClient +internal var _MSG_EOR: CInt { numericCast(MSG_EOR) } /* data completes record */ + +@_alwaysEmitIntoClient +internal var _MSG_OOB: CInt { numericCast(MSG_OOB) } /* process out-of-band data */ + +@_alwaysEmitIntoClient +internal var _MSG_PEEK: CInt { numericCast(MSG_PEEK) } /* peek at incoming message */ +@_alwaysEmitIntoClient +internal var _MSG_TRUNC: CInt { numericCast(MSG_TRUNC) } /* data discarded before delivery */ +@_alwaysEmitIntoClient +internal var _MSG_CTRUNC: CInt { numericCast(MSG_CTRUNC) } /* control data lost before delivery */ +@_alwaysEmitIntoClient +internal var _MSG_WAITALL: CInt { numericCast(MSG_WAITALL) } /* wait for full request or error */ + +@_alwaysEmitIntoClient +internal var _MSG_DONTWAIT: CInt { numericCast(MSG_DONTWAIT) } /* this message should be nonblocking */ + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_alwaysEmitIntoClient +internal var _MSG_EOF: CInt { numericCast(MSG_EOF) } /* data completes connection */ + +@_alwaysEmitIntoClient +internal var _MSG_WAITSTREAM: CInt { numericCast(MSG_WAITSTREAM) } /* wait up to full request.. may return partial */ + +@_alwaysEmitIntoClient +internal var _MSG_FLUSH: CInt { numericCast(MSG_FLUSH) } /* Start of 'hold' seq; dump so_temp, deprecated */ +@_alwaysEmitIntoClient +internal var _MSG_HOLD: CInt { numericCast(MSG_HOLD) } /* Hold frag in so_temp, deprecated */ +@_alwaysEmitIntoClient +internal var _MSG_SEND: CInt { numericCast(MSG_SEND) } /* Send the packet in so_temp, deprecated */ +@_alwaysEmitIntoClient +internal var _MSG_HAVEMORE: CInt { numericCast(MSG_HAVEMORE) } /* Data ready to be read */ +@_alwaysEmitIntoClient +internal var _MSG_RCVMORE: CInt { numericCast(MSG_RCVMORE) } /* Data remains in current pkt */ + +@_alwaysEmitIntoClient +internal var _MSG_NEEDSA: CInt { numericCast(MSG_NEEDSA) } /* Fail receive if socket address cannot be allocated */ + +@_alwaysEmitIntoClient +internal var _MSG_NOSIGNAL: CInt { numericCast(MSG_NOSIGNAL) } /* do not generate SIGPIPE on EOF */ +#endif + +#if os(Linux) +@_alwaysEmitIntoClient +internal var _MSG_CONFIRM: CInt { numericCast(MSG_CONFIRM) } + +@_alwaysEmitIntoClient +internal var _MSG_MORE: CInt { numericCast(MSG_MORE) } +#endif + +@_alwaysEmitIntoClient +internal var _fd_set_count: Int { +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + // __DARWIN_FD_SETSIZE is number of *bits*, so divide by number bits in each element to get element count + // at present this is 1024 / 32 == 32 + return Int(__DARWIN_FD_SETSIZE) / 32 +#elseif os(Linux) || os(FreeBSD) || os(Android) +#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le) + return 32 +#elseif arch(i386) || arch(arm) + return 16 +#else +#error("This architecture isn't known. Add it to the 32-bit or 64-bit line.") +#endif +#elseif os(Windows) + return 32 +#endif +} diff --git a/Sources/System/Internals/NetworkOrder.swift b/Sources/System/Internals/NetworkOrder.swift new file mode 100644 index 00000000..6ef0fc4d --- /dev/null +++ b/Sources/System/Internals/NetworkOrder.swift @@ -0,0 +1,21 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +extension FixedWidthInteger { + + @usableFromInline + internal var networkOrder: Self { + bigEndian + } + + @usableFromInline + internal init(networkOrder value: Self) { + self.init(bigEndian: value) + } +} diff --git a/Sources/System/Internals/Syscalls.swift b/Sources/System/Internals/Syscalls.swift index 453c02fc..11cb906e 100644 --- a/Sources/System/Internals/Syscalls.swift +++ b/Sources/System/Internals/Syscalls.swift @@ -20,6 +20,13 @@ import ucrt // Interacting with the mocking system, tracing, etc., is a potentially significant // amount of code size, so we hand outline that code for every syscall +internal func system_strcpy(_ destination: UnsafeMutablePointer, _ source: UnsafePointer) -> UnsafeMutablePointer { + #if ENABLE_MOCKING + // FIXME + #endif + return strcpy(destination, source) +} + // open internal func system_open( _ path: UnsafePointer, _ oflag: Int32 @@ -115,6 +122,236 @@ internal func system_dup2(_ fd: Int32, _ fd2: Int32) -> Int32 { #endif return dup2(fd, fd2) } + +internal func system_inet_pton( + _ family: Int32, + _ cString: UnsafePointer, + _ address: UnsafeMutableRawPointer) -> Int32 { + #if ENABLE_MOCKING + if mockingEnabled { return _mock(family, cString, address) } + #endif + return inet_pton(family, cString, address) +} + +internal func system_inet_ntop(_ family: Int32, _ pointer : UnsafeRawPointer, _ string: UnsafeMutablePointer, _ length: UInt32) -> UnsafePointer? { + #if ENABLE_MOCKING + //if mockingEnabled { return _mock(family, pointer, string, length) } + #endif + return inet_ntop(family, pointer, string, length) +} + +internal func system_socket(_ fd: Int32, _ fd2: Int32, _ fd3: Int32) -> Int32 { + #if ENABLE_MOCKING + if mockingEnabled { return _mock(fd, fd2, fd3) } + #endif + return socket(fd, fd2, fd3) +} + +internal func system_setsockopt(_ fd: Int32, _ fd2: Int32, _ fd3: Int32, _ pointer: UnsafeRawPointer, _ dataLength: UInt32) -> Int32 { + #if ENABLE_MOCKING + if mockingEnabled { return _mock(fd, fd2, fd3, pointer, dataLength) } + #endif + return setsockopt(fd, fd2, fd3, pointer, dataLength) +} + +internal func system_getsockopt( + _ socket: CInt, + _ level: CInt, + _ option: CInt, + _ value: UnsafeMutableRawPointer?, + _ length: UnsafeMutablePointer? +) -> CInt { + #if ENABLE_MOCKING + if mockingEnabled { return _mock(socket, level, option, value, length) } + #endif + return getsockopt(socket, level, option, value, length) +} + +internal func system_bind( + _ socket: CInt, + _ address: UnsafePointer, + _ length: UInt32 +) -> CInt { + #if ENABLE_MOCKING + if mockingEnabled { return _mock(socket, address, length) } + #endif + return bind(socket, address, length) +} + +internal func system_connect( + _ socket: CInt, + _ addr: UnsafePointer?, + _ len: socklen_t +) -> CInt { + #if ENABLE_MOCKING + if mockingEnabled { return _mock(socket, addr, len) } + #endif + return connect(socket, addr, len) +} + +internal func system_accept( + _ socket: CInt, + _ addr: UnsafeMutablePointer?, + _ len: UnsafeMutablePointer? +) -> CInt { + #if ENABLE_MOCKING + if mockingEnabled { return _mock(socket, addr, len) } + #endif + return accept(socket, addr, len) +} + +internal func system_getaddrinfo( + _ hostname: UnsafePointer?, + _ servname: UnsafePointer?, + _ hints: UnsafePointer?, + _ res: UnsafeMutablePointer?>? +) -> CInt { + #if ENABLE_MOCKING + if mockingEnabled { + return _mock(hostname, + servname, + hints, res) + } + #endif + return getaddrinfo(hostname, servname, hints, res) +} + +internal func system_getnameinfo( + _ sa: UnsafePointer?, + _ salen: UInt32, + _ host: UnsafeMutablePointer?, + _ hostlen: UInt32, + _ serv: UnsafeMutablePointer?, + _ servlen: UInt32, + _ flags: CInt +) -> CInt { + #if ENABLE_MOCKING + if mockingEnabled { + return _mock(sa, salen, host, hostlen, serv, servlen, flags) + } + #endif + return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) +} + +internal func system_freeaddrinfo( + _ addrinfo: UnsafeMutablePointer? +) { + #if ENABLE_MOCKING + if mockingEnabled { + _ = _mock(addrinfo) + return + } + #endif + return freeaddrinfo(addrinfo) +} + +internal func system_gai_strerror(_ error: CInt) -> UnsafePointer { + #if ENABLE_MOCKING + // FIXME + #endif + return gai_strerror(error) +} + +internal func system_shutdown(_ socket: CInt, _ how: CInt) -> CInt { + #if ENABLE_MOCKING + if mockingEnabled { return _mock(socket, how) } + #endif + return shutdown(socket, how) +} + +internal func system_listen(_ socket: CInt, _ backlog: CInt) -> CInt { + #if ENABLE_MOCKING + if mockingEnabled { return _mock(socket, backlog) } + #endif + return listen(socket, backlog) +} + +internal func system_send( + _ socket: Int32, _ buffer: UnsafeRawPointer?, _ len: Int, _ flags: Int32 +) -> Int { + #if ENABLE_MOCKING + if mockingEnabled { return _mockInt(socket, buffer, len, flags) } + #endif + return send(socket, buffer, len, flags) +} + +internal func system_recv( + _ socket: Int32, + _ buffer: UnsafeMutableRawPointer?, + _ len: Int, + _ flags: Int32 +) -> Int { + #if ENABLE_MOCKING + if mockingEnabled { return _mockInt(socket, buffer, len, flags) } + #endif + return recv(socket, buffer, len, flags) +} + +internal func system_sendto( + _ socket: CInt, + _ buffer: UnsafeRawPointer?, + _ length: Int, + _ flags: CInt, + _ dest_addr: UnsafePointer?, + _ dest_len: UInt32 +) -> Int { + #if ENABLE_MOCKING + if mockingEnabled { + return _mockInt(socket, buffer, length, flags, dest_addr, dest_len) + } + #endif + return sendto(socket, buffer, length, flags, dest_addr, dest_len) +} + +internal func system_recvfrom( + _ socket: CInt, + _ buffer: UnsafeMutableRawPointer?, + _ length: Int, + _ flags: CInt, + _ address: UnsafeMutablePointer?, + _ addres_len: UnsafeMutablePointer? +) -> Int { + #if ENABLE_MOCKING + if mockingEnabled { + return _mockInt(socket, buffer, length, flags, address, addres_len) + } + #endif + return recvfrom(socket, buffer, length, flags, address, addres_len) +} + +internal func system_poll( + _ fileDescriptors: UnsafeMutablePointer, + _ fileDescriptorsCount: CInterop.FileDescriptorCount, + _ timeout: CInt +) -> CInt { + #if ENABLE_MOCKING + if mockingEnabled { return _mock(fileDescriptors, fileDescriptorsCount, timeout) } + #endif + return poll(fileDescriptors, fileDescriptorsCount, timeout) +} + +internal func system_sendmsg( + _ socket: CInt, + _ message: UnsafePointer?, + _ flags: CInt +) -> Int { + #if ENABLE_MOCKING + if mockingEnabled { return _mockInt(socket, message, flags) } + #endif + return sendmsg(socket, message, flags) +} + +internal func system_recvmsg( + _ socket: CInt, + _ message: UnsafeMutablePointer?, + _ flags: CInt +) -> Int { + #if ENABLE_MOCKING + if mockingEnabled { return _mockInt(socket, message, flags) } + #endif + return recvmsg(socket, message, flags) +} + #if !os(Windows) internal func system_pipe(_ fds: UnsafeMutablePointer) -> CInt { #if ENABLE_MOCKING diff --git a/Sources/System/InternetProtocol.swift b/Sources/System/InternetProtocol.swift new file mode 100644 index 00000000..a31960f4 --- /dev/null +++ b/Sources/System/InternetProtocol.swift @@ -0,0 +1,208 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +/// Internet Protocol Address +@frozen +public enum IPAddress: Equatable, Hashable, Codable { + + /// IPv4 + case v4(IPv4Address) + + /// IPv6 + case v6(IPv6Address) +} + +public extension IPAddress { + + @_alwaysEmitIntoClient + func withUnsafeBytes(_ body: ((UnsafeRawBufferPointer) -> (Result))) -> Result { + switch self { + case let .v4(address): + return address.withUnsafeBytes(body) + case let .v6(address): + return address.withUnsafeBytes(body) + } + } +} + +extension IPAddress: RawRepresentable { + + public init?(rawValue: String) { + + if let address = IPv4Address(rawValue: rawValue) { + self = .v4(address) + } else if let address = IPv6Address(rawValue: rawValue) { + self = .v6(address) + } else { + return nil + } + } + + public var rawValue: String { + switch self { + case let .v4(address): return address.rawValue + case let .v6(address): return address.rawValue + } + } +} + +extension IPAddress: CustomStringConvertible, CustomDebugStringConvertible { + + public var description: String { + return rawValue + } + + public var debugDescription: String { + return description + } +} + +/// IPv4 Socket Address +@frozen +public struct IPv4Address: Equatable, Hashable, Codable { + + @usableFromInline + internal let bytes: CInterop.IPv4Address + + @_alwaysEmitIntoClient + public init(_ bytes: CInterop.IPv4Address) { + self.bytes = bytes + } + + @_alwaysEmitIntoClient + public func withUnsafeBytes(_ body: ((UnsafeRawBufferPointer) -> (Result))) -> Result { + Swift.withUnsafeBytes(of: bytes, body) + } +} + +public extension IPv4Address { + + /// Initialize with raw bytes. + @_alwaysEmitIntoClient + init(_ byte0: UInt8, _ byte1: UInt8, _ byte2: UInt8, _ byte3: UInt8) { + self.init(unsafeBitCast((byte0, byte1, byte2, byte3), to: CInterop.IPv4Address.self)) + } +} + +public extension IPAddress { + + /// Initialize with a IP v4 address. + @_alwaysEmitIntoClient + init(_ byte0: UInt8, _ byte1: UInt8, _ byte2: UInt8, _ byte3: UInt8) { + self = .v4(IPv4Address(byte0, byte1, byte2, byte3)) + } +} + +public extension IPv4Address { + + @_alwaysEmitIntoClient + static var any: IPv4Address { IPv4Address(_INADDR_ANY) } + + @_alwaysEmitIntoClient + static var loopback: IPv4Address { IPv4Address(_INADDR_LOOPBACK) } +} + +extension IPv4Address: RawRepresentable { + + @_alwaysEmitIntoClient + public init?(rawValue: String) { + guard let bytes = CInterop.IPv4Address(rawValue) else { + return nil + } + self.init(bytes) + } + + @_alwaysEmitIntoClient + public var rawValue: String { + return try! String(bytes) + } +} + +extension IPv4Address: CustomStringConvertible, CustomDebugStringConvertible { + + public var description: String { + return rawValue + } + + public var debugDescription: String { + return description + } +} + +/// IPv6 Socket Address +@frozen +public struct IPv6Address: Equatable, Hashable, Codable { + + @usableFromInline + internal let bytes: CInterop.IPv6Address + + @_alwaysEmitIntoClient + public init(_ bytes: CInterop.IPv6Address) { + self.bytes = bytes + } + + @_alwaysEmitIntoClient + public func withUnsafeBytes(_ body: ((UnsafeRawBufferPointer) -> (Result))) -> Result { + Swift.withUnsafeBytes(of: bytes, body) + } +} + +public extension IPv6Address { + + /// Initialize with bytes + @_alwaysEmitIntoClient + init(_ byte0: UInt16, _ byte1: UInt16, _ byte2: UInt16, _ byte3: UInt16, _ byte4: UInt16, _ byte5: UInt16, _ byte6: UInt16, _ byte7: UInt16) { + self.init(unsafeBitCast((byte0.bigEndian, byte1.bigEndian, byte2.bigEndian, byte3.bigEndian, byte4.bigEndian, byte5.bigEndian, byte6.bigEndian, byte7.bigEndian), to: CInterop.IPv6Address.self)) + } +} + +public extension IPAddress { + + /// Initialize with a IP v6 address. + @_alwaysEmitIntoClient + init(_ byte0: UInt16, _ byte1: UInt16, _ byte2: UInt16, _ byte3: UInt16, _ byte4: UInt16, _ byte5: UInt16, _ byte6: UInt16, _ byte7: UInt16) { + self = .v6(IPv6Address(byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7)) + } +} + +public extension IPv6Address { + + @_alwaysEmitIntoClient + static var any: IPv6Address { IPv6Address(_INADDR6_ANY) } + + @_alwaysEmitIntoClient + static var loopback: IPv6Address { IPv6Address(_INADDR6_LOOPBACK) } +} + +extension IPv6Address: RawRepresentable { + + @_alwaysEmitIntoClient + public init?(rawValue: String) { + guard let bytes = CInterop.IPv6Address(rawValue) else { + return nil + } + self.init(bytes) + } + + @_alwaysEmitIntoClient + public var rawValue: String { + return try! String(bytes) + } +} + +extension IPv6Address: CustomStringConvertible, CustomDebugStringConvertible { + + public var description: String { + return rawValue + } + + public var debugDescription: String { + return description + } +} diff --git a/Sources/System/Socket/MessageFlags.swift b/Sources/System/Socket/MessageFlags.swift new file mode 100644 index 00000000..b85ede7f --- /dev/null +++ b/Sources/System/Socket/MessageFlags.swift @@ -0,0 +1,59 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +/// Message Flags +@frozen +public struct MessageFlags: OptionSet, Hashable, Codable { + + /// The raw C file permissions. + @_alwaysEmitIntoClient + public let rawValue: CInt + + /// Create a strongly-typed file permission from a raw C value. + @_alwaysEmitIntoClient + public init(rawValue: CInt) { self.rawValue = rawValue } + + @_alwaysEmitIntoClient + private init(_ raw: CInt) { self.init(rawValue: raw) } +} + +public extension MessageFlags { + + @_alwaysEmitIntoClient + static var outOfBand: MessageFlags { MessageFlags(_MSG_OOB) } + + @_alwaysEmitIntoClient + static var peek: MessageFlags { MessageFlags(_MSG_PEEK) } + + @_alwaysEmitIntoClient + static var noRoute: MessageFlags { MessageFlags(_MSG_DONTROUTE) } + + @_alwaysEmitIntoClient + static var endOfReadline: MessageFlags { MessageFlags(_MSG_EOR) } +} + +extension MessageFlags + : CustomStringConvertible, CustomDebugStringConvertible +{ + /// A textual representation of the file permissions. + @inline(never) + public var description: String { + let descriptions: [(Element, StaticString)] = [ + (.outOfBand, ".outOfBand"), + (.peek, ".peek"), + (.noRoute, ".noRoute"), + (.endOfReadline, ".endOfReadline") + ] + + return _buildDescription(descriptions) + } + + /// A textual representation of the file permissions, suitable for debugging. + public var debugDescription: String { self.description } +} diff --git a/Sources/System/Socket/SocketAddress.swift b/Sources/System/Socket/SocketAddress.swift new file mode 100644 index 00000000..5ae0e828 --- /dev/null +++ b/Sources/System/Socket/SocketAddress.swift @@ -0,0 +1,151 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +/// Socket Address +public protocol SocketAddress { + + /// Socket Protocol + associatedtype ProtocolID: SocketProtocol + + /// Unsafe pointer closure + func withUnsafePointer( + _ body: (UnsafePointer, UInt32) throws -> Result + ) rethrows -> Result + + static func withUnsafePointer( + _ body: (UnsafeMutablePointer, UInt32) throws -> () + ) rethrows -> Self +} + +public extension SocketAddress { + + @_alwaysEmitIntoClient + static var family: SocketAddressFamily { + return ProtocolID.family + } +} + +/// IPv4 Socket Address +public struct UnixSocketAddress: SocketAddress, Equatable, Hashable { + + public typealias ProtocolID = UnixProtocol + + public var path: FilePath + + @_alwaysEmitIntoClient + public init(path: FilePath) { + self.path = path + } + + public func withUnsafePointer( + _ body: (UnsafePointer, UInt32) throws -> Result + ) rethrows -> Result { + return try path.withPlatformString { platformString in + var socketAddress = CInterop.UnixSocketAddress() + socketAddress.sun_family = numericCast(Self.family.rawValue) + withUnsafeMutableBytes(of: &socketAddress.sun_path) { pathBytes in + pathBytes + .bindMemory(to: CInterop.PlatformChar.self) + .baseAddress! + .assign(from: platformString, count: path.length) + } + return try socketAddress.withUnsafePointer(body) + } + } + + public static func withUnsafePointer( + _ body: (UnsafeMutablePointer, UInt32) throws -> () + ) rethrows -> Self { + var socketAddress = CInterop.UnixSocketAddress() + try socketAddress.withUnsafeMutablePointer(body) + return withUnsafeBytes(of: socketAddress.sun_path) { pathPointer in + Self.init(path: FilePath(platformString: pathPointer.baseAddress!.assumingMemoryBound(to: CInterop.PlatformChar.self))) + } + } +} + +/// IPv4 Socket Address +public struct IPv4SocketAddress: SocketAddress, Equatable, Hashable { + + public typealias ProtocolID = IPv4Protocol + + public var address: IPv4Address + + public var port: UInt16 + + @_alwaysEmitIntoClient + public init(address: IPv4Address, + port: UInt16) { + + self.address = address + self.port = port + } + + public func withUnsafePointer( + _ body: (UnsafePointer, UInt32) throws -> Result + ) rethrows -> Result { + + var socketAddress = CInterop.IPv4SocketAddress() + socketAddress.sin_family = numericCast(Self.family.rawValue) + socketAddress.sin_port = port.networkOrder + socketAddress.sin_addr = address.bytes + return try socketAddress.withUnsafePointer(body) + } + + public static func withUnsafePointer( + _ body: (UnsafeMutablePointer, UInt32) throws -> () + ) rethrows -> Self { + var socketAddress = CInterop.IPv4SocketAddress() + try socketAddress.withUnsafeMutablePointer(body) + return Self.init( + address: IPv4Address(socketAddress.sin_addr), + port: socketAddress.sin_port.networkOrder + ) + } +} + +/// IPv6 Socket Address +public struct IPv6SocketAddress: SocketAddress, Equatable, Hashable { + + public typealias ProtocolID = IPv6Protocol + + public var address: IPv6Address + + public var port: UInt16 + + @_alwaysEmitIntoClient + public init(address: IPv6Address, + port: UInt16) { + + self.address = address + self.port = port + } + + public func withUnsafePointer( + _ body: (UnsafePointer, UInt32) throws -> Result + ) rethrows -> Result { + + var socketAddress = CInterop.IPv6SocketAddress() + socketAddress.sin6_family = numericCast(Self.family.rawValue) + socketAddress.sin6_port = port.networkOrder + socketAddress.sin6_addr = address.bytes + return try socketAddress.withUnsafePointer(body) + } + + public static func withUnsafePointer( + _ body: (UnsafeMutablePointer, UInt32) throws -> () + ) rethrows -> Self { + var socketAddress = CInterop.IPv6SocketAddress() + try socketAddress.withUnsafeMutablePointer(body) + return Self.init( + address: IPv6Address(socketAddress.sin6_addr), + port: socketAddress.sin6_port.networkOrder + ) + } +} diff --git a/Sources/System/Socket/SocketAddressFamily.swift b/Sources/System/Socket/SocketAddressFamily.swift new file mode 100644 index 00000000..6d840b32 --- /dev/null +++ b/Sources/System/Socket/SocketAddressFamily.swift @@ -0,0 +1,224 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +/// POSIX Socket Address Family +@frozen +public struct SocketAddressFamily: RawRepresentable, Hashable, Codable { + + /// The raw socket address family identifier. + @_alwaysEmitIntoClient + public let rawValue: CInt + + /// Creates a strongly-typed socket address family from a raw address family identifier. + @_alwaysEmitIntoClient + public init(rawValue: CInt) { self.rawValue = rawValue } + + @_alwaysEmitIntoClient + private init(_ raw: CInt) { self.init(rawValue: raw) } +} + +public extension SocketAddressFamily { + + /// Local communication + @_alwaysEmitIntoClient + static var unix: SocketAddressFamily { SocketAddressFamily(_AF_UNIX) } + + /// IPv4 Internet protocol + @_alwaysEmitIntoClient + static var ipv4: SocketAddressFamily { SocketAddressFamily(_AF_INET) } + + /// IPv6 Internet protocol + @_alwaysEmitIntoClient + static var ipv6: SocketAddressFamily { SocketAddressFamily(_AF_INET6) } + + /// IPX - Novell protocol + @_alwaysEmitIntoClient + static var ipx: SocketAddressFamily { SocketAddressFamily(_AF_IPX) } + + /// AppleTalk protocol + @_alwaysEmitIntoClient + static var appleTalk: SocketAddressFamily { SocketAddressFamily(_AF_APPLETALK) } +} + +#if !os(Windows) +public extension SocketAddressFamily { + + /// DECet protocol sockets + @_alwaysEmitIntoClient + static var decnet: SocketAddressFamily { SocketAddressFamily(_AF_DECnet) } + + /// VSOCK (originally "VMWare VSockets") protocol for hypervisor-guest communication + @_alwaysEmitIntoClient + static var vsock: SocketAddressFamily { SocketAddressFamily(_AF_VSOCK) } + + /// Integrated Services Digital Network protocol + @_alwaysEmitIntoClient + static var isdn: SocketAddressFamily { SocketAddressFamily(_AF_ISDN) } +} +#endif + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +public extension SocketAddressFamily { + + /// NetBIOS protocol + @_alwaysEmitIntoClient + static var netbios: SocketAddressFamily { SocketAddressFamily(_AF_NETBIOS) } + + /// + @_alwaysEmitIntoClient + static var implink: SocketAddressFamily { SocketAddressFamily(_AF_IMPLINK) } + + /// + @_alwaysEmitIntoClient + static var pup: SocketAddressFamily { SocketAddressFamily(_AF_PUP) } + + /// + @_alwaysEmitIntoClient + static var chaos: SocketAddressFamily { SocketAddressFamily(_AF_CHAOS) } + + /// + @_alwaysEmitIntoClient + static var ns: SocketAddressFamily { SocketAddressFamily(_AF_NS) } + + /// + @_alwaysEmitIntoClient + static var iso: SocketAddressFamily { SocketAddressFamily(_AF_ISO) } + + /// Generic PPP transport layer, for setting up L2 tunnels (L2TP and PPPoE). + @_alwaysEmitIntoClient + static var ppp: SocketAddressFamily { SocketAddressFamily(_AF_PPP) } + + /// + @_alwaysEmitIntoClient + static var link: SocketAddressFamily { SocketAddressFamily(_AF_LINK) } +} +#endif + +#if os(Linux) +public extension SocketAddressFamily { + + /// Amateur radio AX.25 protocol + @_alwaysEmitIntoClient + static var ax25: SocketAddressFamily { SocketAddressFamily(_AF_AX25) } + + /// ITU-T X.25 / ISO-8208 protocol + @_alwaysEmitIntoClient + static var x25: SocketAddressFamily { SocketAddressFamily(_AF_X25) } + + /// Key management protocol + @_alwaysEmitIntoClient + static var key: SocketAddressFamily { SocketAddressFamily(_AF_KEY) } + + /// Kernel user interface device + @_alwaysEmitIntoClient + static var netlink: SocketAddressFamily { SocketAddressFamily(_AF_NETLINK) } + + /// Low-level packet interface + @_alwaysEmitIntoClient + static var packet: SocketAddressFamily { SocketAddressFamily(_AF_PACKET) } + + /// Access to ATM Switched Virtual Circuits + @_alwaysEmitIntoClient + static var atm: SocketAddressFamily { SocketAddressFamily(_AF_ATMSVC) } + + /// Reliable Datagram Sockets (RDS) protocol + @_alwaysEmitIntoClient + static var rds: SocketAddressFamily { SocketAddressFamily(_AF_RDS) } + + /// Generic PPP transport layer, for setting up L2 tunnels (L2TP and PPPoE). + @_alwaysEmitIntoClient + static var ppp: SocketAddressFamily { SocketAddressFamily(_AF_PPPOX) } + + /// Legacy protocol for wide area network (WAN) connectivity that was used by Sangoma WAN cards. + @_alwaysEmitIntoClient + static var wanpipe: SocketAddressFamily { SocketAddressFamily(_AF_WANPIPE) } + + /// Logical link control (IEEE 802.2 LLC) protocol, upper part of data link layer of ISO/OSI networking protocol stack. + @_alwaysEmitIntoClient + static var link: SocketAddressFamily { SocketAddressFamily(_AF_LLC) } + + /// InfiniBand native addressing. + @_alwaysEmitIntoClient + static var ib: SocketAddressFamily { SocketAddressFamily(_AF_IB) } + + /// Multiprotocol Label Switching + @_alwaysEmitIntoClient + static var mpls: SocketAddressFamily { SocketAddressFamily(_AF_MPLS) } + + /// Controller Area Network automotive bus protocol + @_alwaysEmitIntoClient + static var can: SocketAddressFamily { SocketAddressFamily(_AF_CAN) } + + /// TIPC, "cluster domain sockets" protocol + @_alwaysEmitIntoClient + static var tipc: SocketAddressFamily { SocketAddressFamily(_AF_TIPC) } + + /// Bluetooth protocol + @_alwaysEmitIntoClient + static var bluetooth: SocketAddressFamily { SocketAddressFamily(_AF_BLUETOOTH) } + + /// IUCV (inter-user communication vehicle) z/VM protocol for hypervisor-guest interaction + @_alwaysEmitIntoClient + static var iucv: SocketAddressFamily { SocketAddressFamily(_AF_IUCV) } + + /// Rx, Andrew File System remote procedure call protocol + @_alwaysEmitIntoClient + static var rxrpc: SocketAddressFamily { SocketAddressFamily(_AF_RXRPC) } + + /// Nokia cellular modem IPC/RPC interface + @_alwaysEmitIntoClient + static var phonet: SocketAddressFamily { SocketAddressFamily(_AF_PHONET) } + + /// IEEE 802.15.4 WPAN (wireless personal area network) raw packet protocol + @_alwaysEmitIntoClient + static var ieee802154: SocketAddressFamily { SocketAddressFamily(_AF_IEEE802154) } + + /// Ericsson's Communication CPU to Application CPU interface (CAIF) protocol + @_alwaysEmitIntoClient + static var caif: SocketAddressFamily { SocketAddressFamily(_AF_CAIF) } + + /// Interface to kernel crypto API + @_alwaysEmitIntoClient + static var crypto: SocketAddressFamily { SocketAddressFamily(_AF_ALG) } + + /// KCM (kernel connection multiplexer) interface + @_alwaysEmitIntoClient + static var kcm: SocketAddressFamily { SocketAddressFamily(_AF_KCM) } + + /// Qualcomm IPC router interface protocol + @_alwaysEmitIntoClient + static var qipcrtr: SocketAddressFamily { SocketAddressFamily(_AF_QIPCRTR) } + + /// SMC-R (shared memory communications over RDMA) protocol + /// and SMC-D (shared memory communications, direct memory access) protocol for intra-node z/VM quest interaction. + @_alwaysEmitIntoClient + static var smc: SocketAddressFamily { SocketAddressFamily(_AF_SMC) } + + /// XDP (express data path) interface. + @_alwaysEmitIntoClient + static var xdp: SocketAddressFamily { SocketAddressFamily(_AF_XDP) } +} +#endif + +#if os(Windows) +public extension SocketAddressFamily { + + /// NetBIOS protocol + @_alwaysEmitIntoClient + static var netbios: SocketAddressFamily { SocketAddressFamily(_AF_NETBIOS) } + + /// IrDA protocol + @_alwaysEmitIntoClient + static var irda: SocketAddressFamily { SocketAddressFamily(_AF_IRDA) } + + /// Bluetooth protocol + @_alwaysEmitIntoClient + static var bluetooth: SocketAddressFamily { SocketAddressFamily(_AF_BTH) } +} +#endif diff --git a/Sources/System/Socket/SocketFlags.swift b/Sources/System/Socket/SocketFlags.swift new file mode 100644 index 00000000..e2061434 --- /dev/null +++ b/Sources/System/Socket/SocketFlags.swift @@ -0,0 +1,56 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 - 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +#if os(Linux) +/// Flags when opening sockets. +@frozen +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +public struct SocketFlags: OptionSet, Hashable, Codable { + + /// The raw C file events. + @_alwaysEmitIntoClient + public let rawValue: CInt + + /// Create a strongly-typed file events from a raw C value. + @_alwaysEmitIntoClient + public init(rawValue: CInt) { self.rawValue = rawValue } + + @_alwaysEmitIntoClient + private init(_ cValue: CInterop.SocketType) { self.init(rawValue: numericCast(cValue.rawValue)) } +} + +public extension SocketFlags { + + /// Set the `O_NONBLOCK` file status flag on the open file description referred to by the new file + /// descriptor. Using this flag saves extra calls to `fcntl()` to achieve the same result. + @_alwaysEmitIntoClient + static var nonBlocking: SocketFlags { SocketFlags(_SOCK_NONBLOCK) } + + /// Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. + @_alwaysEmitIntoClient + static var closeOnExec: SocketFlags { SocketFlags(_SOCK_CLOEXEC) } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension SocketFlags: CustomStringConvertible, CustomDebugStringConvertible +{ + /// A textual representation of the open options. + @inline(never) + public var description: String { + let descriptions: [(Element, StaticString)] = [ + (.nonBlocking, ".nonBlocking"), + (.closeOnExec, ".closeOnExec") + ] + return _buildDescription(descriptions) + } + + /// A textual representation of the open options, suitable for debugging. + public var debugDescription: String { self.description } +} +#endif diff --git a/Sources/System/Socket/SocketOperations.swift b/Sources/System/Socket/SocketOperations.swift new file mode 100644 index 00000000..32caf886 --- /dev/null +++ b/Sources/System/Socket/SocketOperations.swift @@ -0,0 +1,636 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +extension FileDescriptor { + + /// Creates an endpoint for communication and returns a descriptor. + /// + /// - Parameters: + /// - protocolID: The protocol which will be used for communication. + /// - retryOnInterrupt: Whether to retry the read operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The file descriptor of the opened socket. + /// + @_alwaysEmitIntoClient + public static func socket( + _ protocolID: T, + retryOnInterrupt: Bool = true + ) throws -> FileDescriptor { + try _socket(T.family, type: protocolID.type.rawValue, protocol: protocolID.rawValue, retryOnInterrupt: retryOnInterrupt).get() + } + + #if os(Linux) + /// Creates an endpoint for communication and returns a descriptor. + /// + /// - Parameters: + /// - protocolID: The protocol which will be used for communication. + /// - flags: Flags to set when opening the socket. + /// - retryOnInterrupt: Whether to retry the read operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The file descriptor of the opened socket. + /// + @_alwaysEmitIntoClient + public static func socket( + _ protocolID: T, + flags: SocketFlags, + retryOnInterrupt: Bool = true + ) throws -> FileDescriptor { + try _socket(T.family, type: protocolID.type.rawValue | flags.rawValue, protocol: protocolID.rawValue, retryOnInterrupt: retryOnInterrupt).get() + } + #endif + + @usableFromInline + internal static func _socket( + _ family: SocketAddressFamily, + type: CInt, + protocol protocolID: Int32, + retryOnInterrupt: Bool + ) -> Result { + valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_socket(family.rawValue, type, protocolID) + }.map(FileDescriptor.init(socket:)) + } + + /// Creates an endpoint for communication and returns a descriptor. + /// + /// - Parameters: + /// - protocolID: The protocol which will be used for communication. + /// - flags: Flags to set when opening the socket. + /// - retryOnInterrupt: Whether to retry the read operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The file descriptor of the opened socket. + /// + @_alwaysEmitIntoClient + public static func socket( + _ protocolID: Address.ProtocolID, + bind address: Address, + retryOnInterrupt: Bool = true + ) throws -> FileDescriptor { + return try _socket( + address: address, + type: protocolID.type.rawValue, + protocol: protocolID.rawValue, + retryOnInterrupt: retryOnInterrupt + ).get() + } + + #if os(Linux) + /// Creates an endpoint for communication and returns a descriptor. + /// + /// - Parameters: + /// - protocolID: The protocol which will be used for communication. + /// - flags: Flags to set when opening the socket. + /// - retryOnInterrupt: Whether to retry the read operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The file descriptor of the opened socket. + /// + @_alwaysEmitIntoClient + public static func socket( + _ protocolID: Address.ProtocolID, + bind address: Address, + flags: SocketFlags, + retryOnInterrupt: Bool = true + ) throws -> FileDescriptor { + return try _socket( + address: address, + type: protocolID.type.rawValue | flags.rawValue, + protocol: protocolID.rawValue, + retryOnInterrupt: retryOnInterrupt + ).get() + } + #endif + + @usableFromInline + internal static func _socket( + address: Address, + type: CInt, + protocol protocolID: Int32, + retryOnInterrupt: Bool + ) -> Result { + return _socket( + Address.family, + type: type, + protocol: protocolID, + retryOnInterrupt: retryOnInterrupt + )._closeIfThrows { fileDescriptor in + fileDescriptor + ._bind(address, retryOnInterrupt: retryOnInterrupt) + .map { fileDescriptor } + } + } + + /// Assigns the address specified to the socket referred to by the file descriptor. + /// + /// - Parameter address: Specifies the address to bind the socket. + /// - Parameter retryOnInterrupt: Whether to retry the open operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// + /// The corresponding C function is `bind`. + @_alwaysEmitIntoClient + public func bind( + _ address: Address, + retryOnInterrupt: Bool = true + ) throws { + try _bind(address, retryOnInterrupt: retryOnInterrupt).get() + } + + @usableFromInline + internal func _bind( + _ address: T, + retryOnInterrupt: Bool + ) -> Result<(), Errno> { + nothingOrErrno(retryOnInterrupt: retryOnInterrupt) { + address.withUnsafePointer { (addressPointer, length) in + system_bind(T.family.rawValue, addressPointer, length) + } + } + } + + /// Set the option specified for the socket associated with the file descriptor. + /// + /// - Parameter option: Socket option value to set. + /// - Parameter retryOnInterrupt: Whether to retry the open operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// + /// The method corresponds to the C function `setsockopt`. + @_alwaysEmitIntoClient + public func setSocketOption( + _ option: T, + retryOnInterrupt: Bool = true + ) throws { + try _setSocketOption(option, retryOnInterrupt: retryOnInterrupt).get() + } + + @usableFromInline + internal func _setSocketOption( + _ option: T, + retryOnInterrupt: Bool + ) -> Result<(), Errno> { + nothingOrErrno(retryOnInterrupt: retryOnInterrupt) { + option.withUnsafeBytes { bufferPointer in + system_setsockopt(self.rawValue, T.ID.optionLevel.rawValue, T.id.rawValue, bufferPointer.baseAddress!, UInt32(bufferPointer.count)) + } + } + } + + /// Retrieve the value associated with the option specified for the socket associated with the file descriptor. + /// + /// - Parameter option: Type of `SocketOption` to retrieve. + /// - Parameter retryOnInterrupt: Whether to retry the open operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// + /// The method corresponds to the C function `getsockopt`. + @_alwaysEmitIntoClient + public func getSocketOption( + _ option: T.Type, + retryOnInterrupt: Bool = true + ) throws -> T { + return try _getSocketOption(option, retryOnInterrupt: retryOnInterrupt) + } + + @usableFromInline + internal func _getSocketOption( + _ option: T.Type, + retryOnInterrupt: Bool + ) throws -> T { + return try T.withUnsafeBytes { bufferPointer in + var length = UInt32(bufferPointer.count) + guard system_getsockopt(self.rawValue, T.ID.optionLevel.rawValue, T.id.rawValue, bufferPointer.baseAddress!, &length) != -1 else { + throw Errno.current + } + } + } + + /// Send a message from a socket. + /// + /// - Parameters: + /// - buffer: The region of memory that contains the data being sent. + /// - flags: see `send(2)` + /// - retryOnInterrupt: Whether to retry the send operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The number of bytes that were sent. + /// + /// The corresponding C function is `send`. + @_alwaysEmitIntoClient + public func send( + _ buffer: UnsafeRawBufferPointer, + flags: MessageFlags = [], + retryOnInterrupt: Bool = true + ) throws -> Int { + try _send(buffer, flags: flags, retryOnInterrupt: retryOnInterrupt).get() + } + + /// Send a message from a socket. + /// + /// - Parameters: + /// - data: The sequence of bytes being sent. + /// - address: Address of destination client. + /// - flags: see `send(2)` + /// - retryOnInterrupt: Whether to retry the send operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The number of bytes that were sent. + /// + /// The corresponding C function is `send`. + public func send( + _ data: Data, + flags: MessageFlags = [], + retryOnInterrupt: Bool = true + ) throws -> Int where Data: Sequence, Data.Element == UInt8 { + try data._withRawBufferPointer { dataPointer in + _send(dataPointer, flags: flags, retryOnInterrupt: retryOnInterrupt) + }.get() + } + + @usableFromInline + internal func _send( + _ buffer: UnsafeRawBufferPointer, + flags: MessageFlags, + retryOnInterrupt: Bool + ) -> Result { + valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_send(self.rawValue, buffer.baseAddress!, buffer.count, flags.rawValue) + } + } + + /// Send a message from a socket. + /// + /// - Parameters: + /// - buffer: The region of memory that contains the data being sent. + /// - address: Address of destination client. + /// - flags: see `send(2)` + /// - retryOnInterrupt: Whether to retry the send operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The number of bytes that were sent. + /// + /// The corresponding C function is `send`. + @_alwaysEmitIntoClient + public func send( + _ buffer: UnsafeRawBufferPointer, + to address: Address, + flags: MessageFlags = [], + retryOnInterrupt: Bool = true + ) throws -> Int { + try _send(buffer, to: address, flags: flags, retryOnInterrupt: retryOnInterrupt).get() + } + + /// Send a message from a socket. + /// + /// - Parameters: + /// - data: The sequence of bytes being sent. + /// - address: Address of destination client. + /// - flags: see `send(2)` + /// - retryOnInterrupt: Whether to retry the send operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The number of bytes that were sent. + /// + /// The corresponding C function is `send`. + public func send( + _ data: Data, + to address: Address, + flags: MessageFlags = [], + retryOnInterrupt: Bool = true + ) throws -> Int where Address: SocketAddress, Data: Sequence, Data.Element == UInt8 { + try data._withRawBufferPointer { dataPointer in + _send(dataPointer, to: address, flags: flags, retryOnInterrupt: retryOnInterrupt) + }.get() + } + + /// `send()` + @usableFromInline + internal func _send( + _ data: UnsafeRawBufferPointer, + to address: T, + flags: MessageFlags, + retryOnInterrupt: Bool + ) -> Result { + valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + address.withUnsafePointer { (addressPointer, addressLength) in + system_sendto(self.rawValue, data.baseAddress, data.count, flags.rawValue, addressPointer, addressLength) + } + } + } + + /// Receive a message from a socket. + /// + /// - Parameters: + /// - buffer: The region of memory to receive into. + /// - flags: see `recv(2)` + /// - retryOnInterrupt: Whether to retry the receive operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The number of bytes that were received. + /// + /// The corresponding C function is `recv`. + @_alwaysEmitIntoClient + public func receive( + into buffer: UnsafeMutableRawBufferPointer, + flags: MessageFlags = [], + retryOnInterrupt: Bool = true + ) throws -> Int { + try _receive( + into: buffer, flags: flags, retryOnInterrupt: retryOnInterrupt + ).get() + } + + @usableFromInline + internal func _receive( + into buffer: UnsafeMutableRawBufferPointer, + flags: MessageFlags, + retryOnInterrupt: Bool + ) -> Result { + valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_recv(self.rawValue, buffer.baseAddress!, buffer.count, flags.rawValue) + } + } + + @usableFromInline + internal func _recieve( + _ dataBuffer: UnsafeMutableRawBufferPointer, + flags: MessageFlags, + retryOnInterrupt: Bool + ) -> Result { + valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_recv(self.rawValue, dataBuffer.baseAddress, dataBuffer.count, flags.rawValue) + } + } + /* + @usableFromInline + internal func _recieve( + _ dataBuffer: UnsafeMutableRawBufferPointer, + from address: Address, + flags: MessageFlags, + retryOnInterrupt: Bool + ) -> Result { + valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + address.withUnsafePointer { (addressPointer, addressLength) in + var addressLength = addressLength + system_recvfrom(self.rawValue, dataBuffer.baseAddress, dataBuffer.count, flags.rawValue, addressPointer, &addressLength) + } + } + }*/ + + /// Listen for connections on a socket. + /// + /// Only applies to sockets of connection type `.stream`. + /// + /// - Parameters: + /// - backlog: the maximum length for the queue of pending connections + /// - retryOnInterrupt: Whether to retry the receive operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// + /// The corresponding C function is `listen`. + @_alwaysEmitIntoClient + public func listen( + backlog: Int, + retryOnInterrupt: Bool = true + ) throws { + try _listen(backlog: Int32(backlog), retryOnInterrupt: retryOnInterrupt).get() + } + + @usableFromInline + internal func _listen( + backlog: Int32, + retryOnInterrupt: Bool + ) -> Result<(), Errno> { + nothingOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_listen(self.rawValue, backlog) + } + } + + /// Accept a connection on a socket. + /// + /// - Parameters: + /// - address: The type of the `SocketAddress` expected for the new connection. + /// - retryOnInterrupt: Whether to retry the receive operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: A tuple containing the file descriptor and address of the new connection. + /// + /// The corresponding C function is `accept`. + @_alwaysEmitIntoClient + public func accept( + _ address: Address.Type, + retryOnInterrupt: Bool = true + ) throws -> (FileDescriptor, Address) { + return try _accept(Address.self, retryOnInterrupt: retryOnInterrupt).get() + } + + @usableFromInline + internal func _accept( + _ address: Address.Type, + retryOnInterrupt: Bool + ) -> Result<(FileDescriptor, Address), Errno> { + var result: Result = .success(0) + let address = Address.withUnsafePointer { socketPointer, socketLength in + var length = socketLength + result = valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_accept(self.rawValue, socketPointer, &length) + } + } + return result.map { (FileDescriptor(socket: $0), address) } + } + + /// Accept a connection on a socket. + /// + /// - Parameters: + /// - retryOnInterrupt: Whether to retry the receive operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The file descriptor of the new connection. + /// + /// The corresponding C function is `accept`. + @_alwaysEmitIntoClient + public func accept( + retryOnInterrupt: Bool = true + ) throws -> FileDescriptor { + return try _accept(retryOnInterrupt: retryOnInterrupt).get() + } + + @usableFromInline + internal func _accept( + retryOnInterrupt: Bool + ) -> Result { + var length: UInt32 = 0 + return valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_accept(self.rawValue, nil, &length) + }.map(FileDescriptor.init(socket:)) + } + + /// Initiate a connection on a socket. + /// + /// - Parameters: + /// - address: The peer address. + /// - retryOnInterrupt: Whether to retry the receive operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The file descriptor of the new connection. + /// + /// The corresponding C function is `connect`. + @_alwaysEmitIntoClient + public func connect( + to address: Address, + retryOnInterrupt: Bool = true + ) throws { + try _connect(to: address, retryOnInterrupt: retryOnInterrupt).get() + } + + /// The `connect()` function shall attempt to make a connection on a socket. + @usableFromInline + internal func _connect( + to address: Address, + retryOnInterrupt: Bool + ) -> Result<(), Errno> { + nothingOrErrno(retryOnInterrupt: retryOnInterrupt) { + address.withUnsafePointer { (addressPointer, addressLength) in + system_connect(self.rawValue, addressPointer, addressLength) + } + } + } + + /// Wait for some event on a set of file descriptors. + /// + /// - Parameters: + /// - fileDescriptors: An array of bit mask specifying the events the application is interested in for the file descriptors. + /// - timeout: Specifies the minimum number of milliseconds that this method will block. Specifying a negative value in timeout means an infinite timeout. Specifying a timeout of zero causes this method to return immediately. + /// - retryOnInterrupt: Whether to retry the receive operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns:A array of bitmasks filled by the kernel with the events that actually occurred + /// for the corresponding file descriptors. + /// + /// The corresponding C function is `poll`. + @_alwaysEmitIntoClient + public static func poll( + _ fileDescriptors: [(FileDescriptor, FileEvents)], + timeout: Int = 0, + retryOnInterrupt: Bool = true + ) throws -> [(FileDescriptor, FileEvents)] { + try _poll(fileDescriptors, timeout: CInt(timeout), retryOnInterrupt: retryOnInterrupt).get() + } + + /// wait for some event on file descriptors + @usableFromInline + internal static func _poll( + _ fileDescriptors: [(FileDescriptor, FileEvents)], + timeout: CInt, + retryOnInterrupt: Bool + ) -> Result<[(FileDescriptor, FileEvents)], Errno> { + var pollFDs = fileDescriptors.map { + CInterop.PollFileDescriptor( + fd: $0.0.rawValue, + events: $0.1.rawValue, + revents: 0 + ) + } + return nothingOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_poll(&pollFDs, CInterop.FileDescriptorCount(pollFDs.count), timeout) + }.map { pollFDs.map { (FileDescriptor(rawValue: $0.fd), FileEvents(rawValue: $0.revents)) } } + } + + /// Wait for some event on a file descriptor. + /// + /// - Parameters: + /// - events: A bit mask specifying the events the application is interested in for the file descriptor. + /// - timeout: Specifies the minimum number of milliseconds that this method will block. Specifying a negative value in timeout means an infinite timeout. Specifying a timeout of zero causes this method to return immediately. + /// - retryOnInterrupt: Whether to retry the receive operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: A bitmask filled by the kernel with the events that actually occurred. + /// + /// The corresponding C function is `poll`. + public func poll( + for events: FileEvents, + timeout: Int = 0, + retryOnInterrupt: Bool = true + ) throws -> FileEvents { + try _poll( + events: events, + timeout: CInt(timeout), + retryOnInterrupt: retryOnInterrupt + ).get() + } + + /// `poll()` + /// + /// Wait for some event on a file descriptor. + @usableFromInline + internal func _poll( + events: FileEvents, + timeout: CInt, + retryOnInterrupt: Bool + ) -> Result { + var pollFD = CInterop.PollFileDescriptor( + fd: self.rawValue, + events: events.rawValue, + revents: 0 + ) + return nothingOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_poll(&pollFD, 1, timeout) + }.map { FileEvents(rawValue: events.rawValue) } + } +} + +extension Sequence where Element == FileDescriptor { + + /// Wait for some event on a set of file descriptors. + /// + /// - Parameters: + /// - events: A bit mask specifying the events the application is interested in for the file descriptors. + /// - timeout: Specifies the minimum number of milliseconds that this method will block. Specifying a negative value in timeout means an infinite timeout. Specifying a timeout of zero causes this method to return immediately. + /// - retryOnInterrupt: Whether to retry the receive operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns:A array of bitmasks filled by the kernel with the events that actually occurred + /// for the corresponding file descriptors. + /// + /// The corresponding C function is `poll`. + @_alwaysEmitIntoClient + public func poll( + for events: FileEvents, + timeout: Int = 0, + retryOnInterrupt: Bool = true + ) throws -> [(FileDescriptor, FileEvents)] { + try FileDescriptor._poll( + self.map { ($0, events) }, + timeout: CInt(timeout), + retryOnInterrupt: retryOnInterrupt + ).get() + } +} diff --git a/Sources/System/Socket/SocketOption.swift b/Sources/System/Socket/SocketOption.swift new file mode 100644 index 00000000..7627b8bd --- /dev/null +++ b/Sources/System/Socket/SocketOption.swift @@ -0,0 +1,84 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +/// POSIX Socket Option ID +public protocol SocketOption { + + associatedtype ID: SocketOptionID + + static var id: ID { get } + + func withUnsafeBytes(_ pointer: ((UnsafeRawBufferPointer) throws -> (Result))) rethrows -> Result + + static func withUnsafeBytes( + _ body: (UnsafeMutableRawBufferPointer) throws -> () + ) rethrows -> Self +} + +public protocol BooleanSocketOption: SocketOption { + + init(_ boolValue: Bool) + + var boolValue: Bool { get } +} + +extension BooleanSocketOption where Self: ExpressibleByBooleanLiteral { + + @_alwaysEmitIntoClient + public init(booleanLiteral boolValue: Bool) { + self.init(boolValue) + } +} + +public extension BooleanSocketOption { + + func withUnsafeBytes(_ pointer: ((UnsafeRawBufferPointer) throws -> (Result))) rethrows -> Result { + return try Swift.withUnsafeBytes(of: boolValue.cInt) { bufferPointer in + try pointer(bufferPointer) + } + } + + static func withUnsafeBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ()) rethrows -> Self { + var value: CInt = 0 + try Swift.withUnsafeMutableBytes(of: &value, body) + return Self.init(Bool(value)) + } +} + +/// Platform Socket Option +public extension GenericSocketOption { + + @frozen + struct Debug: BooleanSocketOption, Equatable, Hashable, ExpressibleByBooleanLiteral { + + @_alwaysEmitIntoClient + public static var id: GenericSocketOption { .debug } + + public var boolValue: Bool + + @_alwaysEmitIntoClient + public init(_ boolValue: Bool) { + self.boolValue = boolValue + } + } + + @frozen + struct KeepAlive: BooleanSocketOption, Equatable, Hashable, ExpressibleByBooleanLiteral { + + @_alwaysEmitIntoClient + public static var id: GenericSocketOption { .keepAlive } + + public var boolValue: Bool + + @_alwaysEmitIntoClient + public init(_ boolValue: Bool) { + self.boolValue = boolValue + } + } +} diff --git a/Sources/System/Socket/SocketOptionID.swift b/Sources/System/Socket/SocketOptionID.swift new file mode 100644 index 00000000..80af54f2 --- /dev/null +++ b/Sources/System/Socket/SocketOptionID.swift @@ -0,0 +1,45 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +/// POSIX Socket Option ID +public protocol SocketOptionID: RawRepresentable { + + static var optionLevel: SocketOptionLevel { get } + + init?(rawValue: Int32) + + var rawValue: Int32 { get } +} + +@frozen +public struct GenericSocketOption: RawRepresentable, Equatable, Hashable, SocketOptionID { + + @_alwaysEmitIntoClient + public static var optionLevel: SocketOptionLevel { .default } + + /// The raw socket address family identifier. + @_alwaysEmitIntoClient + public let rawValue: CInt + + /// Creates a strongly-typed socket address family from a raw address family identifier. + @_alwaysEmitIntoClient + public init(rawValue: CInt) { self.rawValue = rawValue } + + @_alwaysEmitIntoClient + private init(_ raw: CInt) { self.init(rawValue: raw) } +} + +public extension GenericSocketOption { + + @_alwaysEmitIntoClient + static var debug: GenericSocketOption { GenericSocketOption(_SO_DEBUG) } + + @_alwaysEmitIntoClient + static var keepAlive: GenericSocketOption { GenericSocketOption(_SO_KEEPALIVE) } +} diff --git a/Sources/System/Socket/SocketOptionLevel.swift b/Sources/System/Socket/SocketOptionLevel.swift new file mode 100644 index 00000000..c52128de --- /dev/null +++ b/Sources/System/Socket/SocketOptionLevel.swift @@ -0,0 +1,41 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +// POSIX Socket Option Level +@frozen +public struct SocketOptionLevel: RawRepresentable, Hashable, Codable { + + /// The raw socket address family identifier. + @_alwaysEmitIntoClient + public let rawValue: CInt + + /// Creates a strongly-typed socket address family from a raw address family identifier. + @_alwaysEmitIntoClient + public init(rawValue: CInt) { self.rawValue = rawValue } + + @_alwaysEmitIntoClient + private init(_ raw: CInt) { self.init(rawValue: raw) } +} + +public extension SocketOptionLevel { + + @_alwaysEmitIntoClient + static var `default`: SocketOptionLevel { SocketOptionLevel(_SOL_SOCKET) } +} + +#if os(Linux) +public extension SocketOptionLevel { + + @_alwaysEmitIntoClient + static var netlink: SocketOptionLevel { SocketOptionLevel(_SOL_NETLINK) } + + @_alwaysEmitIntoClient + static var bluetooth: SocketOptionLevel { SocketOptionLevel(_SOL_BLUETOOTH) } +} +#endif diff --git a/Sources/System/Socket/SocketProtocol.swift b/Sources/System/Socket/SocketProtocol.swift new file mode 100644 index 00000000..02fb3aa5 --- /dev/null +++ b/Sources/System/Socket/SocketProtocol.swift @@ -0,0 +1,94 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +/// POSIX Socket Protocol +public protocol SocketProtocol: RawRepresentable { + + static var family: SocketAddressFamily { get } + + var type: SocketType { get } + + init?(rawValue: Int32) + + var rawValue: Int32 { get } +} + +/// Unix Protocol Family +public enum UnixProtocol: Int32, Codable, SocketProtocol { + + case raw = 0 + + @_alwaysEmitIntoClient + public static var family: SocketAddressFamily { .unix } + + @_alwaysEmitIntoClient + public var type: SocketType { + switch self { + case .raw: return .raw + } + } +} + +/// IPv4 Protocol Family +public enum IPv4Protocol: Int32, Codable, SocketProtocol { + + case raw + case tcp + case udp + + @_alwaysEmitIntoClient + public static var family: SocketAddressFamily { .ipv4 } + + @_alwaysEmitIntoClient + public var type: SocketType { + switch self { + case .raw: return .raw + case .tcp: return .stream + case .udp: return .datagram + } + } + + @_alwaysEmitIntoClient + public var rawValue: Int32 { + switch self { + case .raw: return _IPPROTO_RAW + case .tcp: return _IPPROTO_TCP + case .udp: return _IPPROTO_UDP + } + } +} + +/// IPv6 Protocol Family +public enum IPv6Protocol: Int32, Codable, SocketProtocol { + + case raw + case tcp + case udp + + @_alwaysEmitIntoClient + public static var family: SocketAddressFamily { .ipv6 } + + @_alwaysEmitIntoClient + public var type: SocketType { + switch self { + case .raw: return .raw + case .tcp: return .stream + case .udp: return .datagram + } + } + + @_alwaysEmitIntoClient + public var rawValue: Int32 { + switch self { + case .raw: return _IPPROTO_RAW + case .tcp: return _IPPROTO_TCP + case .udp: return _IPPROTO_UDP + } + } +} diff --git a/Sources/System/Socket/SocketType.swift b/Sources/System/Socket/SocketType.swift new file mode 100644 index 00000000..32fd09dd --- /dev/null +++ b/Sources/System/Socket/SocketType.swift @@ -0,0 +1,69 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +/// POSIX Socket Type +@frozen +public struct SocketType: RawRepresentable, Hashable, Codable { + + /// The raw socket type identifier. + @_alwaysEmitIntoClient + public let rawValue: CInt + + /// Creates a strongly-typed socket type from a raw socket type identifier. + @_alwaysEmitIntoClient + public init(rawValue: CInt) { self.rawValue = rawValue } + + @_alwaysEmitIntoClient + private init(_ cValue: CInterop.SocketType) { + #if os(Linux) + self.init(rawValue: numericCast(cValue.rawValue)) + #else + self.init(rawValue: cValue) + #endif + } +} + +public extension SocketType { + + /// Stream socket + /// + /// Provides sequenced, reliable, two-way, connection-based byte streams. + /// An out-of-band data transmission mechanism may be supported. + @_alwaysEmitIntoClient + static var stream: SocketType { SocketType(_SOCK_STREAM) } + + /// Supports datagrams (connectionless, unreliable messages of a fixed maximum length). + @_alwaysEmitIntoClient + static var datagram: SocketType { SocketType(_SOCK_DGRAM) } + + /// Provides raw network protocol access. + @_alwaysEmitIntoClient + static var raw: SocketType { SocketType(_SOCK_RAW) } + + /// Provides a reliable datagram layer that does not guarantee ordering. + @_alwaysEmitIntoClient + static var reliableDatagramMessage: SocketType { SocketType(_SOCK_RDM) } + + /// Provides a sequenced, reliable, two-way connection-based data transmission + /// path for datagrams of fixed maximum length; a consumer is required to read + /// an entire packet with each input system call. + @_alwaysEmitIntoClient + static var sequencedPacket: SocketType { SocketType(_SOCK_SEQPACKET) } +} + +#if os(Linux) +public extension SocketType { + + /// Datagram Congestion Control Protocol + /// + /// Linux specific way of getting packets at the dev level. + @_alwaysEmitIntoClient + static var datagramCongestionControlProtocol: SocketType { SocketType(_SOCK_DCCP) } +} +#endif diff --git a/Sources/System/Util.swift b/Sources/System/Util.swift index c038d461..72f49994 100644 --- a/Sources/System/Util.swift +++ b/Sources/System/Util.swift @@ -127,3 +127,16 @@ extension MutableCollection where Element: Equatable { } } } + +internal extension Bool { + + @usableFromInline + init(_ cInt: CInt) { + self = cInt != 0 + } + + @usableFromInline + var cInt: CInt { + self ? 1 : 0 + } +} From 258c5a87b61efe10597026b4240f2e07fecf5143 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Mon, 1 Nov 2021 08:31:25 -0500 Subject: [PATCH 2/5] Added `FileDescriptor.Poll` --- Sources/System/Socket/Poll.swift | 143 +++++++++++++++++++ Sources/System/Socket/SocketOperations.swift | 113 --------------- 2 files changed, 143 insertions(+), 113 deletions(-) create mode 100644 Sources/System/Socket/Poll.swift diff --git a/Sources/System/Socket/Poll.swift b/Sources/System/Socket/Poll.swift new file mode 100644 index 00000000..2ff31fcb --- /dev/null +++ b/Sources/System/Socket/Poll.swift @@ -0,0 +1,143 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2021 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +public extension FileDescriptor { + + /// Poll File Descriptor + struct Poll { + + internal private(set) var bytes: CInterop.PollFileDescriptor + + internal init(_ bytes: CInterop.PollFileDescriptor) { + self.bytes = bytes + } + + // Initialize an events request. + public init(fileDescriptor: FileDescriptor, events: FileEvents) { + self.init(CInterop.PollFileDescriptor(fileDescriptor: fileDescriptor, events: events)) + } + + public var fileDescriptor: FileDescriptor { + return FileDescriptor(rawValue: bytes.fd) + } + + public var events: FileEvents { + return FileEvents(rawValue: bytes.events) + } + + public var returnedEvents: FileEvents { + return FileEvents(rawValue: bytes.revents) + } + } +} + +internal extension CInterop.PollFileDescriptor { + + init(fileDescriptor: FileDescriptor, events: FileEvents) { + self.init(fd: fileDescriptor.rawValue, events: events.rawValue, revents: 0) + } +} + +// MARK: - Poll Operations + +extension FileDescriptor { + + /// Wait for some event on a file descriptor. + /// + /// - Parameters: + /// - events: A bit mask specifying the events the application is interested in for the file descriptor. + /// - timeout: Specifies the minimum number of milliseconds that this method will block. Specifying a negative value in timeout means an infinite timeout. Specifying a timeout of zero causes this method to return immediately. + /// - retryOnInterrupt: Whether to retry the receive operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: A bitmask filled by the kernel with the events that actually occurred. + /// + /// The corresponding C function is `poll`. + public func poll( + for events: FileEvents, + timeout: Int = 0, + retryOnInterrupt: Bool = true + ) throws -> FileEvents { + try _poll( + events: events, + timeout: CInt(timeout), + retryOnInterrupt: retryOnInterrupt + ).get() + } + + /// `poll()` + /// + /// Wait for some event on a file descriptor. + @usableFromInline + internal func _poll( + events: FileEvents, + timeout: CInt, + retryOnInterrupt: Bool + ) -> Result { + var pollFD = CInterop.PollFileDescriptor( + fd: self.rawValue, + events: events.rawValue, + revents: 0 + ) + return nothingOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_poll(&pollFD, 1, timeout) + }.map { FileEvents(rawValue: events.rawValue) } + } +} + +extension FileDescriptor { + + /// wait for some event on file descriptors + @usableFromInline + internal static func _poll( + _ pollFDs: inout [Poll], + timeout: CInt, + retryOnInterrupt: Bool + ) -> Result<(), Errno> { + assert(pollFDs.isEmpty == false) + let count = CInterop.FileDescriptorCount(pollFDs.count) + return pollFDs.withUnsafeMutableBufferPointer { buffer in + buffer.withMemoryRebound(to: CInterop.PollFileDescriptor.self) { cBuffer in + nothingOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_poll( + cBuffer.baseAddress!, + count, + timeout + ) + } + } + } + } +} + + +extension Array where Element == FileDescriptor.Poll { + + /// Wait for some event on a set of file descriptors. + /// + /// - Parameters: + /// - fileDescriptors: An array of bit mask specifying the events the application is interested in for the file descriptors. + /// - timeout: Specifies the minimum number of milliseconds that this method will block. Specifying a negative value in timeout means an infinite timeout. Specifying a timeout of zero causes this method to return immediately. + /// - retryOnInterrupt: Whether to retry the receive operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns:A array of bitmasks filled by the kernel with the events that actually occurred + /// for the corresponding file descriptors. + /// + /// The corresponding C function is `poll`. + mutating func poll( + timeout: Int = 0, + retryOnInterrupt: Bool = true + ) throws { + guard isEmpty else { return } + try FileDescriptor._poll(&self, timeout: CInt(timeout), retryOnInterrupt: retryOnInterrupt).get() + } +} diff --git a/Sources/System/Socket/SocketOperations.swift b/Sources/System/Socket/SocketOperations.swift index 32caf886..8a124016 100644 --- a/Sources/System/Socket/SocketOperations.swift +++ b/Sources/System/Socket/SocketOperations.swift @@ -520,117 +520,4 @@ extension FileDescriptor { } } } - - /// Wait for some event on a set of file descriptors. - /// - /// - Parameters: - /// - fileDescriptors: An array of bit mask specifying the events the application is interested in for the file descriptors. - /// - timeout: Specifies the minimum number of milliseconds that this method will block. Specifying a negative value in timeout means an infinite timeout. Specifying a timeout of zero causes this method to return immediately. - /// - retryOnInterrupt: Whether to retry the receive operation - /// if it throws ``Errno/interrupted``. - /// The default is `true`. - /// Pass `false` to try only once and throw an error upon interruption. - /// - Returns:A array of bitmasks filled by the kernel with the events that actually occurred - /// for the corresponding file descriptors. - /// - /// The corresponding C function is `poll`. - @_alwaysEmitIntoClient - public static func poll( - _ fileDescriptors: [(FileDescriptor, FileEvents)], - timeout: Int = 0, - retryOnInterrupt: Bool = true - ) throws -> [(FileDescriptor, FileEvents)] { - try _poll(fileDescriptors, timeout: CInt(timeout), retryOnInterrupt: retryOnInterrupt).get() - } - - /// wait for some event on file descriptors - @usableFromInline - internal static func _poll( - _ fileDescriptors: [(FileDescriptor, FileEvents)], - timeout: CInt, - retryOnInterrupt: Bool - ) -> Result<[(FileDescriptor, FileEvents)], Errno> { - var pollFDs = fileDescriptors.map { - CInterop.PollFileDescriptor( - fd: $0.0.rawValue, - events: $0.1.rawValue, - revents: 0 - ) - } - return nothingOrErrno(retryOnInterrupt: retryOnInterrupt) { - system_poll(&pollFDs, CInterop.FileDescriptorCount(pollFDs.count), timeout) - }.map { pollFDs.map { (FileDescriptor(rawValue: $0.fd), FileEvents(rawValue: $0.revents)) } } - } - - /// Wait for some event on a file descriptor. - /// - /// - Parameters: - /// - events: A bit mask specifying the events the application is interested in for the file descriptor. - /// - timeout: Specifies the minimum number of milliseconds that this method will block. Specifying a negative value in timeout means an infinite timeout. Specifying a timeout of zero causes this method to return immediately. - /// - retryOnInterrupt: Whether to retry the receive operation - /// if it throws ``Errno/interrupted``. - /// The default is `true`. - /// Pass `false` to try only once and throw an error upon interruption. - /// - Returns: A bitmask filled by the kernel with the events that actually occurred. - /// - /// The corresponding C function is `poll`. - public func poll( - for events: FileEvents, - timeout: Int = 0, - retryOnInterrupt: Bool = true - ) throws -> FileEvents { - try _poll( - events: events, - timeout: CInt(timeout), - retryOnInterrupt: retryOnInterrupt - ).get() - } - - /// `poll()` - /// - /// Wait for some event on a file descriptor. - @usableFromInline - internal func _poll( - events: FileEvents, - timeout: CInt, - retryOnInterrupt: Bool - ) -> Result { - var pollFD = CInterop.PollFileDescriptor( - fd: self.rawValue, - events: events.rawValue, - revents: 0 - ) - return nothingOrErrno(retryOnInterrupt: retryOnInterrupt) { - system_poll(&pollFD, 1, timeout) - }.map { FileEvents(rawValue: events.rawValue) } - } -} - -extension Sequence where Element == FileDescriptor { - - /// Wait for some event on a set of file descriptors. - /// - /// - Parameters: - /// - events: A bit mask specifying the events the application is interested in for the file descriptors. - /// - timeout: Specifies the minimum number of milliseconds that this method will block. Specifying a negative value in timeout means an infinite timeout. Specifying a timeout of zero causes this method to return immediately. - /// - retryOnInterrupt: Whether to retry the receive operation - /// if it throws ``Errno/interrupted``. - /// The default is `true`. - /// Pass `false` to try only once and throw an error upon interruption. - /// - Returns:A array of bitmasks filled by the kernel with the events that actually occurred - /// for the corresponding file descriptors. - /// - /// The corresponding C function is `poll`. - @_alwaysEmitIntoClient - public func poll( - for events: FileEvents, - timeout: Int = 0, - retryOnInterrupt: Bool = true - ) throws -> [(FileDescriptor, FileEvents)] { - try FileDescriptor._poll( - self.map { ($0, events) }, - timeout: CInt(timeout), - retryOnInterrupt: retryOnInterrupt - ).get() - } } From a9251fc9f29589d3e0c606148989488aa1d6af71 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Mon, 1 Nov 2021 08:49:32 -0500 Subject: [PATCH 3/5] Removed unused code --- Sources/System/Socket/SocketOperations.swift | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Sources/System/Socket/SocketOperations.swift b/Sources/System/Socket/SocketOperations.swift index 8a124016..de4e2e37 100644 --- a/Sources/System/Socket/SocketOperations.swift +++ b/Sources/System/Socket/SocketOperations.swift @@ -382,21 +382,6 @@ extension FileDescriptor { system_recv(self.rawValue, dataBuffer.baseAddress, dataBuffer.count, flags.rawValue) } } - /* - @usableFromInline - internal func _recieve( - _ dataBuffer: UnsafeMutableRawBufferPointer, - from address: Address, - flags: MessageFlags, - retryOnInterrupt: Bool - ) -> Result { - valueOrErrno(retryOnInterrupt: retryOnInterrupt) { - address.withUnsafePointer { (addressPointer, addressLength) in - var addressLength = addressLength - system_recvfrom(self.rawValue, dataBuffer.baseAddress, dataBuffer.count, flags.rawValue, addressPointer, &addressLength) - } - } - }*/ /// Listen for connections on a socket. /// From 8401aca3b2d9200850d74e8a22fdd37f591688b1 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Mon, 1 Nov 2021 08:53:03 -0500 Subject: [PATCH 4/5] Internal API improvements --- Sources/System/FileDescriptorSet.swift | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Sources/System/FileDescriptorSet.swift b/Sources/System/FileDescriptorSet.swift index c74da0e1..1e2d4552 100644 --- a/Sources/System/FileDescriptorSet.swift +++ b/Sources/System/FileDescriptorSet.swift @@ -110,8 +110,8 @@ internal extension CInterop.FileDescriptorSet { /// - Parameter fd: The fd to add to the fd_set /// @usableFromInline - mutating func set(_ fd: Int32) { - let (index, mask) = Self.address(for: fd) + mutating func set(_ fd: CInt) { + let (index, mask) = Self.offset(for: fd) withUnsafeMutablePointer { $0[index] |= mask } } @@ -121,8 +121,8 @@ internal extension CInterop.FileDescriptorSet { /// - Parameter fd: The fd to clear from the fd_set /// @usableFromInline - mutating func clear(_ fd: Int32) { - let (index, mask) = Self.address(for: fd) + mutating func clear(_ fd: CInt) { + let (index, mask) = Self.offset(for: fd) withUnsafeMutablePointer { $0[index] &= ~mask } } @@ -134,13 +134,13 @@ internal extension CInterop.FileDescriptorSet { /// - Returns: `True` if present, `false` otherwise. /// @usableFromInline - mutating func isSet(_ fd: Int32) -> Bool { - let (index, mask) = Self.address(for: fd) + mutating func isSet(_ fd: CInt) -> Bool { + let (index, mask) = Self.offset(for: fd) return withUnsafeMutablePointer { $0[index] & mask != 0 } } @usableFromInline - static func address(for fd: Int32) -> (Int, Int32) { + static func offset(for fd: CInt) -> (offset: Int, mask: CInt) { var intOffset = Int(fd) / _fd_set_count #if _endian(big) if intOffset % 2 == 0 { @@ -150,29 +150,29 @@ internal extension CInterop.FileDescriptorSet { } #endif let bitOffset = Int(fd) % _fd_set_count - let mask = Int32(bitPattern: UInt32(1 << bitOffset)) + let mask = CInt(bitPattern: UInt32(1 << bitOffset)) return (intOffset, mask) } #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) @usableFromInline - mutating func withUnsafeMutablePointer(_ body: (UnsafeMutablePointer) throws -> T) rethrows -> T { + mutating func withUnsafeMutablePointer(_ body: (UnsafeMutablePointer) throws -> T) rethrows -> T { return try Swift.withUnsafeMutablePointer(to: &fds_bits) { - try body(UnsafeMutableRawPointer($0).assumingMemoryBound(to: Int32.self)) + try body(UnsafeMutableRawPointer($0).assumingMemoryBound(to: CInt.self)) } } #elseif os(Linux) || os(FreeBSD) || os(Android) @usableFromInline - mutating func withUnsafeMutablePointer(_ body: (UnsafeMutablePointer) throws -> T) rethrows -> T { + mutating func withUnsafeMutablePointer(_ body: (UnsafeMutablePointer) throws -> T) rethrows -> T { return try Swift.withUnsafeMutablePointer(to: &__fds_bits) { - try body(UnsafeMutableRawPointer($0).assumingMemoryBound(to: Int32.self)) + try body(UnsafeMutableRawPointer($0).assumingMemoryBound(to: CInt.self)) } } #elseif os(Windows) @usableFromInline - mutating func withUnsafeMutablePointer(_ body: (UnsafeMutablePointer) throws -> T) rethrows -> T { + mutating func withUnsafeMutablePointer(_ body: (UnsafeMutablePointer) throws -> T) rethrows -> T { return try Swift.withUnsafeMutablePointer(to: &fds_bits) { - try body(UnsafeMutableRawPointer($0).assumingMemoryBound(to: Int32.self)) + try body(UnsafeMutableRawPointer($0).assumingMemoryBound(to: CInt.self)) } } #endif From d951e979c178c9638012568c939adb199da660d5 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Mon, 1 Nov 2021 10:57:36 -0500 Subject: [PATCH 5/5] Removed `IPAddress` --- .../System/Internals/CInternetAddress.swift | 76 ------- Sources/System/Internals/CInterop.swift | 12 - Sources/System/Internals/CSocketAddress.swift | 12 - Sources/System/Internals/Constants.swift | 18 -- Sources/System/Internals/NetworkOrder.swift | 21 -- Sources/System/Internals/Syscalls.swift | 17 -- Sources/System/InternetProtocol.swift | 208 ------------------ Sources/System/Socket/SocketAddress.swift | 82 +------ Sources/System/Socket/SocketProtocol.swift | 58 ----- 9 files changed, 1 insertion(+), 503 deletions(-) delete mode 100644 Sources/System/Internals/CInternetAddress.swift delete mode 100644 Sources/System/Internals/NetworkOrder.swift delete mode 100644 Sources/System/InternetProtocol.swift diff --git a/Sources/System/Internals/CInternetAddress.swift b/Sources/System/Internals/CInternetAddress.swift deleted file mode 100644 index 7a54f138..00000000 --- a/Sources/System/Internals/CInternetAddress.swift +++ /dev/null @@ -1,76 +0,0 @@ -/* - This source file is part of the Swift System open source project - - Copyright (c) 2021 Apple Inc. and the Swift System project authors - Licensed under Apache License v2.0 with Runtime Library Exception - - See https://swift.org/LICENSE.txt for license information -*/ - -@usableFromInline -internal protocol CInternetAddress { - - static var stringLength: Int { get } - - static var family: SocketAddressFamily { get } - - init() -} - -internal extension CInternetAddress { - - @usableFromInline - init?(_ string: String) { - self.init() - /** - inet_pton() returns 1 on success (network address was successfully converted). 0 is returned if src does not contain a character string representing a valid network address in the specified address family. If af does not contain a valid address family, -1 is returned and errno is set to EAFNOSUPPORT. - */ - let result = string.withCString { - system_inet_pton(Self.family.rawValue, $0, &self) - } - guard result == 1 else { - assert(result != -1, "Invalid address family") - return nil - } - } -} - -internal extension String { - - @usableFromInline - init(_ cInternetAddress: T) throws { - let cString = UnsafeMutablePointer.allocate(capacity: T.stringLength) - defer { cString.deallocate() } - let success = withUnsafePointer(to: cInternetAddress) { - system_inet_ntop( - T.family.rawValue, - $0, - cString, - numericCast(T.stringLength) - ) != nil - } - guard success else { - throw Errno.current - } - - self.init(cString: cString) - } -} - -extension CInterop.IPv4Address: CInternetAddress { - - @usableFromInline - static var stringLength: Int { return numericCast(_INET_ADDRSTRLEN) } - - @usableFromInline - static var family: SocketAddressFamily { .ipv4 } -} - -extension CInterop.IPv6Address: CInternetAddress { - - @usableFromInline - static var stringLength: Int { return numericCast(_INET6_ADDRSTRLEN) } - - @usableFromInline - static var family: SocketAddressFamily { .ipv6 } -} diff --git a/Sources/System/Internals/CInterop.swift b/Sources/System/Internals/CInterop.swift index 8e6254d4..a9721bd4 100644 --- a/Sources/System/Internals/CInterop.swift +++ b/Sources/System/Internals/CInterop.swift @@ -97,21 +97,9 @@ public enum CInterop { /// The C `addrinfo` type public typealias AddressInfo = addrinfo - /// The C `in_addr` type - public typealias IPv4Address = in_addr - - /// The C `in6_addr` type - public typealias IPv6Address = in6_addr - /// The C `sockaddr_in` type public typealias SocketAddress = sockaddr /// The C `sockaddr_in` type public typealias UnixSocketAddress = sockaddr_un - - /// The C `sockaddr_in` type - public typealias IPv4SocketAddress = sockaddr_in - - /// The C `sockaddr_in6` type - public typealias IPv6SocketAddress = sockaddr_in6 } diff --git a/Sources/System/Internals/CSocketAddress.swift b/Sources/System/Internals/CSocketAddress.swift index a1c06eb9..2644e89a 100644 --- a/Sources/System/Internals/CSocketAddress.swift +++ b/Sources/System/Internals/CSocketAddress.swift @@ -41,15 +41,3 @@ extension CInterop.UnixSocketAddress: CSocketAddress { @_alwaysEmitIntoClient static var family: SocketAddressFamily { .unix } } - -extension CInterop.IPv4SocketAddress: CSocketAddress { - - @_alwaysEmitIntoClient - static var family: SocketAddressFamily { .ipv4 } -} - -extension CInterop.IPv6SocketAddress: CSocketAddress { - - @_alwaysEmitIntoClient - static var family: SocketAddressFamily { .ipv6 } -} diff --git a/Sources/System/Internals/Constants.swift b/Sources/System/Internals/Constants.swift index 8ed5872b..aba25821 100644 --- a/Sources/System/Internals/Constants.swift +++ b/Sources/System/Internals/Constants.swift @@ -559,24 +559,6 @@ internal var _POLLHUP: CInt { POLLHUP } @_alwaysEmitIntoClient internal var _POLLNVAL: CInt { POLLNVAL } -@_alwaysEmitIntoClient -internal var _INET_ADDRSTRLEN: CInt { INET_ADDRSTRLEN } - -@_alwaysEmitIntoClient -internal var _INET6_ADDRSTRLEN: CInt { INET6_ADDRSTRLEN } - -@_alwaysEmitIntoClient -internal var _INADDR_ANY: CInterop.IPv4Address { CInterop.IPv4Address(s_addr: INADDR_ANY) } - -@_alwaysEmitIntoClient -internal var _INADDR_LOOPBACK: CInterop.IPv4Address { CInterop.IPv4Address(s_addr: INADDR_LOOPBACK) } - -@_alwaysEmitIntoClient -internal var _INADDR6_ANY: CInterop.IPv6Address { in6addr_any } - -@_alwaysEmitIntoClient -internal var _INADDR6_LOOPBACK: CInterop.IPv6Address { in6addr_loopback } - @_alwaysEmitIntoClient internal var _AF_UNIX: CInt { AF_UNIX } diff --git a/Sources/System/Internals/NetworkOrder.swift b/Sources/System/Internals/NetworkOrder.swift deleted file mode 100644 index 6ef0fc4d..00000000 --- a/Sources/System/Internals/NetworkOrder.swift +++ /dev/null @@ -1,21 +0,0 @@ -/* - This source file is part of the Swift System open source project - - Copyright (c) 2021 Apple Inc. and the Swift System project authors - Licensed under Apache License v2.0 with Runtime Library Exception - - See https://swift.org/LICENSE.txt for license information -*/ - -extension FixedWidthInteger { - - @usableFromInline - internal var networkOrder: Self { - bigEndian - } - - @usableFromInline - internal init(networkOrder value: Self) { - self.init(bigEndian: value) - } -} diff --git a/Sources/System/Internals/Syscalls.swift b/Sources/System/Internals/Syscalls.swift index 11cb906e..1127eef2 100644 --- a/Sources/System/Internals/Syscalls.swift +++ b/Sources/System/Internals/Syscalls.swift @@ -123,23 +123,6 @@ internal func system_dup2(_ fd: Int32, _ fd2: Int32) -> Int32 { return dup2(fd, fd2) } -internal func system_inet_pton( - _ family: Int32, - _ cString: UnsafePointer, - _ address: UnsafeMutableRawPointer) -> Int32 { - #if ENABLE_MOCKING - if mockingEnabled { return _mock(family, cString, address) } - #endif - return inet_pton(family, cString, address) -} - -internal func system_inet_ntop(_ family: Int32, _ pointer : UnsafeRawPointer, _ string: UnsafeMutablePointer, _ length: UInt32) -> UnsafePointer? { - #if ENABLE_MOCKING - //if mockingEnabled { return _mock(family, pointer, string, length) } - #endif - return inet_ntop(family, pointer, string, length) -} - internal func system_socket(_ fd: Int32, _ fd2: Int32, _ fd3: Int32) -> Int32 { #if ENABLE_MOCKING if mockingEnabled { return _mock(fd, fd2, fd3) } diff --git a/Sources/System/InternetProtocol.swift b/Sources/System/InternetProtocol.swift deleted file mode 100644 index a31960f4..00000000 --- a/Sources/System/InternetProtocol.swift +++ /dev/null @@ -1,208 +0,0 @@ -/* - This source file is part of the Swift System open source project - - Copyright (c) 2021 Apple Inc. and the Swift System project authors - Licensed under Apache License v2.0 with Runtime Library Exception - - See https://swift.org/LICENSE.txt for license information -*/ - -/// Internet Protocol Address -@frozen -public enum IPAddress: Equatable, Hashable, Codable { - - /// IPv4 - case v4(IPv4Address) - - /// IPv6 - case v6(IPv6Address) -} - -public extension IPAddress { - - @_alwaysEmitIntoClient - func withUnsafeBytes(_ body: ((UnsafeRawBufferPointer) -> (Result))) -> Result { - switch self { - case let .v4(address): - return address.withUnsafeBytes(body) - case let .v6(address): - return address.withUnsafeBytes(body) - } - } -} - -extension IPAddress: RawRepresentable { - - public init?(rawValue: String) { - - if let address = IPv4Address(rawValue: rawValue) { - self = .v4(address) - } else if let address = IPv6Address(rawValue: rawValue) { - self = .v6(address) - } else { - return nil - } - } - - public var rawValue: String { - switch self { - case let .v4(address): return address.rawValue - case let .v6(address): return address.rawValue - } - } -} - -extension IPAddress: CustomStringConvertible, CustomDebugStringConvertible { - - public var description: String { - return rawValue - } - - public var debugDescription: String { - return description - } -} - -/// IPv4 Socket Address -@frozen -public struct IPv4Address: Equatable, Hashable, Codable { - - @usableFromInline - internal let bytes: CInterop.IPv4Address - - @_alwaysEmitIntoClient - public init(_ bytes: CInterop.IPv4Address) { - self.bytes = bytes - } - - @_alwaysEmitIntoClient - public func withUnsafeBytes(_ body: ((UnsafeRawBufferPointer) -> (Result))) -> Result { - Swift.withUnsafeBytes(of: bytes, body) - } -} - -public extension IPv4Address { - - /// Initialize with raw bytes. - @_alwaysEmitIntoClient - init(_ byte0: UInt8, _ byte1: UInt8, _ byte2: UInt8, _ byte3: UInt8) { - self.init(unsafeBitCast((byte0, byte1, byte2, byte3), to: CInterop.IPv4Address.self)) - } -} - -public extension IPAddress { - - /// Initialize with a IP v4 address. - @_alwaysEmitIntoClient - init(_ byte0: UInt8, _ byte1: UInt8, _ byte2: UInt8, _ byte3: UInt8) { - self = .v4(IPv4Address(byte0, byte1, byte2, byte3)) - } -} - -public extension IPv4Address { - - @_alwaysEmitIntoClient - static var any: IPv4Address { IPv4Address(_INADDR_ANY) } - - @_alwaysEmitIntoClient - static var loopback: IPv4Address { IPv4Address(_INADDR_LOOPBACK) } -} - -extension IPv4Address: RawRepresentable { - - @_alwaysEmitIntoClient - public init?(rawValue: String) { - guard let bytes = CInterop.IPv4Address(rawValue) else { - return nil - } - self.init(bytes) - } - - @_alwaysEmitIntoClient - public var rawValue: String { - return try! String(bytes) - } -} - -extension IPv4Address: CustomStringConvertible, CustomDebugStringConvertible { - - public var description: String { - return rawValue - } - - public var debugDescription: String { - return description - } -} - -/// IPv6 Socket Address -@frozen -public struct IPv6Address: Equatable, Hashable, Codable { - - @usableFromInline - internal let bytes: CInterop.IPv6Address - - @_alwaysEmitIntoClient - public init(_ bytes: CInterop.IPv6Address) { - self.bytes = bytes - } - - @_alwaysEmitIntoClient - public func withUnsafeBytes(_ body: ((UnsafeRawBufferPointer) -> (Result))) -> Result { - Swift.withUnsafeBytes(of: bytes, body) - } -} - -public extension IPv6Address { - - /// Initialize with bytes - @_alwaysEmitIntoClient - init(_ byte0: UInt16, _ byte1: UInt16, _ byte2: UInt16, _ byte3: UInt16, _ byte4: UInt16, _ byte5: UInt16, _ byte6: UInt16, _ byte7: UInt16) { - self.init(unsafeBitCast((byte0.bigEndian, byte1.bigEndian, byte2.bigEndian, byte3.bigEndian, byte4.bigEndian, byte5.bigEndian, byte6.bigEndian, byte7.bigEndian), to: CInterop.IPv6Address.self)) - } -} - -public extension IPAddress { - - /// Initialize with a IP v6 address. - @_alwaysEmitIntoClient - init(_ byte0: UInt16, _ byte1: UInt16, _ byte2: UInt16, _ byte3: UInt16, _ byte4: UInt16, _ byte5: UInt16, _ byte6: UInt16, _ byte7: UInt16) { - self = .v6(IPv6Address(byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7)) - } -} - -public extension IPv6Address { - - @_alwaysEmitIntoClient - static var any: IPv6Address { IPv6Address(_INADDR6_ANY) } - - @_alwaysEmitIntoClient - static var loopback: IPv6Address { IPv6Address(_INADDR6_LOOPBACK) } -} - -extension IPv6Address: RawRepresentable { - - @_alwaysEmitIntoClient - public init?(rawValue: String) { - guard let bytes = CInterop.IPv6Address(rawValue) else { - return nil - } - self.init(bytes) - } - - @_alwaysEmitIntoClient - public var rawValue: String { - return try! String(bytes) - } -} - -extension IPv6Address: CustomStringConvertible, CustomDebugStringConvertible { - - public var description: String { - return rawValue - } - - public var debugDescription: String { - return description - } -} diff --git a/Sources/System/Socket/SocketAddress.swift b/Sources/System/Socket/SocketAddress.swift index 5ae0e828..76167dd3 100644 --- a/Sources/System/Socket/SocketAddress.swift +++ b/Sources/System/Socket/SocketAddress.swift @@ -31,7 +31,7 @@ public extension SocketAddress { } } -/// IPv4 Socket Address +/// Unix Socket Address public struct UnixSocketAddress: SocketAddress, Equatable, Hashable { public typealias ProtocolID = UnixProtocol @@ -69,83 +69,3 @@ public struct UnixSocketAddress: SocketAddress, Equatable, Hashable { } } } - -/// IPv4 Socket Address -public struct IPv4SocketAddress: SocketAddress, Equatable, Hashable { - - public typealias ProtocolID = IPv4Protocol - - public var address: IPv4Address - - public var port: UInt16 - - @_alwaysEmitIntoClient - public init(address: IPv4Address, - port: UInt16) { - - self.address = address - self.port = port - } - - public func withUnsafePointer( - _ body: (UnsafePointer, UInt32) throws -> Result - ) rethrows -> Result { - - var socketAddress = CInterop.IPv4SocketAddress() - socketAddress.sin_family = numericCast(Self.family.rawValue) - socketAddress.sin_port = port.networkOrder - socketAddress.sin_addr = address.bytes - return try socketAddress.withUnsafePointer(body) - } - - public static func withUnsafePointer( - _ body: (UnsafeMutablePointer, UInt32) throws -> () - ) rethrows -> Self { - var socketAddress = CInterop.IPv4SocketAddress() - try socketAddress.withUnsafeMutablePointer(body) - return Self.init( - address: IPv4Address(socketAddress.sin_addr), - port: socketAddress.sin_port.networkOrder - ) - } -} - -/// IPv6 Socket Address -public struct IPv6SocketAddress: SocketAddress, Equatable, Hashable { - - public typealias ProtocolID = IPv6Protocol - - public var address: IPv6Address - - public var port: UInt16 - - @_alwaysEmitIntoClient - public init(address: IPv6Address, - port: UInt16) { - - self.address = address - self.port = port - } - - public func withUnsafePointer( - _ body: (UnsafePointer, UInt32) throws -> Result - ) rethrows -> Result { - - var socketAddress = CInterop.IPv6SocketAddress() - socketAddress.sin6_family = numericCast(Self.family.rawValue) - socketAddress.sin6_port = port.networkOrder - socketAddress.sin6_addr = address.bytes - return try socketAddress.withUnsafePointer(body) - } - - public static func withUnsafePointer( - _ body: (UnsafeMutablePointer, UInt32) throws -> () - ) rethrows -> Self { - var socketAddress = CInterop.IPv6SocketAddress() - try socketAddress.withUnsafeMutablePointer(body) - return Self.init( - address: IPv6Address(socketAddress.sin6_addr), - port: socketAddress.sin6_port.networkOrder - ) - } -} diff --git a/Sources/System/Socket/SocketProtocol.swift b/Sources/System/Socket/SocketProtocol.swift index 02fb3aa5..d8eb870d 100644 --- a/Sources/System/Socket/SocketProtocol.swift +++ b/Sources/System/Socket/SocketProtocol.swift @@ -34,61 +34,3 @@ public enum UnixProtocol: Int32, Codable, SocketProtocol { } } } - -/// IPv4 Protocol Family -public enum IPv4Protocol: Int32, Codable, SocketProtocol { - - case raw - case tcp - case udp - - @_alwaysEmitIntoClient - public static var family: SocketAddressFamily { .ipv4 } - - @_alwaysEmitIntoClient - public var type: SocketType { - switch self { - case .raw: return .raw - case .tcp: return .stream - case .udp: return .datagram - } - } - - @_alwaysEmitIntoClient - public var rawValue: Int32 { - switch self { - case .raw: return _IPPROTO_RAW - case .tcp: return _IPPROTO_TCP - case .udp: return _IPPROTO_UDP - } - } -} - -/// IPv6 Protocol Family -public enum IPv6Protocol: Int32, Codable, SocketProtocol { - - case raw - case tcp - case udp - - @_alwaysEmitIntoClient - public static var family: SocketAddressFamily { .ipv6 } - - @_alwaysEmitIntoClient - public var type: SocketType { - switch self { - case .raw: return .raw - case .tcp: return .stream - case .udp: return .datagram - } - } - - @_alwaysEmitIntoClient - public var rawValue: Int32 { - switch self { - case .raw: return _IPPROTO_RAW - case .tcp: return _IPPROTO_TCP - case .udp: return _IPPROTO_UDP - } - } -}