diff --git a/quinn-udp/src/unix.rs b/quinn-udp/src/unix.rs index ff1da31f7..031e78845 100644 --- a/quinn-udp/src/unix.rs +++ b/quinn-udp/src/unix.rs @@ -707,17 +707,42 @@ fn decode_recv>( len: usize, ) -> io::Result { let name = unsafe { name.assume_init() }; - let mut ecn_bits = 0; - let mut dst_ip = None; - let mut interface_index = None; - #[allow(unused_mut)] // only mutable on Linux - let mut stride = len; + let mut ctrl = ControlMetadata { + ecn_bits: 0, + dst_ip: None, + interface_index: None, + stride: len, + }; let cmsg_iter = unsafe { cmsg::Iter::new(hdr) }; for cmsg in cmsg_iter { + ctrl.decode(cmsg); + } + + Ok(RecvMeta { + len, + stride: ctrl.stride, + addr: decode_socket_addr(&name)?, + ecn: EcnCodepoint::from_bits(ctrl.ecn_bits), + dst_ip: ctrl.dst_ip, + interface_index: ctrl.interface_index, + }) +} + +/// Metadata decoded from control messages +struct ControlMetadata { + ecn_bits: u8, + dst_ip: Option, + interface_index: Option, + stride: usize, +} + +impl ControlMetadata { + /// Decodes a control message and updates the metadata state + fn decode(&mut self, cmsg: &libc::cmsghdr) { match (cmsg.cmsg_level, cmsg.cmsg_type) { (libc::IPPROTO_IP, libc::IP_TOS) => unsafe { - ecn_bits = cmsg::decode::(cmsg); + self.ecn_bits = cmsg::decode::(cmsg); }, // FreeBSD uses IP_RECVTOS here, and we can be liberal because cmsgs are opt-in. #[cfg(not(any( @@ -727,7 +752,7 @@ fn decode_recv>( solarish )))] (libc::IPPROTO_IP, libc::IP_RECVTOS) => unsafe { - ecn_bits = cmsg::decode::(cmsg); + self.ecn_bits = cmsg::decode::(cmsg); }, (libc::IPPROTO_IPV6, libc::IPV6_TCLASS) => unsafe { // Temporary hack around broken macos ABI. Remove once upstream fixes it. @@ -736,45 +761,36 @@ fn decode_recv>( if cfg!(apple) && cmsg.cmsg_len as usize == libc::CMSG_LEN(mem::size_of::() as _) as usize { - ecn_bits = cmsg::decode::(cmsg); + self.ecn_bits = cmsg::decode::(cmsg); } else { - ecn_bits = cmsg::decode::(cmsg) as u8; + self.ecn_bits = cmsg::decode::(cmsg) as u8; } }, #[cfg(any(target_os = "linux", target_os = "android"))] (libc::IPPROTO_IP, libc::IP_PKTINFO) => { let pktinfo = unsafe { cmsg::decode::(cmsg) }; - dst_ip = Some(IpAddr::V4(Ipv4Addr::from( + self.dst_ip = Some(IpAddr::V4(Ipv4Addr::from( pktinfo.ipi_addr.s_addr.to_ne_bytes(), ))); - interface_index = Some(pktinfo.ipi_ifindex as u32); + self.interface_index = Some(pktinfo.ipi_ifindex as u32); } #[cfg(any(bsd, apple))] (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => { let in_addr = unsafe { cmsg::decode::(cmsg) }; - dst_ip = Some(IpAddr::V4(Ipv4Addr::from(in_addr.s_addr.to_ne_bytes()))); + self.dst_ip = Some(IpAddr::V4(Ipv4Addr::from(in_addr.s_addr.to_ne_bytes()))); } (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => { let pktinfo = unsafe { cmsg::decode::(cmsg) }; - dst_ip = Some(IpAddr::V6(Ipv6Addr::from(pktinfo.ipi6_addr.s6_addr))); - interface_index = Some(pktinfo.ipi6_ifindex as u32); + self.dst_ip = Some(IpAddr::V6(Ipv6Addr::from(pktinfo.ipi6_addr.s6_addr))); + self.interface_index = Some(pktinfo.ipi6_ifindex as u32); } #[cfg(any(target_os = "linux", target_os = "android"))] (libc::SOL_UDP, libc::UDP_GRO) => unsafe { - stride = cmsg::decode::(cmsg) as usize; + self.stride = cmsg::decode::(cmsg) as usize; }, _ => {} } } - - Ok(RecvMeta { - len, - stride, - addr: decode_socket_addr(&name)?, - ecn: EcnCodepoint::from_bits(ecn_bits), - dst_ip, - interface_index, - }) } /// Decodes a `sockaddr_storage` into a `SocketAddr`