| use nix::sys::socket::{AddressFamily, InetAddr, UnixAddr, getsockname}; |
| use std::collections::hash_map::DefaultHasher; |
| use std::hash::{Hash, Hasher}; |
| use std::net::{self, Ipv6Addr, SocketAddr, SocketAddrV6}; |
| use std::os::unix::io::RawFd; |
| use std::path::Path; |
| use std::slice; |
| use std::str::FromStr; |
| use libc::c_char; |
| use tempfile; |
| #[cfg(any(target_os = "linux", target_os= "android"))] |
| use crate::*; |
| |
| #[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)); |
| } |
| |
| 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.clone(); |
| |
| addr2.0.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()); |
| assert!(addr.is_err()); |
| } |
| |
| #[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.clone(); |
| |
| assert_eq!(addr1, addr2); |
| assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2)); |
| |
| addr2.0.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(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!(addr.0.sun_path[0], 0); |
| } |
| |
| #[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"); |
| } |
| |
| mod recvfrom { |
| use nix::Result; |
| use nix::sys::socket::*; |
| use std::thread; |
| use super::*; |
| |
| const MSG: &'static [u8] = b"Hello, World!"; |
| |
| fn sendrecv<Fs, Fr>(rsock: RawFd, ssock: RawFd, f_send: Fs, mut f_recv: Fr) -> Option<SockAddr> |
| where |
| Fs: Fn(RawFd, &[u8], MsgFlags) -> Result<usize> + Send + 'static, |
| Fr: FnMut(usize, Option<SockAddr>), |
| { |
| 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, |s, m, flags| { |
| send(s, m, flags) |
| }, |_, _| {}); |
| } |
| |
| #[test] |
| pub fn udp() { |
| let std_sa = SocketAddr::from_str("127.0.0.1:6789").unwrap(); |
| let inet_addr = InetAddr::from_std(&std_sa); |
| let sock_addr = SockAddr::new_inet(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 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()); |
| } |
| |
| #[cfg(target_os = "linux")] |
| mod udp_offload { |
| use super::*; |
| use nix::sys::uio::IoVec; |
| use nix::sys::socket::sockopt::{UdpGroSegment, UdpGsoSegment}; |
| |
| #[test] |
| // Disable the test on emulated platforms because it fails in Cirrus-CI. Lack of QEMU |
| // support is suspected. |
| #[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), 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 std_sa = SocketAddr::from_str("127.0.0.1:6791").unwrap(); |
| let inet_addr = InetAddr::from_std(&std_sa); |
| let sock_addr = SockAddr::new_inet(inet_addr); |
| 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 = [IoVec::from_slice(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(not(any(target_arch = "x86_64", target_arch="i686")), 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 nix::sys::uio::IoVec; |
| |
| let std_sa = SocketAddr::from_str("127.0.0.1:6793").unwrap(); |
| let std_sa2 = SocketAddr::from_str("127.0.0.1:6794").unwrap(); |
| let inet_addr = InetAddr::from_std(&std_sa); |
| let inet_addr2 = InetAddr::from_std(&std_sa2); |
| let sock_addr = SockAddr::new_inet(inet_addr); |
| let sock_addr2 = SockAddr::new_inet(inet_addr2); |
| |
| 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 iov = [IoVec::from_slice(m)]; |
| let mut msgs = Vec::new(); |
| msgs.push( |
| SendMmsgData { |
| iov: &iov, |
| cmsgs: &[], |
| addr: Some(sock_addr), |
| _lt: Default::default(), |
| }); |
| |
| let batch_size = 15; |
| |
| for _ in 0..batch_size { |
| msgs.push( |
| SendMmsgData { |
| iov: &iov, |
| cmsgs: &[], |
| addr: Some(sock_addr2), |
| _lt: Default::default(), |
| } |
| ); |
| } |
| sendmmsg(s, msgs.iter(), flags) |
| .map(move |sent_bytes| { |
| assert!(sent_bytes.len() >= 1); |
| for sent in &sent_bytes { |
| assert_eq!(*sent, m.len()); |
| } |
| sent_bytes.len() |
| }) |
| }, |_, _ | {}); |
| // UDP sockets should set the from address |
| assert_eq!(AddressFamily::Inet, from.unwrap().family()); |
| } |
| |
| #[cfg(any( |
| target_os = "linux", |
| target_os = "android", |
| target_os = "freebsd", |
| target_os = "netbsd", |
| ))] |
| #[test] |
| pub fn udp_recvmmsg() { |
| use nix::sys::uio::IoVec; |
| use nix::sys::socket::{MsgFlags, recvmmsg}; |
| |
| const NUM_MESSAGES_SENT: usize = 2; |
| const DATA: [u8; 2] = [1,2]; |
| |
| let std_sa = SocketAddr::from_str("127.0.0.1:6798").unwrap(); |
| let inet_addr = InetAddr::from_std(&std_sa); |
| let sock_addr = SockAddr::new_inet(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]; |
| let iovs: Vec<_> = receive_buffers.iter_mut().map(|buf| { |
| [IoVec::from_mut_slice(&mut buf[..])] |
| }).collect(); |
| |
| for iov in &iovs { |
| msgs.push_back(RecvMmsgData { |
| iov: iov, |
| cmsg_buffer: None, |
| }) |
| }; |
| |
| let res = recvmmsg(rsock, &mut msgs, MsgFlags::empty(), None).expect("recvmmsg"); |
| assert_eq!(res.len(), DATA.len()); |
| |
| for RecvMsg { address, bytes, .. } in res.into_iter() { |
| assert_eq!(AddressFamily::Inet, address.unwrap().family()); |
| 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::uio::IoVec; |
| use nix::sys::socket::{MsgFlags, recvmmsg}; |
| |
| const NUM_MESSAGES_SENT: usize = 2; |
| const DATA: [u8; 4] = [1,2,3,4]; |
| |
| let std_sa = SocketAddr::from_str("127.0.0.1:6799").unwrap(); |
| let inet_addr = InetAddr::from_std(&std_sa); |
| let sock_addr = SockAddr::new_inet(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]; |
| let iovs: Vec<_> = receive_buffers.iter_mut().map(|buf| { |
| [IoVec::from_mut_slice(&mut buf[..])] |
| }).collect(); |
| |
| for iov in &iovs { |
| msgs.push_back(RecvMmsgData { |
| iov: iov, |
| cmsg_buffer: None, |
| }) |
| }; |
| |
| let res = recvmmsg(rsock, &mut msgs, MsgFlags::MSG_DONTWAIT, None).expect("recvmmsg"); |
| assert_eq!(res.len(), NUM_MESSAGES_SENT); |
| |
| for RecvMsg { address, bytes, .. } in res.into_iter() { |
| assert_eq!(AddressFamily::Inet, address.unwrap().family()); |
| assert_eq!(DATA.len(), bytes); |
| } |
| |
| for buf in &receive_buffers[..NUM_MESSAGES_SENT] { |
| assert_eq!(&buf[..DATA.len()], DATA); |
| } |
| } |
| } |
| |
| // Test error handling of our recvmsg wrapper |
| #[test] |
| pub fn test_recvmsg_ebadf() { |
| use nix::Error; |
| use nix::errno::Errno; |
| use nix::sys::socket::{MsgFlags, recvmsg}; |
| use nix::sys::uio::IoVec; |
| |
| let mut buf = [0u8; 5]; |
| let iov = [IoVec::from_mut_slice(&mut buf[..])]; |
| let fd = -1; // Bad file descriptor |
| let r = recvmsg(fd, &iov, None, MsgFlags::empty()); |
| assert_eq!(r.err().unwrap(), Error::Sys(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(not(any(target_arch = "x86_64", target_arch="i686")), ignore)] |
| #[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, ControlMessageOwned, 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 = cmsg_space!([RawFd; 1]); |
| let msg = recvmsg(fd2, &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_attr(not(any(target_arch = "x86_64", target_arch = "i686")), ignore)] |
| #[cfg(any(target_os = "linux", target_os= "android"))] |
| #[test] |
| pub fn test_af_alg_cipher() { |
| use libc; |
| use nix::sys::uio::IoVec; |
| use nix::unistd::read; |
| use nix::sys::socket::{socket, sendmsg, bind, accept, setsockopt, |
| AddressFamily, SockType, SockFlag, SockAddr, |
| ControlMessage, MsgFlags}; |
| use nix::sys::socket::sockopt::AlgSetKey; |
| |
| 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 = SockAddr::new_alg(alg_type, alg_name); |
| bind(sock, &sockaddr).expect("bind failed"); |
| |
| if let SockAddr::Alg(alg) = sockaddr { |
| assert_eq!(alg.alg_name().to_string_lossy(), alg_name); |
| assert_eq!(alg.alg_type().to_string_lossy(), alg_type); |
| } else { |
| panic!("unexpected SockAddr"); |
| } |
| |
| 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 = IoVec::from_slice(&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 = IoVec::from_slice(&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_attr(not(any(target_arch = "x86_64", target_arch = "i686")), ignore)] |
| #[cfg(any(target_os = "linux", target_os= "android"))] |
| #[test] |
| pub fn test_af_alg_aead() { |
| use libc::{ALG_OP_DECRYPT, ALG_OP_ENCRYPT}; |
| use nix::sys::uio::IoVec; |
| use nix::unistd::{read, close}; |
| use nix::sys::socket::{socket, sendmsg, bind, accept, setsockopt, |
| AddressFamily, SockType, SockFlag, SockAddr, |
| ControlMessage, MsgFlags}; |
| use nix::sys::socket::sockopt::{AlgSetKey, AlgSetAeadAuthSize}; |
| |
| 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 = SockAddr::new_alg(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 = IoVec::from_slice(&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 = IoVec::from_slice(&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]; |
| 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 nix::sys::uio::IoVec; |
| use nix::sys::socket::{socket, sendmsg, bind, |
| AddressFamily, SockType, SockFlag, SockAddr, |
| ControlMessage, MsgFlags}; |
| |
| let sock = socket(AddressFamily::Inet, |
| SockType::Datagram, |
| SockFlag::empty(), |
| None) |
| .expect("socket failed"); |
| |
| let std_sa = SocketAddr::from_str("127.0.0.1:4000").unwrap(); |
| let inet_addr = InetAddr::from_std(&std_sa); |
| let sock_addr = SockAddr::new_inet(inet_addr); |
| |
| bind(sock, &sock_addr).expect("bind failed"); |
| |
| let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; |
| let iov = [IoVec::from_slice(&slice)]; |
| |
| if let InetAddr::V4(sin) = inet_addr { |
| let pi = libc::in_pktinfo { |
| ipi_ifindex: 0, /* Unspecified interface */ |
| ipi_addr: libc::in_addr { s_addr: 0 }, |
| ipi_spec_dst: sin.sin_addr, |
| }; |
| |
| let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)]; |
| |
| sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr)) |
| .expect("sendmsg"); |
| } else { |
| panic!("No IPv4 addresses available for testing?"); |
| } |
| } |
| |
| // 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::Error; |
| use nix::errno::Errno; |
| use nix::sys::uio::IoVec; |
| use nix::sys::socket::{socket, sendmsg, bind, |
| AddressFamily, SockType, SockFlag, SockAddr, |
| ControlMessage, MsgFlags}; |
| |
| let sock = socket(AddressFamily::Inet6, |
| SockType::Datagram, |
| SockFlag::empty(), |
| None) |
| .expect("socket failed"); |
| |
| let std_sa = SocketAddr::from_str("[::1]:6000").unwrap(); |
| let inet_addr = InetAddr::from_std(&std_sa); |
| let sock_addr = SockAddr::new_inet(inet_addr); |
| |
| match bind(sock, &sock_addr) { |
| Err(Error::Sys(Errno::EADDRNOTAVAIL)) => { |
| println!("IPv6 not available, skipping test."); |
| return; |
| }, |
| _ => (), |
| } |
| |
| let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; |
| let iov = [IoVec::from_slice(&slice)]; |
| |
| if let InetAddr::V6(sin) = inet_addr { |
| let pi = libc::in6_pktinfo { |
| ipi6_ifindex: 0, /* Unspecified interface */ |
| ipi6_addr: sin.sin6_addr, |
| }; |
| |
| let cmsg = [ControlMessage::Ipv6PacketInfo(&pi)]; |
| |
| sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr)) |
| .expect("sendmsg"); |
| } else { |
| println!("No IPv6 addresses available for testing: skipping testing Ipv6PacketInfo"); |
| } |
| } |
| |
| /// 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(not(any(target_arch = "x86_64", target_arch="i686")), ignore)] |
| #[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::{ControlMessage, ControlMessageOwned, 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 = cmsg_space!([RawFd; 2]); |
| 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(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].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, 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 = cmsg_space!([RawFd; 1]); |
| 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)); |
| 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::uio::IoVec; |
| use nix::unistd::{close, getpid, getuid, getgid}; |
| use nix::sys::socket::{socketpair, sendmsg, recvmsg, |
| AddressFamily, SockType, SockFlag, |
| ControlMessage, ControlMessageOwned, MsgFlags, |
| UnixCredentials}; |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| use nix::sys::socket::{setsockopt, sockopt::PassCred}; |
| |
| 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 = [IoVec::from_slice(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 iov = [IoVec::from_mut_slice(&mut buf[..])]; |
| let mut cmsgspace = cmsg_space!(UnixCredentials); |
| let msg = recvmsg(recv, &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 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 libc; |
| |
| 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 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() { |
| 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::uio::IoVec; |
| use nix::unistd::{pipe, write, close, getpid, getuid, getgid}; |
| use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt, |
| SockType, SockFlag, |
| ControlMessage, ControlMessageOwned, 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 = 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 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 { |
| 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_unixdomain() { |
| use nix::sys::socket::{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::{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 = "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 std::io; |
| use std::io::Write; |
| use nix::ifaddrs::getifaddrs; |
| use nix::sys::socket::SockAddr; |
| use nix::net::if_::*; |
| |
| 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; |
| }, |
| }; |
| // return first address matching family |
| for ifaddr in addrs { |
| if ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) { |
| match ifaddr.address { |
| Some(SockAddr::Inet(InetAddr::V4(..))) => { |
| match family { |
| AddressFamily::Inet => return Some(ifaddr), |
| _ => continue |
| } |
| }, |
| Some(SockAddr::Inet(InetAddr::V6(..))) => { |
| match family { |
| AddressFamily::Inet6 => return Some(ifaddr), |
| _ => continue |
| } |
| }, |
| _ => continue, |
| } |
| } |
| } |
| None |
| } |
| |
| #[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(any( |
| target_arch = "mips", |
| target_arch = "mips64", |
| target_arch = "powerpc64", |
| ), ignore)] |
| #[test] |
| pub fn test_recv_ipv4pktinfo() { |
| use libc; |
| use nix::sys::socket::sockopt::Ipv4PacketInfo; |
| use nix::sys::socket::{bind, SockFlag, SockType}; |
| use nix::sys::socket::{getsockname, setsockopt, socket}; |
| use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; |
| use nix::sys::uio::IoVec; |
| use nix::net::if_::*; |
| |
| 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 = getsockname(receive).expect("getsockname failed"); |
| setsockopt(receive, Ipv4PacketInfo, &true).expect("setsockopt failed"); |
| |
| { |
| 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"); |
| } |
| |
| { |
| let mut buf = [0u8; 8]; |
| let iovec = [IoVec::from_mut_slice(&mut buf)]; |
| let mut space = cmsg_space!(libc::in_pktinfo); |
| 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(ControlMessageOwned::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!(msg.bytes, 8); |
| assert_eq!( |
| iovec[0].as_slice(), |
| [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(any( |
| target_arch = "mips", |
| target_arch = "mips64", |
| target_arch = "powerpc64", |
| ), ignore)] |
| #[test] |
| pub fn test_recvif() { |
| use libc; |
| use nix::net::if_::*; |
| use nix::sys::socket::sockopt::{Ipv4RecvIf, Ipv4RecvDstAddr}; |
| use nix::sys::socket::{bind, SockFlag, SockType}; |
| use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr}; |
| use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; |
| use nix::sys::uio::IoVec; |
| |
| 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 = 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 = [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"); |
| } |
| |
| { |
| let mut buf = [0u8; 8]; |
| let iovec = [IoVec::from_mut_slice(&mut buf)]; |
| let mut space = cmsg_space!(libc::sockaddr_dl, libc::in_addr); |
| let msg = recvmsg( |
| receive, |
| &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 SockAddr::Inet(InetAddr::V4(a)) = lo { |
| assert_eq!(a.sin_addr.s_addr, |
| addr.s_addr, |
| "unexpected destination address (expected {}, got {})", |
| a.sin_addr.s_addr, |
| addr.s_addr); |
| } else { |
| panic!("unexpected Sockaddr"); |
| } |
| }, |
| _ => panic!("unexpected additional control msg"), |
| } |
| } |
| assert_eq!(rx_recvif, true); |
| assert_eq!(rx_recvdstaddr, true); |
| assert_eq!(msg.bytes, 8); |
| assert_eq!( |
| iovec[0].as_slice(), |
| [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(any( |
| target_arch = "mips", |
| target_arch = "mips64", |
| target_arch = "powerpc64", |
| ), ignore)] |
| #[test] |
| pub fn test_recv_ipv6pktinfo() { |
| use libc; |
| use nix::net::if_::*; |
| use nix::sys::socket::sockopt::Ipv6RecvPacketInfo; |
| use nix::sys::socket::{bind, SockFlag, SockType}; |
| use nix::sys::socket::{getsockname, setsockopt, socket}; |
| use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; |
| use nix::sys::uio::IoVec; |
| |
| let lo_ifaddr = loopback_address(AddressFamily::Inet6); |
| 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 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"); |
| } |
| |
| { |
| let mut buf = [0u8; 8]; |
| let iovec = [IoVec::from_mut_slice(&mut buf)]; |
| let mut space = cmsg_space!(libc::in6_pktinfo); |
| 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(ControlMessageOwned::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!(msg.bytes, 8); |
| assert_eq!( |
| iovec[0].as_slice(), |
| [1u8, 2, 3, 4, 5, 6, 7, 8] |
| ); |
| } |
| } |
| |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| #[test] |
| pub fn test_vsock() { |
| use libc; |
| use nix::Error; |
| use nix::errno::Errno; |
| use nix::sys::socket::{AddressFamily, socket, bind, connect, listen, |
| SockAddr, SockType, SockFlag}; |
| 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 and VMADDR_CID_LOCAL are reserved, so we expect |
| // an EADDRNOTAVAIL error. |
| let sockaddr = SockAddr::new_vsock(libc::VMADDR_CID_HYPERVISOR, port); |
| assert_eq!(bind(s1, &sockaddr).err(), |
| Some(Error::Sys(Errno::EADDRNOTAVAIL))); |
| |
| let sockaddr = SockAddr::new_vsock(libc::VMADDR_CID_LOCAL, port); |
| assert_eq!(bind(s1, &sockaddr).err(), |
| Some(Error::Sys(Errno::EADDRNOTAVAIL))); |
| |
| |
| let sockaddr = SockAddr::new_vsock(libc::VMADDR_CID_ANY, port); |
| assert_eq!(bind(s1, &sockaddr), 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 = SockAddr::new_vsock(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), Ok(())); |
| |
| close(s2).unwrap(); |
| }); |
| |
| close(s1).unwrap(); |
| thr.join().unwrap(); |
| } |