|
|
@@ -0,0 +1,313 @@
|
|
|
+/*
|
|
|
+ * Copyright 2024, gRPC Authors All rights reserved.
|
|
|
+ *
|
|
|
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
+ * you may not use this file except in compliance with the License.
|
|
|
+ * You may obtain a copy of the License at
|
|
|
+ *
|
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+ *
|
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+ * See the License for the specific language governing permissions and
|
|
|
+ * limitations under the License.
|
|
|
+ */
|
|
|
+
|
|
|
+/// An address to which a socket may connect or bind.
|
|
|
+public struct SocketAddress: Hashable, Sendable {
|
|
|
+ private enum Value: Hashable, Sendable {
|
|
|
+ case ipv4(IPv4)
|
|
|
+ case ipv6(IPv6)
|
|
|
+ case unix(UnixDomainSocket)
|
|
|
+ case vsock(VirtualSocket)
|
|
|
+ }
|
|
|
+
|
|
|
+ private var value: Value
|
|
|
+ private init(_ value: Value) {
|
|
|
+ self.value = value
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns the address as an IPv4 address, if possible.
|
|
|
+ public var ipv4: IPv4? {
|
|
|
+ switch self.value {
|
|
|
+ case .ipv4(let address):
|
|
|
+ return address
|
|
|
+ default:
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns the address as an IPv6 address, if possible.
|
|
|
+ public var ipv6: IPv6? {
|
|
|
+ switch self.value {
|
|
|
+ case .ipv6(let address):
|
|
|
+ return address
|
|
|
+ default:
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns the address as an Unix domain socket address, if possible.
|
|
|
+ public var unixDomainSocket: UnixDomainSocket? {
|
|
|
+ switch self.value {
|
|
|
+ case .unix(let address):
|
|
|
+ return address
|
|
|
+ default:
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns the address as an VSOCK address, if possible.
|
|
|
+ public var virtualSocket: VirtualSocket? {
|
|
|
+ switch self.value {
|
|
|
+ case .vsock(let address):
|
|
|
+ return address
|
|
|
+ default:
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension SocketAddress {
|
|
|
+ /// Creates a socket address by wrapping a ``SocketAddress/IPv4-swift.struct``.
|
|
|
+ public static func ipv4(_ address: IPv4) -> Self {
|
|
|
+ return Self(.ipv4(address))
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Creates a socket address by wrapping a ``SocketAddress/IPv6-swift.struct``.
|
|
|
+ public static func ipv6(_ address: IPv6) -> Self {
|
|
|
+ return Self(.ipv6(address))
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Creates a socket address by wrapping a ``SocketAddress/UnixDomainSocket-swift.struct``.
|
|
|
+ public static func unixDomainSocket(_ address: UnixDomainSocket) -> Self {
|
|
|
+ return Self(.unix(address))
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Creates a socket address by wrapping a ``SocketAddress/VirtualSocket-swift.struct``.
|
|
|
+ public static func vsock(_ address: VirtualSocket) -> Self {
|
|
|
+ return Self(.vsock(address))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension SocketAddress {
|
|
|
+ /// Creates an IPv4 socket address.
|
|
|
+ public static func ipv4(host: String, port: Int) -> Self {
|
|
|
+ return .ipv4(IPv4(host: host, port: port))
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Creates an IPv6 socket address.
|
|
|
+ public static func ipv6(host: String, port: Int) -> Self {
|
|
|
+ return .ipv6(IPv6(host: host, port: port))
|
|
|
+ }
|
|
|
+ /// Creates a Unix socket address.
|
|
|
+ public static func unixDomainSocket(path: String) -> Self {
|
|
|
+ return .unixDomainSocket(UnixDomainSocket(path: path))
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Create a Virtual Socket ('vsock') address.
|
|
|
+ public static func vsock(contextID: VirtualSocket.ContextID, port: VirtualSocket.Port) -> Self {
|
|
|
+ return .vsock(VirtualSocket(contextID: contextID, port: port))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension SocketAddress: CustomStringConvertible {
|
|
|
+ public var description: String {
|
|
|
+ switch self.value {
|
|
|
+ case .ipv4(let address):
|
|
|
+ return String(describing: address)
|
|
|
+ case .ipv6(let address):
|
|
|
+ return String(describing: address)
|
|
|
+ case .unix(let address):
|
|
|
+ return String(describing: address)
|
|
|
+ case .vsock(let address):
|
|
|
+ return String(describing: address)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension SocketAddress {
|
|
|
+ public struct IPv4: Hashable, Sendable {
|
|
|
+ /// The resolved host address.
|
|
|
+ public var host: String
|
|
|
+ /// The port to connect to.
|
|
|
+ public var port: Int
|
|
|
+
|
|
|
+ /// Creates a new IPv4 address.
|
|
|
+ ///
|
|
|
+ /// - Parameters:
|
|
|
+ /// - host: Resolved host address.
|
|
|
+ /// - port: Port to connect to.
|
|
|
+ public init(host: String, port: Int) {
|
|
|
+ self.host = host
|
|
|
+ self.port = port
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public struct IPv6: Hashable, Sendable {
|
|
|
+ /// The resolved host address.
|
|
|
+ public var host: String
|
|
|
+ /// The port to connect to.
|
|
|
+ public var port: Int
|
|
|
+
|
|
|
+ /// Creates a new IPv6 address.
|
|
|
+ ///
|
|
|
+ /// - Parameters:
|
|
|
+ /// - host: Resolved host address.
|
|
|
+ /// - port: Port to connect to.
|
|
|
+ public init(host: String, port: Int) {
|
|
|
+ self.host = host
|
|
|
+ self.port = port
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public struct UnixDomainSocket: Hashable, Sendable {
|
|
|
+ /// The path name of the Unix domain socket.
|
|
|
+ public var path: String
|
|
|
+
|
|
|
+ /// Create a new Unix domain socket address.
|
|
|
+ ///
|
|
|
+ /// - Parameter path: The path name of the Unix domain socket.
|
|
|
+ public init(path: String) {
|
|
|
+ self.path = path
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public struct VirtualSocket: Hashable, Sendable {
|
|
|
+ /// A context identifier.
|
|
|
+ ///
|
|
|
+ /// Indicates the source or destination which is either a virtual machine or the host.
|
|
|
+ public var contextID: ContextID
|
|
|
+
|
|
|
+ /// The port number.
|
|
|
+ public var port: Port
|
|
|
+
|
|
|
+ /// Create a new VSOCK address.
|
|
|
+ ///
|
|
|
+ /// - Parameters:
|
|
|
+ /// - contextID: The context ID (or 'cid') of the address.
|
|
|
+ /// - port: The port number.
|
|
|
+ public init(contextID: ContextID, port: Port) {
|
|
|
+ self.contextID = contextID
|
|
|
+ self.port = port
|
|
|
+ }
|
|
|
+
|
|
|
+ public struct Port: Hashable, Sendable, RawRepresentable, ExpressibleByIntegerLiteral {
|
|
|
+ /// The port number.
|
|
|
+ public var rawValue: UInt32
|
|
|
+
|
|
|
+ public init(rawValue: UInt32) {
|
|
|
+ self.rawValue = rawValue
|
|
|
+ }
|
|
|
+
|
|
|
+ public init(integerLiteral value: UInt32) {
|
|
|
+ self.rawValue = value
|
|
|
+ }
|
|
|
+
|
|
|
+ public init(_ value: Int) {
|
|
|
+ self.init(rawValue: UInt32(bitPattern: Int32(truncatingIfNeeded: value)))
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Used to bind to any port number.
|
|
|
+ ///
|
|
|
+ /// This is equal to `VMADDR_PORT_ANY (-1U)`.
|
|
|
+ public static var any: Self {
|
|
|
+ Self(rawValue: UInt32(bitPattern: -1))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public struct ContextID: Hashable, Sendable, RawRepresentable, ExpressibleByIntegerLiteral {
|
|
|
+ /// The context identifier.
|
|
|
+ public var rawValue: UInt32
|
|
|
+
|
|
|
+ public init(rawValue: UInt32) {
|
|
|
+ self.rawValue = rawValue
|
|
|
+ }
|
|
|
+
|
|
|
+ public init(integerLiteral value: UInt32) {
|
|
|
+ self.rawValue = value
|
|
|
+ }
|
|
|
+
|
|
|
+ public init(_ value: Int) {
|
|
|
+ self.rawValue = UInt32(bitPattern: Int32(truncatingIfNeeded: value))
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Wildcard, matches any address.
|
|
|
+ ///
|
|
|
+ /// On all platforms, using this value with `bind(2)` means "any address".
|
|
|
+ ///
|
|
|
+ /// On Darwin platforms, the man page states this can be used with `connect(2)`
|
|
|
+ /// to mean "this host".
|
|
|
+ ///
|
|
|
+ /// This is equal to `VMADDR_CID_ANY (-1U)`.
|
|
|
+ public static var any: Self {
|
|
|
+ Self(rawValue: UInt32(bitPattern: -1))
|
|
|
+ }
|
|
|
+
|
|
|
+ /// The address of the hypervisor.
|
|
|
+ ///
|
|
|
+ /// This is equal to `VMADDR_CID_HYPERVISOR (0)`.
|
|
|
+ public static var hypervisor: Self {
|
|
|
+ Self(rawValue: 0)
|
|
|
+ }
|
|
|
+
|
|
|
+ /// The address of the host.
|
|
|
+ ///
|
|
|
+ /// This is equal to `VMADDR_CID_HOST (2)`.
|
|
|
+ public static var host: Self {
|
|
|
+ Self(rawValue: 2)
|
|
|
+ }
|
|
|
+
|
|
|
+ /// The address for local communication (loopback).
|
|
|
+ ///
|
|
|
+ /// This directs packets to the same host that generated them. This is useful for testing
|
|
|
+ /// applications on a single host and for debugging.
|
|
|
+ ///
|
|
|
+ /// This is equal to `VMADDR_CID_LOCAL (1)` on platforms that define it.
|
|
|
+ ///
|
|
|
+ /// - Warning: `VMADDR_CID_LOCAL (1)` is available from Linux 5.6. Its use is unsupported on
|
|
|
+ /// other platforms.
|
|
|
+ /// - SeeAlso: https://man7.org/linux/man-pages/man7/vsock.7.html
|
|
|
+ public static var local: Self {
|
|
|
+ Self(rawValue: 1)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension SocketAddress.IPv4: CustomStringConvertible {
|
|
|
+ public var description: String {
|
|
|
+ "[ipv4]\(self.host):\(self.port)"
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension SocketAddress.IPv6: CustomStringConvertible {
|
|
|
+ public var description: String {
|
|
|
+ "[ipv6]\(self.host):\(self.port)"
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension SocketAddress.UnixDomainSocket: CustomStringConvertible {
|
|
|
+ public var description: String {
|
|
|
+ "[unix]\(self.path)"
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension SocketAddress.VirtualSocket: CustomStringConvertible {
|
|
|
+ public var description: String {
|
|
|
+ "[vsock]\(self.contextID):\(self.port)"
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension SocketAddress.VirtualSocket.ContextID: CustomStringConvertible {
|
|
|
+ public var description: String {
|
|
|
+ self == .any ? "-1" : String(describing: self.rawValue)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension SocketAddress.VirtualSocket.Port: CustomStringConvertible {
|
|
|
+ public var description: String {
|
|
|
+ self == .any ? "-1" : String(describing: self.rawValue)
|
|
|
+ }
|
|
|
+}
|