Skip to content

Commit 12be3ae

Browse files
committed
refactor(quinn-udp): extract decode_cmsg helper
Extract control message decoding logic into dedicated helper functions. This refactoring prepares the codebase for future work that will make `decode_recv` generic over the `MsgHdr` trait, enabling shared decoding logic between `libc::msghdr` and `msghdr_x` message types. Broken out of quinn-rs#2463 as suggested by @djc.
1 parent 4de6d2c commit 12be3ae

File tree

1 file changed

+100
-77
lines changed

1 file changed

+100
-77
lines changed

quinn-udp/src/unix.rs

Lines changed: 100 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
715788
fn 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

Comments
 (0)