/src/swift-nio/Sources/NIOCore/SocketAddresses.swift
Line | Count | Source (jump to first uncovered line) |
1 | | //===----------------------------------------------------------------------===// |
2 | | // |
3 | | // This source file is part of the SwiftNIO open source project |
4 | | // |
5 | | // Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors |
6 | | // Licensed under Apache License v2.0 |
7 | | // |
8 | | // See LICENSE.txt for license information |
9 | | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors |
10 | | // |
11 | | // SPDX-License-Identifier: Apache-2.0 |
12 | | // |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | #if os(Windows) |
16 | | import ucrt |
17 | | |
18 | | import let WinSDK.AF_INET |
19 | | import let WinSDK.AF_INET6 |
20 | | |
21 | | import let WinSDK.INET_ADDRSTRLEN |
22 | | import let WinSDK.INET6_ADDRSTRLEN |
23 | | |
24 | | import func WinSDK.FreeAddrInfoW |
25 | | import func WinSDK.GetAddrInfoW |
26 | | |
27 | | import struct WinSDK.ADDRESS_FAMILY |
28 | | import struct WinSDK.ADDRINFOW |
29 | | import struct WinSDK.IN_ADDR |
30 | | import struct WinSDK.IN6_ADDR |
31 | | |
32 | | import struct WinSDK.sockaddr |
33 | | import struct WinSDK.sockaddr_in |
34 | | import struct WinSDK.sockaddr_in6 |
35 | | import struct WinSDK.sockaddr_storage |
36 | | import struct WinSDK.sockaddr_un |
37 | | |
38 | | import typealias WinSDK.u_short |
39 | | |
40 | | private typealias in_addr = WinSDK.IN_ADDR |
41 | | private typealias in6_addr = WinSDK.IN6_ADDR |
42 | | private typealias in_port_t = WinSDK.u_short |
43 | | private typealias sa_family_t = WinSDK.ADDRESS_FAMILY |
44 | | #elseif canImport(Darwin) |
45 | | import Darwin |
46 | | #elseif os(Linux) || os(FreeBSD) || os(Android) |
47 | | #if canImport(Glibc) |
48 | | @preconcurrency import Glibc |
49 | | #elseif canImport(Musl) |
50 | | @preconcurrency import Musl |
51 | | #elseif canImport(Android) |
52 | | @preconcurrency import Android |
53 | | #endif |
54 | | import CNIOLinux |
55 | | #elseif canImport(WASILibc) |
56 | | @preconcurrency import WASILibc |
57 | | #else |
58 | | #error("The Socket Addresses module was unable to identify your C library.") |
59 | | #endif |
60 | | |
61 | | /// Special `Error` that may be thrown if we fail to create a `SocketAddress`. |
62 | | public enum SocketAddressError: Error, Equatable, Hashable { |
63 | | /// The host is unknown (could not be resolved). |
64 | | case unknown(host: String, port: Int) |
65 | | /// The requested `SocketAddress` is not supported. |
66 | | case unsupported |
67 | | /// The requested UDS path is too long. |
68 | | case unixDomainSocketPathTooLong |
69 | | /// Unable to parse a given IP string |
70 | | case failedToParseIPString(String) |
71 | | } |
72 | | |
73 | | extension SocketAddressError { |
74 | | /// Unable to parse a given IP ByteBuffer |
75 | | public struct FailedToParseIPByteBuffer: Error, Hashable { |
76 | | public var address: ByteBuffer |
77 | | |
78 | 0 | public init(address: ByteBuffer) { |
79 | 0 | self.address = address |
80 | 0 | } |
81 | | } |
82 | | } |
83 | | |
84 | | /// Represent a socket address to which we may want to connect or bind. |
85 | | public enum SocketAddress: CustomStringConvertible, Sendable { |
86 | | |
87 | | /// A single IPv4 address for `SocketAddress`. |
88 | | public struct IPv4Address { |
89 | | private let _storage: Box<(address: sockaddr_in, host: String)> |
90 | | |
91 | | /// The libc socket address for an IPv4 address. |
92 | 0 | public var address: sockaddr_in { _storage.value.address } |
93 | | |
94 | | /// The host this address is for, if known. |
95 | 0 | public var host: String { _storage.value.host } |
96 | | |
97 | 0 | fileprivate init(address: sockaddr_in, host: String) { |
98 | 0 | self._storage = Box((address: address, host: host)) |
99 | 0 | } |
100 | | } |
101 | | |
102 | | /// A single IPv6 address for `SocketAddress`. |
103 | | public struct IPv6Address { |
104 | | private let _storage: Box<(address: sockaddr_in6, host: String)> |
105 | | |
106 | | /// The libc socket address for an IPv6 address. |
107 | 0 | public var address: sockaddr_in6 { _storage.value.address } |
108 | | |
109 | | /// The host this address is for, if known. |
110 | 0 | public var host: String { _storage.value.host } |
111 | | |
112 | 0 | fileprivate init(address: sockaddr_in6, host: String) { |
113 | 0 | self._storage = Box((address: address, host: host)) |
114 | 0 | } |
115 | | } |
116 | | |
117 | | /// A single Unix socket address for `SocketAddress`. |
118 | | public struct UnixSocketAddress: Sendable { |
119 | | private let _storage: Box<sockaddr_un> |
120 | | |
121 | | /// The libc socket address for a Unix Domain Socket. |
122 | 0 | public var address: sockaddr_un { _storage.value } |
123 | | |
124 | 0 | fileprivate init(address: sockaddr_un) { |
125 | 0 | self._storage = Box(address) |
126 | 0 | } |
127 | | } |
128 | | |
129 | | /// An IPv4 `SocketAddress`. |
130 | | case v4(IPv4Address) |
131 | | |
132 | | /// An IPv6 `SocketAddress`. |
133 | | case v6(IPv6Address) |
134 | | |
135 | | /// An UNIX Domain `SocketAddress`. |
136 | | case unixDomainSocket(UnixSocketAddress) |
137 | | |
138 | | /// A human-readable description of this `SocketAddress`. Mostly useful for logging. |
139 | 0 | public var description: String { |
140 | 0 | let addressString: String |
141 | 0 | let port: String |
142 | 0 | let host: String? |
143 | 0 | let type: String |
144 | 0 | switch self { |
145 | 0 | case .v4(let addr): |
146 | 0 | host = addr.host.isEmpty ? nil : addr.host |
147 | 0 | type = "IPv4" |
148 | 0 | var mutAddr = addr.address.sin_addr |
149 | 0 | // this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC) |
150 | 0 | addressString = try! descriptionForAddress(family: .inet, bytes: &mutAddr, length: Int(INET_ADDRSTRLEN)) |
151 | 0 |
|
152 | 0 | port = "\(self.port!)" |
153 | 0 | case .v6(let addr): |
154 | 0 | host = addr.host.isEmpty ? nil : addr.host |
155 | 0 | type = "IPv6" |
156 | 0 | var mutAddr = addr.address.sin6_addr |
157 | 0 | // this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC) |
158 | 0 | addressString = try! descriptionForAddress(family: .inet6, bytes: &mutAddr, length: Int(INET6_ADDRSTRLEN)) |
159 | 0 |
|
160 | 0 | port = "\(self.port!)" |
161 | 0 | case .unixDomainSocket(_): |
162 | 0 | host = nil |
163 | 0 | type = "UDS" |
164 | 0 | return "[\(type)]\(self.pathname ?? "")" |
165 | 0 | } |
166 | 0 |
|
167 | 0 | return "[\(type)]\(host.map { "\($0)/\(addressString):" } ?? "\(addressString):")\(port)" |
168 | 0 | } |
169 | | |
170 | | @available(*, deprecated, renamed: "SocketAddress.protocol") |
171 | 0 | public var protocolFamily: Int32 { |
172 | 0 | Int32(self.protocol.rawValue) |
173 | 0 | } |
174 | | |
175 | | /// Returns the protocol family as defined in `man 2 socket` of this `SocketAddress`. |
176 | 0 | public var `protocol`: NIOBSDSocket.ProtocolFamily { |
177 | 0 | switch self { |
178 | 0 | case .v4: |
179 | 0 | return .inet |
180 | 0 | case .v6: |
181 | 0 | return .inet6 |
182 | 0 | case .unixDomainSocket: |
183 | 0 | #if os(WASI) |
184 | 0 | fatalError("unix domain sockets are currently not supported by WASILibc") |
185 | 0 | #else |
186 | 0 | return .unix |
187 | 0 | #endif |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | | /// Get the IP address as a string |
192 | 0 | public var ipAddress: String? { |
193 | 0 | switch self { |
194 | 0 | case .v4(let addr): |
195 | 0 | var mutAddr = addr.address.sin_addr |
196 | 0 | // this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC) |
197 | 0 | return try! descriptionForAddress(family: .inet, bytes: &mutAddr, length: Int(INET_ADDRSTRLEN)) |
198 | 0 | case .v6(let addr): |
199 | 0 | var mutAddr = addr.address.sin6_addr |
200 | 0 | // this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC) |
201 | 0 | return try! descriptionForAddress(family: .inet6, bytes: &mutAddr, length: Int(INET6_ADDRSTRLEN)) |
202 | 0 | case .unixDomainSocket(_): |
203 | 0 | return nil |
204 | 0 | } |
205 | 0 | } |
206 | | /// Get and set the port associated with the address, if defined. |
207 | | /// When setting to `nil` the port will default to `0` for compatible sockets. The rationale for this is that both `nil` and `0` can |
208 | | /// be interpreted as "no preference". |
209 | | /// Setting a non-nil value for a unix domain socket is invalid and will result in a fatal error. |
210 | | public var port: Int? { |
211 | 0 | get { |
212 | 0 | switch self { |
213 | 0 | case .v4(let addr): |
214 | 0 | // looks odd but we need to first convert the endianness as `in_port_t` and then make the result an `Int`. |
215 | 0 | return Int(in_port_t(bigEndian: addr.address.sin_port)) |
216 | 0 | case .v6(let addr): |
217 | 0 | // looks odd but we need to first convert the endianness as `in_port_t` and then make the result an `Int`. |
218 | 0 | return Int(in_port_t(bigEndian: addr.address.sin6_port)) |
219 | 0 | case .unixDomainSocket: |
220 | 0 | return nil |
221 | 0 | } |
222 | 0 | } |
223 | 0 | set { |
224 | 0 | switch self { |
225 | 0 | case .v4(let addr): |
226 | 0 | var mutAddr = addr.address |
227 | 0 | mutAddr.sin_port = in_port_t(newValue ?? 0).bigEndian |
228 | 0 | self = .v4(.init(address: mutAddr, host: addr.host)) |
229 | 0 | case .v6(let addr): |
230 | 0 | var mutAddr = addr.address |
231 | 0 | mutAddr.sin6_port = in_port_t(newValue ?? 0).bigEndian |
232 | 0 | self = .v6(.init(address: mutAddr, host: addr.host)) |
233 | 0 | case .unixDomainSocket: |
234 | 0 | precondition(newValue == nil, "attempting to set a non-nil value to a unix socket is not valid") |
235 | 0 | } |
236 | 0 | } |
237 | | } |
238 | | |
239 | | /// Get the pathname of a UNIX domain socket as a string |
240 | 0 | public var pathname: String? { |
241 | 0 | #if os(WASI) |
242 | 0 | return nil |
243 | 0 | #else |
244 | 0 | switch self { |
245 | 0 | case .v4: |
246 | 0 | return nil |
247 | 0 | case .v6: |
248 | 0 | return nil |
249 | 0 | case .unixDomainSocket(let addr): |
250 | 0 | // This is a static assert that exists just to verify the safety of the assumption below. |
251 | 0 | assert(Swift.type(of: addr.address.sun_path.0) == CChar.self) |
252 | 0 | let pathname: String = withUnsafePointer(to: addr.address.sun_path) { ptr in |
253 | 0 | // Homogeneous tuples are always implicitly also bound to their element type, so this assumption below is safe. |
254 | 0 | let charPtr = UnsafeRawPointer(ptr).assumingMemoryBound(to: CChar.self) |
255 | 0 | return String(cString: charPtr) |
256 | 0 | } |
257 | 0 | return pathname |
258 | 0 | } |
259 | 0 | #endif |
260 | 0 | } |
261 | | |
262 | | /// Calls the given function with a pointer to a `sockaddr` structure and the associated size |
263 | | /// of that structure. |
264 | 0 | public func withSockAddr<T>(_ body: (UnsafePointer<sockaddr>, Int) throws -> T) rethrows -> T { |
265 | 0 | switch self { |
266 | 0 | case .v4(let addr): |
267 | 0 | return try addr.address.withSockAddr({ try body($0, $1) }) |
268 | 0 | case .v6(let addr): |
269 | 0 | return try addr.address.withSockAddr({ try body($0, $1) }) |
270 | 0 | case .unixDomainSocket(let addr): |
271 | 0 | return try addr.address.withSockAddr({ try body($0, $1) }) |
272 | 0 | } |
273 | 0 | } |
274 | | |
275 | | /// Creates a new IPv4 `SocketAddress`. |
276 | | /// |
277 | | /// - Parameters: |
278 | | /// - addr: the `sockaddr_in` that holds the ipaddress and port. |
279 | | /// - host: the hostname that resolved to the ipaddress. |
280 | 0 | public init(_ addr: sockaddr_in, host: String) { |
281 | 0 | self = .v4(.init(address: addr, host: host)) |
282 | 0 | } |
283 | | |
284 | | /// Creates a new IPv6 `SocketAddress`. |
285 | | /// |
286 | | /// - Parameters: |
287 | | /// - addr: the `sockaddr_in` that holds the ipaddress and port. |
288 | | /// - host: the hostname that resolved to the ipaddress. |
289 | 0 | public init(_ addr: sockaddr_in6, host: String) { |
290 | 0 | self = .v6(.init(address: addr, host: host)) |
291 | 0 | } |
292 | | |
293 | | /// Creates a new IPv4 `SocketAddress`. |
294 | | /// |
295 | | /// - Parameters: |
296 | | /// - addr: the `sockaddr_in` that holds the ipaddress and port. |
297 | 0 | public init(_ addr: sockaddr_in) { |
298 | 0 | self = .v4(.init(address: addr, host: addr.addressDescription())) |
299 | 0 | } |
300 | | |
301 | | /// Creates a new IPv6 `SocketAddress`. |
302 | | /// |
303 | | /// - Parameters: |
304 | | /// - addr: the `sockaddr_in` that holds the ipaddress and port. |
305 | 0 | public init(_ addr: sockaddr_in6) { |
306 | 0 | self = .v6(.init(address: addr, host: addr.addressDescription())) |
307 | 0 | } |
308 | | |
309 | | /// Creates a new Unix Domain Socket `SocketAddress`. |
310 | | /// |
311 | | /// - Parameters: |
312 | | /// - addr: the `sockaddr_un` that holds the socket path. |
313 | 0 | public init(_ addr: sockaddr_un) { |
314 | 0 | self = .unixDomainSocket(.init(address: addr)) |
315 | 0 | } |
316 | | |
317 | | /// Creates a new UDS `SocketAddress`. |
318 | | /// |
319 | | /// - Parameters: |
320 | | /// - unixDomainSocketPath: the path to use for the `SocketAddress`. |
321 | | /// - Throws: may throw `SocketAddressError.unixDomainSocketPathTooLong` if the path is too long. |
322 | 0 | public init(unixDomainSocketPath: String) throws { |
323 | 0 | guard unixDomainSocketPath.utf8.count <= 103 else { |
324 | 0 | throw SocketAddressError.unixDomainSocketPathTooLong |
325 | 0 | } |
326 | 0 |
|
327 | 0 | let pathBytes = unixDomainSocketPath.utf8 + [0] |
328 | 0 |
|
329 | 0 | var addr = sockaddr_un() |
330 | 0 | addr.sun_family = sa_family_t(NIOBSDSocket.AddressFamily.unix.rawValue) |
331 | 0 |
|
332 | 0 | #if !os(WASI) |
333 | 0 | pathBytes.withUnsafeBytes { srcBuffer in |
334 | 0 | withUnsafeMutableBytes(of: &addr.sun_path) { dstPtr in |
335 | 0 | dstPtr.copyMemory(from: srcBuffer) |
336 | 0 | } |
337 | 0 | } |
338 | 0 | #endif |
339 | 0 |
|
340 | 0 | self = .unixDomainSocket(.init(address: addr)) |
341 | 0 | } |
342 | | |
343 | | /// Create a new `SocketAddress` for an IP address in string form. |
344 | | /// |
345 | | /// - Parameters: |
346 | | /// - ipAddress: The IP address, in string form. |
347 | | /// - port: The target port. |
348 | | /// - Throws: may throw `SocketAddressError.failedToParseIPString` if the IP address cannot be parsed. |
349 | 0 | public init(ipAddress: String, port: Int) throws { |
350 | 0 | self = try ipAddress.withCString { |
351 | 0 | do { |
352 | 0 | var ipv4Addr = in_addr() |
353 | 0 | try NIOBSDSocket.inet_pton(addressFamily: .inet, addressDescription: $0, address: &ipv4Addr) |
354 | 0 |
|
355 | 0 | var addr = sockaddr_in() |
356 | 0 | addr.sin_family = sa_family_t(NIOBSDSocket.AddressFamily.inet.rawValue) |
357 | 0 | addr.sin_port = in_port_t(port).bigEndian |
358 | 0 | addr.sin_addr = ipv4Addr |
359 | 0 |
|
360 | 0 | return .v4(.init(address: addr, host: "")) |
361 | 0 | } catch { |
362 | 0 | // If `inet_pton` fails as an IPv4 address, we will try as an |
363 | 0 | // IPv6 address. |
364 | 0 | } |
365 | 0 |
|
366 | 0 | do { |
367 | 0 | var ipv6Addr = in6_addr() |
368 | 0 | try NIOBSDSocket.inet_pton(addressFamily: .inet6, addressDescription: $0, address: &ipv6Addr) |
369 | 0 |
|
370 | 0 | var addr = sockaddr_in6() |
371 | 0 | addr.sin6_family = sa_family_t(NIOBSDSocket.AddressFamily.inet6.rawValue) |
372 | 0 | addr.sin6_port = in_port_t(port).bigEndian |
373 | 0 | addr.sin6_flowinfo = 0 |
374 | 0 | addr.sin6_addr = ipv6Addr |
375 | 0 | addr.sin6_scope_id = 0 |
376 | 0 | return .v6(.init(address: addr, host: "")) |
377 | 0 | } catch { |
378 | 0 | // If `inet_pton` fails as an IPv6 address (and has failed as an |
379 | 0 | // IPv4 address above), we will throw an error below. |
380 | 0 | } |
381 | 0 |
|
382 | 0 | throw SocketAddressError.failedToParseIPString(ipAddress) |
383 | 0 | } |
384 | 0 | } |
385 | | |
386 | | /// Create a new `SocketAddress` for an IP address in ByteBuffer form. |
387 | | /// |
388 | | /// - Parameters: |
389 | | /// - packedIPAddress: The IP address, in ByteBuffer form. |
390 | | /// - port: The target port. |
391 | | /// - Throws: may throw `SocketAddressError.failedToParseIPByteBuffer` if the IP address cannot be parsed. |
392 | 0 | public init(packedIPAddress: ByteBuffer, port: Int) throws { |
393 | 0 | let packed = packedIPAddress.readableBytesView |
394 | 0 |
|
395 | 0 | switch packedIPAddress.readableBytes { |
396 | 0 | case 4: |
397 | 0 | var ipv4Addr = sockaddr_in() |
398 | 0 | ipv4Addr.sin_family = sa_family_t(AF_INET) |
399 | 0 | ipv4Addr.sin_port = in_port_t(port).bigEndian |
400 | 0 | withUnsafeMutableBytes(of: &ipv4Addr.sin_addr) { $0.copyBytes(from: packed) } |
401 | 0 | self = .v4(.init(address: ipv4Addr, host: "")) |
402 | 0 | case 16: |
403 | 0 | var ipv6Addr = sockaddr_in6() |
404 | 0 | ipv6Addr.sin6_family = sa_family_t(AF_INET6) |
405 | 0 | ipv6Addr.sin6_port = in_port_t(port).bigEndian |
406 | 0 | withUnsafeMutableBytes(of: &ipv6Addr.sin6_addr) { $0.copyBytes(from: packed) } |
407 | 0 | self = .v6(.init(address: ipv6Addr, host: "")) |
408 | 0 | default: |
409 | 0 | throw SocketAddressError.FailedToParseIPByteBuffer(address: packedIPAddress) |
410 | 0 | } |
411 | 0 | } |
412 | | |
413 | | /// Creates a new `SocketAddress` corresponding to the netmask for a subnet prefix. |
414 | | /// |
415 | | /// 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". |
416 | | /// This initializer will produce a `SocketAddress` that contains "255.0.0.0". |
417 | | /// |
418 | | /// - Parameters: |
419 | | /// - prefix: The prefix of the subnet. |
420 | | /// - Returns: A `SocketAddress` containing the associated netmask. |
421 | 0 | internal init(ipv4MaskForPrefix prefix: Int) { |
422 | 0 | precondition((0...32).contains(prefix)) |
423 | 0 |
|
424 | 0 | let packedAddress = (UInt32(0xFFFF_FFFF) << (32 - prefix)).bigEndian |
425 | 0 | var ipv4Addr = sockaddr_in() |
426 | 0 | ipv4Addr.sin_family = sa_family_t(AF_INET) |
427 | 0 | ipv4Addr.sin_port = 0 |
428 | 0 | withUnsafeMutableBytes(of: &ipv4Addr.sin_addr) { $0.storeBytes(of: packedAddress, as: UInt32.self) } |
429 | 0 | self = .v4(.init(address: ipv4Addr, host: "")) |
430 | 0 | } |
431 | | |
432 | | /// Creates a new `SocketAddress` corresponding to the netmask for a subnet prefix. |
433 | | /// |
434 | | /// As an example, consider the subnet "fe80::/10". The "subnet prefix" is "10", and the corresponding netmask is "ff30::". |
435 | | /// This initializer will produce a `SocketAddress` that contains "ff30::". |
436 | | /// |
437 | | /// - Parameters: |
438 | | /// - prefix: The prefix of the subnet. |
439 | | /// - Returns: A `SocketAddress` containing the associated netmask. |
440 | 0 | internal init(ipv6MaskForPrefix prefix: Int) { |
441 | 0 | precondition((0...128).contains(prefix)) |
442 | 0 |
|
443 | 0 | // This defends against the possibility of a greater-than-/64 subnet, which would produce a negative shift |
444 | 0 | // operand which is absolutely not what we want. |
445 | 0 | let highShift = min(prefix, 64) |
446 | 0 | let packedAddressHigh = (UInt64(0xFFFF_FFFF_FFFF_FFFF) << (64 - highShift)).bigEndian |
447 | 0 |
|
448 | 0 | let packedAddressLow = (UInt64(0xFFFF_FFFF_FFFF_FFFF) << (128 - prefix)).bigEndian |
449 | 0 | let packedAddress = (packedAddressHigh, packedAddressLow) |
450 | 0 |
|
451 | 0 | var ipv6Addr = sockaddr_in6() |
452 | 0 | ipv6Addr.sin6_family = sa_family_t(AF_INET6) |
453 | 0 | ipv6Addr.sin6_port = 0 |
454 | 0 | withUnsafeMutableBytes(of: &ipv6Addr.sin6_addr) { $0.storeBytes(of: packedAddress, as: (UInt64, UInt64).self) } |
455 | 0 | self = .v6(.init(address: ipv6Addr, host: "")) |
456 | 0 | } |
457 | | |
458 | | /// Creates a new `SocketAddress` for the given host (which will be resolved) and port. |
459 | | /// |
460 | | /// - warning: This is a blocking call, so please avoid calling this from an `EventLoop`. |
461 | | /// |
462 | | /// - Parameters: |
463 | | /// - host: the hostname which should be resolved. |
464 | | /// - port: the port itself |
465 | | /// - Returns: the `SocketAddress` for the host / port pair. |
466 | | /// - Throws: a `SocketAddressError.unknown` if we could not resolve the `host`, or `SocketAddressError.unsupported` if the address itself is not supported (yet). |
467 | 0 | public static func makeAddressResolvingHost(_ host: String, port: Int) throws -> SocketAddress { |
468 | 0 | #if os(WASI) |
469 | 0 | throw SocketAddressError.unsupported |
470 | 0 | #endif |
471 | 0 |
|
472 | 0 | #if os(Windows) |
473 | 0 | return try host.withCString(encodedAs: UTF16.self) { wszHost in |
474 | 0 | try String(port).withCString(encodedAs: UTF16.self) { wszPort in |
475 | 0 | var pResult: UnsafeMutablePointer<ADDRINFOW>? |
476 | 0 |
|
477 | 0 | guard GetAddrInfoW(wszHost, wszPort, nil, &pResult) == 0 else { |
478 | 0 | throw SocketAddressError.unknown(host: host, port: port) |
479 | 0 | } |
480 | 0 |
|
481 | 0 | defer { |
482 | 0 | FreeAddrInfoW(pResult) |
483 | 0 | } |
484 | 0 |
|
485 | 0 | if let pResult = pResult, let addressBytes = UnsafeRawPointer(pResult.pointee.ai_addr) { |
486 | 0 | switch pResult.pointee.ai_family { |
487 | 0 | case AF_INET: |
488 | 0 | return .v4(IPv4Address(address: addressBytes.load(as: sockaddr_in.self), host: host)) |
489 | 0 | case AF_INET6: |
490 | 0 | return .v6(IPv6Address(address: addressBytes.load(as: sockaddr_in6.self), host: host)) |
491 | 0 | default: |
492 | 0 | break |
493 | 0 | } |
494 | 0 | } |
495 | 0 |
|
496 | 0 | throw SocketAddressError.unsupported |
497 | 0 | } |
498 | 0 | } |
499 | 0 | #elseif !os(WASI) |
500 | 0 | var info: UnsafeMutablePointer<addrinfo>? |
501 | 0 |
|
502 | 0 | // FIXME: this is blocking! |
503 | 0 | if getaddrinfo(host, String(port), nil, &info) != 0 { |
504 | 0 | throw SocketAddressError.unknown(host: host, port: port) |
505 | 0 | } |
506 | 0 |
|
507 | 0 | defer { |
508 | 0 | if info != nil { |
509 | 0 | freeaddrinfo(info) |
510 | 0 | } |
511 | 0 | } |
512 | 0 |
|
513 | 0 | if let info = info, let addrPointer = info.pointee.ai_addr { |
514 | 0 | let addressBytes = UnsafeRawPointer(addrPointer) |
515 | 0 | switch NIOBSDSocket.AddressFamily(rawValue: info.pointee.ai_family) { |
516 | 0 | case .inet: |
517 | 0 | return .v4(.init(address: addressBytes.load(as: sockaddr_in.self), host: host)) |
518 | 0 | case .inet6: |
519 | 0 | return .v6(.init(address: addressBytes.load(as: sockaddr_in6.self), host: host)) |
520 | 0 | default: |
521 | 0 | throw SocketAddressError.unsupported |
522 | 0 | } |
523 | 0 | } else { |
524 | 0 | // this is odd, getaddrinfo returned NULL |
525 | 0 | throw SocketAddressError.unsupported |
526 | 0 | } |
527 | 0 | #endif |
528 | 0 | } |
529 | | } |
530 | | |
531 | | /// We define an extension on `SocketAddress` that gives it an elementwise equatable conformance, using |
532 | | /// only the elements defined on the structure in their man pages (excluding lengths). |
533 | | extension SocketAddress: Equatable { |
534 | 0 | public static func == (lhs: SocketAddress, rhs: SocketAddress) -> Bool { |
535 | 0 | switch (lhs, rhs) { |
536 | 0 | case (.v4(let addr1), .v4(let addr2)): |
537 | 0 | #if os(Windows) |
538 | 0 | return addr1.address.sin_family == addr2.address.sin_family |
539 | 0 | && addr1.address.sin_port == addr2.address.sin_port |
540 | 0 | && addr1.address.sin_addr.S_un.S_addr == addr2.address.sin_addr.S_un.S_addr |
541 | 0 | #else |
542 | 0 | return addr1.address.sin_family == addr2.address.sin_family |
543 | 0 | && addr1.address.sin_port == addr2.address.sin_port |
544 | 0 | && addr1.address.sin_addr.s_addr == addr2.address.sin_addr.s_addr |
545 | 0 | #endif |
546 | 0 | case (.v6(let addr1), .v6(let addr2)): |
547 | 0 | guard |
548 | 0 | addr1.address.sin6_family == addr2.address.sin6_family |
549 | 0 | && addr1.address.sin6_port == addr2.address.sin6_port |
550 | 0 | && addr1.address.sin6_flowinfo == addr2.address.sin6_flowinfo |
551 | 0 | && addr1.address.sin6_scope_id == addr2.address.sin6_scope_id |
552 | 0 | else { |
553 | 0 | return false |
554 | 0 | } |
555 | 0 |
|
556 | 0 | var s6addr1 = addr1.address.sin6_addr |
557 | 0 | var s6addr2 = addr2.address.sin6_addr |
558 | 0 | return memcmp(&s6addr1, &s6addr2, MemoryLayout.size(ofValue: s6addr1)) == 0 |
559 | 0 | case (.unixDomainSocket(let addr1), .unixDomainSocket(let addr2)): |
560 | 0 | guard addr1.address.sun_family == addr2.address.sun_family else { |
561 | 0 | return false |
562 | 0 | } |
563 | 0 |
|
564 | 0 | #if os(WASI) |
565 | 0 | return true |
566 | 0 | #else |
567 | 0 | let bufferSize = MemoryLayout.size(ofValue: addr1.address.sun_path) |
568 | 0 |
|
569 | 0 | // Swift implicitly binds the memory for homogeneous tuples to both the tuple type and the element type. |
570 | 0 | // This allows us to use assumingMemoryBound(to:) for managing the types. However, we add a static assertion here to validate |
571 | 0 | // that the element type _really is_ what we're assuming it to be. |
572 | 0 | assert(Swift.type(of: addr1.address.sun_path.0) == CChar.self) |
573 | 0 | assert(Swift.type(of: addr2.address.sun_path.0) == CChar.self) |
574 | 0 | return withUnsafePointer(to: addr1.address.sun_path) { sunpath1 in |
575 | 0 | withUnsafePointer(to: addr2.address.sun_path) { sunpath2 in |
576 | 0 | let typedSunpath1 = UnsafeRawPointer(sunpath1).assumingMemoryBound(to: CChar.self) |
577 | 0 | let typedSunpath2 = UnsafeRawPointer(sunpath2).assumingMemoryBound(to: CChar.self) |
578 | 0 | return strncmp(typedSunpath1, typedSunpath2, bufferSize) == 0 |
579 | 0 | } |
580 | 0 | } |
581 | 0 | #endif |
582 | 0 | case (.v4, _), (.v6, _), (.unixDomainSocket, _): |
583 | 0 | return false |
584 | 0 | } |
585 | 0 | } |
586 | | } |
587 | | |
588 | | extension SocketAddress.IPv4Address: Sendable {} |
589 | | extension SocketAddress.IPv6Address: Sendable {} |
590 | | |
591 | | /// We define an extension on `SocketAddress` that gives it an elementwise hashable conformance, using |
592 | | /// only the elements defined on the structure in their man pages (excluding lengths). |
593 | | extension SocketAddress: Hashable { |
594 | 0 | public func hash(into hasher: inout Hasher) { |
595 | 0 | switch self { |
596 | 0 | case .unixDomainSocket(let uds): |
597 | 0 | hasher.combine(0) |
598 | 0 | hasher.combine(uds.address.sun_family) |
599 | 0 |
|
600 | 0 | #if !os(WASI) |
601 | 0 | let pathSize = MemoryLayout.size(ofValue: uds.address.sun_path) |
602 | 0 |
|
603 | 0 | // Swift implicitly binds the memory of homogeneous tuples to both the tuple type and the element type. |
604 | 0 | // We can therefore use assumingMemoryBound(to:) for pointer type conversion. We add a static assert just to |
605 | 0 | // validate that we are actually right about the element type. |
606 | 0 | assert(Swift.type(of: uds.address.sun_path.0) == CChar.self) |
607 | 0 | withUnsafePointer(to: uds.address.sun_path) { pathPtr in |
608 | 0 | let typedPathPointer = UnsafeRawPointer(pathPtr).assumingMemoryBound(to: CChar.self) |
609 | 0 | let length = strnlen(typedPathPointer, pathSize) |
610 | 0 | let bytes = UnsafeRawBufferPointer(start: UnsafeRawPointer(typedPathPointer), count: length) |
611 | 0 | hasher.combine(bytes: bytes) |
612 | 0 | } |
613 | 0 | #endif |
614 | 0 | case .v4(let v4Addr): |
615 | 0 | hasher.combine(1) |
616 | 0 | hasher.combine(v4Addr.address.sin_family) |
617 | 0 | hasher.combine(v4Addr.address.sin_port) |
618 | 0 | #if os(Windows) |
619 | 0 | hasher.combine(v4Addr.address.sin_addr.S_un.S_addr) |
620 | 0 | #else |
621 | 0 | hasher.combine(v4Addr.address.sin_addr.s_addr) |
622 | 0 | #endif |
623 | 0 | case .v6(let v6Addr): |
624 | 0 | hasher.combine(2) |
625 | 0 | hasher.combine(v6Addr.address.sin6_family) |
626 | 0 | hasher.combine(v6Addr.address.sin6_port) |
627 | 0 | hasher.combine(v6Addr.address.sin6_flowinfo) |
628 | 0 | hasher.combine(v6Addr.address.sin6_scope_id) |
629 | 0 | withUnsafeBytes(of: v6Addr.address.sin6_addr) { |
630 | 0 | hasher.combine(bytes: $0) |
631 | 0 | } |
632 | 0 | } |
633 | 0 | } |
634 | | } |
635 | | |
636 | | extension SocketAddress { |
637 | | /// Whether this `SocketAddress` corresponds to a multicast address. |
638 | 0 | public var isMulticast: Bool { |
639 | 0 | switch self { |
640 | 0 | case .unixDomainSocket: |
641 | 0 | // No multicast on unix sockets. |
642 | 0 | return false |
643 | 0 | case .v4(let v4Addr): |
644 | 0 | // For IPv4 a multicast address is in the range 224.0.0.0/4. |
645 | 0 | // The easy way to check if this is the case is to just mask off |
646 | 0 | // the address. |
647 | 0 | #if os(Windows) |
648 | 0 | let v4WireAddress = v4Addr.address.sin_addr.S_un.S_addr |
649 | 0 | let mask = UInt32(0xF000_0000).bigEndian |
650 | 0 | let subnet = UInt32(0xE000_0000).bigEndian |
651 | 0 | #else |
652 | 0 | let v4WireAddress = v4Addr.address.sin_addr.s_addr |
653 | 0 | let mask = in_addr_t(0xF000_0000 as UInt32).bigEndian |
654 | 0 | let subnet = in_addr_t(0xE000_0000 as UInt32).bigEndian |
655 | 0 | #endif |
656 | 0 | return v4WireAddress & mask == subnet |
657 | 0 | case .v6(let v6Addr): |
658 | 0 | // For IPv6 a multicast address is in the range ff00::/8. |
659 | 0 | // Here we don't need a bitmask, as all the top bits are set, |
660 | 0 | // so we can just ask for equality on the top byte. |
661 | 0 | var v6WireAddress = v6Addr.address.sin6_addr |
662 | 0 | return withUnsafeBytes(of: &v6WireAddress) { $0[0] == 0xff } |
663 | 0 | } |
664 | 0 | } |
665 | | } |
666 | | |
667 | | protocol SockAddrProtocol { |
668 | | func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R |
669 | | } |
670 | | |
671 | | /// Returns a description for the given address. |
672 | | internal func descriptionForAddress( |
673 | | family: NIOBSDSocket.AddressFamily, |
674 | | bytes: UnsafeRawPointer, |
675 | | length byteCount: Int |
676 | 0 | ) throws -> String { |
677 | 0 | var addressBytes: [Int8] = Array(repeating: 0, count: byteCount) |
678 | 0 | return try addressBytes.withUnsafeMutableBufferPointer { |
679 | 0 | (addressBytesPtr: inout UnsafeMutableBufferPointer<Int8>) -> String in |
680 | 0 | try NIOBSDSocket.inet_ntop( |
681 | 0 | addressFamily: family, |
682 | 0 | addressBytes: bytes, |
683 | 0 | addressDescription: addressBytesPtr.baseAddress!, |
684 | 0 | addressDescriptionLength: socklen_t(byteCount) |
685 | 0 | ) |
686 | 0 | return addressBytesPtr.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: byteCount) { |
687 | 0 | addressBytesPtr -> String in |
688 | 0 | String(cString: addressBytesPtr) |
689 | 0 | } |
690 | 0 | } |
691 | 0 | } |
692 | | |
693 | | extension sockaddr_in: SockAddrProtocol { |
694 | 0 | func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R { |
695 | 0 | try withUnsafeBytes(of: self) { p in |
696 | 0 | try body(p.baseAddress!.assumingMemoryBound(to: sockaddr.self), p.count) |
697 | 0 | } |
698 | 0 | } |
699 | | |
700 | | /// Returns a description of the `sockaddr_in`. |
701 | 0 | func addressDescription() -> String { |
702 | 0 | withUnsafePointer(to: self.sin_addr) { addrPtr in |
703 | 0 | // this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC) |
704 | 0 | try! descriptionForAddress(family: .inet, bytes: addrPtr, length: Int(INET_ADDRSTRLEN)) |
705 | 0 | } |
706 | 0 | } |
707 | | } |
708 | | |
709 | | extension sockaddr_in6: SockAddrProtocol { |
710 | 0 | func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R { |
711 | 0 | try withUnsafeBytes(of: self) { p in |
712 | 0 | try body(p.baseAddress!.assumingMemoryBound(to: sockaddr.self), p.count) |
713 | 0 | } |
714 | 0 | } |
715 | | |
716 | | /// Returns a description of the `sockaddr_in6`. |
717 | 0 | func addressDescription() -> String { |
718 | 0 | withUnsafePointer(to: self.sin6_addr) { addrPtr in |
719 | 0 | // this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC) |
720 | 0 | try! descriptionForAddress(family: .inet6, bytes: addrPtr, length: Int(INET6_ADDRSTRLEN)) |
721 | 0 | } |
722 | 0 | } |
723 | | } |
724 | | |
725 | | extension sockaddr_un: SockAddrProtocol { |
726 | 0 | func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R { |
727 | 0 | try withUnsafeBytes(of: self) { p in |
728 | 0 | try body(p.baseAddress!.assumingMemoryBound(to: sockaddr.self), p.count) |
729 | 0 | } |
730 | 0 | } |
731 | | } |
732 | | |
733 | | extension sockaddr_storage: SockAddrProtocol { |
734 | 0 | func withSockAddr<R>(_ body: (UnsafePointer<sockaddr>, Int) throws -> R) rethrows -> R { |
735 | 0 | try withUnsafeBytes(of: self) { p in |
736 | 0 | try body(p.baseAddress!.assumingMemoryBound(to: sockaddr.self), p.count) |
737 | 0 | } |
738 | 0 | } |
739 | | } |
740 | | |
741 | | // MARK: Workarounds for SR-14268 |
742 | | // We need these free functions to expose our extension methods, because otherwise |
743 | | // the compiler falls over when we try to access them from test code. As these functions |
744 | | // exist purely to make the behaviours accessible from test code, we name them truly awfully. |
745 | 0 | func __testOnly_addressDescription(_ addr: sockaddr_in) -> String { |
746 | 0 | addr.addressDescription() |
747 | 0 | } |
748 | | |
749 | 0 | func __testOnly_addressDescription(_ addr: sockaddr_in6) -> String { |
750 | 0 | addr.addressDescription() |
751 | 0 | } |
752 | | |
753 | | func __testOnly_withSockAddr<ReturnType>( |
754 | | _ addr: sockaddr_in, |
755 | | _ body: (UnsafePointer<sockaddr>, Int) throws -> ReturnType |
756 | 0 | ) rethrows -> ReturnType { |
757 | 0 | try addr.withSockAddr(body) |
758 | 0 | } |
759 | | |
760 | | func __testOnly_withSockAddr<ReturnType>( |
761 | | _ addr: sockaddr_in6, |
762 | | _ body: (UnsafePointer<sockaddr>, Int) throws -> ReturnType |
763 | 0 | ) rethrows -> ReturnType { |
764 | 0 | try addr.withSockAddr(body) |
765 | 0 | } |