Skip to content

Commit decd383

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 5d57ca0 commit decd383

File tree

5 files changed

+236
-84
lines changed

5 files changed

+236
-84
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: 159 additions & 76 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+
break
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
@@ -289,97 +370,99 @@ extension NIONetworkDevice: Hashable {
289370
}
290371
}
291372

292-
/// A representation of a single network interface on a system.
293-
@available(*, deprecated, renamed: "NIONetworkDevice")
294-
public final class NIONetworkInterface {
295-
// This is a class because in almost all cases this will carry
296-
// four structs that are backed by classes, and so will incur 4
297-
// refcount operations each time it is copied.
373+
#if !os(Windows)
374+
/// A representation of a single network interface on a system.
375+
@available(*, deprecated, renamed: "NIONetworkDevice")
376+
public final class NIONetworkInterface {
377+
// This is a class because in almost all cases this will carry
378+
// four structs that are backed by classes, and so will incur 4
379+
// refcount operations each time it is copied.
298380

299-
/// The name of the network interface.
300-
public let name: String
381+
/// The name of the network interface.
382+
public let name: String
301383

302-
/// The address associated with the given network interface.
303-
public let address: SocketAddress
384+
/// The address associated with the given network interface.
385+
public let address: SocketAddress
304386

305-
/// The netmask associated with this address, if any.
306-
public let netmask: SocketAddress?
387+
/// The netmask associated with this address, if any.
388+
public let netmask: SocketAddress?
307389

308-
/// The broadcast address associated with this socket interface, if it has one. Some
309-
/// interfaces do not, especially those that have a `pointToPointDestinationAddress`.
310-
public let broadcastAddress: SocketAddress?
390+
/// The broadcast address associated with this socket interface, if it has one. Some
391+
/// interfaces do not, especially those that have a `pointToPointDestinationAddress`.
392+
public let broadcastAddress: SocketAddress?
311393

312-
/// The address of the peer on a point-to-point interface, if this is one. Some
313-
/// interfaces do not have such an address: most of those have a `broadcastAddress`
314-
/// instead.
315-
public let pointToPointDestinationAddress: SocketAddress?
394+
/// The address of the peer on a point-to-point interface, if this is one. Some
395+
/// interfaces do not have such an address: most of those have a `broadcastAddress`
396+
/// instead.
397+
public let pointToPointDestinationAddress: SocketAddress?
316398

317-
/// If the Interface supports Multicast
318-
public let multicastSupported: Bool
399+
/// If the Interface supports Multicast
400+
public let multicastSupported: Bool
319401

320-
/// The index of the interface, as provided by `if_nametoindex`.
321-
public let interfaceIndex: Int
402+
/// The index of the interface, as provided by `if_nametoindex`.
403+
public let interfaceIndex: Int
322404

323-
/// Create a brand new network interface.
324-
///
325-
/// This constructor will fail if NIO does not understand the format of the underlying
326-
/// socket address family. This is quite common: for example, Linux will return AF_PACKET
327-
/// addressed interfaces on most platforms, which NIO does not currently understand.
328-
internal init?(_ caddr: ifaddrs) {
329-
self.name = String(cString: caddr.ifa_name)
330-
guard let address = caddr.ifa_addr!.convert() else {
331-
return nil
332-
}
333-
self.address = address
405+
/// Create a brand new network interface.
406+
///
407+
/// This constructor will fail if NIO does not understand the format of the underlying
408+
/// socket address family. This is quite common: for example, Linux will return AF_PACKET
409+
/// addressed interfaces on most platforms, which NIO does not currently understand.
410+
internal init?(_ caddr: ifaddrs) {
411+
self.name = String(cString: caddr.ifa_name)
412+
guard let address = caddr.ifa_addr!.convert() else {
413+
return nil
414+
}
415+
self.address = address
334416

335-
if let netmask = caddr.ifa_netmask {
336-
self.netmask = netmask.convert()
337-
} else {
338-
self.netmask = nil
339-
}
417+
if let netmask = caddr.ifa_netmask {
418+
self.netmask = netmask.convert()
419+
} else {
420+
self.netmask = nil
421+
}
340422

341-
if (caddr.ifa_flags & UInt32(IFF_BROADCAST)) != 0, let addr = caddr.broadaddr {
342-
self.broadcastAddress = addr.convert()
343-
self.pointToPointDestinationAddress = nil
344-
} else if (caddr.ifa_flags & UInt32(IFF_POINTOPOINT)) != 0, let addr = caddr.dstaddr {
345-
self.broadcastAddress = nil
346-
self.pointToPointDestinationAddress = addr.convert()
347-
} else {
348-
self.broadcastAddress = nil
349-
self.pointToPointDestinationAddress = nil
350-
}
423+
if (caddr.ifa_flags & UInt32(IFF_BROADCAST)) != 0, let addr = caddr.broadaddr {
424+
self.broadcastAddress = addr.convert()
425+
self.pointToPointDestinationAddress = nil
426+
} else if (caddr.ifa_flags & UInt32(IFF_POINTOPOINT)) != 0, let addr = caddr.dstaddr {
427+
self.broadcastAddress = nil
428+
self.pointToPointDestinationAddress = addr.convert()
429+
} else {
430+
self.broadcastAddress = nil
431+
self.pointToPointDestinationAddress = nil
432+
}
351433

352-
if (caddr.ifa_flags & UInt32(IFF_MULTICAST)) != 0 {
353-
self.multicastSupported = true
354-
} else {
355-
self.multicastSupported = false
356-
}
434+
if (caddr.ifa_flags & UInt32(IFF_MULTICAST)) != 0 {
435+
self.multicastSupported = true
436+
} else {
437+
self.multicastSupported = false
438+
}
357439

358-
do {
359-
self.interfaceIndex = Int(try Posix.if_nametoindex(caddr.ifa_name))
360-
} catch {
361-
return nil
440+
do {
441+
self.interfaceIndex = Int(try Posix.if_nametoindex(caddr.ifa_name))
442+
} catch {
443+
return nil
444+
}
362445
}
363446
}
364-
}
365447

366-
@available(*, deprecated, renamed: "NIONetworkDevice")
367-
extension NIONetworkInterface: CustomDebugStringConvertible {
368-
public var debugDescription: String {
369-
let baseString = "Interface \(self.name): address \(self.address)"
370-
let maskString = self.netmask != nil ? " netmask \(self.netmask!)" : ""
371-
return baseString + maskString
448+
@available(*, deprecated, renamed: "NIONetworkDevice")
449+
extension NIONetworkInterface: CustomDebugStringConvertible {
450+
public var debugDescription: String {
451+
let baseString = "Interface \(self.name): address \(self.address)"
452+
let maskString = self.netmask != nil ? " netmask \(self.netmask!)" : ""
453+
return baseString + maskString
454+
}
372455
}
373-
}
374456

375-
@available(*, deprecated, renamed: "NIONetworkDevice")
376-
extension NIONetworkInterface: Equatable {
377-
public static func ==(lhs: NIONetworkInterface, rhs: NIONetworkInterface) -> Bool {
378-
return lhs.name == rhs.name &&
379-
lhs.address == rhs.address &&
380-
lhs.netmask == rhs.netmask &&
381-
lhs.broadcastAddress == rhs.broadcastAddress &&
382-
lhs.pointToPointDestinationAddress == rhs.pointToPointDestinationAddress &&
383-
lhs.interfaceIndex == rhs.interfaceIndex
457+
@available(*, deprecated, renamed: "NIONetworkDevice")
458+
extension NIONetworkInterface: Equatable {
459+
public static func ==(lhs: NIONetworkInterface, rhs: NIONetworkInterface) -> Bool {
460+
return lhs.name == rhs.name &&
461+
lhs.address == rhs.address &&
462+
lhs.netmask == rhs.netmask &&
463+
lhs.broadcastAddress == rhs.broadcastAddress &&
464+
lhs.pointToPointDestinationAddress == rhs.pointToPointDestinationAddress &&
465+
lhs.interfaceIndex == rhs.interfaceIndex
466+
}
384467
}
385-
}
468+
#endif

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)

0 commit comments

Comments
 (0)