summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/test/sys/test_socket.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/nix/test/sys/test_socket.rs
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/nix/test/sys/test_socket.rs')
-rw-r--r--third_party/rust/nix/test/sys/test_socket.rs2628
1 files changed, 2628 insertions, 0 deletions
diff --git a/third_party/rust/nix/test/sys/test_socket.rs b/third_party/rust/nix/test/sys/test_socket.rs
new file mode 100644
index 0000000000..5adc77ed6b
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_socket.rs
@@ -0,0 +1,2628 @@
+#[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();
+}