//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2018 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// import Darwin import Foundation import _SwiftNetworkOverlayShims internal extension sockaddr_in { init(_ address:in_addr, _ port: in_port_t) { self.init(sin_len: UInt8(MemoryLayout.size), sin_family: sa_family_t(AF_INET), sin_port: port, sin_addr: address, sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) } func withSockAddr(_ body: (_ sa: UnsafePointer) throws -> ReturnType) rethrows -> ReturnType { // We need to create a mutable copy of `self` so that we can pass it to `withUnsafePointer(to:_:)`. var sin = self return try withUnsafePointer(to: &sin) { try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { try body($0) } } } } internal extension sockaddr_in6 { init(_ address:in6_addr, _ port: in_port_t, flow: UInt32 = 0, scope: UInt32 = 0) { self.init(sin6_len: UInt8(MemoryLayout.size), sin6_family: sa_family_t(AF_INET6), sin6_port: port, sin6_flowinfo: flow, sin6_addr: address, sin6_scope_id: scope) } func withSockAddr(_ body: (_ sa: UnsafePointer) throws -> ReturnType) rethrows -> ReturnType { // We need to create a mutable copy of `self` so that we can pass it to `withUnsafePointer(to:_:)`. var sin6 = self return try withUnsafePointer(to: &sin6) { try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { try body($0) } } } } internal extension in_addr { init(address: UInt32) { self.init() self.s_addr = address.bigEndian } } @available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) private func getaddrinfo_numeric(_ string: String, family: Int32 = 0) -> NWEndpoint.Host? { // Determine if this string has an interface scope specified "127.0.0.1%lo0" or "fe80::1%lo0" var string = string var interface : NWInterface? = nil if let range = string.range(of: "%", options: String.CompareOptions.backwards) { interface = NWInterface(String(string[range.upperBound...])) if interface != nil { string.removeSubrange(range.lowerBound...) } } // call getaddrinfo var hints = addrinfo(ai_flags: AI_NUMERICHOST, ai_family: family, ai_socktype: SOCK_STREAM, ai_protocol: 0, ai_addrlen: 0, ai_canonname: nil, ai_addr: nil, ai_next: nil) var resolved : UnsafeMutablePointer? = nil // After this point we must ensure we free addrinfo before we return guard getaddrinfo(string, nil, &hints, &resolved) == 0, let addrinfo = resolved else { return nil } var result: NWEndpoint.Host? = nil if let sa = addrinfo.pointee.ai_addr { if sa.pointee.sa_family == AF_INET { sa.withMemoryRebound(to: sockaddr_in.self, capacity: 1, { (sin) -> Void in result = NWEndpoint.Host.ipv4(IPv4Address(sin.pointee.sin_addr, interface)) }) } else if sa.pointee.sa_family == AF_INET6 { sa.withMemoryRebound(to: sockaddr_in6.self, capacity: 1, { (sin6) -> Void in if sin6.pointee.sin6_scope_id != 0 { interface = NWInterface(Int(sin6.pointee.sin6_scope_id)) } let ipv6 = IPv6Address(sin6.pointee.sin6_addr, interface); if ipv6.isIPv4Mapped && family == AF_UNSPEC, let ipv4 = ipv6.asIPv4 { // Treat IPv4 mapped as IPv4 result = NWEndpoint.Host.ipv4(ipv4) } else { result = NWEndpoint.Host.ipv6(ipv6) } }) } } freeaddrinfo(addrinfo) return result } private func getnameinfo_numeric(address: UnsafeRawPointer) -> String { let sa = address.assumingMemoryBound(to: sockaddr.self) var result : String? = nil let maxLen = socklen_t(100) let storage = UnsafeMutablePointer.allocate(capacity: Int(maxLen)) if getnameinfo(sa, socklen_t(sa.pointee.sa_len), storage, maxLen, nil, 0, NI_NUMERICHOST) == 0 { result = String(cString: storage) } storage.deallocate() return result ?? "?" } /// An IP address @available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) public protocol IPAddress { /// Fetch the raw address as data var rawValue: Data { get } /// Create an IP address from data. The length of the data must /// match the expected length of addresses in the address family /// (four bytes for IPv4, and sixteen bytes for IPv6) init?(_ rawValue: Data, _ interface: NWInterface?) /// Create an IP address from an address literal string. /// If the string contains '%' to indicate an interface, the interface will be /// associated with the address, such as "::1%lo0" being associated with the loopback /// interface. /// This function does not perform host name to address resolution. This is the same as calling getaddrinfo /// and using AI_NUMERICHOST. init?(_ string: String) /// The interface the address is scoped to, if any. var interface: NWInterface? { get } /// Indicates if this address is loopback var isLoopback : Bool { get } /// Indicates if this address is link-local var isLinkLocal : Bool { get } /// Indicates if this address is multicast var isMulticast : Bool { get } } /// IPv4Address /// Base type to hold an IPv4 address and convert between strings and raw bytes. /// Note that an IPv4 address may be scoped to an interface. @available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) public struct IPv4Address: IPAddress, Hashable, CustomDebugStringConvertible { /// The IPv4 any address used for listening public static let any = IPv4Address(in_addr(address: INADDR_ANY), nil) /// The IPv4 broadcast address used to broadcast to all hosts public static let broadcast = IPv4Address(in_addr(address: INADDR_BROADCAST), nil) /// The IPv4 loopback address public static let loopback = IPv4Address(in_addr(address: INADDR_LOOPBACK), nil) /// The IPv4 all hosts multicast group public static let allHostsGroup = IPv4Address(in_addr(address: INADDR_ALLHOSTS_GROUP), nil) /// The IPv4 all routers multicast group public static let allRoutersGroup = IPv4Address(in_addr(address: INADDR_ALLRTRS_GROUP), nil) /// The IPv4 all reports multicast group for ICMPv3 membership reports public static let allReportsGroup = IPv4Address(in_addr(address: INADDR_ALLRPTS_GROUP), nil) /// The IPv4 multicast DNS group. (Note: Use the dns_sd APIs instead of creating your own responder/resolver) public static let mdnsGroup = IPv4Address(in_addr(address: INADDR_ALLMDNS_GROUP), nil) /// Indicates if this IPv4 address is loopback (127.0.0.1) public var isLoopback : Bool { return self == IPv4Address.loopback } /// Indicates if this IPv4 address is link-local public var isLinkLocal : Bool { let linkLocalMask: UInt32 = IN_CLASSB_NET let linkLocalCompare: UInt32 = IN_LINKLOCALNETNUM return (self.address.s_addr & linkLocalMask.bigEndian) == linkLocalCompare.bigEndian } /// Indicates if this IPv4 address is multicast public var isMulticast : Bool { let multicastMask: UInt32 = IN_CLASSD_NET let multicastCompare: UInt32 = INADDR_UNSPEC_GROUP return (self.address.s_addr & multicastMask.bigEndian) == multicastCompare.bigEndian } /// Fetch the raw address (four bytes) public var rawValue: Data { var temporary = self.address return withUnsafeBytes(of: &temporary) { (bytes) -> Data in Data(bytes) } } internal init(_ address: in_addr, _ interface: NWInterface?) { self.address = address self.interface = interface } /// Create an IPv4 address from a 4-byte data. Optionally specify an interface. /// /// - Parameter rawValue: The raw bytes of the IPv4 address, must be exactly 4 bytes or init will fail. /// - Parameter interface: An optional network interface to scope the address to. Defaults to nil. /// - Returns: An IPv4Address or nil if the Data parameter did not contain an IPv4 address. public init?(_ rawValue: Data, _ interface: NWInterface? = nil) { if rawValue.count != MemoryLayout.size { return nil } let v4 = rawValue.withUnsafeBytes { $0.load(as: in_addr.self) } self.init(v4, interface) } /// Create an IPv4 address from an address literal string. /// /// This function does not perform host name to address resolution. This is the same as calling getaddrinfo /// and using AI_NUMERICHOST. /// /// - Parameter string: An IPv4 address literal string such as "127.0.0.1", "169.254.8.8%en0". /// - Returns: An IPv4Address or nil if the string parameter did not /// contain an IPv4 address literal. public init?(_ string: String) { guard let result = getaddrinfo_numeric(string, family: AF_INET) else { return nil } guard case .ipv4(let address) = result else { return nil } self = address } fileprivate let address : in_addr /// The interface the address is scoped to, if any. public let interface: NWInterface? // Hashable public static func == (lhs: IPv4Address, rhs: IPv4Address) -> Bool { return lhs.address.s_addr == rhs.address.s_addr && lhs.interface == rhs.interface } public func hash(into hasher: inout Hasher) { hasher.combine(self.address.s_addr) hasher.combine(self.interface) } // CustomDebugStringConvertible public var debugDescription: String { var sin = sockaddr_in(self.address, 0) let addressString = getnameinfo_numeric(address: &sin) if let interface = self.interface { return String("\(addressString)%\(interface)") } else { return addressString } } } /// IPv6Address /// Base type to hold an IPv6 address and convert between strings and raw bytes. /// Note that an IPv6 address may be scoped to an interface. @available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) public struct IPv6Address: IPAddress, Hashable, CustomDebugStringConvertible { /// IPv6 any address public static let any = IPv6Address(in6addr_any, nil) /// IPv6 broadcast address public static let broadcast = IPv6Address(in6addr_any, nil) /// IPv6 loopback address public static let loopback = IPv6Address(in6addr_loopback, nil) /// IPv6 all node local nodes multicast public static let nodeLocalNodes = IPv6Address(in6addr_nodelocal_allnodes, nil) /// IPv6 all link local nodes multicast public static let linkLocalNodes = IPv6Address(in6addr_linklocal_allnodes, nil) /// IPv6 all link local routers multicast public static let linkLocalRouters = IPv6Address(in6addr_linklocal_allrouters, nil) public enum Scope: UInt8 { case nodeLocal = 1 case linkLocal = 2 case siteLocal = 5 case organizationLocal = 8 case global = 0x0e } /// Is the Any address "::0" public var isAny : Bool { return self.address.__u6_addr.__u6_addr32.0 == 0 && self.address.__u6_addr.__u6_addr32.1 == 0 && self.address.__u6_addr.__u6_addr32.2 == 0 && self.address.__u6_addr.__u6_addr32.3 == 0 } /// Is the looback address "::1" public var isLoopback : Bool { return self.address.__u6_addr.__u6_addr32.0 == 0 && self.address.__u6_addr.__u6_addr32.1 == 0 && self.address.__u6_addr.__u6_addr32.2 == 0 && self.address.__u6_addr.__u6_addr32.3 != 0 && self.address.__u6_addr.__u6_addr32.3 == UInt32(1).bigEndian } /// Is an IPv4 compatible address public var isIPv4Compatabile : Bool { return self.address.__u6_addr.__u6_addr32.0 == 0 && self.address.__u6_addr.__u6_addr32.1 == 0 && self.address.__u6_addr.__u6_addr32.2 == 0 && self.address.__u6_addr.__u6_addr32.3 != 0 && self.address.__u6_addr.__u6_addr32.3 != UInt32(1).bigEndian } /// Is an IPv4 mapped address such as "::ffff:1.2.3.4" public var isIPv4Mapped : Bool { return self.address.__u6_addr.__u6_addr32.0 == 0 && self.address.__u6_addr.__u6_addr32.1 == 0 && self.address.__u6_addr.__u6_addr32.2 == UInt32(0x0000ffff).bigEndian } /// For IPv6 addresses that are IPv4 mapped, returns the IPv4 address /// /// - Returns: nil unless the IPv6 address was mapped or compatible, in which case the IPv4 address is /// returned. public var asIPv4 : IPv4Address? { guard self.isIPv4Mapped || self.isIPv4Compatabile else { return nil } return IPv4Address(in_addr(address: self.address.__u6_addr.__u6_addr32.3.bigEndian), self.interface) } /// Is a 6to4 IPv6 address public var is6to4 : Bool { return self.address.__u6_addr.__u6_addr16.0 == UInt16(0x2002).bigEndian } /// Is a link-local address public var isLinkLocal : Bool { return self.address.__u6_addr.__u6_addr8.0 == UInt8(0xfe) && (self.address.__u6_addr.__u6_addr8.1 & 0xc0) == 0x80 } /// Is multicast public var isMulticast : Bool { return self.address.__u6_addr.__u6_addr8.0 == 0xff } /// Returns the multicast scope public var multicastScope : IPv6Address.Scope? { if (self.isMulticast) { return IPv6Address.Scope(rawValue: self.address.__u6_addr.__u6_addr8.1 & 0x0f) } return nil } internal init(_ ip6: in6_addr, _ interface: NWInterface?) { self.address = ip6 self.interface = interface } /// Create an IPv6 from a raw 16 byte value and optional interface /// /// - Parameter rawValue: A 16 byte IPv6 address /// - Parameter interface: An optional interface the address is scoped to. Defaults to nil. /// - Returns: nil unless the raw data contained an IPv6 address public init?(_ rawValue: Data, _ interface: NWInterface? = nil) { if rawValue.count != MemoryLayout.size { return nil } let v6 = rawValue.withUnsafeBytes { $0.load(as: in6_addr.self) } self.init(v6, interface) } /// Create an IPv6 address from a string literal such as "fe80::1%lo0" or "2001:DB8::5" /// /// This function does not perform hostname resolution. This is similar to calling getaddrinfo with /// AI_NUMERICHOST. /// /// - Parameter string: An IPv6 address literal string. /// - Returns: nil unless the string contained an IPv6 literal public init?(_ string: String) { guard let result = getaddrinfo_numeric(string, family: AF_INET6) else { return nil } guard case .ipv6(let address) = result else { return nil } self = address } fileprivate let address: in6_addr /// The interface the address is scoped to, if any. public let interface: NWInterface? /// Fetch the raw address (sixteen bytes) public var rawValue: Data { var temporary = self.address return withUnsafeBytes(of: &temporary) { (bytes) -> Data in Data(bytes) } } // Hashable public static func ==(lhs: IPv6Address, rhs: IPv6Address) -> Bool { return lhs.address.__u6_addr.__u6_addr32.0 == rhs.address.__u6_addr.__u6_addr32.0 && lhs.address.__u6_addr.__u6_addr32.1 == rhs.address.__u6_addr.__u6_addr32.1 && lhs.address.__u6_addr.__u6_addr32.2 == rhs.address.__u6_addr.__u6_addr32.2 && lhs.address.__u6_addr.__u6_addr32.3 == rhs.address.__u6_addr.__u6_addr32.3 && lhs.interface == rhs.interface } public func hash(into hasher: inout Hasher) { hasher.combine(self.address.__u6_addr.__u6_addr32.0) hasher.combine(self.address.__u6_addr.__u6_addr32.1) hasher.combine(self.address.__u6_addr.__u6_addr32.2) hasher.combine(self.address.__u6_addr.__u6_addr32.3) hasher.combine(self.interface) } // CustomDebugStringConvertible public var debugDescription: String { var sin6 = sockaddr_in6(self.address, 0) let addressString = getnameinfo_numeric(address: &sin6) if let interface = self.interface { return String("\(addressString)%\(interface)") } else { return addressString } } } @available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) /// NWEndpoint represents something that can be connected to. public enum NWEndpoint: Hashable, CustomDebugStringConvertible { // Depends on non-exhaustive enum support for forward compatibility - in the event we need to add // a new case in the future // https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md /// A host port endpoint represents an endpoint defined by the host and port. case hostPort(host: NWEndpoint.Host, port: NWEndpoint.Port) /// A service endpoint represents a Bonjour service case service(name: String, type: String, domain: String, interface: NWInterface?) /// A unix endpoint represents a path that supports connections using AF_UNIX domain sockets. case unix(path: String) /// A Host is a name or address public enum Host: Hashable, CustomDebugStringConvertible, ExpressibleByStringLiteral { public typealias StringLiteralType = String public func hash(into hasher: inout Hasher) { switch self { case .name(let hostName, let hostInterface): hasher.combine(hostInterface) hasher.combine(hostName) case .ipv4(let v4Address): hasher.combine(v4Address) case .ipv6(let v6Address): hasher.combine(v6Address) } } /// A host specified as a name and optional interface scope case name(String, NWInterface?) /// A host specified as an IPv4 address case ipv4(IPv4Address) /// A host specified an an IPv6 address case ipv6(IPv6Address) public init(stringLiteral: StringLiteralType) { self.init(stringLiteral) } /// Create a host from a string. /// /// This is the preferred way to create a host. If the string is an IPv4 address literal ("198.51.100.2"), an /// IPv4 host will be created. If the string is an IPv6 address literal ("2001:DB8::2", "fe80::1%lo", etc), an IPv6 /// host will be created. If the string is an IPv4 mapped IPv6 address literal ("::ffff:198.51.100.2") an IPv4 /// host will be created. Otherwise, a named host will be created. /// /// - Parameter string: An IPv4 address literal, an IPv6 address literal, or a hostname. /// - Returns: A Host object public init(_ string: String) { if let result = getaddrinfo_numeric(string) { self = result } else { if let range = string.range(of: "%", options: String.CompareOptions.backwards), let interface = NWInterface(String(string[range.upperBound...])){ self = .name(String(string[..? = nil if getaddrinfo(nil, service, &hints, &resolved) != 0 { return nil } // Check that it didn't return NULL. guard let addrinfo = resolved else { return nil } // After this point we must ensure we free addrinfo before we return guard let sa = addrinfo.pointee.ai_addr, sa.pointee.sa_family == AF_INET6 else { freeaddrinfo(addrinfo) return nil } self.port = sa.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {sin6 in return sin6.pointee.sin6_port.bigEndian } freeaddrinfo(addrinfo) } public init(integerLiteral value: IntegerLiteralType) { self.port = value } public init?(rawValue: UInt16) { self.port = rawValue } internal init(_ value: UInt16) { self.init(integerLiteral: value) } public var debugDescription: String { return String(port) } } /// Returns the interface the endpoint is scoped to if any public var interface : NWInterface? { switch self { case .hostPort(host: let host, port: _): return host.interface case .service(name: _, type: _, domain: _, interface: let interface): return interface case .unix(_): return nil } } internal init?(_ nw: nw_endpoint_t?) { guard let nw = nw else { return nil } var interface: NWInterface? = nil if let nwinterface = nw_endpoint_copy_interface(nw) { interface = NWInterface(nwinterface) } if nw_endpoint_get_type(nw) == Network.nw_endpoint_type_host { let host = NWEndpoint.Host.name(String(cString: nw_endpoint_get_hostname(nw)), interface) self = .hostPort(host: host, port: NWEndpoint.Port(nw_endpoint_get_port(nw))) } else if nw_endpoint_get_type(nw) == Network.nw_endpoint_type_address { let port = NWEndpoint.Port(nw_endpoint_get_port(nw)) let address = nw_endpoint_get_address(nw) if address.pointee.sa_family == AF_INET && address.pointee.sa_len == MemoryLayout.size { let host = address.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { (sin: UnsafePointer) -> NWEndpoint.Host in return NWEndpoint.Host.ipv4(IPv4Address(sin.pointee.sin_addr, interface)) } self = .hostPort(host: host, port: port) } else if address.pointee.sa_family == AF_INET6 && address.pointee.sa_len == MemoryLayout.size { let host = address.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) { (sin6) -> NWEndpoint.Host in if interface == nil && sin6.pointee.sin6_scope_id != 0 { interface = NWInterface(Int(sin6.pointee.sin6_scope_id)) } return NWEndpoint.Host.ipv6(IPv6Address(sin6.pointee.sin6_addr, interface)) } self = .hostPort(host: host, port: port) } else if address.pointee.sa_family == AF_UNIX { // sockaddr_un is very difficult to deal with in swift. Fortunately, nw_endpoint_copy_address_string // already does exactly what we need. let path = nw_endpoint_copy_address_string(nw) self = .unix(path: String(cString: path)) path.deallocate() } else { return nil } } else if nw_endpoint_get_type(nw) == Network.nw_endpoint_type_bonjour_service { self = .service(name: String(cString: nw_endpoint_get_bonjour_service_name(nw)), type: String(cString: nw_endpoint_get_bonjour_service_type(nw)), domain: String(cString: nw_endpoint_get_bonjour_service_domain(nw)), interface: interface) } else { return nil } } public func hash(into hasher: inout Hasher) { switch self { case .hostPort(host: let host, port: let port): hasher.combine(host) hasher.combine(port) case .service(name: let name, type: let type, domain: let domain, interface: let interface): hasher.combine(name) hasher.combine(type) hasher.combine(domain) hasher.combine(interface) case .unix(let path): hasher.combine(path) } } public var debugDescription: String { switch self { case .hostPort(host: let host, port: let port): var separator = ":" if case .ipv6 = host { separator = "." } return String("\(host)\(separator)\(port)") case .service(name: let name, type: let type, domain: let domain, interface: let interface): if let interface = interface { return String("\(name).\(type)\(domain)%\(interface)") } return String("\(name).\(type)\(domain)") case .unix(let path): return path } } internal var nw: nw_endpoint_t? { var endpoint: nw_endpoint_t? = nil var interface: NWInterface? = nil switch self { case .hostPort(host: let host, port: let port): switch host { case .ipv4(let ipv4): let sin = sockaddr_in(ipv4.address, port.port.bigEndian) endpoint = sin.withSockAddr { (sa) -> nw_endpoint_t in nw_endpoint_create_address(sa) } interface = ipv4.interface case .ipv6(let ipv6): let sin6 = sockaddr_in6(ipv6.address, port.port.bigEndian) endpoint = sin6.withSockAddr { (sa) -> nw_endpoint_t? in nw_endpoint_create_address(sa) } interface = ipv6.interface case .name(let host, let hostInterface): endpoint = nw_endpoint_create_host(host, port.debugDescription) interface = hostInterface } case .service(name: let name, type: let type, domain: let domain, interface: let bonjourInterface): endpoint = nw_endpoint_create_bonjour_service(name, type, domain) interface = bonjourInterface case .unix(let path): endpoint = nw_endpoint_create_unix(path) } if interface != nil && endpoint != nil { nw_endpoint_set_interface(endpoint!, interface!.nw) } return endpoint } }