-
Notifications
You must be signed in to change notification settings - Fork 631
/
SocketAddresses.swift
734 lines (643 loc) · 29.8 KB
/
SocketAddresses.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
//===----------------------------------------------------------------------===//
//
// 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 canImport(Darwin)
import Darwin
#elseif os(Linux) || os(FreeBSD) || os(Android)
#if canImport(Glibc)
import Glibc
#elseif canImport(Musl)
import Musl
#endif
import CNIOLinux
#else
#error("The Socket Addresses module was unable to identify your C library.")
#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)
}