Coverage Report

Created: 2025-07-23 06:54

/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
}