diff options
Diffstat (limited to 'third_party/rust/nix/test/sys/test_socket.rs')
-rw-r--r-- | third_party/rust/nix/test/sys/test_socket.rs | 685 |
1 files changed, 685 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..1ed6c1f6ca --- /dev/null +++ b/third_party/rust/nix/test/sys/test_socket.rs @@ -0,0 +1,685 @@ +use nix::sys::socket::{InetAddr, UnixAddr, getsockname}; +use std::slice; +use std::net::{self, Ipv6Addr, SocketAddr, SocketAddrV6}; +use std::path::Path; +use std::str::FromStr; +use std::os::unix::io::RawFd; +use libc::c_char; +use tempfile; + +#[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_str(), "127.0.0.1:3000"); + + let inet = addr.to_std(); + assert_eq!(actual, inet); +} + +#[test] +pub fn test_inetv6_addr_to_sock_addr() { + 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); + + match addr { + InetAddr::V6(addr) => { + assert_eq!(addr.sin6_port, port.to_be()); + assert_eq!(addr.sin6_flowinfo, flowinfo); + assert_eq!(addr.sin6_scope_id, scope_id); + } + _ => panic!("nope"), + } + + assert_eq!(actual, addr.to_std()); +} + +#[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_bytes().as_ptr() as *const c_char, path.len()) + }; + assert_eq!(&addr.0.sun_path[..8], expect); + + assert_eq!(addr.path(), Some(actual)); +} + +// Test getting/setting abstract addresses (without unix socket creation) +#[cfg(target_os = "linux")] +#[test] +pub fn test_abstract_uds_addr() { + let empty = String::new(); + let addr = UnixAddr::new_abstract(empty.as_bytes()).unwrap(); + assert_eq!(addr.as_abstract(), Some(empty.as_bytes())); + + let name = String::from("nix\0abstract\0test"); + let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap(); + assert_eq!(addr.as_abstract(), Some(name.as_bytes())); + assert_eq!(addr.path(), None); + + // Internally, name is null-prefixed (abstract namespace) + let internal: &[u8] = unsafe { + slice::from_raw_parts(addr.0.sun_path.as_ptr() as *const u8, addr.1) + }; + let mut abstract_name = name.clone(); + abstract_name.insert(0, '\0'); + assert_eq!(internal, abstract_name.as_bytes()); +} + +#[test] +pub fn test_getsockname() { + use nix::sys::socket::{socket, AddressFamily, SockType, SockFlag}; + use nix::sys::socket::{bind, SockAddr}; + + 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 = SockAddr::new_unix(&sockname).unwrap(); + bind(sock, &sockaddr).expect("bind failed"); + assert_eq!(sockaddr.to_str(), + getsockname(sock).expect("getsockname failed").to_str()); +} + +#[test] +pub fn test_socketpair() { + use nix::unistd::{read, write}; + use nix::sys::socket::{socketpair, AddressFamily, SockType, SockFlag}; + + 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_scm_rights() { + use nix::sys::uio::IoVec; + use nix::unistd::{pipe, read, write, close}; + use nix::sys::socket::{socketpair, sendmsg, recvmsg, + AddressFamily, SockType, SockFlag, + ControlMessage, CmsgSpace, MsgFlags}; + + 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 = [IoVec::from_slice(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 iov = [IoVec::from_mut_slice(&mut buf[..])]; + let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new(); + let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap(); + + for cmsg in msg.cmsgs() { + if let ControlMessage::ScmRights(fd) = cmsg { + assert_eq!(received_r, None); + assert_eq!(fd.len(), 1); + received_r = Some(fd[0]); + } else { + panic!("unexpected cmsg"); + } + } + 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(); +} + +/// Tests that passing multiple fds using a single `ControlMessage` works. +#[test] +fn test_scm_rights_single_cmsg_multiple_fds() { + use std::os::unix::net::UnixDatagram; + use std::os::unix::io::{RawFd, AsRawFd}; + use std::thread; + use nix::sys::socket::{CmsgSpace, ControlMessage, MsgFlags, sendmsg, recvmsg}; + use nix::sys::uio::IoVec; + use libc; + + let (send, receive) = UnixDatagram::pair().unwrap(); + let thread = thread::spawn(move || { + let mut buf = [0u8; 8]; + let iovec = [IoVec::from_mut_slice(&mut buf)]; + let mut space = CmsgSpace::<[RawFd; 2]>::new(); + let msg = recvmsg( + receive.as_raw_fd(), + &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(ControlMessage::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!(iovec[0].as_slice(), [1u8, 2, 3, 4, 5, 6, 7, 8]); + }); + + let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let iov = [IoVec::from_slice(&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::uio::IoVec; + use nix::unistd::close; + use nix::sys::socket::{socketpair, sendmsg, recvmsg, + AddressFamily, SockType, SockFlag, + CmsgSpace, MsgFlags}; + + let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) + .unwrap(); + + { + let iov = [IoVec::from_slice(b"hello")]; + assert_eq!(sendmsg(fd1, &iov, &[], MsgFlags::empty(), None).unwrap(), 5); + close(fd1).unwrap(); + } + + { + let mut buf = [0u8; 5]; + let iov = [IoVec::from_mut_slice(&mut buf[..])]; + let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new(); + let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap(); + + for _ in msg.cmsgs() { + panic!("unexpected cmsg"); + } + assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); + close(fd2).unwrap(); + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_scm_credentials() { + use libc; + use nix::sys::uio::IoVec; + use nix::unistd::{close, getpid, getuid, getgid}; + use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt, + AddressFamily, SockType, SockFlag, + ControlMessage, CmsgSpace, MsgFlags}; + use nix::sys::socket::sockopt::PassCred; + + let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) + .unwrap(); + setsockopt(recv, PassCred, &true).unwrap(); + + { + let iov = [IoVec::from_slice(b"hello")]; + let cred = libc::ucred { + pid: getpid().as_raw(), + uid: getuid().as_raw(), + gid: getgid().as_raw(), + }; + let cmsg = ControlMessage::ScmCredentials(&cred); + assert_eq!(sendmsg(send, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), 5); + close(send).unwrap(); + } + + { + let mut buf = [0u8; 5]; + let iov = [IoVec::from_mut_slice(&mut buf[..])]; + let mut cmsgspace: CmsgSpace<libc::ucred> = CmsgSpace::new(); + let msg = recvmsg(recv, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap(); + let mut received_cred = None; + + for cmsg in msg.cmsgs() { + if let ControlMessage::ScmCredentials(cred) = cmsg { + 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); + } else { + panic!("unexpected cmsg"); + } + } + received_cred.expect("no creds received"); + 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 on non-x86 +// see https://bugs.launchpad.net/qemu/+bug/1781280 +#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)] +#[test] +fn test_scm_credentials_and_rights() { + use nix::sys::socket::CmsgSpace; + use libc; + + test_impl_scm_credentials_and_rights(CmsgSpace::<(libc::ucred, CmsgSpace<RawFd>)>::new()); +} + +/// Ensure that passing a `CmsgSpace` with too much space for the received +/// messages still works. +#[cfg(any(target_os = "android", target_os = "linux"))] +// qemu's handling of multiple cmsgs is bugged, ignore tests on non-x86 +// see https://bugs.launchpad.net/qemu/+bug/1781280 +#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)] +#[test] +fn test_too_large_cmsgspace() { + use nix::sys::socket::CmsgSpace; + + test_impl_scm_credentials_and_rights(CmsgSpace::<[u8; 1024]>::new()); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_impl_scm_credentials_and_rights<T>(mut space: ::nix::sys::socket::CmsgSpace<T>) { + use libc; + use nix::sys::uio::IoVec; + use nix::unistd::{pipe, read, write, close, getpid, getuid, getgid}; + use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt, + AddressFamily, SockType, SockFlag, + ControlMessage, MsgFlags}; + use nix::sys::socket::sockopt::PassCred; + + 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 = [IoVec::from_slice(b"hello")]; + let cred = libc::ucred { + pid: getpid().as_raw(), + uid: getuid().as_raw(), + gid: getgid().as_raw(), + }; + 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 iov = [IoVec::from_mut_slice(&mut buf[..])]; + let msg = recvmsg(recv, &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 { + ControlMessage::ScmRights(fds) => { + assert_eq!(received_r, None, "already received fd"); + assert_eq!(fds.len(), 1); + received_r = Some(fds[0]); + } + ControlMessage::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!(!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_unixdomain() { + use nix::sys::socket::{AddressFamily, SockType, SockFlag}; + use nix::sys::socket::{bind, socket, connect, listen, accept, SockAddr}; + use nix::unistd::{read, write, close}; + 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 = SockAddr::new_unix(&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 creating and using named system control sockets +#[cfg(any(target_os = "macos", target_os = "ios"))] +#[test] +pub fn test_syscontrol() { + use nix::Error; + use nix::errno::Errno; + use nix::sys::socket::{AddressFamily, socket, SockAddr, SockType, SockFlag, SockProtocol}; + + let fd = socket(AddressFamily::System, SockType::Datagram, + SockFlag::empty(), SockProtocol::KextControl) + .expect("socket failed"); + let _sockaddr = SockAddr::new_sys_control(fd, "com.apple.net.utun_control", 0).expect("resolving sys_control name failed"); + assert_eq!(SockAddr::new_sys_control(fd, "foo.bar.lol", 0).err(), Some(Error::Sys(Errno::ENOENT))); + + // requires root privileges + // connect(fd, &sockaddr).expect("connect failed"); +} + +#[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" +))] +// qemu doesn't seem to be emulating this correctly in these architectures +#[cfg_attr(any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "powerpc64", +), ignore)] +#[test] +pub fn test_recv_ipv4pktinfo() { + use libc; + use nix::ifaddrs::{getifaddrs, InterfaceAddress}; + use nix::net::if_::*; + use nix::sys::socket::sockopt::Ipv4PacketInfo; + use nix::sys::socket::{bind, AddressFamily, SockFlag, SockType}; + use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr}; + use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags}; + use nix::sys::uio::IoVec; + use std::io; + use std::io::Write; + use std::thread; + + fn loopback_v4addr() -> Option<InterfaceAddress> { + let addrs = match getifaddrs() { + Ok(iter) => iter, + Err(e) => { + let stdioerr = io::stderr(); + let mut handle = stdioerr.lock(); + writeln!(handle, "getifaddrs: {:?}", e).unwrap(); + return None; + }, + }; + for ifaddr in addrs { + if ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) { + match ifaddr.address { + Some(SockAddr::Inet(InetAddr::V4(..))) => { + return Some(ifaddr); + } + _ => continue, + } + } + } + None + } + + let lo_ifaddr = loopback_v4addr(); + 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 = getsockname(receive).expect("getsockname failed"); + setsockopt(receive, Ipv4PacketInfo, &true).expect("setsockopt failed"); + + let thread = thread::spawn(move || { + let mut buf = [0u8; 8]; + let iovec = [IoVec::from_mut_slice(&mut buf)]; + let mut space = CmsgSpace::<libc::in_pktinfo>::new(); + let msg = recvmsg( + receive, + &iovec, + Some(&mut space), + MsgFlags::empty(), + ).expect("recvmsg failed"); + assert!( + !msg.flags + .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC) + ); + + let mut cmsgs = msg.cmsgs(); + match cmsgs.next() { + Some(ControlMessage::Ipv4PacketInfo(pktinfo)) => { + 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!( + iovec[0].as_slice(), + [1u8, 2, 3, 4, 5, 6, 7, 8] + ); + }); + + let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let iov = [IoVec::from_slice(&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"); + + thread.join().unwrap(); +} + +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" +))] +// qemu doesn't seem to be emulating this correctly in these architectures +#[cfg_attr(any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "powerpc64", +), ignore)] +#[test] +pub fn test_recv_ipv6pktinfo() { + use libc; + use nix::ifaddrs::{getifaddrs, InterfaceAddress}; + use nix::net::if_::*; + use nix::sys::socket::sockopt::Ipv6RecvPacketInfo; + use nix::sys::socket::{bind, AddressFamily, SockFlag, SockType}; + use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr}; + use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags}; + use nix::sys::uio::IoVec; + use std::io; + use std::io::Write; + use std::thread; + + fn loopback_v6addr() -> Option<InterfaceAddress> { + let addrs = match getifaddrs() { + Ok(iter) => iter, + Err(e) => { + let stdioerr = io::stderr(); + let mut handle = stdioerr.lock(); + writeln!(handle, "getifaddrs: {:?}", e).unwrap(); + return None; + }, + }; + for ifaddr in addrs { + if ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) { + match ifaddr.address { + Some(SockAddr::Inet(InetAddr::V6(..))) => { + return Some(ifaddr); + } + _ => continue, + } + } + } + None + } + + let lo_ifaddr = loopback_v6addr(); + 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::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("receive socket failed"); + bind(receive, &lo).expect("bind failed"); + let sa = getsockname(receive).expect("getsockname failed"); + setsockopt(receive, Ipv6RecvPacketInfo, &true).expect("setsockopt failed"); + + let thread = thread::spawn(move || { + let mut buf = [0u8; 8]; + let iovec = [IoVec::from_mut_slice(&mut buf)]; + let mut space = CmsgSpace::<libc::in6_pktinfo>::new(); + let msg = recvmsg( + receive, + &iovec, + Some(&mut space), + MsgFlags::empty(), + ).expect("recvmsg failed"); + assert!( + !msg.flags + .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC) + ); + + let mut cmsgs = msg.cmsgs(); + match cmsgs.next() { + Some(ControlMessage::Ipv6PacketInfo(pktinfo)) => { + let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); + assert_eq!( + pktinfo.ipi6_ifindex, + i, + "unexpected ifindex (expected {}, got {})", + i, + pktinfo.ipi6_ifindex + ); + } + _ => (), + } + assert!(cmsgs.next().is_none(), "unexpected additional control msg"); + assert_eq!( + iovec[0].as_slice(), + [1u8, 2, 3, 4, 5, 6, 7, 8] + ); + }); + + let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let iov = [IoVec::from_slice(&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"); + + thread.join().unwrap(); +} |