#[cfg(linux_android)] usecrate::*; use libc::c_char; use nix::sys::socket::{getsockname, AddressFamily, UnixAddr}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use std::net::{SocketAddrV4, SocketAddrV6}; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; use std::slice; use std::str::FromStr;
letmut ts = None; for c in recv.cmsgs().unwrap() { iflet ControlMessageOwned::ScmMonotonic(timeval) = c {
ts = Some(timeval);
}
} let ts = ts.expect("ScmMonotonic is present"); let sys_time =
::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_MONOTONIC)
.unwrap(); let diff = sys_time - ts; // Monotonic clock sys_time must be greater
assert!(std::time::Duration::from(diff).as_secs() < 60);
}
#[test] pubfn test_path_to_sock_addr() { let path = "/foo/bar"; let actual = Path::new(path); let addr = UnixAddr::new(actual).unwrap();
#[test] pubfn test_addr_equality_path() { let path = "/foo/bar"; let actual = Path::new(path); let addr1 = UnixAddr::new(actual).unwrap(); letmut addr2 = addr1;
let tempdir = tempfile::tempdir().unwrap(); let sockname = tempdir.path().join("sock"); let sock = socket(
AddressFamily::Unix,
SockType::Datagram,
SockFlag::empty(),
None,
)
.expect("socket failed"); let sockaddr = UnixAddr::new(&sockname).unwrap();
bind(sock.as_raw_fd(), &sockaddr).expect("bind failed");
// Send a message let send_buffer = "hello".as_bytes(); iflet Err(e) = socket::sendmsg(
sock.as_raw_fd(),
&[std::io::IoSlice::new(send_buffer)],
&[],
MsgFlags::empty(),
Some(&sockaddr),
) { crate::skip!("Couldn't send ({e:?}), so skipping test");
}
// Receive the message letmut recv_buffer = [0u8; 32]; letmut iov = [std::io::IoSliceMut::new(&mut recv_buffer)]; let received =
socket::recvmsg(sock.as_raw_fd(), &mut iov, None, MsgFlags::empty())
.unwrap(); // Check the address in the received message
assert_eq!(sockaddr, received.address.unwrap());
}
#[test] pubfn 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 { usesuper::*; use nix::sys::socket::*; use nix::{errno::Errno, Result}; use std::thread;
let send_thread = thread::spawn(move || { letmut 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] pubfn stream() { let (fd2, fd1) = socketpair(
AddressFamily::Unix,
SockType::Stream,
None,
SockFlag::empty(),
)
.unwrap(); // Ignore from for stream sockets let _ = sendrecv(fd1.as_raw_fd(), fd2.as_raw_fd(), send, |_, _| {});
}
#[test] pubfn udp() { let std_sa = SocketAddrV4::from_str("127.0.0.1:6795").unwrap(); let sock_addr = SockaddrIn::from(std_sa); let rsock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
bind(rsock.as_raw_fd(), &sock_addr).unwrap(); let ssock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.expect("send socket failed"); let from = sendrecv(
rsock.as_raw_fd(),
ssock.as_raw_fd(), move |s, m, flags| sendto(s.as_raw_fd(), 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 { usesuper::*; 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)] pubfn 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");
sendrecv(
rsock.as_raw_fd(),
ssock.as_raw_fd(), move |s, m, flags| { let iov = [IoSlice::new(m)]; let cmsg = ControlMessage::UdpGsoSegments(&segment_size);
sendmsg(
s.as_raw_fd(),
&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)] pubfn 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();
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 send_thread = thread::spawn(move || { for _ in0..NUM_MESSAGES_SENT {
sendto(
ssock.as_raw_fd(),
&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();
// 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`. letmut receive_buffers = [[0u8; 32]; NUM_MESSAGES_SENT + 2];
msgs.extend(
receive_buffers
.iter_mut()
.map(|buf| [IoSliceMut::new(&mut buf[..])]),
);
letmut data = MultiHeaders::<SockaddrIn>::preallocate(
NUM_MESSAGES_SENT + 2,
None,
);
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] pubfn udp_inet6() { let addr = std::net::Ipv6Addr::from_str("::1").unwrap(); let rport = 6796; let rstd_sa = SocketAddrV6::new(addr, rport, 0, 0); let raddr = SockaddrIn6::from(rstd_sa); let sport = 6798; 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.as_raw_fd(), &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.as_raw_fd(), &saddr).unwrap(); let from = sendrecv(
rsock.as_raw_fd(),
ssock.as_raw_fd(), move |s, m, flags| sendto(s.as_raw_fd(), 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] pubfn test_recvmsg_ebadf() { use nix::errno::Errno; use nix::sys::socket::{recvmsg, MsgFlags}; use std::io::IoSliceMut;
let fd = -1; // Bad file descriptor let r = recvmsg::<()>(fd.as_raw_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] pubfn 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 received_r = received_r.expect("Did not receive passed fd"); // Ensure that the received file descriptor works
write(&w, b"world").unwrap(); letmut buf = [0u8; 5];
read(received_r.as_raw_fd(), &mut buf).unwrap();
assert_eq!(&buf[..], b"world");
close(received_r).unwrap();
}
// Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross #[cfg(linux_android)] #[cfg_attr(qemu, ignore)] #[test] pubfn 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];
// Disable the test on emulated platforms due to not enabled support of AF_ALG // in QEMU from rust cross #[cfg(linux_android)] #[cfg_attr(qemu, ignore)] #[test] pubfn 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::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; letmut payload =
vec![2u8; payload_len + (assoc_size as usize) + auth_size];
for i in0..assoc_size {
payload[i as usize] = 10;
}
let len = payload.len();
for i in0..auth_size {
payload[len - 1 - i] = 0;
}
// allocate buffer for decrypted data letmut 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.as_raw_fd(), &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", apple_targets, target_os = "netbsd"))] #[test] pubfn 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;
// 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",
apple_targets,
target_os = "netbsd",
target_os = "freebsd"
))] #[test] pubfn test_sendmsg_ipv6packetinfo() { use nix::errno::Errno; use nix::sys::socket::{
bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
SockFlag, SockType, SockaddrIn6,
}; use std::io::IoSlice;
// 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(freebsdlike, netbsdlike))] #[test] pubfn test_sendmsg_ipv4sendsrcaddr() { use nix::sys::socket::{
bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
SockFlag, SockType, SockaddrIn,
}; use std::io::IoSlice;
/// 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 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] pubfn test_sendmsg_empty_cmsgs() { use nix::sys::socket::{
recvmsg, sendmsg, socketpair, AddressFamily, MsgFlags, SockFlag,
SockType,
}; use std::io::{IoSlice, IoSliceMut};
for cmsg in msg.cmsgs().unwrap() { let cred = match cmsg { #[cfg(linux_android)]
ControlMessageOwned::ScmCredentials(cred) => cred, #[cfg(freebsdlike)]
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));
}
}
/// Ensure that we can send `SCM_CREDENTIALS` and `SCM_RIGHTS` with a single /// `sendmsg` call. #[cfg(linux_android)] // 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).unwrap();
}
/// Ensure that passing a an oversized control message buffer to recvmsg /// still works. #[cfg(linux_android)] // 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).unwrap();
}
#[cfg(linux_android)] #[test] fn test_too_small_cmsgspace() { let space = vec![0u8; 4];
assert_eq!(
test_impl_scm_credentials_and_rights(space),
Err(nix::errno::Errno::ENOBUFS)
);
}
#[cfg(linux_android)] fn test_impl_scm_credentials_and_rights( mut space: Vec<u8>,
) -> Result<(), nix::errno::Errno> { 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 received_r = received_r.expect("Did not receive passed fd"); // Ensure that the received file descriptor works
write(&w, b"world").unwrap(); letmut buf = [0u8; 5];
read(received_r.as_raw_fd(), &mut buf).unwrap();
assert_eq!(&buf[..], b"world");
close(received_r).unwrap();
Ok(())
}
// Test creating and using named unix domain sockets #[test] pubfn test_named_unixdomain() { use nix::sys::socket::{
accept, bind, connect, listen, socket, Backlog, UnixAddr,
}; use nix::sys::socket::{SockFlag, SockType}; use nix::unistd::{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.as_raw_fd(), &sockaddr).expect("bind failed");
listen(&s1, Backlog::new(10).unwrap()).expect("listen failed");
// Test using unnamed unix domain addresses #[cfg(linux_android)] #[test] pubfn test_unnamed_unixdomain() { use nix::sys::socket::{getsockname, socketpair}; use nix::sys::socket::{SockFlag, SockType};
let addr_1: UnixAddr =
getsockname(fd_1.as_raw_fd()).expect("getsockname failed");
assert!(addr_1.is_unnamed());
}
// Test creating and using unnamed unix domain addresses for autobinding sockets #[cfg(linux_android)] #[test] pubfn test_unnamed_unixdomain_autobind() { use nix::sys::socket::{bind, getsockname, socket}; use nix::sys::socket::{SockFlag, SockType};
// 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.as_raw_fd(), &UnixAddr::new_unnamed()).expect("bind failed");
let addr: UnixAddr =
getsockname(fd.as_raw_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);
}
// Test creating and using named system control sockets #[cfg(apple_targets)] #[test] pubfn test_syscontrol() { use nix::errno::Errno; use nix::sys::socket::{
socket, SockFlag, SockProtocol, SockType, SysControlAddr,
};
#[cfg(any(bsd, linux_android))] 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 addr1 = VsockAddr::new(libc::VMADDR_CID_HOST, port); let addr2 = VsockAddr::new(libc::VMADDR_CID_HOST, port);
assert_eq!(addr1, addr2);
assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
let addr3 = unsafe {
VsockAddr::from_raw(
addr2.as_ref() as *const libc::sockaddr_vm as *const libc::sockaddr,
Some(mem::size_of::<libc::sockaddr_vm>().try_into().unwrap()),
)
}
.unwrap();
assert_eq!(
addr3.as_ref().svm_family,
AddressFamily::Vsock as libc::sa_family_t
);
assert_eq!(addr3.as_ref().svm_cid, addr1.cid());
assert_eq!(addr3.as_ref().svm_port, addr1.port());
}
#[cfg(apple_targets)] #[test] pubfn test_vsock() { use nix::sys::socket::SockaddrLike; use nix::sys::socket::{AddressFamily, VsockAddr}; use std::mem;
let port: u32 = 3000;
// macOS doesn't have a VMADDR_CID_LOCAL, so test with host again let addr_host = VsockAddr::new(libc::VMADDR_CID_HOST, port);
assert_eq!(addr_host.cid(), libc::VMADDR_CID_HOST);
assert_eq!(addr_host.port(), port);
let addr_any = VsockAddr::new(libc::VMADDR_CID_ANY, libc::VMADDR_PORT_ANY);
assert_eq!(addr_any.cid(), libc::VMADDR_CID_ANY);
assert_eq!(addr_any.port(), libc::VMADDR_PORT_ANY);
let addr1 = VsockAddr::new(libc::VMADDR_CID_HOST, port); let addr2 = VsockAddr::new(libc::VMADDR_CID_HOST, port);
assert_eq!(addr1, addr2);
assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
let addr3 = unsafe {
VsockAddr::from_raw(
addr2.as_ref() as *const libc::sockaddr_vm as *const libc::sockaddr,
Some(mem::size_of::<libc::sockaddr_vm>().try_into().unwrap()),
)
}
.unwrap();
assert_eq!(
addr3.as_ref().svm_family,
AddressFamily::Vsock as libc::sa_family_t
); let cid = addr3.as_ref().svm_cid; let port = addr3.as_ref().svm_port;
assert_eq!(cid, addr1.cid());
assert_eq!(port, addr1.port());
}
// 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] 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.as_raw_fd(), &localhost).unwrap(); let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).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.as_raw_fd(), &iov, &[], flags, Some(&address))
.unwrap();
assert_eq!(message.len(), l); // Receive the message letmut buffer = vec![0u8; message.len()]; letmut cmsgspace = nix::cmsg_space!(TimeSpec);
letmut iov = [IoSliceMut::new(&mut buffer)]; let r = recvmsg::<()>(
in_socket.as_raw_fd(),
&mut iov,
Some(&mut cmsgspace),
flags,
)
.unwrap(); let rtime = match r.cmsgs().unwrap().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());
}
// 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] 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.as_raw_fd(), &localhost).unwrap(); let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).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.as_raw_fd(), &iov, &[], flags, Some(&address))
.unwrap();
assert_eq!(message.len(), l); // Receive the message letmut buffer = vec![0u8; message.len()]; let cmsgspace = nix::cmsg_space!(TimeSpec); letmut iov = [[IoSliceMut::new(&mut buffer)]]; letmut data = MultiHeaders::preallocate(1, Some(cmsgspace)); let r: Vec<RecvMsg<()>> = recvmmsg(
in_socket.as_raw_fd(),
&mut data,
iov.iter_mut(),
flags,
None,
)
.unwrap()
.collect(); let rtime = match r[0].cmsgs().unwrap().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());
}
// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack // of QEMU support is suspected. #[cfg_attr(qemu, ignore)] #[cfg(any(linux_android, target_os = "fuchsia"))] #[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.as_raw_fd(), &localhost).unwrap();
let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).unwrap();
connect(out_socket.as_raw_fd(), &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();
letmut drop_counter = 0;
for _ in0..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 _ in0..3 { let l = sendmsg(
out_socket.as_raw_fd(),
&iov,
&[],
flags,
Some(&address),
)
.unwrap();
assert_eq!(message.len(), l);
}
// Receive the message and check the drop counter if any. loop { letmut buffer = vec![0u8; message.len()]; letmut cmsgspace = nix::cmsg_space!(u32);
// One packet lost.
assert_eq!(drop_counter, 1);
}
#[cfg(linux_android)] mod linux_errqueue { usesuper::FromStr; use nix::sys::socket::*; use std::os::unix::io::AsRawFd;
// 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| { iflet ControlMessageOwned::Ipv4RecvErr(ext_err, err_addr) =
cmsg
{ iflet 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| { iflet ControlMessageOwned::Ipv6RecvErr(ext_err, err_addr) =
cmsg
{ iflet 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:?}");
}
},
)
}
let msg = recvmsg(
sock.as_raw_fd(),
&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().unwrap().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] pubfn 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 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 owned_fd = socket(
AddressFamily::Inet,
SockType::Raw,
SockFlag::empty(),
SockProtocol::Icmp,
)
.unwrap();
// Send a minimal ICMP packet with no payload. let packet = [ 0x08, // Type 0x00, // Code 0x84, 0x85, // Checksum 0x73, 0x8a, // ID 0x00, 0x00, // Sequence Number
];
// test contains both recvmmsg and timestaping which is linux only // there are existing tests for recvmmsg only in tests/ #[cfg_attr(qemu, ignore)] #[cfg(target_os = "linux")] #[test] fn test_recvmm2() -> nix::Result<()> { use nix::sys::{
socket::{
bind, recvmmsg, sendmsg, setsockopt, socket, sockopt::Timestamping,
AddressFamily, ControlMessageOwned, MsgFlags, MultiHeaders,
SockFlag, SockType, SockaddrIn, TimestampingFlag, Timestamps,
},
time::TimeSpec,
}; use std::io::{IoSlice, IoSliceMut}; use std::os::unix::io::AsRawFd; use std::str::FromStr;
let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap();
let ssock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)?;
let rsock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::SOCK_NONBLOCK,
None,
)?;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.