@@ -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