diff --git a/quinn/Cargo.toml b/quinn/Cargo.toml index f7eb4c315..b6e266319 100644 --- a/quinn/Cargo.toml +++ b/quinn/Cargo.toml @@ -33,6 +33,7 @@ libc = "0.2.69" mio = { version = "0.7.7", features = ["net"] } proto = { package = "quinn-proto", path = "../quinn-proto", version = "0.6.1" } rustls = { version = "0.19", features = ["quic"], optional = true } +socket2 = "0.3" thiserror = "1.0.21" tracing = "0.1.10" tokio = { version = "1.0.1", features = ["net", "rt", "rt-multi-thread", "time"] } diff --git a/quinn/src/platform/unix.rs b/quinn/src/platform/unix.rs index 95ec7716e..f3e000a35 100644 --- a/quinn/src/platform/unix.rs +++ b/quinn/src/platform/unix.rs @@ -198,9 +198,25 @@ fn send(io: &mio::net::UdpSocket, transmits: &[Transmit]) -> io::Result { let mut msgs: [libc::mmsghdr; BATCH_SIZE] = unsafe { mem::zeroed() }; let mut iovecs: [libc::iovec; BATCH_SIZE] = unsafe { mem::zeroed() }; let mut cmsgs = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE]; + // This assume_init looks a bit weird because one might think it + // assumes the SockAddr data to be initialized, but that call + // refers to the whole array, which itself is made up of MaybeUninit + // containers. Their presence protects the SockAddr inside from + // being assumed as initialized by the assume_init call. + // TODO: Replace this with uninit_array once it becomes MSRV-stable + let mut addrs: [MaybeUninit; BATCH_SIZE] = + unsafe { MaybeUninit::uninit().assume_init() }; for (i, transmit) in transmits.iter().enumerate().take(BATCH_SIZE) { + let dst_addr = unsafe { + std::ptr::write( + addrs[i].as_mut_ptr(), + socket2::SockAddr::from(transmit.destination), + ); + &*addrs[i].as_ptr() + }; prepare_msg( transmit, + dst_addr, &mut msgs[i].msg_hdr, &mut iovecs[i], &mut cmsgs[i], @@ -233,7 +249,8 @@ fn send(io: &mio::net::UdpSocket, transmits: &[Transmit]) -> io::Result { let mut ctrl = cmsg::Aligned([0u8; CMSG_LEN]); let mut sent = 0; while sent < transmits.len() { - prepare_msg(&transmits[sent], &mut hdr, &mut iov, &mut ctrl); + let addr = socket2::SockAddr::from(transmits[sent].destination); + prepare_msg(&transmits[sent], &addr, &mut hdr, &mut iov, &mut ctrl); let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) }; if n == -1 { let e = io::Error::last_os_error(); @@ -334,6 +351,7 @@ const CMSG_LEN: usize = 80; fn prepare_msg( transmit: &Transmit, + dst_addr: &socket2::SockAddr, hdr: &mut libc::msghdr, iov: &mut libc::iovec, ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>, @@ -341,12 +359,15 @@ fn prepare_msg( iov.iov_base = transmit.contents.as_ptr() as *const _ as *mut _; iov.iov_len = transmit.contents.len(); - let (name, namelen) = match transmit.destination { - SocketAddr::V4(ref addr) => (addr as *const _ as _, mem::size_of::()), - SocketAddr::V6(ref addr) => (addr as *const _ as _, mem::size_of::()), - }; - hdr.msg_name = name; - hdr.msg_namelen = namelen as _; + // SAFETY: Casting the pointer to a mutable one is legal, + // as sendmsg is guaranteed to not alter the mutable pointer + // as per the POSIX spec. See the section on the sys/socket.h + // header for details. The type is only mutable in the first + // place because it is reused by recvmsg as well. + let name = dst_addr.as_ptr() as *mut libc::c_void; + let namelen = dst_addr.len(); + hdr.msg_name = name as *mut _; + hdr.msg_namelen = namelen; hdr.msg_iov = iov; hdr.msg_iovlen = 1;