729 lines
30 KiB
Swift
729 lines
30 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the SwiftNIO open source project
|
|
//
|
|
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#if os(Windows)
|
|
import ucrt
|
|
|
|
import let WinSDK.AF_INET
|
|
import let WinSDK.AF_INET6
|
|
|
|
import let WinSDK.INET_ADDRSTRLEN
|
|
import let WinSDK.INET6_ADDRSTRLEN
|
|
|
|
import func WinSDK.FreeAddrInfoW
|
|
import func WinSDK.GetAddrInfoW
|
|
|
|
import struct WinSDK.ADDRESS_FAMILY
|
|
import struct WinSDK.ADDRINFOW
|
|
import struct WinSDK.IN_ADDR
|
|
import struct WinSDK.IN6_ADDR
|
|
|
|
import struct WinSDK.sockaddr
|
|
import struct WinSDK.sockaddr_in
|
|
import struct WinSDK.sockaddr_in6
|
|
import struct WinSDK.sockaddr_storage
|
|
import struct WinSDK.sockaddr_un
|
|
|
|
import typealias WinSDK.u_short
|
|
|
|
fileprivate typealias in_addr = WinSDK.IN_ADDR
|
|
fileprivate typealias in6_addr = WinSDK.IN6_ADDR
|
|
fileprivate typealias in_port_t = WinSDK.u_short
|
|
fileprivate typealias sa_family_t = WinSDK.ADDRESS_FAMILY
|
|
#elseif os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
|
import Darwin
|
|
#elseif os(Linux) || os(FreeBSD) || os(Android)
|
|
import Glibc
|
|
import CNIOLinux
|
|
#endif
|
|
|
|
/// Special `Error` that may be thrown if we fail to create a `SocketAddress`.
|
|
public enum SocketAddressError: Error {
|
|
/// The host is unknown (could not be resolved).
|
|
case unknown(host: String, port: Int)
|
|
/// The requested `SocketAddress` is not supported.
|
|
case unsupported
|
|
/// The requested UDS path is too long.
|
|
case unixDomainSocketPathTooLong
|
|
/// Unable to parse a given IP string
|
|
case failedToParseIPString(String)
|
|
}
|
|
|
|
extension SocketAddressError {
|
|
/// Unable to parse a given IP ByteBuffer
|
|
public struct FailedToParseIPByteBuffer: Error, Hashable {
|
|
public var address: ByteBuffer
|
|
|
|
public init(address: ByteBuffer) {
|
|
self.address = address
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Represent a socket address to which we may want to connect or bind.
|
|
public enum SocketAddress: CustomStringConvertible, Sendable {
|
|
|
|
/// A single IPv4 address for `SocketAddress`.
|
|
public struct IPv4Address {
|
|
private let _storage: Box<(address: sockaddr_in, host: String)>
|
|
|
|
/// The libc socket address for an IPv4 address.
|
|
public var address: sockaddr_in { return _storage.value.address }
|
|
|
|
/// The host this address is for, if known.
|
|
public var host: String { return _storage.value.host }
|
|
|
|
fileprivate init(address: sockaddr_in, host: String) {
|
|
self._storage = Box((address: address, host: host))
|
|
}
|
|
}
|
|
|
|
/// A single IPv6 address for `SocketAddress`.
|
|
public struct IPv6Address {
|
|
private let _storage: Box<(address: sockaddr_in6, host: String)>
|
|
|
|
/// The libc socket address for an IPv6 address.
|
|
public var address: sockaddr_in6 { return _storage.value.address }
|
|
|
|
/// The host this address is for, if known.
|
|
public var host: String { return _storage.value.host }
|
|
|
|
fileprivate init(address: sockaddr_in6, host: String) {
|
|
self._storage = Box((address: address, host: host))
|
|
}
|
|
}
|
|
|
|
/// A single Unix socket address for `SocketAddress`.
|
|
public struct UnixSocketAddress: Sendable {
|
|
private let _storage: Box<sockaddr_un>
|
|
|
|
/// The libc socket address for a Unix Domain Socket.
|
|
public var address: sockaddr_un { return _storage.value }
|
|
|
|
fileprivate init(address: sockaddr_un) {
|
|
self._storage = Box(address)
|
|
}
|
|
}
|
|
|
|
/// An IPv4 `SocketAddress`.
|
|
case v4(IPv4Address)
|
|
|
|
/// An IPv6 `SocketAddress`.
|
|
case v6(IPv6Address)
|
|
|
|
/// An UNIX Domain `SocketAddress`.
|
|
case unixDomainSocket(UnixSocketAddress)
|
|
|
|
/// A human-readable description of this `SocketAddress`. Mostly useful for logging.
|
|
public var description: String {
|
|
let addressString: String
|
|
let port: String
|
|
let host: String?
|
|
let type: String
|
|
switch self {
|
|
case .v4(let addr):
|
|
host = addr.host.isEmpty ? nil : addr.host
|
|
type = "IPv4"
|
|
var mutAddr = addr.address.sin_addr
|
|
// this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC)
|
|
addressString = try! descriptionForAddress(family: .inet, bytes: &mutAddr, length: Int(INET_ADDRSTRLEN))
|
|
|
|
port = "\(self.port!)"
|
|
case .v6(let addr):
|
|
host = addr.host.isEmpty ? nil : addr.host
|
|
type = "IPv6"
|
|
var mutAddr = addr.address.sin6_addr
|
|
// this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC)
|
|
addressString = try! descriptionForAddress(family: .inet6, bytes: &mutAddr, length: Int(INET6_ADDRSTRLEN))
|
|
|
|
port = "\(self.port!)"
|
|
case .unixDomainSocket(_):
|
|
host = nil
|
|
type = "UDS"
|
|
return "[\(type)]\(self.pathname ?? "")"
|
|
}
|
|
|
|
return "[\(type)]\(host.map { "\($0)/\(addressString):" } ?? "\(addressString):")\(port)"
|
|
}
|
|
|
|
@available(*, deprecated, renamed: "SocketAddress.protocol")
|
|
public var protocolFamily: Int32 {
|
|
return Int32(self.protocol.rawValue)
|
|
}
|
|
|
|
/// Returns the protocol family as defined in `man 2 socket` of this `SocketAddress`.
|
|
public var `protocol`: NIOBSDSocket.ProtocolFamily {
|
|
switch self {
|
|
case .v4:
|
|
return .inet
|
|
case .v6:
|
|
return .inet6
|
|
case .unixDomainSocket:
|
|
return .unix
|
|
}
|
|
}
|
|
|
|
/// Get the IP address as a string
|
|
public var ipAddress: String? {
|
|
switch self {
|
|
case .v4(let addr):
|
|
var mutAddr = addr.address.sin_addr
|
|
// this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC)
|
|
return try! descriptionForAddress(family: .inet, bytes: &mutAddr, length: Int(INET_ADDRSTRLEN))
|
|
case .v6(let addr):
|
|
var mutAddr = addr.address.sin6_addr
|
|
// this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC)
|
|
return try! descriptionForAddress(family: .inet6, bytes: &mutAddr, length: Int(INET6_ADDRSTRLEN))
|
|
case .unixDomainSocket(_):
|
|
return nil
|
|
}
|
|
}
|
|
|
|
/// Get and set the port associated with the address, if defined.
|
|
/// When setting to `nil` the port will default to `0` for compatible sockets. The rationale for this is that both `nil` and `0` can
|
|
/// be interpreted as "no preference".
|
|
/// Setting a non-nil value for a unix domain socket is invalid and will result in a fatal error.
|
|
public var port: Int? {
|
|
get {
|
|
switch self {
|
|
case .v4(let addr):
|
|
// looks odd but we need to first convert the endianness as `in_port_t` and then make the result an `Int`.
|
|
return Int(in_port_t(bigEndian: addr.address.sin_port))
|
|
case .v6(let addr):
|
|
// looks odd but we need to first convert the endianness as `in_port_t` and then make the result an `Int`.
|
|
return Int(in_port_t(bigEndian: addr.address.sin6_port))
|
|
case .unixDomainSocket:
|
|
return nil
|
|
}
|
|
}
|
|
set {
|
|
switch self {
|
|
case .v4(let addr):
|
|
var mutAddr = addr.address
|
|
mutAddr.sin_port = in_port_t(newValue ?? 0).bigEndian
|
|
self = .v4(.init(address: mutAddr, host: addr.host))
|
|
case .v6(let addr):
|
|
var mutAddr = addr.address
|
|
mutAddr.sin6_port = in_port_t(newValue ?? 0).bigEndian
|
|
self = .v6(.init(address: mutAddr, host: addr.host))
|
|
case .unixDomainSocket:
|
|
precondition(newValue == nil, "attempting to set a non-nil value to a unix socket is not valid")
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get the pathname of a UNIX domain socket as a string
|
|
public var pathname: String? {
|
|
switch self {
|
|
case .v4:
|
|
return nil
|
|
case .v6:
|
|
return nil
|
|
case .unixDomainSocket(let addr):
|
|
// This is a static assert that exists just to verify the safety of the assumption below.
|
|
assert(Swift.type(of: addr.address.sun_path.0) == CChar.self)
|
|
let pathname: String = withUnsafePointer(to: addr.address.sun_path) { ptr in
|
|
// Homogeneous tuples are always implicitly also bound to their element type, so this assumption below is safe.
|
|
let charPtr = UnsafeRawPointer(ptr).assumingMemoryBound(to: CChar.self)
|
|
return String(cString: charPtr)
|
|
}
|
|
return pathname
|
|
}
|
|
}
|
|
|
|
/// Calls the given function with a pointer to a `sockaddr` structure and the associated size
|
|
/// of that structure.
|
|
public func withSockAddr<T>(_ body: (UnsafePointer<sockaddr>, Int) throws -> T) rethrows -> T {
|
|
switch self {
|
|
case .v4(let addr):
|
|
return try addr.address.withSockAddr({ try body($0, $1) })
|
|
case .v6(let addr):
|
|
return try addr.address.withSockAddr({ try body($0, $1) })
|
|
case .unixDomainSocket(let addr):
|
|
return try addr.address.withSockAddr({ try body($0, $1) })
|
|
}
|
|
}
|
|
|
|
/// Creates a new IPv4 `SocketAddress`.
|
|
///
|
|
/// - parameters:
|
|
/// - addr: the `sockaddr_in` that holds the ipaddress and port.
|
|
/// - host: the hostname that resolved to the ipaddress.
|
|
public init(_ addr: sockaddr_in, host: String) {
|
|
self = .v4(.init(address: addr, host: host))
|
|
}
|
|
|
|
/// Creates a new IPv6 `SocketAddress`.
|
|
///
|
|
/// - parameters:
|
|
/// - addr: the `sockaddr_in` that holds the ipaddress and port.
|
|
/// - host: the hostname that resolved to the ipaddress.
|
|
public init(_ addr: sockaddr_in6, host: String) {
|
|
self = .v6(.init(address: addr, host: host))
|
|
}
|
|
|
|
/// Creates a new IPv4 `SocketAddress`.
|
|
///
|
|
/// - parameters:
|
|
/// - addr: the `sockaddr_in` that holds the ipaddress and port.
|
|
public init(_ addr: sockaddr_in) {
|
|
self = .v4(.init(address: addr, host: addr.addressDescription()))
|
|
}
|
|
|
|
/// Creates a new IPv6 `SocketAddress`.
|
|
///
|
|
/// - parameters:
|
|
/// - addr: the `sockaddr_in` that holds the ipaddress and port.
|
|
public init(_ addr: sockaddr_in6) {
|
|
self = .v6(.init(address: addr, host: addr.addressDescription()))
|
|
}
|
|
|
|
/// Creates a new Unix Domain Socket `SocketAddress`.
|
|
///
|
|
/// - parameters:
|
|
/// - addr: the `sockaddr_un` that holds the socket path.
|
|
public init(_ addr: sockaddr_un) {
|
|
self = .unixDomainSocket(.init(address: addr))
|
|
}
|
|
|
|
/// Creates a new UDS `SocketAddress`.
|
|
///
|
|
/// - parameters:
|
|
/// - path: the path to use for the `SocketAddress`.
|
|
/// - returns: the `SocketAddress` for the given path.
|
|
/// - throws: may throw `SocketAddressError.unixDomainSocketPathTooLong` if the path is too long.
|
|
public init(unixDomainSocketPath: String) throws {
|
|
guard unixDomainSocketPath.utf8.count <= 103 else {
|
|
throw SocketAddressError.unixDomainSocketPathTooLong
|
|
}
|
|
|
|
let pathBytes = unixDomainSocketPath.utf8 + [0]
|
|
|
|
var addr = sockaddr_un()
|
|
addr.sun_family = sa_family_t(NIOBSDSocket.AddressFamily.unix.rawValue)
|
|
|
|
pathBytes.withUnsafeBytes { srcBuffer in
|
|
withUnsafeMutableBytes(of: &addr.sun_path) { dstPtr in
|
|
dstPtr.copyMemory(from: srcBuffer)
|
|
}
|
|
}
|
|
|
|
self = .unixDomainSocket(.init(address: addr))
|
|
}
|
|
|
|
/// Create a new `SocketAddress` for an IP address in string form.
|
|
///
|
|
/// - parameters:
|
|
/// - string: The IP address, in string form.
|
|
/// - port: The target port.
|
|
/// - returns: the `SocketAddress` corresponding to this string and port combination.
|
|
/// - throws: may throw `SocketAddressError.failedToParseIPString` if the IP address cannot be parsed.
|
|
public init(ipAddress: String, port: Int) throws {
|
|
self = try ipAddress.withCString {
|
|
do {
|
|
var ipv4Addr = in_addr()
|
|
try NIOBSDSocket.inet_pton(addressFamily: .inet, addressDescription: $0, address: &ipv4Addr)
|
|
|
|
var addr = sockaddr_in()
|
|
addr.sin_family = sa_family_t(NIOBSDSocket.AddressFamily.inet.rawValue)
|
|
addr.sin_port = in_port_t(port).bigEndian
|
|
addr.sin_addr = ipv4Addr
|
|
|
|
return .v4(.init(address: addr, host: ""))
|
|
} catch {
|
|
// If `inet_pton` fails as an IPv4 address, we will try as an
|
|
// IPv6 address.
|
|
}
|
|
|
|
do {
|
|
var ipv6Addr = in6_addr()
|
|
try NIOBSDSocket.inet_pton(addressFamily: .inet6, addressDescription: $0, address: &ipv6Addr)
|
|
|
|
var addr = sockaddr_in6()
|
|
addr.sin6_family = sa_family_t(NIOBSDSocket.AddressFamily.inet6.rawValue)
|
|
addr.sin6_port = in_port_t(port).bigEndian
|
|
addr.sin6_flowinfo = 0
|
|
addr.sin6_addr = ipv6Addr
|
|
addr.sin6_scope_id = 0
|
|
return .v6(.init(address: addr, host: ""))
|
|
} catch {
|
|
// If `inet_pton` fails as an IPv6 address (and has failed as an
|
|
// IPv4 address above), we will throw an error below.
|
|
}
|
|
|
|
throw SocketAddressError.failedToParseIPString(ipAddress)
|
|
}
|
|
}
|
|
|
|
/// Create a new `SocketAddress` for an IP address in ByteBuffer form.
|
|
///
|
|
/// - parameters:
|
|
/// - packedIPAddress: The IP address, in ByteBuffer form.
|
|
/// - port: The target port.
|
|
/// - returns: the `SocketAddress` corresponding to this string and port combination.
|
|
/// - throws: may throw `SocketAddressError.failedToParseIPByteBuffer` if the IP address cannot be parsed.
|
|
public init(packedIPAddress: ByteBuffer, port: Int) throws {
|
|
let packed = packedIPAddress.readableBytesView
|
|
|
|
switch packedIPAddress.readableBytes {
|
|
case 4:
|
|
var ipv4Addr = sockaddr_in()
|
|
ipv4Addr.sin_family = sa_family_t(AF_INET)
|
|
ipv4Addr.sin_port = in_port_t(port).bigEndian
|
|
withUnsafeMutableBytes(of: &ipv4Addr.sin_addr) { $0.copyBytes(from: packed) }
|
|
self = .v4(.init(address: ipv4Addr, host: ""))
|
|
case 16:
|
|
var ipv6Addr = sockaddr_in6()
|
|
ipv6Addr.sin6_family = sa_family_t(AF_INET6)
|
|
ipv6Addr.sin6_port = in_port_t(port).bigEndian
|
|
withUnsafeMutableBytes(of: &ipv6Addr.sin6_addr) { $0.copyBytes(from: packed) }
|
|
self = .v6(.init(address: ipv6Addr, host: ""))
|
|
default:
|
|
throw SocketAddressError.FailedToParseIPByteBuffer(address: packedIPAddress)
|
|
}
|
|
}
|
|
|
|
/// Creates a new `SocketAddress` corresponding to the netmask for a subnet prefix.
|
|
///
|
|
/// As an example, consider the subnet "127.0.0.1/8". The "subnet prefix" is "8", and the corresponding netmask is "255.0.0.0".
|
|
/// This initializer will produce a `SocketAddress` that contains "255.0.0.0".
|
|
///
|
|
/// - parameters:
|
|
/// - prefix: The prefix of the subnet.
|
|
/// - returns: A `SocketAddress` containing the associated netmask.
|
|
internal init(ipv4MaskForPrefix prefix: Int) {
|
|
precondition((0...32).contains(prefix))
|
|
|
|
let packedAddress = (UInt32(0xFFFFFFFF) << (32 - prefix)).bigEndian
|
|
var ipv4Addr = sockaddr_in()
|
|
ipv4Addr.sin_family = sa_family_t(AF_INET)
|
|
ipv4Addr.sin_port = 0
|
|
withUnsafeMutableBytes(of: &ipv4Addr.sin_addr) { $0.storeBytes(of: packedAddress, as: UInt32.self) }
|
|
self = .v4(.init(address: ipv4Addr, host: ""))
|
|
}
|
|
|
|
/// Creates a new `SocketAddress` corresponding to the netmask for a subnet prefix.
|
|
///
|
|
/// As an example, consider the subnet "fe80::/10". The "subnet prefix" is "10", and the corresponding netmask is "ff30::".
|
|
/// This initializer will produce a `SocketAddress` that contains "ff30::".
|
|
///
|
|
/// - parameters:
|
|
/// - prefix: The prefix of the subnet.
|
|
/// - returns: A `SocketAddress` containing the associated netmask.
|
|
internal init(ipv6MaskForPrefix prefix: Int) {
|
|
precondition((0...128).contains(prefix))
|
|
|
|
// This defends against the possibility of a greater-than-/64 subnet, which would produce a negative shift
|
|
// operand which is absolutely not what we want.
|
|
let highShift = min(prefix, 64)
|
|
let packedAddressHigh = (UInt64(0xFFFFFFFFFFFFFFFF) << (64 - highShift)).bigEndian
|
|
|
|
let packedAddressLow = (UInt64(0xFFFFFFFFFFFFFFFF) << (128 - prefix)).bigEndian
|
|
let packedAddress = (packedAddressHigh, packedAddressLow)
|
|
|
|
var ipv6Addr = sockaddr_in6()
|
|
ipv6Addr.sin6_family = sa_family_t(AF_INET6)
|
|
ipv6Addr.sin6_port = 0
|
|
withUnsafeMutableBytes(of: &ipv6Addr.sin6_addr) { $0.storeBytes(of: packedAddress, as: (UInt64, UInt64).self) }
|
|
self = .v6(.init(address: ipv6Addr, host: ""))
|
|
}
|
|
|
|
/// Creates a new `SocketAddress` for the given host (which will be resolved) and port.
|
|
///
|
|
/// - warning: This is a blocking call, so please avoid calling this from an `EventLoop`.
|
|
///
|
|
/// - parameters:
|
|
/// - host: the hostname which should be resolved.
|
|
/// - port: the port itself
|
|
/// - returns: the `SocketAddress` for the host / port pair.
|
|
/// - throws: a `SocketAddressError.unknown` if we could not resolve the `host`, or `SocketAddressError.unsupported` if the address itself is not supported (yet).
|
|
public static func makeAddressResolvingHost(_ host: String, port: Int) throws -> SocketAddress {
|
|
#if os(Windows)
|
|
return try host.withCString(encodedAs: UTF16.self) { wszHost in
|
|
return try String(port).withCString(encodedAs: UTF16.self) { wszPort in
|
|
var pResult: UnsafeMutablePointer<ADDRINFOW>?
|
|
|
|
guard GetAddrInfoW(wszHost, wszPort, nil, &pResult) == 0 else {
|
|
throw SocketAddressError.unknown(host: host, port: port)
|
|
}
|
|
|
|
defer {
|
|
FreeAddrInfoW(pResult)
|
|
}
|
|
|
|
if let pResult = pResult, let addressBytes = UnsafeRawPointer(pResult.pointee.ai_addr) {
|
|
switch pResult.pointee.ai_family {
|
|
case AF_INET:
|
|
return .v4(IPv4Address(address: addressBytes.load(as: sockaddr_in.self), host: host))
|
|
case AF_INET6:
|
|
return .v6(IPv6Address(address: addressBytes.load(as: sockaddr_in6.self), host: host))
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
throw SocketAddressError.unsupported
|
|
}
|
|
}
|
|
#else
|
|
var info: UnsafeMutablePointer<addrinfo>?
|
|
|
|
/* FIXME: this is blocking! */
|
|
if getaddrinfo(host, String(port), nil, &info) != 0 {
|
|
throw SocketAddressError.unknown(host: host, port: port)
|
|
}
|
|
|
|
defer {
|
|
if info != nil {
|
|
freeaddrinfo(info)
|
|
}
|
|
}
|
|
|
|
if let info = info, let addrPointer = info.pointee.ai_addr {
|
|
let addressBytes = UnsafeRawPointer(addrPointer)
|
|
switch NIOBSDSocket.AddressFamily(rawValue: info.pointee.ai_family) {
|
|
case .inet:
|
|
return .v4(.init(address: addressBytes.load(as: sockaddr_in.self), host: host))
|
|
case .inet6:
|
|
return .v6(.init(address: addressBytes.load(as: sockaddr_in6.self), host: host))
|
|
default:
|
|
throw SocketAddressError.unsupported
|
|
}
|
|
} else {
|
|
/* this is odd, getaddrinfo returned NULL */
|
|
throw SocketAddressError.unsupported
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// We define an extension on `SocketAddress` that gives it an elementwise equatable conformance, using
|
|
/// only the elements defined on the structure in their man pages (excluding lengths).
|
|
extension SocketAddress: Equatable {
|
|
public static func ==(lhs: SocketAddress, rhs: SocketAddress) -> Bool {
|
|
switch (lhs, rhs) {
|
|
case (.v4(let addr1), .v4(let addr2)):
|
|
#if os(Windows)
|
|
return addr1.address.sin_family == addr2.address.sin_family &&
|
|
addr1.address.sin_port == addr2.address.sin_port &&
|
|
addr1.address.sin_addr.S_un.S_addr == addr2.address.sin_addr.S_un.S_addr
|
|
#else
|
|
return addr1.address.sin_family == addr2.address.sin_family &&
|
|
addr1.address.sin_port == addr2.address.sin_port &&
|
|
addr1.address.sin_addr.s_addr == addr2.address.sin_addr.s_addr
|
|
#endif
|
|
case (.v6(let addr1), .v6(let addr2)):
|
|
guard addr1.address.sin6_family == addr2.address.sin6_family &&
|
|
addr1.address.sin6_port == addr2.address.sin6_port &&
|
|
addr1.address.sin6_flowinfo == addr2.address.sin6_flowinfo &&
|
|
addr1.address.sin6_scope_id == addr2.address.sin6_scope_id else {
|
|
return false
|
|
}
|
|
|
|
var s6addr1 = addr1.address.sin6_addr
|
|
var s6addr2 = addr2.address.sin6_addr
|
|
return memcmp(&s6addr1, &s6addr2, MemoryLayout.size(ofValue: s6addr1)) == 0
|
|
case (.unixDomainSocket(let addr1), .unixDomainSocket(let addr2)):
|
|
guard addr1.address.sun_family == addr2.address.sun_family else {
|
|
return false
|
|
}
|
|
|
|
let bufferSize = MemoryLayout.size(ofValue: addr1.address.sun_path)
|
|
|
|
|
|
// Swift implicitly binds the memory for homogeneous tuples to both the tuple type and the element type.
|
|
// This allows us to use assumingMemoryBound(to:) for managing the types. However, we add a static assertion here to validate
|
|
// that the element type _really is_ what we're assuming it to be.
|
|
assert(Swift.type(of: addr1.address.sun_path.0) == CChar.self)
|
|
assert(Swift.type(of: addr2.address.sun_path.0) == CChar.self)
|
|
return withUnsafePointer(to: addr1.address.sun_path) { sunpath1 in
|
|
return withUnsafePointer(to: addr2.address.sun_path) { sunpath2 in
|
|
let typedSunpath1 = UnsafeRawPointer(sunpath1).assumingMemoryBound(to: CChar.self)
|
|
let typedSunpath2 = UnsafeRawPointer(sunpath2).assumingMemoryBound(to: CChar.self)
|
|
return strncmp(typedSunpath1, typedSunpath2, bufferSize) == 0
|
|
}
|
|
}
|
|
case (.v4, _), (.v6, _), (.unixDomainSocket, _):
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
extension SocketAddress.IPv4Address: Sendable {}
|
|
extension SocketAddress.IPv6Address: Sendable {}
|
|
|
|
/// We define an extension on `SocketAddress` that gives it an elementwise hashable conformance, using
|
|
/// only the elements defined on the structure in their man pages (excluding lengths).
|
|
extension SocketAddress: Hashable {
|
|
public func hash(into hasher: inout Hasher) {
|
|
switch self {
|
|
case .unixDomainSocket(let uds):
|
|
hasher.combine(0)
|
|
hasher.combine(uds.address.sun_family)
|
|
|
|
let pathSize = MemoryLayout.size(ofValue: uds.address.sun_path)
|
|
|
|
// Swift implicitly binds the memory of homogeneous tuples to both the tuple type and the element type.
|
|
// We can therefore use assumingMemoryBound(to:) for pointer type conversion. We add a static assert just to
|
|
// validate that we are actually right about the element type.
|
|
assert(Swift.type(of: uds.address.sun_path.0) == CChar.self)
|
|
withUnsafePointer(to: uds.address.sun_path) { pathPtr in
|
|
let typedPathPointer = UnsafeRawPointer(pathPtr).assumingMemoryBound(to: CChar.self)
|
|
let length = strnlen(typedPathPointer, pathSize)
|
|
let bytes = UnsafeRawBufferPointer(start: UnsafeRawPointer(typedPathPointer), count: length)
|
|
hasher.combine(bytes: bytes)
|
|
}
|
|
case .v4(let v4Addr):
|
|
hasher.combine(1)
|
|
hasher.combine(v4Addr.address.sin_family)
|
|
hasher.combine(v4Addr.address.sin_port)
|
|
#if os(Windows)
|
|
hasher.combine(v4Addr.address.sin_addr.S_un.S_addr)
|
|
#else
|
|
hasher.combine(v4Addr.address.sin_addr.s_addr)
|
|
#endif
|
|
case .v6(let v6Addr):
|
|
hasher.combine(2)
|
|
hasher.combine(v6Addr.address.sin6_family)
|
|
hasher.combine(v6Addr.address.sin6_port)
|
|
hasher.combine(v6Addr.address.sin6_flowinfo)
|
|
hasher.combine(v6Addr.address.sin6_scope_id)
|
|
withUnsafeBytes(of: v6Addr.address.sin6_addr) {
|
|
hasher.combine(bytes: $0)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
extension SocketAddress {
|
|
/// Whether this `SocketAddress` corresponds to a multicast address.
|
|
public var isMulticast: Bool {
|
|
switch self {
|
|
case .unixDomainSocket:
|
|
// No multicast on unix sockets.
|
|
return false
|
|
case .v4(let v4Addr):
|
|
// For IPv4 a multicast address is in the range 224.0.0.0/4.
|
|
// The easy way to check if this is the case is to just mask off
|
|
// the address.
|
|
#if os(Windows)
|
|
let v4WireAddress = v4Addr.address.sin_addr.S_un.S_addr
|
|
let mask = UInt32(0xF000_0000).bigEndian
|
|
let subnet = UInt32(0xE000_0000).bigEndian
|
|
#else
|
|
let v4WireAddress = v4Addr.address.sin_addr.s_addr
|
|
let mask = in_addr_t(0xF000_0000 as UInt32).bigEndian
|
|
let subnet = in_addr_t(0xE000_0000 as UInt32).bigEndian
|
|
#endif
|
|
return v4WireAddress & mask == subnet
|
|
case .v6(let v6Addr):
|
|
// For IPv6 a multicast address is in the range ff00::/8.
|
|
// Here we don't need a bitmask, as all the top bits are set,
|
|
// so we can just ask for equality on the top byte.
|
|
var v6WireAddress = v6Addr.address.sin6_addr
|
|
return withUnsafeBytes(of: &v6WireAddress) { $0[0] == 0xff }
|
|
}
|
|
}
|
|
}
|
|
|
|
protocol SockAddrProtocol {
|
|
func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R
|
|
}
|
|
|
|
/// Returns a description for the given address.
|
|
internal func descriptionForAddress(family: NIOBSDSocket.AddressFamily, bytes: UnsafeRawPointer, length byteCount: Int) throws -> String {
|
|
var addressBytes: [Int8] = Array(repeating: 0, count: byteCount)
|
|
return try addressBytes.withUnsafeMutableBufferPointer { (addressBytesPtr: inout UnsafeMutableBufferPointer<Int8>) -> String in
|
|
try NIOBSDSocket.inet_ntop(addressFamily: family, addressBytes: bytes,
|
|
addressDescription: addressBytesPtr.baseAddress!,
|
|
addressDescriptionLength: socklen_t(byteCount))
|
|
return addressBytesPtr.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: byteCount) { addressBytesPtr -> String in
|
|
String(cString: addressBytesPtr)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension sockaddr_in: SockAddrProtocol {
|
|
func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R {
|
|
return try withUnsafeBytes(of: self) { p in
|
|
try body(p.baseAddress!.assumingMemoryBound(to: sockaddr.self), p.count)
|
|
}
|
|
}
|
|
|
|
/// Returns a description of the `sockaddr_in`.
|
|
func addressDescription() -> String {
|
|
return withUnsafePointer(to: self.sin_addr) { addrPtr in
|
|
// this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC)
|
|
try! descriptionForAddress(family: .inet, bytes: addrPtr, length: Int(INET_ADDRSTRLEN))
|
|
}
|
|
}
|
|
}
|
|
|
|
extension sockaddr_in6: SockAddrProtocol {
|
|
func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R {
|
|
return try withUnsafeBytes(of: self) { p in
|
|
try body(p.baseAddress!.assumingMemoryBound(to: sockaddr.self), p.count)
|
|
}
|
|
}
|
|
|
|
/// Returns a description of the `sockaddr_in6`.
|
|
func addressDescription() -> String {
|
|
return withUnsafePointer(to: self.sin6_addr) { addrPtr in
|
|
// this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC)
|
|
try! descriptionForAddress(family: .inet6, bytes: addrPtr, length: Int(INET6_ADDRSTRLEN))
|
|
}
|
|
}
|
|
}
|
|
|
|
extension sockaddr_un: SockAddrProtocol {
|
|
func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R {
|
|
return try withUnsafeBytes(of: self) { p in
|
|
try body(p.baseAddress!.assumingMemoryBound(to: sockaddr.self), p.count)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension sockaddr_storage: SockAddrProtocol {
|
|
func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R {
|
|
return try withUnsafeBytes(of: self) { p in
|
|
try body(p.baseAddress!.assumingMemoryBound(to: sockaddr.self), p.count)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: Workarounds for SR-14268
|
|
// We need these free functions to expose our extension methods, because otherwise
|
|
// the compiler falls over when we try to access them from test code. As these functions
|
|
// exist purely to make the behaviours accessible from test code, we name them truly awfully.
|
|
func __testOnly_addressDescription(_ addr: sockaddr_in) -> String {
|
|
return addr.addressDescription()
|
|
}
|
|
|
|
func __testOnly_addressDescription(_ addr: sockaddr_in6) -> String {
|
|
return addr.addressDescription()
|
|
}
|
|
|
|
func __testOnly_withSockAddr<ReturnType>(
|
|
_ addr: sockaddr_in, _ body: (UnsafePointer<sockaddr>, Int) throws -> ReturnType
|
|
) rethrows -> ReturnType {
|
|
return try addr.withSockAddr(body)
|
|
}
|
|
|
|
func __testOnly_withSockAddr<ReturnType>(
|
|
_ addr: sockaddr_in6, _ body: (UnsafePointer<sockaddr>, Int) throws -> ReturnType
|
|
) rethrows -> ReturnType {
|
|
return try addr.withSockAddr(body)
|
|
}
|