Skip to content

Commit 2af9fe0

Browse files
committed
UDP: support for multihoming with unbound sockets
1 parent b707600 commit 2af9fe0

File tree

8 files changed

+398
-1
lines changed

8 files changed

+398
-1
lines changed

FlyingSocks/Sources/AsyncSocket.swift

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,25 @@ public extension AsyncSocketPool where Self == SocketPool<Poll> {
6262

6363
public struct AsyncSocket: Sendable {
6464

65+
public struct Message {
66+
public let peerAddress: sockaddr_storage
67+
public let bytes: [UInt8]
68+
public let interfaceIndex: UInt32?
69+
public let localAddress: sockaddr_storage?
70+
71+
public init(
72+
peerAddress: sockaddr_storage,
73+
bytes: [UInt8],
74+
interfaceIndex: UInt32? = nil,
75+
localAddress: sockaddr_storage? = nil
76+
) {
77+
self.peerAddress = peerAddress
78+
self.bytes = bytes
79+
self.interfaceIndex = interfaceIndex
80+
self.localAddress = localAddress
81+
}
82+
}
83+
6584
public let socket: Socket
6685
let pool: any AsyncSocketPool
6786

@@ -143,6 +162,21 @@ public struct AsyncSocket: Sendable {
143162
} while true
144163
}
145164

165+
public func receive(atMost length: Int) async throws -> Message {
166+
try Task.checkCancellation()
167+
168+
repeat {
169+
do {
170+
let (peerAddress, bytes, interfaceIndex, localAddress) = try socket.receive(length: length)
171+
return Message(peerAddress: peerAddress, bytes: bytes, interfaceIndex: interfaceIndex, localAddress: localAddress)
172+
} catch SocketError.blocked {
173+
try await pool.suspendSocket(socket, untilReadyFor: .read)
174+
} catch {
175+
throw error
176+
}
177+
} while true
178+
}
179+
146180
/// Reads bytes from the socket up to by not over/
147181
/// - Parameter bytes: The max number of bytes to read
148182
/// - Returns: an array of the read bytes capped to the number of bytes provided.
@@ -190,6 +224,29 @@ public struct AsyncSocket: Sendable {
190224
try await send(Array(data), to: address)
191225
}
192226

227+
public func send(
228+
message: [UInt8],
229+
to peerAddress: some SocketAddress,
230+
interfaceIndex: UInt32? = nil,
231+
from localAddress: (some SocketAddress)? = nil
232+
) async throws {
233+
let sent = try await pool.loopUntilReady(for: .write, on: socket) {
234+
try socket.send(message: message, to: peerAddress, interfaceIndex: interfaceIndex, from: localAddress)
235+
}
236+
guard sent == message.count else {
237+
throw SocketError.disconnected
238+
}
239+
}
240+
241+
public func send(
242+
message: Data,
243+
to peerAddress: some SocketAddress,
244+
interfaceIndex: UInt32? = nil,
245+
from localAddress: (some SocketAddress)? = nil
246+
) async throws {
247+
try await send(message: Array(message), to: peerAddress, interfaceIndex: interfaceIndex, from: localAddress)
248+
}
249+
193250
public func close() throws {
194251
try socket.close()
195252
}
@@ -275,7 +332,7 @@ public struct AsyncSocketSequence: AsyncSequence, AsyncIteratorProtocol, Sendabl
275332
public struct AsyncSocketMessageSequence: AsyncSequence, AsyncIteratorProtocol, Sendable {
276333
public static let DefaultMaxMessageLength: Int = 1500
277334

278-
public typealias Element = (sockaddr_storage, [UInt8])
335+
public typealias Element = AsyncSocket.Message
279336

280337
private let socket: AsyncSocket
281338
private let maxMessageLength: Int

FlyingSocks/Sources/Socket+Android.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ extension Socket {
4747
static let stream = Int32(SOCK_STREAM)
4848
static let datagram = Int32(SOCK_DGRAM)
4949
static let in_addr_any = Android.in_addr(s_addr: Android.in_addr_t(0))
50+
static let ip_pktinfo = Int32(IP_PKTINFO)
51+
static let ipv6_pktinfo = Int32(IPV6_PKTINFO)
5052

5153
static func makeAddressINET(port: UInt16) -> Android.sockaddr_in {
5254
Android.sockaddr_in(
@@ -184,6 +186,14 @@ extension Socket {
184186
static func sendto(_ fd: FileDescriptorType, _ buffer: UnsafeRawPointer!, _ nbyte: Int, _ flags: Int32, _ destaddr: UnsafePointer<sockaddr>!, _ destlen: socklen_t) -> Int {
185187
Android.sendto(fd, buffer, nbyte, flags, destaddr, destlen)
186188
}
189+
190+
static func recvmsg(_ fd: FileDescriptorType, _ message: UnsafeMutablePointer<msghdr>, _ flags: Int32) -> Int {
191+
Android.recvmsg(fd, message, flags)
192+
}
193+
194+
static func sendmsg(_ fd: FileDescriptorType, _ message: UnsafePointer<msghdr>, _ flags: Int32) -> Int {
195+
Android.sendmsg(fd, message, flags)
196+
}
187197
}
188198

189199
#endif

FlyingSocks/Sources/Socket+Darwin.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ extension Socket {
4444
static let stream = Int32(SOCK_STREAM)
4545
static let datagram = Int32(SOCK_DGRAM)
4646
static let in_addr_any = Darwin.in_addr(s_addr: Darwin.in_addr_t(0))
47+
static let ip_pktinfo = Int32(IP_PKTINFO)
48+
static let ipv6_pktinfo = Int32(50) // __APPLE_USE_RFC_2292
4749

4850
static func makeAddressINET(port: UInt16) -> Darwin.sockaddr_in {
4951
Darwin.sockaddr_in(
@@ -185,6 +187,14 @@ extension Socket {
185187
static func sendto(_ fd: FileDescriptorType, _ buffer: UnsafeRawPointer!, _ nbyte: Int, _ flags: Int32, _ destaddr: UnsafePointer<sockaddr>!, _ destlen: socklen_t) -> Int {
186188
Darwin.sendto(fd, buffer, nbyte, flags, destaddr, destlen)
187189
}
190+
191+
static func recvmsg(_ fd: FileDescriptorType, _ message: UnsafeMutablePointer<msghdr>, _ flags: Int32) -> Int {
192+
Darwin.recvmsg(fd, message, flags)
193+
}
194+
195+
static func sendmsg(_ fd: FileDescriptorType, _ message: UnsafePointer<msghdr>, _ flags: Int32) -> Int {
196+
Darwin.sendmsg(fd, message, flags)
197+
}
188198
}
189199

190200
#endif

FlyingSocks/Sources/Socket+Glibc.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ extension Socket {
4444
static let stream = Int32(SOCK_STREAM.rawValue)
4545
static let datagram = Int32(SOCK_DGRAM.rawValue)
4646
static let in_addr_any = Glibc.in_addr(s_addr: Glibc.in_addr_t(0))
47+
static let ip_pktinfo = Int32(IP_PKTINFO.rawValue)
48+
static let ipv6_pktinfo = Int32(IPV6_PKTINFO.rawValue)
4749

4850
static func makeAddressINET(port: UInt16) -> Glibc.sockaddr_in {
4951
Glibc.sockaddr_in(
@@ -181,6 +183,14 @@ extension Socket {
181183
static func sendto(_ fd: FileDescriptorType, _ buffer: UnsafeRawPointer!, _ nbyte: Int, _ flags: Int32, _ destaddr: UnsafePointer<sockaddr>!, _ destlen: socklen_t) -> Int {
182184
Glibc.sendto(fd, buffer, nbyte, flags, destaddr, destlen)
183185
}
186+
187+
static func recvmsg(_ fd: FileDescriptorType, _ message: UnsafeMutablePointer<msghdr>, _ flags: Int32) -> Int {
188+
Glibc.recvmsg(fd, message, flags)
189+
}
190+
191+
static func sendmsg(_ fd: FileDescriptorType, _ message: UnsafePointer<msghdr>, _ flags: Int32) -> Int {
192+
Glibc.sendmsg(fd, message, flags)
193+
}
184194
}
185195

186196
#endif

FlyingSocks/Sources/Socket+Musl.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ extension Socket {
4444
static let stream = Int32(SOCK_STREAM)
4545
static let datagram = Int32(SOCK_DGRAM)
4646
static let in_addr_any = Musl.in_addr(s_addr: Musl.in_addr_t(0))
47+
static let ip_pktinfo = Int32(IP_PKTINFO)
48+
static let ipv6_pktinfo = Int32(IPV6_PKTINFO)
4749

4850
static func makeAddressINET(port: UInt16) -> Musl.sockaddr_in {
4951
Musl.sockaddr_in(
@@ -181,6 +183,14 @@ extension Socket {
181183
static func sendto(_ fd: FileDescriptorType, _ buffer: UnsafeRawPointer!, _ nbyte: Int, _ flags: Int32, _ destaddr: UnsafePointer<sockaddr>!, _ destlen: socklen_t) -> Int {
182184
Musl.sendto(fd, buffer, nbyte, flags, destaddr, destlen)
183185
}
186+
187+
static func recvmsg(_ fd: FileDescriptorType, _ message: UnsafeMutablePointer<msghdr>, _ flags: Int32) -> Int {
188+
Musl.recvmsg(fd, message, flags)
189+
}
190+
191+
static func sendmsg(_ fd: FileDescriptorType, _ message: UnsafePointer<msghdr>, _ flags: Int32) -> Int {
192+
Musl.sendmsg(fd, message, flags)
193+
}
184194
}
185195

186196
#endif

FlyingSocks/Sources/Socket+WinSock2.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ extension Socket {
5454
static let stream = Int32(SOCK_STREAM)
5555
static let datagram = Int32(SOCK_DGRAM)
5656
static let in_addr_any = WinSDK.in_addr()
57+
static let ip_pktinfo = Int32(IP_PKTINFO)
58+
static let ipv6_pktinfo = Int32(IPV6_PKTINFO)
5759

5860
static func makeAddressINET(port: UInt16) -> WinSDK.sockaddr_in {
5961
WinSDK.sockaddr_in(
@@ -193,6 +195,14 @@ extension Socket {
193195
static func sendto(_ fd: FileDescriptorType, _ buffer: UnsafeRawPointer!, _ nbyte: Int, _ flags: Int32, _ destaddr: UnsafePointer<sockaddr>!, _ destlen: socklen_t) -> Int {
194196
WinSDK.sendto(fd, buffer, nbyte, flags, destaddr, destlen)
195197
}
198+
199+
static func recvmsg(_ fd: FileDescriptorType, _ message: UnsafeMutablePointer<msghdr>, _ flags: Int32) -> Int {
200+
WinSDK.recvmsg(fd, message, flags)
201+
}
202+
203+
static func sendmsg(_ fd: FileDescriptorType, _ message: UnsafePointer<msghdr>, _ flags: Int32) -> Int {
204+
WinSDK.sendmsg(fd, message, flags)
205+
}
196206
}
197207

198208
#endif

0 commit comments

Comments
 (0)