diff options
Diffstat (limited to 'vendor/nix/test/sys/test_socket.rs')
-rw-r--r-- | vendor/nix/test/sys/test_socket.rs | 2628 |
1 files changed, 0 insertions, 2628 deletions
diff --git a/vendor/nix/test/sys/test_socket.rs b/vendor/nix/test/sys/test_socket.rs deleted file mode 100644 index 5adc77ed6..000000000 --- a/vendor/nix/test/sys/test_socket.rs +++ /dev/null @@ -1,2628 +0,0 @@ -#[cfg(any(target_os = "linux", target_os = "android"))] -use crate::*; -use libc::{c_char, sockaddr_storage}; -#[allow(deprecated)] -use nix::sys::socket::InetAddr; -use nix::sys::socket::{ - getsockname, sockaddr, sockaddr_in6, AddressFamily, UnixAddr, -}; -use std::collections::hash_map::DefaultHasher; -use std::hash::{Hash, Hasher}; -use std::mem::{self, MaybeUninit}; -use std::net::{self, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; -use std::os::unix::io::RawFd; -use std::path::Path; -use std::slice; -use std::str::FromStr; - -#[allow(deprecated)] -#[test] -pub fn test_inetv4_addr_to_sock_addr() { - let actual: net::SocketAddr = FromStr::from_str("127.0.0.1:3000").unwrap(); - let addr = InetAddr::from_std(&actual); - - match addr { - InetAddr::V4(addr) => { - let ip: u32 = 0x7f00_0001; - let port: u16 = 3000; - let saddr = addr.sin_addr.s_addr; - - assert_eq!(saddr, ip.to_be()); - assert_eq!(addr.sin_port, port.to_be()); - } - _ => panic!("nope"), - } - - assert_eq!(addr.to_string(), "127.0.0.1:3000"); - - let inet = addr.to_std(); - assert_eq!(actual, inet); -} - -#[allow(deprecated)] -#[test] -pub fn test_inetv4_addr_roundtrip_sockaddr_storage_to_addr() { - use nix::sys::socket::{sockaddr_storage_to_addr, SockAddr}; - - let actual: net::SocketAddr = FromStr::from_str("127.0.0.1:3000").unwrap(); - let addr = InetAddr::from_std(&actual); - let sockaddr = SockAddr::new_inet(addr); - - let (storage, ffi_size) = { - let mut storage = MaybeUninit::<sockaddr_storage>::zeroed(); - let storage_ptr = storage.as_mut_ptr().cast::<sockaddr>(); - let (ffi_ptr, ffi_size) = sockaddr.as_ffi_pair(); - assert_eq!(mem::size_of::<sockaddr>(), ffi_size as usize); - unsafe { - storage_ptr.copy_from_nonoverlapping(ffi_ptr as *const sockaddr, 1); - (storage.assume_init(), ffi_size) - } - }; - - let from_storage = - sockaddr_storage_to_addr(&storage, ffi_size as usize).unwrap(); - assert_eq!(from_storage, sockaddr); - let from_storage = - sockaddr_storage_to_addr(&storage, mem::size_of::<sockaddr_storage>()) - .unwrap(); - assert_eq!(from_storage, sockaddr); -} - -#[cfg(any(target_os = "linux"))] -#[cfg_attr(qemu, ignore)] -#[test] -pub fn test_timestamping() { - use nix::sys::socket::{ - recvmsg, sendmsg, setsockopt, socket, sockopt::Timestamping, - ControlMessageOwned, MsgFlags, SockFlag, SockType, SockaddrIn, - TimestampingFlag, - }; - use std::io::{IoSlice, IoSliceMut}; - - let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap(); - - let ssock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - - let rsock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .unwrap(); - nix::sys::socket::bind(rsock, &sock_addr).unwrap(); - - setsockopt(rsock, Timestamping, &TimestampingFlag::all()).unwrap(); - - let sbuf = [0u8; 2048]; - let mut rbuf = [0u8; 2048]; - let flags = MsgFlags::empty(); - let iov1 = [IoSlice::new(&sbuf)]; - let mut iov2 = [IoSliceMut::new(&mut rbuf)]; - - let mut cmsg = cmsg_space!(nix::sys::socket::Timestamps); - sendmsg(ssock, &iov1, &[], flags, Some(&sock_addr)).unwrap(); - let recv = recvmsg::<()>(rsock, &mut iov2, Some(&mut cmsg), flags).unwrap(); - - let mut ts = None; - for c in recv.cmsgs() { - if let ControlMessageOwned::ScmTimestampsns(timestamps) = c { - ts = Some(timestamps.system); - } - } - let ts = ts.expect("ScmTimestampns is present"); - let sys_time = - ::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_REALTIME) - .unwrap(); - let diff = if ts > sys_time { - ts - sys_time - } else { - sys_time - ts - }; - assert!(std::time::Duration::from(diff).as_secs() < 60); -} - -#[allow(deprecated)] -#[test] -pub fn test_inetv6_addr_roundtrip_sockaddr_storage_to_addr() { - use nix::sys::socket::{sockaddr_storage_to_addr, SockAddr}; - - let port: u16 = 3000; - let flowinfo: u32 = 1; - let scope_id: u32 = 2; - let ip: Ipv6Addr = "fe80::1".parse().unwrap(); - - let actual = - SocketAddr::V6(SocketAddrV6::new(ip, port, flowinfo, scope_id)); - let addr = InetAddr::from_std(&actual); - let sockaddr = SockAddr::new_inet(addr); - - let (storage, ffi_size) = { - let mut storage = MaybeUninit::<sockaddr_storage>::zeroed(); - let storage_ptr = storage.as_mut_ptr().cast::<sockaddr_in6>(); - let (ffi_ptr, ffi_size) = sockaddr.as_ffi_pair(); - assert_eq!(mem::size_of::<sockaddr_in6>(), ffi_size as usize); - unsafe { - storage_ptr.copy_from_nonoverlapping( - (ffi_ptr as *const sockaddr).cast::<sockaddr_in6>(), - 1, - ); - (storage.assume_init(), ffi_size) - } - }; - - let from_storage = - sockaddr_storage_to_addr(&storage, ffi_size as usize).unwrap(); - assert_eq!(from_storage, sockaddr); - let from_storage = - sockaddr_storage_to_addr(&storage, mem::size_of::<sockaddr_storage>()) - .unwrap(); - assert_eq!(from_storage, sockaddr); -} - -#[test] -pub fn test_path_to_sock_addr() { - let path = "/foo/bar"; - let actual = Path::new(path); - let addr = UnixAddr::new(actual).unwrap(); - - let expect: &[c_char] = unsafe { - slice::from_raw_parts(path.as_ptr() as *const c_char, path.len()) - }; - assert_eq!(unsafe { &(*addr.as_ptr()).sun_path[..8] }, expect); - - assert_eq!(addr.path(), Some(actual)); -} - -fn calculate_hash<T: Hash>(t: &T) -> u64 { - let mut s = DefaultHasher::new(); - t.hash(&mut s); - s.finish() -} - -#[test] -pub fn test_addr_equality_path() { - let path = "/foo/bar"; - let actual = Path::new(path); - let addr1 = UnixAddr::new(actual).unwrap(); - let mut addr2 = addr1; - - unsafe { (*addr2.as_mut_ptr()).sun_path[10] = 127 }; - - assert_eq!(addr1, addr2); - assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2)); -} - -#[cfg(any(target_os = "android", target_os = "linux"))] -#[test] -pub fn test_abstract_sun_path_too_long() { - let name = String::from("nix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0testttttnix\0abstract\0test\0make\0sure\0this\0is\0long\0enough"); - let addr = UnixAddr::new_abstract(name.as_bytes()); - addr.expect_err("assertion failed"); -} - -#[cfg(any(target_os = "android", target_os = "linux"))] -#[test] -pub fn test_addr_equality_abstract() { - let name = String::from("nix\0abstract\0test"); - let addr1 = UnixAddr::new_abstract(name.as_bytes()).unwrap(); - let mut addr2 = addr1; - - assert_eq!(addr1, addr2); - assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2)); - - unsafe { (*addr2.as_mut_ptr()).sun_path[17] = 127 }; - assert_ne!(addr1, addr2); - assert_ne!(calculate_hash(&addr1), calculate_hash(&addr2)); -} - -// Test getting/setting abstract addresses (without unix socket creation) -#[cfg(any(target_os = "android", target_os = "linux"))] -#[test] -pub fn test_abstract_uds_addr() { - let empty = String::new(); - let addr = UnixAddr::new_abstract(empty.as_bytes()).unwrap(); - let sun_path: [u8; 0] = []; - assert_eq!(addr.as_abstract(), Some(&sun_path[..])); - - let name = String::from("nix\0abstract\0test"); - let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap(); - let sun_path = [ - 110u8, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, - 115, 116, - ]; - assert_eq!(addr.as_abstract(), Some(&sun_path[..])); - assert_eq!(addr.path(), None); - - // Internally, name is null-prefixed (abstract namespace) - assert_eq!(unsafe { (*addr.as_ptr()).sun_path[0] }, 0); -} - -// Test getting an unnamed address (without unix socket creation) -#[cfg(any(target_os = "android", target_os = "linux"))] -#[test] -pub fn test_unnamed_uds_addr() { - use crate::nix::sys::socket::SockaddrLike; - - let addr = UnixAddr::new_unnamed(); - - assert!(addr.is_unnamed()); - assert_eq!(addr.len(), 2); - assert!(addr.path().is_none()); - assert_eq!(addr.path_len(), 0); - - assert!(addr.as_abstract().is_none()); -} - -#[test] -pub fn test_getsockname() { - use nix::sys::socket::bind; - use nix::sys::socket::{socket, AddressFamily, SockFlag, SockType}; - - let tempdir = tempfile::tempdir().unwrap(); - let sockname = tempdir.path().join("sock"); - let sock = socket( - AddressFamily::Unix, - SockType::Stream, - SockFlag::empty(), - None, - ) - .expect("socket failed"); - let sockaddr = UnixAddr::new(&sockname).unwrap(); - bind(sock, &sockaddr).expect("bind failed"); - assert_eq!(sockaddr, getsockname(sock).expect("getsockname failed")); -} - -#[test] -pub fn test_socketpair() { - use nix::sys::socket::{socketpair, AddressFamily, SockFlag, SockType}; - use nix::unistd::{read, write}; - - let (fd1, fd2) = socketpair( - AddressFamily::Unix, - SockType::Stream, - None, - SockFlag::empty(), - ) - .unwrap(); - write(fd1, b"hello").unwrap(); - let mut buf = [0; 5]; - read(fd2, &mut buf).unwrap(); - - assert_eq!(&buf[..], b"hello"); -} - -#[test] -pub fn test_std_conversions() { - use nix::sys::socket::*; - - let std_sa = SocketAddrV4::from_str("127.0.0.1:6789").unwrap(); - let sock_addr = SockaddrIn::from(std_sa); - assert_eq!(std_sa, sock_addr.into()); - - let std_sa = SocketAddrV6::from_str("[::1]:6000").unwrap(); - let sock_addr: SockaddrIn6 = SockaddrIn6::from(std_sa); - assert_eq!(std_sa, sock_addr.into()); -} - -mod recvfrom { - use super::*; - use nix::sys::socket::*; - use nix::{errno::Errno, Result}; - use std::thread; - - const MSG: &[u8] = b"Hello, World!"; - - fn sendrecv<Fs, Fr>( - rsock: RawFd, - ssock: RawFd, - f_send: Fs, - mut f_recv: Fr, - ) -> Option<SockaddrStorage> - where - Fs: Fn(RawFd, &[u8], MsgFlags) -> Result<usize> + Send + 'static, - Fr: FnMut(usize, Option<SockaddrStorage>), - { - let mut buf: [u8; 13] = [0u8; 13]; - let mut l = 0; - let mut from = None; - - let send_thread = thread::spawn(move || { - let mut l = 0; - while l < std::mem::size_of_val(MSG) { - l += f_send(ssock, &MSG[l..], MsgFlags::empty()).unwrap(); - } - }); - - while l < std::mem::size_of_val(MSG) { - let (len, from_) = recvfrom(rsock, &mut buf[l..]).unwrap(); - f_recv(len, from_); - from = from_; - l += len; - } - assert_eq!(&buf, MSG); - send_thread.join().unwrap(); - from - } - - #[test] - pub fn stream() { - let (fd2, fd1) = socketpair( - AddressFamily::Unix, - SockType::Stream, - None, - SockFlag::empty(), - ) - .unwrap(); - // Ignore from for stream sockets - let _ = sendrecv(fd1, fd2, send, |_, _| {}); - } - - #[test] - pub fn udp() { - let std_sa = SocketAddrV4::from_str("127.0.0.1:6789").unwrap(); - let sock_addr = SockaddrIn::from(std_sa); - let rsock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .unwrap(); - bind(rsock, &sock_addr).unwrap(); - let ssock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - let from = sendrecv( - rsock, - ssock, - move |s, m, flags| sendto(s, m, &sock_addr, flags), - |_, _| {}, - ); - // UDP sockets should set the from address - assert_eq!(AddressFamily::Inet, from.unwrap().family().unwrap()); - } - - #[cfg(target_os = "linux")] - mod udp_offload { - use super::*; - use nix::sys::socket::sockopt::{UdpGroSegment, UdpGsoSegment}; - use std::io::IoSlice; - - #[test] - // Disable the test under emulation because it fails in Cirrus-CI. Lack - // of QEMU support is suspected. - #[cfg_attr(qemu, ignore)] - pub fn gso() { - require_kernel_version!(udp_offload::gso, ">= 4.18"); - - // In this test, we send the data and provide a GSO segment size. - // Since we are sending the buffer of size 13, six UDP packets - // with size 2 and two UDP packet with size 1 will be sent. - let segment_size: u16 = 2; - - let sock_addr = SockaddrIn::new(127, 0, 0, 1, 6791); - let rsock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .unwrap(); - - setsockopt(rsock, UdpGsoSegment, &(segment_size as _)) - .expect("setsockopt UDP_SEGMENT failed"); - - bind(rsock, &sock_addr).unwrap(); - let ssock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - - let mut num_packets_received: i32 = 0; - - sendrecv( - rsock, - ssock, - move |s, m, flags| { - let iov = [IoSlice::new(m)]; - let cmsg = ControlMessage::UdpGsoSegments(&segment_size); - sendmsg(s, &iov, &[cmsg], flags, Some(&sock_addr)) - }, - { - let num_packets_received_ref = &mut num_packets_received; - - move |len, _| { - // check that we receive UDP packets with payload size - // less or equal to segment size - assert!(len <= segment_size as usize); - *num_packets_received_ref += 1; - } - }, - ); - - // Buffer size is 13, we will receive six packets of size 2, - // and one packet of size 1. - assert_eq!(7, num_packets_received); - } - - #[test] - // Disable the test on emulated platforms because it fails in Cirrus-CI. - // Lack of QEMU support is suspected. - #[cfg_attr(qemu, ignore)] - pub fn gro() { - require_kernel_version!(udp_offload::gro, ">= 5.3"); - - // It's hard to guarantee receiving GRO packets. Just checking - // that `setsockopt` doesn't fail with error - - let rsock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .unwrap(); - - setsockopt(rsock, UdpGroSegment, &true) - .expect("setsockopt UDP_GRO failed"); - } - } - - #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", - ))] - #[test] - pub fn udp_sendmmsg() { - use std::io::IoSlice; - - let std_sa = SocketAddrV4::from_str("127.0.0.1:6793").unwrap(); - let std_sa2 = SocketAddrV4::from_str("127.0.0.1:6794").unwrap(); - let sock_addr = SockaddrIn::from(std_sa); - let sock_addr2 = SockaddrIn::from(std_sa2); - - let rsock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .unwrap(); - bind(rsock, &sock_addr).unwrap(); - let ssock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - - let from = sendrecv( - rsock, - ssock, - move |s, m, flags| { - let batch_size = 15; - let mut iovs = Vec::with_capacity(1 + batch_size); - let mut addrs = Vec::with_capacity(1 + batch_size); - let mut data = MultiHeaders::preallocate(1 + batch_size, None); - let iov = IoSlice::new(m); - // first chunk: - iovs.push([iov]); - addrs.push(Some(sock_addr)); - - for _ in 0..batch_size { - iovs.push([iov]); - addrs.push(Some(sock_addr2)); - } - - let res = sendmmsg(s, &mut data, &iovs, addrs, [], flags)?; - let mut sent_messages = 0; - let mut sent_bytes = 0; - for item in res { - sent_messages += 1; - sent_bytes += item.bytes; - } - // - assert_eq!(sent_messages, iovs.len()); - assert_eq!(sent_bytes, sent_messages * m.len()); - Ok(sent_messages) - }, - |_, _| {}, - ); - // UDP sockets should set the from address - assert_eq!(AddressFamily::Inet, from.unwrap().family().unwrap()); - } - - #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", - ))] - #[test] - pub fn udp_recvmmsg() { - use nix::sys::socket::{recvmmsg, MsgFlags}; - use std::io::IoSliceMut; - - const NUM_MESSAGES_SENT: usize = 2; - const DATA: [u8; 2] = [1, 2]; - - let inet_addr = SocketAddrV4::from_str("127.0.0.1:6798").unwrap(); - let sock_addr = SockaddrIn::from(inet_addr); - - let rsock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .unwrap(); - bind(rsock, &sock_addr).unwrap(); - let ssock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - - let send_thread = thread::spawn(move || { - for _ in 0..NUM_MESSAGES_SENT { - sendto(ssock, &DATA[..], &sock_addr, MsgFlags::empty()) - .unwrap(); - } - }); - - let mut msgs = std::collections::LinkedList::new(); - - // Buffers to receive exactly `NUM_MESSAGES_SENT` messages - let mut receive_buffers = [[0u8; 32]; NUM_MESSAGES_SENT]; - msgs.extend( - receive_buffers - .iter_mut() - .map(|buf| [IoSliceMut::new(&mut buf[..])]), - ); - - let mut data = - MultiHeaders::<SockaddrIn>::preallocate(msgs.len(), None); - - let res: Vec<RecvMsg<SockaddrIn>> = - recvmmsg(rsock, &mut data, msgs.iter(), MsgFlags::empty(), None) - .expect("recvmmsg") - .collect(); - assert_eq!(res.len(), DATA.len()); - - for RecvMsg { address, bytes, .. } in res.into_iter() { - assert_eq!(AddressFamily::Inet, address.unwrap().family().unwrap()); - assert_eq!(DATA.len(), bytes); - } - - for buf in &receive_buffers { - assert_eq!(&buf[..DATA.len()], DATA); - } - - send_thread.join().unwrap(); - } - - #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", - ))] - #[test] - pub fn udp_recvmmsg_dontwait_short_read() { - use nix::sys::socket::{recvmmsg, MsgFlags}; - use std::io::IoSliceMut; - - const NUM_MESSAGES_SENT: usize = 2; - const DATA: [u8; 4] = [1, 2, 3, 4]; - - let inet_addr = SocketAddrV4::from_str("127.0.0.1:6799").unwrap(); - let sock_addr = SockaddrIn::from(inet_addr); - - let rsock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .unwrap(); - bind(rsock, &sock_addr).unwrap(); - let ssock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - - let send_thread = thread::spawn(move || { - for _ in 0..NUM_MESSAGES_SENT { - sendto(ssock, &DATA[..], &sock_addr, MsgFlags::empty()) - .unwrap(); - } - }); - // Ensure we've sent all the messages before continuing so `recvmmsg` - // will return right away - send_thread.join().unwrap(); - - let mut msgs = std::collections::LinkedList::new(); - - // Buffers to receive >`NUM_MESSAGES_SENT` messages to ensure `recvmmsg` - // will return when there are fewer than requested messages in the - // kernel buffers when using `MSG_DONTWAIT`. - let mut receive_buffers = [[0u8; 32]; NUM_MESSAGES_SENT + 2]; - msgs.extend( - receive_buffers - .iter_mut() - .map(|buf| [IoSliceMut::new(&mut buf[..])]), - ); - - let mut data = MultiHeaders::<SockaddrIn>::preallocate( - NUM_MESSAGES_SENT + 2, - None, - ); - - let res: Vec<RecvMsg<SockaddrIn>> = recvmmsg( - rsock, - &mut data, - msgs.iter(), - MsgFlags::MSG_DONTWAIT, - None, - ) - .expect("recvmmsg") - .collect(); - assert_eq!(res.len(), NUM_MESSAGES_SENT); - - for RecvMsg { address, bytes, .. } in res.into_iter() { - assert_eq!(AddressFamily::Inet, address.unwrap().family().unwrap()); - assert_eq!(DATA.len(), bytes); - } - - for buf in &receive_buffers[..NUM_MESSAGES_SENT] { - assert_eq!(&buf[..DATA.len()], DATA); - } - } - - #[test] - pub fn udp_inet6() { - let addr = std::net::Ipv6Addr::from_str("::1").unwrap(); - let rport = 6789; - let rstd_sa = SocketAddrV6::new(addr, rport, 0, 0); - let raddr = SockaddrIn6::from(rstd_sa); - let sport = 6790; - let sstd_sa = SocketAddrV6::new(addr, sport, 0, 0); - let saddr = SockaddrIn6::from(sstd_sa); - let rsock = socket( - AddressFamily::Inet6, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("receive socket failed"); - match bind(rsock, &raddr) { - Err(Errno::EADDRNOTAVAIL) => { - println!("IPv6 not available, skipping test."); - return; - } - Err(e) => panic!("bind: {}", e), - Ok(()) => (), - } - let ssock = socket( - AddressFamily::Inet6, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - bind(ssock, &saddr).unwrap(); - let from = sendrecv( - rsock, - ssock, - move |s, m, flags| sendto(s, m, &raddr, flags), - |_, _| {}, - ); - assert_eq!(AddressFamily::Inet6, from.unwrap().family().unwrap()); - let osent_addr = from.unwrap(); - let sent_addr = osent_addr.as_sockaddr_in6().unwrap(); - assert_eq!(sent_addr.ip(), addr); - assert_eq!(sent_addr.port(), sport); - } -} - -// Test error handling of our recvmsg wrapper -#[test] -pub fn test_recvmsg_ebadf() { - use nix::errno::Errno; - use nix::sys::socket::{recvmsg, MsgFlags}; - use std::io::IoSliceMut; - - let mut buf = [0u8; 5]; - let mut iov = [IoSliceMut::new(&mut buf[..])]; - - let fd = -1; // Bad file descriptor - let r = recvmsg::<()>(fd, &mut iov, None, MsgFlags::empty()); - - assert_eq!(r.err().unwrap(), Errno::EBADF); -} - -// Disable the test on emulated platforms due to a bug in QEMU versions < -// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808 -#[cfg_attr(qemu, ignore)] -#[test] -pub fn test_scm_rights() { - use nix::sys::socket::{ - recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage, - ControlMessageOwned, MsgFlags, SockFlag, SockType, - }; - use nix::unistd::{close, pipe, read, write}; - use std::io::{IoSlice, IoSliceMut}; - - let (fd1, fd2) = socketpair( - AddressFamily::Unix, - SockType::Stream, - None, - SockFlag::empty(), - ) - .unwrap(); - let (r, w) = pipe().unwrap(); - let mut received_r: Option<RawFd> = None; - - { - let iov = [IoSlice::new(b"hello")]; - let fds = [r]; - let cmsg = ControlMessage::ScmRights(&fds); - assert_eq!( - sendmsg::<()>(fd1, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), - 5 - ); - close(r).unwrap(); - close(fd1).unwrap(); - } - - { - let mut buf = [0u8; 5]; - - let mut iov = [IoSliceMut::new(&mut buf[..])]; - let mut cmsgspace = cmsg_space!([RawFd; 1]); - let msg = recvmsg::<()>( - fd2, - &mut iov, - Some(&mut cmsgspace), - MsgFlags::empty(), - ) - .unwrap(); - - for cmsg in msg.cmsgs() { - if let ControlMessageOwned::ScmRights(fd) = cmsg { - assert_eq!(received_r, None); - assert_eq!(fd.len(), 1); - received_r = Some(fd[0]); - } else { - panic!("unexpected cmsg"); - } - } - assert_eq!(msg.bytes, 5); - assert!(!msg - .flags - .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - close(fd2).unwrap(); - } - - let received_r = received_r.expect("Did not receive passed fd"); - // Ensure that the received file descriptor works - write(w, b"world").unwrap(); - let mut buf = [0u8; 5]; - read(received_r, &mut buf).unwrap(); - assert_eq!(&buf[..], b"world"); - close(received_r).unwrap(); - close(w).unwrap(); -} - -// Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross -#[cfg(any(target_os = "linux", target_os = "android"))] -#[cfg_attr(qemu, ignore)] -#[test] -pub fn test_af_alg_cipher() { - use nix::sys::socket::sockopt::AlgSetKey; - use nix::sys::socket::{ - accept, bind, sendmsg, setsockopt, socket, AddressFamily, AlgAddr, - ControlMessage, MsgFlags, SockFlag, SockType, - }; - use nix::unistd::read; - use std::io::IoSlice; - - skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1352"); - // Travis's seccomp profile blocks AF_ALG - // https://docs.docker.com/engine/security/seccomp/ - skip_if_seccomp!(test_af_alg_cipher); - - let alg_type = "skcipher"; - let alg_name = "ctr-aes-aesni"; - // 256-bits secret key - let key = vec![0u8; 32]; - // 16-bytes IV - let iv_len = 16; - let iv = vec![1u8; iv_len]; - // 256-bytes plain payload - let payload_len = 256; - let payload = vec![2u8; payload_len]; - - let sock = socket( - AddressFamily::Alg, - SockType::SeqPacket, - SockFlag::empty(), - None, - ) - .expect("socket failed"); - - let sockaddr = AlgAddr::new(alg_type, alg_name); - bind(sock, &sockaddr).expect("bind failed"); - - assert_eq!(sockaddr.alg_name().to_string_lossy(), alg_name); - assert_eq!(sockaddr.alg_type().to_string_lossy(), alg_type); - - setsockopt(sock, AlgSetKey::default(), &key).expect("setsockopt"); - let session_socket = accept(sock).expect("accept failed"); - - let msgs = [ - ControlMessage::AlgSetOp(&libc::ALG_OP_ENCRYPT), - ControlMessage::AlgSetIv(iv.as_slice()), - ]; - let iov = IoSlice::new(&payload); - sendmsg::<()>(session_socket, &[iov], &msgs, MsgFlags::empty(), None) - .expect("sendmsg encrypt"); - - // allocate buffer for encrypted data - let mut encrypted = vec![0u8; payload_len]; - let num_bytes = read(session_socket, &mut encrypted).expect("read encrypt"); - assert_eq!(num_bytes, payload_len); - - let iov = IoSlice::new(&encrypted); - - let iv = vec![1u8; iv_len]; - - let msgs = [ - ControlMessage::AlgSetOp(&libc::ALG_OP_DECRYPT), - ControlMessage::AlgSetIv(iv.as_slice()), - ]; - sendmsg::<()>(session_socket, &[iov], &msgs, MsgFlags::empty(), None) - .expect("sendmsg decrypt"); - - // allocate buffer for decrypted data - let mut decrypted = vec![0u8; payload_len]; - let num_bytes = read(session_socket, &mut decrypted).expect("read decrypt"); - - assert_eq!(num_bytes, payload_len); - assert_eq!(decrypted, payload); -} - -// Disable the test on emulated platforms due to not enabled support of AF_ALG -// in QEMU from rust cross -#[cfg(any(target_os = "linux", target_os = "android"))] -#[cfg_attr(qemu, ignore)] -#[test] -pub fn test_af_alg_aead() { - use libc::{ALG_OP_DECRYPT, ALG_OP_ENCRYPT}; - use nix::fcntl::{fcntl, FcntlArg, OFlag}; - use nix::sys::socket::sockopt::{AlgSetAeadAuthSize, AlgSetKey}; - use nix::sys::socket::{ - accept, bind, sendmsg, setsockopt, socket, AddressFamily, AlgAddr, - ControlMessage, MsgFlags, SockFlag, SockType, - }; - use nix::unistd::{close, read}; - use std::io::IoSlice; - - skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1352"); - // Travis's seccomp profile blocks AF_ALG - // https://docs.docker.com/engine/security/seccomp/ - skip_if_seccomp!(test_af_alg_aead); - - let auth_size = 4usize; - let assoc_size = 16u32; - - let alg_type = "aead"; - let alg_name = "gcm(aes)"; - // 256-bits secret key - let key = vec![0u8; 32]; - // 12-bytes IV - let iv_len = 12; - let iv = vec![1u8; iv_len]; - // 256-bytes plain payload - let payload_len = 256; - let mut payload = - vec![2u8; payload_len + (assoc_size as usize) + auth_size]; - - for i in 0..assoc_size { - payload[i as usize] = 10; - } - - let len = payload.len(); - - for i in 0..auth_size { - payload[len - 1 - i] = 0; - } - - let sock = socket( - AddressFamily::Alg, - SockType::SeqPacket, - SockFlag::empty(), - None, - ) - .expect("socket failed"); - - let sockaddr = AlgAddr::new(alg_type, alg_name); - bind(sock, &sockaddr).expect("bind failed"); - - setsockopt(sock, AlgSetAeadAuthSize, &auth_size) - .expect("setsockopt AlgSetAeadAuthSize"); - setsockopt(sock, AlgSetKey::default(), &key).expect("setsockopt AlgSetKey"); - let session_socket = accept(sock).expect("accept failed"); - - let msgs = [ - ControlMessage::AlgSetOp(&ALG_OP_ENCRYPT), - ControlMessage::AlgSetIv(iv.as_slice()), - ControlMessage::AlgSetAeadAssoclen(&assoc_size), - ]; - - let iov = IoSlice::new(&payload); - sendmsg::<()>(session_socket, &[iov], &msgs, MsgFlags::empty(), None) - .expect("sendmsg encrypt"); - - // allocate buffer for encrypted data - let mut encrypted = - vec![0u8; (assoc_size as usize) + payload_len + auth_size]; - let num_bytes = read(session_socket, &mut encrypted).expect("read encrypt"); - assert_eq!(num_bytes, payload_len + auth_size + (assoc_size as usize)); - close(session_socket).expect("close"); - - for i in 0..assoc_size { - encrypted[i as usize] = 10; - } - - let iov = IoSlice::new(&encrypted); - - let iv = vec![1u8; iv_len]; - - let session_socket = accept(sock).expect("accept failed"); - - let msgs = [ - ControlMessage::AlgSetOp(&ALG_OP_DECRYPT), - ControlMessage::AlgSetIv(iv.as_slice()), - ControlMessage::AlgSetAeadAssoclen(&assoc_size), - ]; - sendmsg::<()>(session_socket, &[iov], &msgs, MsgFlags::empty(), None) - .expect("sendmsg decrypt"); - - // allocate buffer for decrypted data - let mut decrypted = - vec![0u8; payload_len + (assoc_size as usize) + auth_size]; - // Starting with kernel 4.9, the interface changed slightly such that the - // authentication tag memory is only needed in the output buffer for encryption - // and in the input buffer for decryption. - // Do not block on read, as we may have fewer bytes than buffer size - fcntl(session_socket, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)) - .expect("fcntl non_blocking"); - let num_bytes = read(session_socket, &mut decrypted).expect("read decrypt"); - - assert!(num_bytes >= payload_len + (assoc_size as usize)); - assert_eq!( - decrypted[(assoc_size as usize)..(payload_len + (assoc_size as usize))], - payload[(assoc_size as usize)..payload_len + (assoc_size as usize)] - ); -} - -// Verify `ControlMessage::Ipv4PacketInfo` for `sendmsg`. -// This creates a (udp) socket bound to localhost, then sends a message to -// itself but uses Ipv4PacketInfo to force the source address to be localhost. -// -// This would be a more interesting test if we could assume that the test host -// has more than one IP address (since we could select a different address to -// test from). -#[cfg(any(target_os = "linux", target_os = "macos", target_os = "netbsd"))] -#[test] -pub fn test_sendmsg_ipv4packetinfo() { - use cfg_if::cfg_if; - use nix::sys::socket::{ - bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags, - SockFlag, SockType, SockaddrIn, - }; - use std::io::IoSlice; - - let sock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("socket failed"); - - let sock_addr = SockaddrIn::new(127, 0, 0, 1, 4000); - - bind(sock, &sock_addr).expect("bind failed"); - - let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; - let iov = [IoSlice::new(&slice)]; - - cfg_if! { - if #[cfg(target_os = "netbsd")] { - let pi = libc::in_pktinfo { - ipi_ifindex: 0, /* Unspecified interface */ - ipi_addr: libc::in_addr { s_addr: 0 }, - }; - } else { - let pi = libc::in_pktinfo { - ipi_ifindex: 0, /* Unspecified interface */ - ipi_addr: libc::in_addr { s_addr: 0 }, - ipi_spec_dst: sock_addr.as_ref().sin_addr, - }; - } - } - - let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)]; - - sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr)) - .expect("sendmsg"); -} - -// Verify `ControlMessage::Ipv6PacketInfo` for `sendmsg`. -// This creates a (udp) socket bound to ip6-localhost, then sends a message to -// itself but uses Ipv6PacketInfo to force the source address to be -// ip6-localhost. -// -// This would be a more interesting test if we could assume that the test host -// has more than one IP address (since we could select a different address to -// test from). -#[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "freebsd" -))] -#[test] -pub fn test_sendmsg_ipv6packetinfo() { - use nix::errno::Errno; - use nix::sys::socket::{ - bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags, - SockFlag, SockType, SockaddrIn6, - }; - use std::io::IoSlice; - - let sock = socket( - AddressFamily::Inet6, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("socket failed"); - - let std_sa = SocketAddrV6::from_str("[::1]:6000").unwrap(); - let sock_addr: SockaddrIn6 = SockaddrIn6::from(std_sa); - - if let Err(Errno::EADDRNOTAVAIL) = bind(sock, &sock_addr) { - println!("IPv6 not available, skipping test."); - return; - } - - let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; - let iov = [IoSlice::new(&slice)]; - - let pi = libc::in6_pktinfo { - ipi6_ifindex: 0, /* Unspecified interface */ - ipi6_addr: sock_addr.as_ref().sin6_addr, - }; - - let cmsg = [ControlMessage::Ipv6PacketInfo(&pi)]; - - sendmsg::<SockaddrIn6>( - sock, - &iov, - &cmsg, - MsgFlags::empty(), - Some(&sock_addr), - ) - .expect("sendmsg"); -} - -// Verify that ControlMessage::Ipv4SendSrcAddr works for sendmsg. This -// creates a UDP socket bound to all local interfaces (0.0.0.0). It then -// sends message to itself at 127.0.0.1 while explicitly specifying -// 127.0.0.1 as the source address through an Ipv4SendSrcAddr -// (IP_SENDSRCADDR) control message. -// -// Note that binding to 0.0.0.0 is *required* on FreeBSD; sendmsg -// returns EINVAL otherwise. (See FreeBSD's ip(4) man page.) -#[cfg(any( - target_os = "netbsd", - target_os = "freebsd", - target_os = "openbsd", - target_os = "dragonfly", -))] -#[test] -pub fn test_sendmsg_ipv4sendsrcaddr() { - use nix::sys::socket::{ - bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags, - SockFlag, SockType, SockaddrIn, - }; - use std::io::IoSlice; - - let sock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("socket failed"); - - let unspec_sock_addr = SockaddrIn::new(0, 0, 0, 0, 0); - bind(sock, &unspec_sock_addr).expect("bind failed"); - let bound_sock_addr: SockaddrIn = getsockname(sock).unwrap(); - let localhost_sock_addr: SockaddrIn = - SockaddrIn::new(127, 0, 0, 1, bound_sock_addr.port()); - - let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; - let iov = [IoSlice::new(&slice)]; - let cmsg = [ControlMessage::Ipv4SendSrcAddr( - &localhost_sock_addr.as_ref().sin_addr, - )]; - - sendmsg( - sock, - &iov, - &cmsg, - MsgFlags::empty(), - Some(&localhost_sock_addr), - ) - .expect("sendmsg"); -} - -/// Tests that passing multiple fds using a single `ControlMessage` works. -// Disable the test on emulated platforms due to a bug in QEMU versions < -// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808 -#[cfg_attr(qemu, ignore)] -#[test] -fn test_scm_rights_single_cmsg_multiple_fds() { - use nix::sys::socket::{ - recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags, - }; - use std::io::{IoSlice, IoSliceMut}; - use std::os::unix::io::{AsRawFd, RawFd}; - use std::os::unix::net::UnixDatagram; - use std::thread; - - let (send, receive) = UnixDatagram::pair().unwrap(); - let thread = thread::spawn(move || { - let mut buf = [0u8; 8]; - let mut iovec = [IoSliceMut::new(&mut buf)]; - - let mut space = cmsg_space!([RawFd; 2]); - let msg = recvmsg::<()>( - receive.as_raw_fd(), - &mut iovec, - Some(&mut space), - MsgFlags::empty(), - ) - .unwrap(); - assert!(!msg - .flags - .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - - let mut cmsgs = msg.cmsgs(); - match cmsgs.next() { - Some(ControlMessageOwned::ScmRights(fds)) => { - assert_eq!( - fds.len(), - 2, - "unexpected fd count (expected 2 fds, got {})", - fds.len() - ); - } - _ => panic!(), - } - assert!(cmsgs.next().is_none(), "unexpected control msg"); - - assert_eq!(msg.bytes, 8); - assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]); - }); - - let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; - let iov = [IoSlice::new(&slice)]; - let fds = [libc::STDIN_FILENO, libc::STDOUT_FILENO]; // pass stdin and stdout - let cmsg = [ControlMessage::ScmRights(&fds)]; - sendmsg::<()>(send.as_raw_fd(), &iov, &cmsg, MsgFlags::empty(), None) - .unwrap(); - thread.join().unwrap(); -} - -// Verify `sendmsg` builds a valid `msghdr` when passing an empty -// `cmsgs` argument. This should result in a msghdr with a nullptr -// msg_control field and a msg_controllen of 0 when calling into the -// raw `sendmsg`. -#[test] -pub fn test_sendmsg_empty_cmsgs() { - use nix::sys::socket::{ - recvmsg, sendmsg, socketpair, AddressFamily, MsgFlags, SockFlag, - SockType, - }; - use nix::unistd::close; - use std::io::{IoSlice, IoSliceMut}; - - let (fd1, fd2) = socketpair( - AddressFamily::Unix, - SockType::Stream, - None, - SockFlag::empty(), - ) - .unwrap(); - - { - let iov = [IoSlice::new(b"hello")]; - assert_eq!( - sendmsg::<()>(fd1, &iov, &[], MsgFlags::empty(), None).unwrap(), - 5 - ); - close(fd1).unwrap(); - } - - { - let mut buf = [0u8; 5]; - let mut iov = [IoSliceMut::new(&mut buf[..])]; - - let mut cmsgspace = cmsg_space!([RawFd; 1]); - let msg = recvmsg::<()>( - fd2, - &mut iov, - Some(&mut cmsgspace), - MsgFlags::empty(), - ) - .unwrap(); - - for _ in msg.cmsgs() { - panic!("unexpected cmsg"); - } - assert!(!msg - .flags - .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - assert_eq!(msg.bytes, 5); - close(fd2).unwrap(); - } -} - -#[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "freebsd", - target_os = "dragonfly", -))] -#[test] -fn test_scm_credentials() { - use nix::sys::socket::{ - recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage, - ControlMessageOwned, MsgFlags, SockFlag, SockType, UnixCredentials, - }; - #[cfg(any(target_os = "android", target_os = "linux"))] - use nix::sys::socket::{setsockopt, sockopt::PassCred}; - use nix::unistd::{close, getgid, getpid, getuid}; - use std::io::{IoSlice, IoSliceMut}; - - let (send, recv) = socketpair( - AddressFamily::Unix, - SockType::Stream, - None, - SockFlag::empty(), - ) - .unwrap(); - #[cfg(any(target_os = "android", target_os = "linux"))] - setsockopt(recv, PassCred, &true).unwrap(); - - { - let iov = [IoSlice::new(b"hello")]; - #[cfg(any(target_os = "android", target_os = "linux"))] - let cred = UnixCredentials::new(); - #[cfg(any(target_os = "android", target_os = "linux"))] - let cmsg = ControlMessage::ScmCredentials(&cred); - #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] - let cmsg = ControlMessage::ScmCreds; - assert_eq!( - sendmsg::<()>(send, &iov, &[cmsg], MsgFlags::empty(), None) - .unwrap(), - 5 - ); - close(send).unwrap(); - } - - { - let mut buf = [0u8; 5]; - let mut iov = [IoSliceMut::new(&mut buf[..])]; - - let mut cmsgspace = cmsg_space!(UnixCredentials); - let msg = recvmsg::<()>( - recv, - &mut iov, - Some(&mut cmsgspace), - MsgFlags::empty(), - ) - .unwrap(); - let mut received_cred = None; - - for cmsg in msg.cmsgs() { - let cred = match cmsg { - #[cfg(any(target_os = "android", target_os = "linux"))] - ControlMessageOwned::ScmCredentials(cred) => cred, - #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] - ControlMessageOwned::ScmCreds(cred) => cred, - other => panic!("unexpected cmsg {:?}", other), - }; - assert!(received_cred.is_none()); - assert_eq!(cred.pid(), getpid().as_raw()); - assert_eq!(cred.uid(), getuid().as_raw()); - assert_eq!(cred.gid(), getgid().as_raw()); - received_cred = Some(cred); - } - received_cred.expect("no creds received"); - assert_eq!(msg.bytes, 5); - assert!(!msg - .flags - .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - close(recv).unwrap(); - } -} - -/// Ensure that we can send `SCM_CREDENTIALS` and `SCM_RIGHTS` with a single -/// `sendmsg` call. -#[cfg(any(target_os = "android", target_os = "linux"))] -// qemu's handling of multiple cmsgs is bugged, ignore tests under emulation -// see https://bugs.launchpad.net/qemu/+bug/1781280 -#[cfg_attr(qemu, ignore)] -#[test] -fn test_scm_credentials_and_rights() { - let space = cmsg_space!(libc::ucred, RawFd); - test_impl_scm_credentials_and_rights(space); -} - -/// Ensure that passing a an oversized control message buffer to recvmsg -/// still works. -#[cfg(any(target_os = "android", target_os = "linux"))] -// qemu's handling of multiple cmsgs is bugged, ignore tests under emulation -// see https://bugs.launchpad.net/qemu/+bug/1781280 -#[cfg_attr(qemu, ignore)] -#[test] -fn test_too_large_cmsgspace() { - let space = vec![0u8; 1024]; - test_impl_scm_credentials_and_rights(space); -} - -#[cfg(any(target_os = "android", target_os = "linux"))] -fn test_impl_scm_credentials_and_rights(mut space: Vec<u8>) { - use libc::ucred; - use nix::sys::socket::sockopt::PassCred; - use nix::sys::socket::{ - recvmsg, sendmsg, setsockopt, socketpair, ControlMessage, - ControlMessageOwned, MsgFlags, SockFlag, SockType, - }; - use nix::unistd::{close, getgid, getpid, getuid, pipe, write}; - use std::io::{IoSlice, IoSliceMut}; - - let (send, recv) = socketpair( - AddressFamily::Unix, - SockType::Stream, - None, - SockFlag::empty(), - ) - .unwrap(); - setsockopt(recv, PassCred, &true).unwrap(); - - let (r, w) = pipe().unwrap(); - let mut received_r: Option<RawFd> = None; - - { - let iov = [IoSlice::new(b"hello")]; - let cred = ucred { - pid: getpid().as_raw(), - uid: getuid().as_raw(), - gid: getgid().as_raw(), - } - .into(); - let fds = [r]; - let cmsgs = [ - ControlMessage::ScmCredentials(&cred), - ControlMessage::ScmRights(&fds), - ]; - assert_eq!( - sendmsg::<()>(send, &iov, &cmsgs, MsgFlags::empty(), None).unwrap(), - 5 - ); - close(r).unwrap(); - close(send).unwrap(); - } - - { - let mut buf = [0u8; 5]; - let mut iov = [IoSliceMut::new(&mut buf[..])]; - let msg = - recvmsg::<()>(recv, &mut iov, Some(&mut space), MsgFlags::empty()) - .unwrap(); - let mut received_cred = None; - - assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs"); - - for cmsg in msg.cmsgs() { - match cmsg { - ControlMessageOwned::ScmRights(fds) => { - assert_eq!(received_r, None, "already received fd"); - assert_eq!(fds.len(), 1); - received_r = Some(fds[0]); - } - ControlMessageOwned::ScmCredentials(cred) => { - assert!(received_cred.is_none()); - assert_eq!(cred.pid(), getpid().as_raw()); - assert_eq!(cred.uid(), getuid().as_raw()); - assert_eq!(cred.gid(), getgid().as_raw()); - received_cred = Some(cred); - } - _ => panic!("unexpected cmsg"), - } - } - received_cred.expect("no creds received"); - assert_eq!(msg.bytes, 5); - assert!(!msg - .flags - .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - close(recv).unwrap(); - } - - let received_r = received_r.expect("Did not receive passed fd"); - // Ensure that the received file descriptor works - write(w, b"world").unwrap(); - let mut buf = [0u8; 5]; - read(received_r, &mut buf).unwrap(); - assert_eq!(&buf[..], b"world"); - close(received_r).unwrap(); - close(w).unwrap(); -} - -// Test creating and using named unix domain sockets -#[test] -pub fn test_named_unixdomain() { - use nix::sys::socket::{accept, bind, connect, listen, socket, UnixAddr}; - use nix::sys::socket::{SockFlag, SockType}; - use nix::unistd::{close, read, write}; - use std::thread; - - let tempdir = tempfile::tempdir().unwrap(); - let sockname = tempdir.path().join("sock"); - let s1 = socket( - AddressFamily::Unix, - SockType::Stream, - SockFlag::empty(), - None, - ) - .expect("socket failed"); - let sockaddr = UnixAddr::new(&sockname).unwrap(); - bind(s1, &sockaddr).expect("bind failed"); - listen(s1, 10).expect("listen failed"); - - let thr = thread::spawn(move || { - let s2 = socket( - AddressFamily::Unix, - SockType::Stream, - SockFlag::empty(), - None, - ) - .expect("socket failed"); - connect(s2, &sockaddr).expect("connect failed"); - write(s2, b"hello").expect("write failed"); - close(s2).unwrap(); - }); - - let s3 = accept(s1).expect("accept failed"); - - let mut buf = [0; 5]; - read(s3, &mut buf).unwrap(); - close(s3).unwrap(); - close(s1).unwrap(); - thr.join().unwrap(); - - assert_eq!(&buf[..], b"hello"); -} - -// Test using unnamed unix domain addresses -#[cfg(any(target_os = "android", target_os = "linux"))] -#[test] -pub fn test_unnamed_unixdomain() { - use nix::sys::socket::{getsockname, socketpair}; - use nix::sys::socket::{SockFlag, SockType}; - use nix::unistd::close; - - let (fd_1, fd_2) = socketpair( - AddressFamily::Unix, - SockType::Stream, - None, - SockFlag::empty(), - ) - .expect("socketpair failed"); - - let addr_1: UnixAddr = getsockname(fd_1).expect("getsockname failed"); - assert!(addr_1.is_unnamed()); - - close(fd_1).unwrap(); - close(fd_2).unwrap(); -} - -// Test creating and using unnamed unix domain addresses for autobinding sockets -#[cfg(any(target_os = "android", target_os = "linux"))] -#[test] -pub fn test_unnamed_unixdomain_autobind() { - use nix::sys::socket::{bind, getsockname, socket}; - use nix::sys::socket::{SockFlag, SockType}; - use nix::unistd::close; - - let fd = socket( - AddressFamily::Unix, - SockType::Stream, - SockFlag::empty(), - None, - ) - .expect("socket failed"); - - // unix(7): "If a bind(2) call specifies addrlen as `sizeof(sa_family_t)`, or [...], then the - // socket is autobound to an abstract address" - bind(fd, &UnixAddr::new_unnamed()).expect("bind failed"); - - let addr: UnixAddr = getsockname(fd).expect("getsockname failed"); - let addr = addr.as_abstract().unwrap(); - - // changed from 8 to 5 bytes in Linux 2.3.15, and rust's minimum supported Linux version is 3.2 - // (as of 2022-11) - assert_eq!(addr.len(), 5); - - close(fd).unwrap(); -} - -// Test creating and using named system control sockets -#[cfg(any(target_os = "macos", target_os = "ios"))] -#[test] -pub fn test_syscontrol() { - use nix::errno::Errno; - use nix::sys::socket::{ - socket, SockFlag, SockProtocol, SockType, SysControlAddr, - }; - - let fd = socket( - AddressFamily::System, - SockType::Datagram, - SockFlag::empty(), - SockProtocol::KextControl, - ) - .expect("socket failed"); - SysControlAddr::from_name(fd, "com.apple.net.utun_control", 0) - .expect("resolving sys_control name failed"); - assert_eq!( - SysControlAddr::from_name(fd, "foo.bar.lol", 0).err(), - Some(Errno::ENOENT) - ); - - // requires root privileges - // connect(fd, &sockaddr).expect("connect failed"); -} - -#[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", -))] -fn loopback_address( - family: AddressFamily, -) -> Option<nix::ifaddrs::InterfaceAddress> { - use nix::ifaddrs::getifaddrs; - use nix::net::if_::*; - use nix::sys::socket::SockaddrLike; - use std::io; - use std::io::Write; - - let mut addrs = match getifaddrs() { - Ok(iter) => iter, - Err(e) => { - let stdioerr = io::stderr(); - let mut handle = stdioerr.lock(); - writeln!(handle, "getifaddrs: {:?}", e).unwrap(); - return None; - } - }; - // return first address matching family - addrs.find(|ifaddr| { - ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) - && ifaddr.address.as_ref().and_then(SockaddrLike::family) - == Some(family) - }) -} - -#[cfg(any( - target_os = "android", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", -))] -// qemu doesn't seem to be emulating this correctly in these architectures -#[cfg_attr( - all( - qemu, - any( - target_arch = "mips", - target_arch = "mips64", - target_arch = "powerpc64", - ) - ), - ignore -)] -#[test] -pub fn test_recv_ipv4pktinfo() { - use nix::net::if_::*; - use nix::sys::socket::sockopt::Ipv4PacketInfo; - use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn}; - use nix::sys::socket::{getsockname, setsockopt, socket}; - use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; - use std::io::{IoSlice, IoSliceMut}; - - let lo_ifaddr = loopback_address(AddressFamily::Inet); - let (lo_name, lo) = match lo_ifaddr { - Some(ifaddr) => ( - ifaddr.interface_name, - ifaddr.address.expect("Expect IPv4 address on interface"), - ), - None => return, - }; - let receive = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("receive socket failed"); - bind(receive, &lo).expect("bind failed"); - let sa: SockaddrIn = getsockname(receive).expect("getsockname failed"); - setsockopt(receive, Ipv4PacketInfo, &true).expect("setsockopt failed"); - - { - let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; - let iov = [IoSlice::new(&slice)]; - - let send = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)) - .expect("sendmsg failed"); - } - - { - let mut buf = [0u8; 8]; - let mut iovec = [IoSliceMut::new(&mut buf)]; - - let mut space = cmsg_space!(libc::in_pktinfo); - let msg = recvmsg::<()>( - receive, - &mut iovec, - Some(&mut space), - MsgFlags::empty(), - ) - .expect("recvmsg failed"); - assert!(!msg - .flags - .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - - let mut cmsgs = msg.cmsgs(); - if let Some(ControlMessageOwned::Ipv4PacketInfo(pktinfo)) = cmsgs.next() - { - let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); - assert_eq!( - pktinfo.ipi_ifindex as libc::c_uint, i, - "unexpected ifindex (expected {}, got {})", - i, pktinfo.ipi_ifindex - ); - } - assert!(cmsgs.next().is_none(), "unexpected additional control msg"); - assert_eq!(msg.bytes, 8); - assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]); - } -} - -#[cfg(any( - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", -))] -// qemu doesn't seem to be emulating this correctly in these architectures -#[cfg_attr( - all( - qemu, - any( - target_arch = "mips", - target_arch = "mips64", - target_arch = "powerpc64", - ) - ), - ignore -)] -#[test] -pub fn test_recvif() { - use nix::net::if_::*; - use nix::sys::socket::sockopt::{Ipv4RecvDstAddr, Ipv4RecvIf}; - use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn}; - use nix::sys::socket::{getsockname, setsockopt, socket}; - use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; - use std::io::{IoSlice, IoSliceMut}; - - let lo_ifaddr = loopback_address(AddressFamily::Inet); - let (lo_name, lo) = match lo_ifaddr { - Some(ifaddr) => ( - ifaddr.interface_name, - ifaddr.address.expect("Expect IPv4 address on interface"), - ), - None => return, - }; - let receive = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("receive socket failed"); - bind(receive, &lo).expect("bind failed"); - let sa: SockaddrIn = getsockname(receive).expect("getsockname failed"); - setsockopt(receive, Ipv4RecvIf, &true) - .expect("setsockopt IP_RECVIF failed"); - setsockopt(receive, Ipv4RecvDstAddr, &true) - .expect("setsockopt IP_RECVDSTADDR failed"); - - { - let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; - let iov = [IoSlice::new(&slice)]; - - let send = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)) - .expect("sendmsg failed"); - } - - { - let mut buf = [0u8; 8]; - let mut iovec = [IoSliceMut::new(&mut buf)]; - let mut space = cmsg_space!(libc::sockaddr_dl, libc::in_addr); - let msg = recvmsg::<()>( - receive, - &mut iovec, - Some(&mut space), - MsgFlags::empty(), - ) - .expect("recvmsg failed"); - assert!(!msg - .flags - .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs"); - - let mut rx_recvif = false; - let mut rx_recvdstaddr = false; - for cmsg in msg.cmsgs() { - match cmsg { - ControlMessageOwned::Ipv4RecvIf(dl) => { - rx_recvif = true; - let i = if_nametoindex(lo_name.as_bytes()) - .expect("if_nametoindex"); - assert_eq!( - dl.sdl_index as libc::c_uint, i, - "unexpected ifindex (expected {}, got {})", - i, dl.sdl_index - ); - } - ControlMessageOwned::Ipv4RecvDstAddr(addr) => { - rx_recvdstaddr = true; - if let Some(sin) = lo.as_sockaddr_in() { - assert_eq!(sin.as_ref().sin_addr.s_addr, - addr.s_addr, - "unexpected destination address (expected {}, got {})", - sin.as_ref().sin_addr.s_addr, - addr.s_addr); - } else { - panic!("unexpected Sockaddr"); - } - } - _ => panic!("unexpected additional control msg"), - } - } - assert!(rx_recvif); - assert!(rx_recvdstaddr); - assert_eq!(msg.bytes, 8); - assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]); - } -} - -#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] -#[cfg_attr(qemu, ignore)] -#[test] -pub fn test_recvif_ipv4() { - use nix::sys::socket::sockopt::Ipv4OrigDstAddr; - use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn}; - use nix::sys::socket::{getsockname, setsockopt, socket}; - use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; - use std::io::{IoSlice, IoSliceMut}; - - let lo_ifaddr = loopback_address(AddressFamily::Inet); - let (_lo_name, lo) = match lo_ifaddr { - Some(ifaddr) => ( - ifaddr.interface_name, - ifaddr.address.expect("Expect IPv4 address on interface"), - ), - None => return, - }; - let receive = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("receive socket failed"); - bind(receive, &lo).expect("bind failed"); - let sa: SockaddrIn = getsockname(receive).expect("getsockname failed"); - setsockopt(receive, Ipv4OrigDstAddr, &true) - .expect("setsockopt IP_ORIGDSTADDR failed"); - - { - let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; - let iov = [IoSlice::new(&slice)]; - - let send = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)) - .expect("sendmsg failed"); - } - - { - let mut buf = [0u8; 8]; - let mut iovec = [IoSliceMut::new(&mut buf)]; - let mut space = cmsg_space!(libc::sockaddr_in); - let msg = recvmsg::<()>( - receive, - &mut iovec, - Some(&mut space), - MsgFlags::empty(), - ) - .expect("recvmsg failed"); - assert!(!msg - .flags - .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs"); - - let mut rx_recvorigdstaddr = false; - for cmsg in msg.cmsgs() { - match cmsg { - ControlMessageOwned::Ipv4OrigDstAddr(addr) => { - rx_recvorigdstaddr = true; - if let Some(sin) = lo.as_sockaddr_in() { - assert_eq!(sin.as_ref().sin_addr.s_addr, - addr.sin_addr.s_addr, - "unexpected destination address (expected {}, got {})", - sin.as_ref().sin_addr.s_addr, - addr.sin_addr.s_addr); - } else { - panic!("unexpected Sockaddr"); - } - } - _ => panic!("unexpected additional control msg"), - } - } - assert!(rx_recvorigdstaddr); - assert_eq!(msg.bytes, 8); - assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]); - } -} - -#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] -#[cfg_attr(qemu, ignore)] -#[test] -pub fn test_recvif_ipv6() { - use nix::sys::socket::sockopt::Ipv6OrigDstAddr; - use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn6}; - use nix::sys::socket::{getsockname, setsockopt, socket}; - use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; - use std::io::{IoSlice, IoSliceMut}; - - let lo_ifaddr = loopback_address(AddressFamily::Inet6); - let (_lo_name, lo) = match lo_ifaddr { - Some(ifaddr) => ( - ifaddr.interface_name, - ifaddr.address.expect("Expect IPv6 address on interface"), - ), - None => return, - }; - let receive = socket( - AddressFamily::Inet6, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("receive socket failed"); - bind(receive, &lo).expect("bind failed"); - let sa: SockaddrIn6 = getsockname(receive).expect("getsockname failed"); - setsockopt(receive, Ipv6OrigDstAddr, &true) - .expect("setsockopt IP_ORIGDSTADDR failed"); - - { - let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; - let iov = [IoSlice::new(&slice)]; - - let send = socket( - AddressFamily::Inet6, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)) - .expect("sendmsg failed"); - } - - { - let mut buf = [0u8; 8]; - let mut iovec = [IoSliceMut::new(&mut buf)]; - let mut space = cmsg_space!(libc::sockaddr_in6); - let msg = recvmsg::<()>( - receive, - &mut iovec, - Some(&mut space), - MsgFlags::empty(), - ) - .expect("recvmsg failed"); - assert!(!msg - .flags - .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs"); - - let mut rx_recvorigdstaddr = false; - for cmsg in msg.cmsgs() { - match cmsg { - ControlMessageOwned::Ipv6OrigDstAddr(addr) => { - rx_recvorigdstaddr = true; - if let Some(sin) = lo.as_sockaddr_in6() { - assert_eq!(sin.as_ref().sin6_addr.s6_addr, - addr.sin6_addr.s6_addr, - "unexpected destination address (expected {:?}, got {:?})", - sin.as_ref().sin6_addr.s6_addr, - addr.sin6_addr.s6_addr); - } else { - panic!("unexpected Sockaddr"); - } - } - _ => panic!("unexpected additional control msg"), - } - } - assert!(rx_recvorigdstaddr); - assert_eq!(msg.bytes, 8); - assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]); - } -} - -#[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", -))] -// qemu doesn't seem to be emulating this correctly in these architectures -#[cfg_attr( - all( - qemu, - any( - target_arch = "mips", - target_arch = "mips64", - target_arch = "powerpc64", - ) - ), - ignore -)] -#[test] -pub fn test_recv_ipv6pktinfo() { - use nix::net::if_::*; - use nix::sys::socket::sockopt::Ipv6RecvPacketInfo; - use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn6}; - use nix::sys::socket::{getsockname, setsockopt, socket}; - use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; - use std::io::{IoSlice, IoSliceMut}; - - let lo_ifaddr = loopback_address(AddressFamily::Inet6); - let (lo_name, lo) = match lo_ifaddr { - Some(ifaddr) => ( - ifaddr.interface_name, - ifaddr.address.expect("Expect IPv6 address on interface"), - ), - None => return, - }; - let receive = socket( - AddressFamily::Inet6, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("receive socket failed"); - bind(receive, &lo).expect("bind failed"); - let sa: SockaddrIn6 = getsockname(receive).expect("getsockname failed"); - setsockopt(receive, Ipv6RecvPacketInfo, &true).expect("setsockopt failed"); - - { - let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; - let iov = [IoSlice::new(&slice)]; - - let send = socket( - AddressFamily::Inet6, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)) - .expect("sendmsg failed"); - } - - { - let mut buf = [0u8; 8]; - let mut iovec = [IoSliceMut::new(&mut buf)]; - - let mut space = cmsg_space!(libc::in6_pktinfo); - let msg = recvmsg::<()>( - receive, - &mut iovec, - Some(&mut space), - MsgFlags::empty(), - ) - .expect("recvmsg failed"); - assert!(!msg - .flags - .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - - let mut cmsgs = msg.cmsgs(); - if let Some(ControlMessageOwned::Ipv6PacketInfo(pktinfo)) = cmsgs.next() - { - let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); - assert_eq!( - pktinfo.ipi6_ifindex as libc::c_uint, i, - "unexpected ifindex (expected {}, got {})", - i, pktinfo.ipi6_ifindex - ); - } - assert!(cmsgs.next().is_none(), "unexpected additional control msg"); - assert_eq!(msg.bytes, 8); - assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]); - } -} - -#[cfg(any(target_os = "android", target_os = "linux"))] -#[cfg_attr(graviton, ignore = "Not supported by the CI environment")] -#[test] -pub fn test_vsock() { - use nix::errno::Errno; - use nix::sys::socket::{ - bind, connect, listen, socket, AddressFamily, SockFlag, SockType, - VsockAddr, - }; - use nix::unistd::close; - use std::thread; - - let port: u32 = 3000; - - let s1 = socket( - AddressFamily::Vsock, - SockType::Stream, - SockFlag::empty(), - None, - ) - .expect("socket failed"); - - // VMADDR_CID_HYPERVISOR is reserved, so we expect an EADDRNOTAVAIL error. - let sockaddr_hv = VsockAddr::new(libc::VMADDR_CID_HYPERVISOR, port); - assert_eq!(bind(s1, &sockaddr_hv).err(), Some(Errno::EADDRNOTAVAIL)); - - let sockaddr_any = VsockAddr::new(libc::VMADDR_CID_ANY, port); - assert_eq!(bind(s1, &sockaddr_any), Ok(())); - listen(s1, 10).expect("listen failed"); - - let thr = thread::spawn(move || { - let cid: u32 = libc::VMADDR_CID_HOST; - - let s2 = socket( - AddressFamily::Vsock, - SockType::Stream, - SockFlag::empty(), - None, - ) - .expect("socket failed"); - - let sockaddr_host = VsockAddr::new(cid, port); - - // The current implementation does not support loopback devices, so, - // for now, we expect a failure on the connect. - assert_ne!(connect(s2, &sockaddr_host), Ok(())); - - close(s2).unwrap(); - }); - - close(s1).unwrap(); - thr.join().unwrap(); -} - -// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack -// of QEMU support is suspected. -#[cfg_attr(qemu, ignore)] -#[cfg(all(target_os = "linux"))] -#[test] -fn test_recvmsg_timestampns() { - use nix::sys::socket::*; - use nix::sys::time::*; - use std::io::{IoSlice, IoSliceMut}; - use std::time::*; - - // Set up - let message = "Ohayō!".as_bytes(); - let in_socket = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .unwrap(); - setsockopt(in_socket, sockopt::ReceiveTimestampns, &true).unwrap(); - let localhost = SockaddrIn::new(127, 0, 0, 1, 0); - bind(in_socket, &localhost).unwrap(); - let address: SockaddrIn = getsockname(in_socket).unwrap(); - // Get initial time - let time0 = SystemTime::now(); - // Send the message - let iov = [IoSlice::new(message)]; - let flags = MsgFlags::empty(); - let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap(); - assert_eq!(message.len(), l); - // Receive the message - let mut buffer = vec![0u8; message.len()]; - let mut cmsgspace = nix::cmsg_space!(TimeSpec); - - let mut iov = [IoSliceMut::new(&mut buffer)]; - let r = recvmsg::<()>(in_socket, &mut iov, Some(&mut cmsgspace), flags) - .unwrap(); - let rtime = match r.cmsgs().next() { - Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime, - Some(_) => panic!("Unexpected control message"), - None => panic!("No control message"), - }; - // Check the final time - let time1 = SystemTime::now(); - // the packet's received timestamp should lie in-between the two system - // times, unless the system clock was adjusted in the meantime. - let rduration = - Duration::new(rtime.tv_sec() as u64, rtime.tv_nsec() as u32); - assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration); - assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); - // Close socket - nix::unistd::close(in_socket).unwrap(); -} - -// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack -// of QEMU support is suspected. -#[cfg_attr(qemu, ignore)] -#[cfg(all(target_os = "linux"))] -#[test] -fn test_recvmmsg_timestampns() { - use nix::sys::socket::*; - use nix::sys::time::*; - use std::io::{IoSlice, IoSliceMut}; - use std::time::*; - - // Set up - let message = "Ohayō!".as_bytes(); - let in_socket = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .unwrap(); - setsockopt(in_socket, sockopt::ReceiveTimestampns, &true).unwrap(); - let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap(); - bind(in_socket, &localhost).unwrap(); - let address: SockaddrIn = getsockname(in_socket).unwrap(); - // Get initial time - let time0 = SystemTime::now(); - // Send the message - let iov = [IoSlice::new(message)]; - let flags = MsgFlags::empty(); - let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap(); - assert_eq!(message.len(), l); - // Receive the message - let mut buffer = vec![0u8; message.len()]; - let cmsgspace = nix::cmsg_space!(TimeSpec); - let iov = vec![[IoSliceMut::new(&mut buffer)]]; - let mut data = MultiHeaders::preallocate(1, Some(cmsgspace)); - let r: Vec<RecvMsg<()>> = - recvmmsg(in_socket, &mut data, iov.iter(), flags, None) - .unwrap() - .collect(); - let rtime = match r[0].cmsgs().next() { - Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime, - Some(_) => panic!("Unexpected control message"), - None => panic!("No control message"), - }; - // Check the final time - let time1 = SystemTime::now(); - // the packet's received timestamp should lie in-between the two system - // times, unless the system clock was adjusted in the meantime. - let rduration = - Duration::new(rtime.tv_sec() as u64, rtime.tv_nsec() as u32); - assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration); - assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); - // Close socket - nix::unistd::close(in_socket).unwrap(); -} - -// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack -// of QEMU support is suspected. -#[cfg_attr(qemu, ignore)] -#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] -#[test] -fn test_recvmsg_rxq_ovfl() { - use nix::sys::socket::sockopt::{RcvBuf, RxqOvfl}; - use nix::sys::socket::*; - use nix::Error; - use std::io::{IoSlice, IoSliceMut}; - - let message = [0u8; 2048]; - let bufsize = message.len() * 2; - - let in_socket = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .unwrap(); - let out_socket = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .unwrap(); - - let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap(); - bind(in_socket, &localhost).unwrap(); - - let address: SockaddrIn = getsockname(in_socket).unwrap(); - connect(out_socket, &address).unwrap(); - - // Set SO_RXQ_OVFL flag. - setsockopt(in_socket, RxqOvfl, &1).unwrap(); - - // Set the receiver buffer size to hold only 2 messages. - setsockopt(in_socket, RcvBuf, &bufsize).unwrap(); - - let mut drop_counter = 0; - - for _ in 0..2 { - let iov = [IoSlice::new(&message)]; - let flags = MsgFlags::empty(); - - // Send the 3 messages (the receiver buffer can only hold 2 messages) - // to create an overflow. - for _ in 0..3 { - let l = - sendmsg(out_socket, &iov, &[], flags, Some(&address)).unwrap(); - assert_eq!(message.len(), l); - } - - // Receive the message and check the drop counter if any. - loop { - let mut buffer = vec![0u8; message.len()]; - let mut cmsgspace = nix::cmsg_space!(u32); - - let mut iov = [IoSliceMut::new(&mut buffer)]; - - match recvmsg::<()>( - in_socket, - &mut iov, - Some(&mut cmsgspace), - MsgFlags::MSG_DONTWAIT, - ) { - Ok(r) => { - drop_counter = match r.cmsgs().next() { - Some(ControlMessageOwned::RxqOvfl(drop_counter)) => { - drop_counter - } - Some(_) => panic!("Unexpected control message"), - None => 0, - }; - } - Err(Error::EAGAIN) => { - break; - } - _ => { - panic!("unknown recvmsg() error"); - } - } - } - } - - // One packet lost. - assert_eq!(drop_counter, 1); - - // Close sockets - nix::unistd::close(in_socket).unwrap(); - nix::unistd::close(out_socket).unwrap(); -} - -#[cfg(any(target_os = "linux", target_os = "android",))] -mod linux_errqueue { - use super::FromStr; - use nix::sys::socket::*; - - // Send a UDP datagram to a bogus destination address and observe an ICMP error (v4). - // - // Disable the test on QEMU because QEMU emulation of IP_RECVERR is broken (as documented on PR - // #1514). - #[cfg_attr(qemu, ignore)] - #[test] - fn test_recverr_v4() { - #[repr(u8)] - enum IcmpTypes { - DestUnreach = 3, // ICMP_DEST_UNREACH - } - #[repr(u8)] - enum IcmpUnreachCodes { - PortUnreach = 3, // ICMP_PORT_UNREACH - } - - test_recverr_impl::<sockaddr_in, _, _>( - "127.0.0.1:6800", - AddressFamily::Inet, - sockopt::Ipv4RecvErr, - libc::SO_EE_ORIGIN_ICMP, - IcmpTypes::DestUnreach as u8, - IcmpUnreachCodes::PortUnreach as u8, - // Closure handles protocol-specific testing and returns generic sock_extended_err for - // protocol-independent test impl. - |cmsg| { - if let ControlMessageOwned::Ipv4RecvErr(ext_err, err_addr) = - cmsg - { - if let Some(origin) = err_addr { - // Validate that our network error originated from 127.0.0.1:0. - assert_eq!(origin.sin_family, AddressFamily::Inet as _); - assert_eq!( - origin.sin_addr.s_addr, - u32::from_be(0x7f000001) - ); - assert_eq!(origin.sin_port, 0); - } else { - panic!("Expected some error origin"); - } - *ext_err - } else { - panic!("Unexpected control message {:?}", cmsg); - } - }, - ) - } - - // Essentially the same test as v4. - // - // Disable the test on QEMU because QEMU emulation of IPV6_RECVERR is broken (as documented on - // PR #1514). - #[cfg_attr(qemu, ignore)] - #[test] - fn test_recverr_v6() { - #[repr(u8)] - enum IcmpV6Types { - DestUnreach = 1, // ICMPV6_DEST_UNREACH - } - #[repr(u8)] - enum IcmpV6UnreachCodes { - PortUnreach = 4, // ICMPV6_PORT_UNREACH - } - - test_recverr_impl::<sockaddr_in6, _, _>( - "[::1]:6801", - AddressFamily::Inet6, - sockopt::Ipv6RecvErr, - libc::SO_EE_ORIGIN_ICMP6, - IcmpV6Types::DestUnreach as u8, - IcmpV6UnreachCodes::PortUnreach as u8, - // Closure handles protocol-specific testing and returns generic sock_extended_err for - // protocol-independent test impl. - |cmsg| { - if let ControlMessageOwned::Ipv6RecvErr(ext_err, err_addr) = - cmsg - { - if let Some(origin) = err_addr { - // Validate that our network error originated from localhost:0. - assert_eq!( - origin.sin6_family, - AddressFamily::Inet6 as _ - ); - assert_eq!( - origin.sin6_addr.s6_addr, - std::net::Ipv6Addr::LOCALHOST.octets() - ); - assert_eq!(origin.sin6_port, 0); - } else { - panic!("Expected some error origin"); - } - *ext_err - } else { - panic!("Unexpected control message {:?}", cmsg); - } - }, - ) - } - - fn test_recverr_impl<SA, OPT, TESTF>( - sa: &str, - af: AddressFamily, - opt: OPT, - ee_origin: u8, - ee_type: u8, - ee_code: u8, - testf: TESTF, - ) where - OPT: SetSockOpt<Val = bool>, - TESTF: FnOnce(&ControlMessageOwned) -> libc::sock_extended_err, - { - use nix::errno::Errno; - use std::io::IoSliceMut; - - const MESSAGE_CONTENTS: &str = "ABCDEF"; - let std_sa = std::net::SocketAddr::from_str(sa).unwrap(); - let sock_addr = SockaddrStorage::from(std_sa); - let sock = socket(af, SockType::Datagram, SockFlag::SOCK_CLOEXEC, None) - .unwrap(); - setsockopt(sock, opt, &true).unwrap(); - if let Err(e) = sendto( - sock, - MESSAGE_CONTENTS.as_bytes(), - &sock_addr, - MsgFlags::empty(), - ) { - assert_eq!(e, Errno::EADDRNOTAVAIL); - println!("{:?} not available, skipping test.", af); - return; - } - - let mut buf = [0u8; 8]; - let mut iovec = [IoSliceMut::new(&mut buf)]; - let mut cspace = cmsg_space!(libc::sock_extended_err, SA); - - let msg = recvmsg( - sock, - &mut iovec, - Some(&mut cspace), - MsgFlags::MSG_ERRQUEUE, - ) - .unwrap(); - // The sent message / destination associated with the error is returned: - assert_eq!(msg.bytes, MESSAGE_CONTENTS.as_bytes().len()); - // recvmsg(2): "The original destination address of the datagram that caused the error is - // supplied via msg_name;" however, this is not literally true. E.g., an earlier version - // of this test used 0.0.0.0 (::0) as the destination address, which was mutated into - // 127.0.0.1 (::1). - assert_eq!(msg.address, Some(sock_addr)); - - // Check for expected control message. - let ext_err = match msg.cmsgs().next() { - Some(cmsg) => testf(&cmsg), - None => panic!("No control message"), - }; - - assert_eq!(ext_err.ee_errno, libc::ECONNREFUSED as u32); - assert_eq!(ext_err.ee_origin, ee_origin); - // ip(7): ee_type and ee_code are set from the type and code fields of the ICMP (ICMPv6) - // header. - assert_eq!(ext_err.ee_type, ee_type); - assert_eq!(ext_err.ee_code, ee_code); - // ip(7): ee_info contains the discovered MTU for EMSGSIZE errors. - assert_eq!(ext_err.ee_info, 0); - - let bytes = msg.bytes; - assert_eq!(&buf[..bytes], MESSAGE_CONTENTS.as_bytes()); - } -} - -// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack -// of QEMU support is suspected. -#[cfg_attr(qemu, ignore)] -#[cfg(target_os = "linux")] -#[test] -pub fn test_txtime() { - use nix::sys::socket::{ - bind, recvmsg, sendmsg, setsockopt, socket, sockopt, ControlMessage, - MsgFlags, SockFlag, SockType, SockaddrIn, - }; - use nix::sys::time::TimeValLike; - use nix::time::{clock_gettime, ClockId}; - - require_kernel_version!(test_txtime, ">= 5.8"); - - let sock_addr = SockaddrIn::from_str("127.0.0.1:6802").unwrap(); - - let ssock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .expect("send socket failed"); - - let txtime_cfg = libc::sock_txtime { - clockid: libc::CLOCK_MONOTONIC, - flags: 0, - }; - setsockopt(ssock, sockopt::TxTime, &txtime_cfg).unwrap(); - - let rsock = socket( - AddressFamily::Inet, - SockType::Datagram, - SockFlag::empty(), - None, - ) - .unwrap(); - bind(rsock, &sock_addr).unwrap(); - - let sbuf = [0u8; 2048]; - let iov1 = [std::io::IoSlice::new(&sbuf)]; - - let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap(); - let delay = std::time::Duration::from_secs(1).into(); - let txtime = (now + delay).num_nanoseconds() as u64; - - let cmsg = ControlMessage::TxTime(&txtime); - sendmsg(ssock, &iov1, &[cmsg], MsgFlags::empty(), Some(&sock_addr)) - .unwrap(); - - let mut rbuf = [0u8; 2048]; - let mut iov2 = [std::io::IoSliceMut::new(&mut rbuf)]; - recvmsg::<()>(rsock, &mut iov2, None, MsgFlags::empty()).unwrap(); -} |