@@ -712,109 +712,132 @@ fn prepare_recv(
712712 hdr. msg_datalen = buf. len ( ) ;
713713}
714714
715+ /// Decoded metadata from control messages.
716+ struct DecodedCmsg {
717+ ecn_bits : u8 ,
718+ dst_ip : Option < IpAddr > ,
719+ interface_index : Option < u32 > ,
720+ stride : usize ,
721+ }
722+
723+ impl DecodedCmsg {
724+ fn new ( len : usize ) -> Self {
725+ Self {
726+ ecn_bits : 0 ,
727+ dst_ip : None ,
728+ interface_index : None ,
729+ stride : len,
730+ }
731+ }
732+ }
733+
734+ /// Decodes a control message and updates the decoded state.
735+ fn decode_cmsg ( cmsg : & libc:: cmsghdr , decoded : & mut DecodedCmsg ) {
736+ match ( cmsg. cmsg_level , cmsg. cmsg_type ) {
737+ ( libc:: IPPROTO_IP , libc:: IP_TOS ) => unsafe {
738+ decoded. ecn_bits = cmsg:: decode :: < u8 , libc:: cmsghdr > ( cmsg) ;
739+ } ,
740+ // FreeBSD uses IP_RECVTOS here, and we can be liberal because cmsgs are opt-in.
741+ #[ cfg( not( any(
742+ target_os = "openbsd" ,
743+ target_os = "netbsd" ,
744+ target_os = "dragonfly" ,
745+ solarish
746+ ) ) ) ]
747+ ( libc:: IPPROTO_IP , libc:: IP_RECVTOS ) => unsafe {
748+ decoded. ecn_bits = cmsg:: decode :: < u8 , libc:: cmsghdr > ( cmsg) ;
749+ } ,
750+ ( libc:: IPPROTO_IPV6 , libc:: IPV6_TCLASS ) => unsafe {
751+ // Temporary hack around broken macos ABI. Remove once upstream fixes it.
752+ // https://bugreport.apple.com/web/?problemID=48761855
753+ #[ allow( clippy:: unnecessary_cast) ] // cmsg.cmsg_len defined as size_t
754+ if cfg ! ( apple)
755+ && cmsg. cmsg_len as usize == libc:: CMSG_LEN ( mem:: size_of :: < u8 > ( ) as _ ) as usize
756+ {
757+ decoded. ecn_bits = cmsg:: decode :: < u8 , libc:: cmsghdr > ( cmsg) ;
758+ } else {
759+ decoded. ecn_bits = cmsg:: decode :: < libc:: c_int , libc:: cmsghdr > ( cmsg) as u8 ;
760+ }
761+ } ,
762+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
763+ ( libc:: IPPROTO_IP , libc:: IP_PKTINFO ) => {
764+ let pktinfo = unsafe { cmsg:: decode :: < libc:: in_pktinfo , libc:: cmsghdr > ( cmsg) } ;
765+ decoded. dst_ip = Some ( IpAddr :: V4 ( Ipv4Addr :: from (
766+ pktinfo. ipi_addr . s_addr . to_ne_bytes ( ) ,
767+ ) ) ) ;
768+ decoded. interface_index = Some ( pktinfo. ipi_ifindex as u32 ) ;
769+ }
770+ #[ cfg( any( bsd, apple) ) ]
771+ ( libc:: IPPROTO_IP , libc:: IP_RECVDSTADDR ) => {
772+ let in_addr = unsafe { cmsg:: decode :: < libc:: in_addr , libc:: cmsghdr > ( cmsg) } ;
773+ decoded. dst_ip = Some ( IpAddr :: V4 ( Ipv4Addr :: from ( in_addr. s_addr . to_ne_bytes ( ) ) ) ) ;
774+ }
775+ ( libc:: IPPROTO_IPV6 , libc:: IPV6_PKTINFO ) => {
776+ let pktinfo = unsafe { cmsg:: decode :: < libc:: in6_pktinfo , libc:: cmsghdr > ( cmsg) } ;
777+ decoded. dst_ip = Some ( IpAddr :: V6 ( Ipv6Addr :: from ( pktinfo. ipi6_addr . s6_addr ) ) ) ;
778+ decoded. interface_index = Some ( pktinfo. ipi6_ifindex as u32 ) ;
779+ }
780+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
781+ ( libc:: SOL_UDP , gro:: UDP_GRO ) => unsafe {
782+ decoded. stride = cmsg:: decode :: < libc:: c_int , libc:: cmsghdr > ( cmsg) as usize ;
783+ } ,
784+ _ => { }
785+ }
786+ }
787+
715788fn decode_recv (
716789 name : & MaybeUninit < libc:: sockaddr_storage > ,
717790 #[ cfg( not( apple_fast) ) ] hdr : & libc:: msghdr ,
718791 #[ cfg( apple_fast) ] hdr : & msghdr_x ,
719792 len : usize ,
720793) -> io:: Result < RecvMeta > {
721794 let name = unsafe { name. assume_init ( ) } ;
722- let mut ecn_bits = 0 ;
723- let mut dst_ip = None ;
724- let mut interface_index = None ;
725- #[ allow( unused_mut) ] // only mutable on Linux
726- let mut stride = len;
795+ let mut decoded = DecodedCmsg :: new ( len) ;
727796
728797 let cmsg_iter = unsafe { cmsg:: Iter :: new ( hdr) } ;
729798 for cmsg in cmsg_iter {
730- match ( cmsg. cmsg_level , cmsg. cmsg_type ) {
731- ( libc:: IPPROTO_IP , libc:: IP_TOS ) => unsafe {
732- ecn_bits = cmsg:: decode :: < u8 , libc:: cmsghdr > ( cmsg) ;
733- } ,
734- // FreeBSD uses IP_RECVTOS here, and we can be liberal because cmsgs are opt-in.
735- #[ cfg( not( any(
736- target_os = "openbsd" ,
737- target_os = "netbsd" ,
738- target_os = "dragonfly" ,
739- solarish
740- ) ) ) ]
741- ( libc:: IPPROTO_IP , libc:: IP_RECVTOS ) => unsafe {
742- ecn_bits = cmsg:: decode :: < u8 , libc:: cmsghdr > ( cmsg) ;
743- } ,
744- ( libc:: IPPROTO_IPV6 , libc:: IPV6_TCLASS ) => unsafe {
745- // Temporary hack around broken macos ABI. Remove once upstream fixes it.
746- // https://bugreport.apple.com/web/?problemID=48761855
747- #[ allow( clippy:: unnecessary_cast) ] // cmsg.cmsg_len defined as size_t
748- if cfg ! ( apple)
749- && cmsg. cmsg_len as usize == libc:: CMSG_LEN ( mem:: size_of :: < u8 > ( ) as _ ) as usize
750- {
751- ecn_bits = cmsg:: decode :: < u8 , libc:: cmsghdr > ( cmsg) ;
752- } else {
753- ecn_bits = cmsg:: decode :: < libc:: c_int , libc:: cmsghdr > ( cmsg) as u8 ;
754- }
755- } ,
756- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
757- ( libc:: IPPROTO_IP , libc:: IP_PKTINFO ) => {
758- let pktinfo = unsafe { cmsg:: decode :: < libc:: in_pktinfo , libc:: cmsghdr > ( cmsg) } ;
759- dst_ip = Some ( IpAddr :: V4 ( Ipv4Addr :: from (
760- pktinfo. ipi_addr . s_addr . to_ne_bytes ( ) ,
761- ) ) ) ;
762- interface_index = Some ( pktinfo. ipi_ifindex as u32 ) ;
763- }
764- #[ cfg( any( bsd, apple) ) ]
765- ( libc:: IPPROTO_IP , libc:: IP_RECVDSTADDR ) => {
766- let in_addr = unsafe { cmsg:: decode :: < libc:: in_addr , libc:: cmsghdr > ( cmsg) } ;
767- dst_ip = Some ( IpAddr :: V4 ( Ipv4Addr :: from ( in_addr. s_addr . to_ne_bytes ( ) ) ) ) ;
768- }
769- ( libc:: IPPROTO_IPV6 , libc:: IPV6_PKTINFO ) => {
770- let pktinfo = unsafe { cmsg:: decode :: < libc:: in6_pktinfo , libc:: cmsghdr > ( cmsg) } ;
771- dst_ip = Some ( IpAddr :: V6 ( Ipv6Addr :: from ( pktinfo. ipi6_addr . s6_addr ) ) ) ;
772- interface_index = Some ( pktinfo. ipi6_ifindex as u32 ) ;
773- }
774- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
775- ( libc:: SOL_UDP , gro:: UDP_GRO ) => unsafe {
776- stride = cmsg:: decode :: < libc:: c_int , libc:: cmsghdr > ( cmsg) as usize ;
777- } ,
778- _ => { }
779- }
799+ decode_cmsg ( cmsg, & mut decoded) ;
780800 }
781801
782- let addr = match libc:: c_int:: from ( name. ss_family ) {
802+ let addr = decode_sockaddr ( & name) ?;
803+
804+ Ok ( RecvMeta {
805+ len,
806+ stride : decoded. stride ,
807+ addr,
808+ ecn : EcnCodepoint :: from_bits ( decoded. ecn_bits ) ,
809+ dst_ip : decoded. dst_ip ,
810+ interface_index : decoded. interface_index ,
811+ } )
812+ }
813+
814+ /// Decodes a `sockaddr_storage` into a `SocketAddr`.
815+ fn decode_sockaddr ( name : & libc:: sockaddr_storage ) -> io:: Result < SocketAddr > {
816+ match libc:: c_int:: from ( name. ss_family ) {
783817 libc:: AF_INET => {
784818 // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
785819 let addr: & libc:: sockaddr_in =
786- unsafe { & * ( & name as * const _ as * const libc:: sockaddr_in ) } ;
787- SocketAddr :: V4 ( SocketAddrV4 :: new (
820+ unsafe { & * ( name as * const _ as * const libc:: sockaddr_in ) } ;
821+ Ok ( SocketAddr :: V4 ( SocketAddrV4 :: new (
788822 Ipv4Addr :: from ( addr. sin_addr . s_addr . to_ne_bytes ( ) ) ,
789823 u16:: from_be ( addr. sin_port ) ,
790- ) )
824+ ) ) )
791825 }
792826 libc:: AF_INET6 => {
793827 // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
794828 let addr: & libc:: sockaddr_in6 =
795- unsafe { & * ( & name as * const _ as * const libc:: sockaddr_in6 ) } ;
796- SocketAddr :: V6 ( SocketAddrV6 :: new (
829+ unsafe { & * ( name as * const _ as * const libc:: sockaddr_in6 ) } ;
830+ Ok ( SocketAddr :: V6 ( SocketAddrV6 :: new (
797831 Ipv6Addr :: from ( addr. sin6_addr . s6_addr ) ,
798832 u16:: from_be ( addr. sin6_port ) ,
799833 addr. sin6_flowinfo ,
800834 addr. sin6_scope_id ,
801- ) )
835+ ) ) )
802836 }
803- f => {
804- return Err ( io:: Error :: other ( format ! (
805- "expected AF_INET or AF_INET6, got {f} in decode_recv"
806- ) ) ) ;
807- }
808- } ;
809-
810- Ok ( RecvMeta {
811- len,
812- stride,
813- addr,
814- ecn : EcnCodepoint :: from_bits ( ecn_bits) ,
815- dst_ip,
816- interface_index,
817- } )
837+ f => Err ( io:: Error :: other ( format ! (
838+ "expected AF_INET or AF_INET6, got {f}"
839+ ) ) ) ,
840+ }
818841}
819842
820843#[ cfg( not( apple_slow) ) ]
0 commit comments