Skip to content

Commit 7aea334

Browse files
committed
NIO: implement network interface enumeration for Windows
Partially implement network interface enumeration. This is sufficient to build up the structures for basic operations. Although the interface information is incomplete, this provides enough structure to continue porting the rest of the NIO interfaces.
1 parent 74bedaf commit 7aea334

File tree

5 files changed

+213
-8
lines changed

5 files changed

+213
-8
lines changed

Sources/NIO/Channel.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,9 +338,11 @@ public enum ChannelError: Error {
338338
/// address.
339339
case illegalMulticastAddress(SocketAddress)
340340

341+
#if !os(Windows)
341342
/// Multicast is not supported on Interface
342343
@available(*, deprecated, renamed: "NIOMulticastNotSupportedError")
343344
case multicastNotSupported(NIONetworkInterface)
345+
#endif
344346

345347
/// An operation that was inappropriate given the current `Channel` state was attempted.
346348
case inappropriateOperationForState

Sources/NIO/Interfaces.swift

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,21 @@
2020

2121
import CNIOLinux
2222

23+
#if os(Windows)
24+
import let WinSDK.AF_INET
25+
import let WinSDK.AF_INET6
26+
27+
import let WinSDK.INET_ADDRSTRLEN
28+
import let WinSDK.INET6_ADDRSTRLEN
29+
30+
import struct WinSDK.ADDRESS_FAMILY
31+
import struct WinSDK.IP_ADAPTER_ADDRESSES
32+
import struct WinSDK.IP_ADAPTER_UNICAST_ADDRESS
33+
34+
import typealias WinSDK.UINT8
35+
#endif
36+
37+
#if !os(Windows)
2338
private extension ifaddrs {
2439
var dstaddr: UnsafeMutablePointer<sockaddr>? {
2540
#if os(Linux)
@@ -37,6 +52,7 @@ private extension ifaddrs {
3752
#endif
3853
}
3954
}
55+
#endif
4056

4157
/// A representation of a single network device on a system.
4258
public struct NIONetworkDevice {
@@ -127,14 +143,25 @@ public struct NIONetworkDevice {
127143
/// This constructor will fail if NIO does not understand the format of the underlying
128144
/// socket address family. This is quite common: for example, Linux will return AF_PACKET
129145
/// addressed interfaces on most platforms, which NIO does not currently understand.
146+
#if os(Windows)
147+
internal init?(_ pAdapter: UnsafeMutablePointer<IP_ADAPTER_ADDRESSES>,
148+
_ pAddress: UnsafeMutablePointer<IP_ADAPTER_UNICAST_ADDRESS>) {
149+
guard let backing = Backing(pAdapter, pAddress) else {
150+
return nil
151+
}
152+
self.backing = backing
153+
}
154+
#else
130155
internal init?(_ caddr: ifaddrs) {
131156
guard let backing = Backing(caddr) else {
132157
return nil
133158
}
134159

135160
self.backing = backing
136161
}
162+
#endif
137163

164+
#if !os(Windows)
138165
/// Convert a `NIONetworkInterface` to a `NIONetworkDevice`. As `NIONetworkDevice`s are a superset of `NIONetworkInterface`s,
139166
/// it is always possible to perform this conversion.
140167
@available(*, deprecated, message: "This is a compatibility helper, and will be removed in a future release")
@@ -149,6 +176,7 @@ public struct NIONetworkDevice {
149176
interfaceIndex: interface.interfaceIndex
150177
)
151178
}
179+
#endif
152180

153181
public init(name: String,
154182
address: SocketAddress?,
@@ -206,6 +234,58 @@ extension NIONetworkDevice {
206234
/// This constructor will fail if NIO does not understand the format of the underlying
207235
/// socket address family. This is quite common: for example, Linux will return AF_PACKET
208236
/// addressed interfaces on most platforms, which NIO does not currently understand.
237+
#if os(Windows)
238+
internal init?(_ pAdapter: UnsafeMutablePointer<IP_ADAPTER_ADDRESSES>,
239+
_ pAddress: UnsafeMutablePointer<IP_ADAPTER_UNICAST_ADDRESS>) {
240+
self.name = String(decodingCString: pAdapter.pointee.FriendlyName,
241+
as: UTF16.self)
242+
self.address = pAddress.pointee.Address.lpSockaddr.convert()
243+
244+
// TODO: convert the prefix length to the mask itself
245+
let v4mask: (UINT8) -> SocketAddress? = { _ in
246+
var buffer: [CChar] =
247+
Array<CChar>(repeating: 0, count: Int(INET_ADDRSTRLEN))
248+
var mask: sockaddr_in = sockaddr_in()
249+
mask.sin_family = ADDRESS_FAMILY(AF_INET)
250+
_ = buffer.withUnsafeMutableBufferPointer {
251+
try! NIOBSDSocket.inet_ntop(af: .inet, src: &mask,
252+
dst: $0.baseAddress!,
253+
size: INET_ADDRSTRLEN)
254+
}
255+
return SocketAddress(mask, host: mask.addressDescription())
256+
}
257+
let v6mask: (UINT8) -> SocketAddress? = { _ in
258+
var buffer: [CChar] =
259+
Array<CChar>(repeating: 0, count: Int(INET6_ADDRSTRLEN))
260+
var mask: sockaddr_in6 = sockaddr_in6()
261+
mask.sin6_family = ADDRESS_FAMILY(AF_INET6)
262+
_ = buffer.withUnsafeMutableBufferPointer {
263+
try! NIOBSDSocket.inet_ntop(af: .inet6, src: &mask,
264+
dst: $0.baseAddress!,
265+
size: INET6_ADDRSTRLEN)
266+
}
267+
return SocketAddress(mask, host: mask.addressDescription())
268+
}
269+
270+
switch pAddress.pointee.Address.lpSockaddr.pointee.sa_family {
271+
case ADDRESS_FAMILY(AF_INET):
272+
self.netmask = v4mask(pAddress.pointee.OnLinkPrefixLength)
273+
self.interfaceIndex = Int(pAdapter.pointee.IfIndex)
274+
break
275+
case ADDRESS_FAMILY(AF_INET6):
276+
self.netmask = v6mask(pAddress.pointee.OnLinkPrefixLength)
277+
self.interfaceIndex = Int(pAdapter.pointee.Ipv6IfIndex)
278+
break
279+
default:
280+
return nil
281+
}
282+
283+
// TODO(compnerd) handle broadcast/ppp/multicast information
284+
self.broadcastAddress = nil
285+
self.pointToPointDestinationAddress = nil
286+
self.multicastSupported = false
287+
}
288+
#else
209289
internal init?(_ caddr: ifaddrs) {
210290
self.name = String(cString: caddr.ifa_name)
211291
self.address = caddr.ifa_addr.flatMap { $0.convert() }
@@ -229,6 +309,7 @@ extension NIONetworkDevice {
229309
return nil
230310
}
231311
}
312+
#endif
232313

233314
init(copying original: Backing) {
234315
self.name = original.name
@@ -325,6 +406,60 @@ public final class NIONetworkInterface {
325406
/// This constructor will fail if NIO does not understand the format of the underlying
326407
/// socket address family. This is quite common: for example, Linux will return AF_PACKET
327408
/// addressed interfaces on most platforms, which NIO does not currently understand.
409+
#if os(Windows)
410+
internal init?(_ pAdapter: UnsafeMutablePointer<IP_ADAPTER_ADDRESSES>,
411+
_ pAddress: UnsafeMutablePointer<IP_ADAPTER_UNICAST_ADDRESS>) {
412+
self.name = String(decodingCString: pAdapter.pointee.FriendlyName,
413+
as: UTF16.self)
414+
415+
guard let address = pAddress.pointee.Address.lpSockaddr.convert() else {
416+
return nil
417+
}
418+
self.address = address
419+
420+
// TODO: convert the prefix length to the mask itself
421+
let v4mask: (UINT8) -> SocketAddress? = { _ in
422+
var buffer: [CChar] =
423+
Array<CChar>(repeating: 0, count: Int(INET_ADDRSTRLEN))
424+
var mask: sockaddr_in = sockaddr_in()
425+
mask.sin_family = ADDRESS_FAMILY(AF_INET)
426+
_ = buffer.withUnsafeMutableBufferPointer {
427+
try! NIOBSDSocket.inet_ntop(af: .inet, src: &mask,
428+
dst: $0.baseAddress!,
429+
size: INET_ADDRSTRLEN)
430+
}
431+
return SocketAddress(mask, host: mask.addressDescription())
432+
}
433+
let v6mask: (UINT8) -> SocketAddress? = { _ in
434+
var buffer: [CChar] =
435+
Array<CChar>(repeating: 0, count: Int(INET6_ADDRSTRLEN))
436+
var mask: sockaddr_in6 = sockaddr_in6()
437+
mask.sin6_family = ADDRESS_FAMILY(AF_INET6)
438+
_ = buffer.withUnsafeMutableBufferPointer {
439+
try! NIOBSDSocket.inet_ntop(af: .inet6, src: &mask,
440+
dst: $0.baseAddress!,
441+
size: INET6_ADDRSTRLEN)
442+
}
443+
return SocketAddress(mask, host: mask.addressDescription())
444+
}
445+
446+
switch pAddress.pointee.Address.lpSockaddr.pointee.sa_family {
447+
case ADDRESS_FAMILY(AF_INET):
448+
self.netmask = v4mask(pAddress.pointee.OnLinkPrefixLength)
449+
self.interfaceIndex = Int(pAdapter.pointee.IfIndex)
450+
case ADDRESS_FAMILY(AF_INET6):
451+
self.netmask = v6mask(pAddress.pointee.OnLinkPrefixLength)
452+
self.interfaceIndex = Int(pAdapter.pointee.Ipv6IfIndex)
453+
default:
454+
return nil
455+
}
456+
457+
// TODO(compnerd) handle broadcast/ppp/multicast information
458+
self.broadcastAddress = nil
459+
self.pointToPointDestinationAddress = nil
460+
self.multicastSupported = false
461+
}
462+
#else
328463
internal init?(_ caddr: ifaddrs) {
329464
self.name = String(cString: caddr.ifa_name)
330465
guard let address = caddr.ifa_addr!.convert() else {
@@ -361,6 +496,7 @@ public final class NIONetworkInterface {
361496
return nil
362497
}
363498
}
499+
#endif
364500
}
365501

366502
@available(*, deprecated, renamed: "NIONetworkDevice")

Sources/NIO/MulticastChannel.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public protocol MulticastChannel: Channel {
2525
/// `nil` if you are not interested in the result of the operation.
2626
func joinGroup(_ group: SocketAddress, promise: EventLoopPromise<Void>?)
2727

28+
#if !os(Windows)
2829
/// Request that the `MulticastChannel` join the multicast group given by `group` on the interface
2930
/// given by `interface`.
3031
///
@@ -35,6 +36,7 @@ public protocol MulticastChannel: Channel {
3536
/// `nil` if you are not interested in the result of the operation.
3637
@available(*, deprecated, renamed: "joinGroup(_:device:promise:)")
3738
func joinGroup(_ group: SocketAddress, interface: NIONetworkInterface?, promise: EventLoopPromise<Void>?)
39+
#endif
3840

3941
/// Request that the `MulticastChannel` join the multicast group given by `group` on the device
4042
/// given by `device`.
@@ -54,6 +56,7 @@ public protocol MulticastChannel: Channel {
5456
/// `nil` if you are not interested in the result of the operation.
5557
func leaveGroup(_ group: SocketAddress, promise: EventLoopPromise<Void>?)
5658

59+
#if !os(Windows)
5760
/// Request that the `MulticastChannel` leave the multicast group given by `group` on the interface
5861
/// given by `interface`.
5962
///
@@ -64,6 +67,7 @@ public protocol MulticastChannel: Channel {
6467
/// `nil` if you are not interested in the result of the operation.
6568
@available(*, deprecated, renamed: "leaveGroup(_:device:promise:)")
6669
func leaveGroup(_ group: SocketAddress, interface: NIONetworkInterface?, promise: EventLoopPromise<Void>?)
70+
#endif
6771

6872
/// Request that the `MulticastChannel` leave the multicast group given by `group` on the device
6973
/// given by `device`.
@@ -89,12 +93,14 @@ extension MulticastChannel {
8993
return promise.futureResult
9094
}
9195

96+
#if !os(Windows)
9297
@available(*, deprecated, renamed: "joinGroup(_:device:)")
9398
public func joinGroup(_ group: SocketAddress, interface: NIONetworkInterface?) -> EventLoopFuture<Void> {
9499
let promise = self.eventLoop.makePromise(of: Void.self)
95100
self.joinGroup(group, interface: interface, promise: promise)
96101
return promise.futureResult
97102
}
103+
#endif
98104

99105
public func joinGroup(_ group: SocketAddress, device: NIONetworkDevice?) -> EventLoopFuture<Void> {
100106
let promise = self.eventLoop.makePromise(of: Void.self)
@@ -112,12 +118,14 @@ extension MulticastChannel {
112118
return promise.futureResult
113119
}
114120

121+
#if !os(Windows)
115122
@available(*, deprecated, renamed: "leaveGroup(_:device:)")
116123
public func leaveGroup(_ group: SocketAddress, interface: NIONetworkInterface?) -> EventLoopFuture<Void> {
117124
let promise = self.eventLoop.makePromise(of: Void.self)
118125
self.leaveGroup(group, interface: interface, promise: promise)
119126
return promise.futureResult
120127
}
128+
#endif
121129

122130
public func leaveGroup(_ group: SocketAddress, device: NIONetworkDevice?) -> EventLoopFuture<Void> {
123131
let promise = self.eventLoop.makePromise(of: Void.self)

Sources/NIO/SocketChannel.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
#if os(Windows)
16+
import let WinSDK.ECONNABORTED
17+
import let WinSDK.ECONNREFUSED
18+
import let WinSDK.EMFILE
19+
import let WinSDK.ENFILE
20+
import let WinSDK.ENOBUFS
21+
import let WinSDK.ENOMEM
22+
#endif
23+
1524
extension ByteBuffer {
1625
mutating func withMutableWritePointer(body: (UnsafeMutableRawBufferPointer) throws -> IOResult<Int>) rethrows -> IOResult<Int> {
1726
var singleResult: IOResult<Int>!
@@ -755,6 +764,7 @@ extension DatagramChannel: MulticastChannel {
755764
}
756765
}
757766

767+
#if !os(Windows)
758768
@available(*, deprecated, renamed: "joinGroup(_:device:promise:)")
759769
func joinGroup(_ group: SocketAddress, interface: NIONetworkInterface?, promise: EventLoopPromise<Void>?) {
760770
if eventLoop.inEventLoop {
@@ -776,6 +786,7 @@ extension DatagramChannel: MulticastChannel {
776786
}
777787
}
778788
}
789+
#endif
779790

780791
func joinGroup(_ group: SocketAddress, device: NIONetworkDevice?, promise: EventLoopPromise<Void>?) {
781792
if eventLoop.inEventLoop {

0 commit comments

Comments
 (0)