Skip to content

Commit 0e8d25c

Browse files
committed
msghdr control helper API
1 parent a710195 commit 0e8d25c

File tree

1 file changed

+139
-0
lines changed

1 file changed

+139
-0
lines changed

Sources/IORingUtils/Socket.swift

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,3 +406,142 @@ public struct Socket: CustomStringConvertible, Equatable, Hashable, Sendable {
406406
return fileHandle.fileDescriptor < 0
407407
}
408408
}
409+
410+
struct in6_pktinfo {
411+
var ipi6_addr: in6_addr
412+
var ipi6_ifindex: CUnsignedInt
413+
}
414+
415+
extension Message {
416+
static func withControlMessage(
417+
control: UnsafeRawPointer,
418+
controllen: Int,
419+
_ body: (cmsghdr, UnsafeRawBufferPointer) -> ()
420+
) {
421+
let controlBuffer = UnsafeRawBufferPointer(start: control, count: Int(controllen))
422+
var cmsgHeaderIndex = 0
423+
424+
while true {
425+
let cmsgDataIndex = cmsgHeaderIndex + MemoryLayout<cmsghdr>.stride
426+
427+
if cmsgDataIndex > controllen {
428+
break
429+
}
430+
431+
let header = controlBuffer.load(fromByteOffset: cmsgHeaderIndex, as: cmsghdr.self)
432+
if Int(header.cmsg_len) < MemoryLayout<cmsghdr>.stride {
433+
break
434+
}
435+
436+
cmsgHeaderIndex = cmsgDataIndex
437+
cmsgHeaderIndex += Int(header.cmsg_len) - MemoryLayout<cmsghdr>.stride
438+
if cmsgHeaderIndex > controlBuffer.count {
439+
break
440+
}
441+
body(
442+
header,
443+
UnsafeRawBufferPointer(rebasing: controlBuffer[cmsgDataIndex..<cmsgHeaderIndex])
444+
)
445+
446+
cmsgHeaderIndex += MemoryLayout<cmsghdr>.alignment - 1
447+
cmsgHeaderIndex &= ~(MemoryLayout<cmsghdr>.alignment - 1)
448+
}
449+
}
450+
451+
static func getPacketInfoControl(
452+
msghdr: msghdr
453+
) -> (UInt32?, (any SocketAddress)?) {
454+
var interfaceIndex: UInt32?
455+
var localAddress: (any SocketAddress)?
456+
457+
withControlMessage(
458+
control: msghdr.msg_control,
459+
controllen: msghdr.msg_controllen
460+
) { cmsghdr, cmsgdata in
461+
switch Int(cmsghdr.cmsg_level) {
462+
case IPPROTO_IP:
463+
guard cmsghdr.cmsg_type == IP_PKTINFO else { break }
464+
cmsgdata.baseAddress!
465+
.withMemoryRebound(to: in_pktinfo.self, capacity: 1) { pktinfo in
466+
var sin = sockaddr_in()
467+
sin.sin_addr = pktinfo.pointee.ipi_addr
468+
interfaceIndex = UInt32(pktinfo.pointee.ipi_ifindex)
469+
localAddress = sin
470+
}
471+
case IPPROTO_IPV6:
472+
guard cmsghdr.cmsg_type == IPV6_PKTINFO else { break }
473+
cmsgdata.baseAddress!
474+
.withMemoryRebound(to: in6_pktinfo.self, capacity: 1) { pktinfo in
475+
var sin6 = sockaddr_in6()
476+
sin6.sin6_addr = pktinfo.pointee.ipi6_addr
477+
interfaceIndex = UInt32(pktinfo.pointee.ipi6_ifindex)
478+
localAddress = sin6
479+
}
480+
default:
481+
break
482+
}
483+
}
484+
485+
return (interfaceIndex, localAddress)
486+
}
487+
488+
func withPacketInfoControl<T>(
489+
family: sa_family_t,
490+
interfaceIndex: UInt32?,
491+
address: (some SocketAddress)?,
492+
_ body: (UnsafePointer<cmsghdr>?, Int) -> T
493+
) -> T {
494+
switch Int32(family) {
495+
case AF_INET:
496+
let buffer = ManagedBuffer<cmsghdr, in_pktinfo>.create(minimumCapacity: 1) { buffer in
497+
buffer.withUnsafeMutablePointers { header, element in
498+
header.pointee
499+
.cmsg_len = Int(MemoryLayout<cmsghdr>.size + MemoryLayout<in_pktinfo>.size)
500+
header.pointee.cmsg_level = SOL_SOCKET
501+
header.pointee.cmsg_type = Int32(IPPROTO_IP)
502+
element.pointee.ipi_ifindex = Int32(interfaceIndex ?? 0)
503+
if let address {
504+
var address = address
505+
withUnsafePointer(to: &address) {
506+
$0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
507+
element.pointee.ipi_addr = $0.pointee.sin_addr
508+
}
509+
}
510+
} else {
511+
element.pointee.ipi_addr.s_addr = 0
512+
}
513+
514+
return header.pointee
515+
}
516+
}
517+
518+
return buffer.withUnsafeMutablePointerToHeader { body($0, Int($0.pointee.cmsg_len)) }
519+
case AF_INET6:
520+
let buffer = ManagedBuffer<cmsghdr, in6_pktinfo>.create(minimumCapacity: 1) { buffer in
521+
buffer.withUnsafeMutablePointers { header, element in
522+
header.pointee
523+
.cmsg_len = Int(MemoryLayout<cmsghdr>.size + MemoryLayout<in6_pktinfo>.size)
524+
header.pointee.cmsg_level = SOL_SOCKET
525+
header.pointee.cmsg_type = Int32(IPPROTO_IPV6)
526+
element.pointee.ipi6_ifindex = interfaceIndex ?? 0
527+
if let address {
528+
var address = address
529+
withUnsafePointer(to: &address) {
530+
$0.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
531+
element.pointee.ipi6_addr = $0.pointee.sin6_addr
532+
}
533+
}
534+
} else {
535+
element.pointee.ipi6_addr = in6_addr()
536+
}
537+
538+
return header.pointee
539+
}
540+
}
541+
542+
return buffer.withUnsafeMutablePointerToHeader { body($0, Int($0.pointee.cmsg_len)) }
543+
default:
544+
return body(nil, 0)
545+
}
546+
}
547+
}

0 commit comments

Comments
 (0)