diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 48099a48..febf9fcc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,6 +65,21 @@ jobs: - uses: taiki-e/install-action@cargo-hack - name: Run check run: cargo hack check --feature-powerset --all-targets --examples --bins --tests --target ${{ matrix.target }} + CheckTier3: + name: Check + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: ["armv7-sony-vita-newlibeabihf"] + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + with: + components: "rust-src" + - uses: taiki-e/install-action@cargo-hack + - name: Run check + run: cargo hack check -Z build-std=std,panic_abort --feature-powerset --all-targets --examples --bins --tests --target ${{ matrix.target }} Clippy: name: Clippy runs-on: ubuntu-latest @@ -87,3 +102,17 @@ jobs: targets: ${{ matrix.target }} - name: Check docs for docs.rs run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --no-deps --all-features --target ${{ matrix.target }} + DocsTier3: + name: Docs + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: ["armv7-sony-vita-newlibeabihf"] + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly # NOTE: need nightly for `doc_cfg` feature. + with: + components: "rust-src" + - name: Check docs for docs.rs + run: RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc -Z build-std=std,panic_abort --no-deps --all-features --target ${{ matrix.target }} diff --git a/Cargo.toml b/Cargo.toml index 512246c0..0b82cab8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ targets = ["aarch64-apple-ios", "aarch64-linux-android", "x86_64-apple-darwin", features = ["all"] [target."cfg(unix)".dependencies] -libc = "0.2.141" +libc = "0.2.149" [target.'cfg(windows)'.dependencies.windows-sys] version = "0.48" diff --git a/src/lib.rs b/src/lib.rs index 7ed91054..127e0703 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -425,7 +425,7 @@ impl<'a> DerefMut for MaybeUninitSlice<'a> { /// See [`Socket::set_tcp_keepalive`]. #[derive(Debug, Clone)] pub struct TcpKeepalive { - #[cfg_attr(target_os = "openbsd", allow(dead_code))] + #[cfg_attr(any(target_os = "openbsd", target_os = "vita"), allow(dead_code))] time: Option, #[cfg(not(any( target_os = "openbsd", @@ -433,6 +433,7 @@ pub struct TcpKeepalive { target_os = "solaris", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] interval: Option, #[cfg(not(any( @@ -442,6 +443,7 @@ pub struct TcpKeepalive { target_os = "windows", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] retries: Option, } @@ -457,6 +459,7 @@ impl TcpKeepalive { target_os = "solaris", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] interval: None, #[cfg(not(any( @@ -466,6 +469,7 @@ impl TcpKeepalive { target_os = "windows", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] retries: None, } @@ -680,6 +684,7 @@ impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> { /// /// Corresponds to setting `msg_name` and `msg_namelen` on Unix and `name` /// and `namelen` on Windows. + #[allow(clippy::needless_pass_by_ref_mut)] pub fn with_addr(mut self, addr: &'addr mut SockAddr) -> Self { sys::set_msghdr_name(&mut self.inner, addr); self diff --git a/src/socket.rs b/src/socket.rs index eaf2cb18..efe2b0a4 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -791,6 +791,7 @@ fn set_common_flags(socket: Socket) -> io::Result { target_os = "netbsd", target_os = "openbsd", target_os = "espidf", + target_os = "vita", )) ))] socket._set_cloexec(true)?; @@ -1245,6 +1246,7 @@ impl Socket { target_os = "solaris", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] pub fn join_multicast_v4_n( &self, @@ -1277,6 +1279,7 @@ impl Socket { target_os = "solaris", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] pub fn leave_multicast_v4_n( &self, @@ -1310,6 +1313,7 @@ impl Socket { target_os = "fuchsia", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] pub fn join_ssm_v4( &self, @@ -1346,6 +1350,7 @@ impl Socket { target_os = "fuchsia", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] pub fn leave_ssm_v4( &self, @@ -1524,6 +1529,7 @@ impl Socket { target_os = "haiku", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] pub fn set_recv_tos(&self, recv_tos: bool) -> io::Result<()> { unsafe { @@ -1553,6 +1559,7 @@ impl Socket { target_os = "haiku", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] pub fn recv_tos(&self) -> io::Result { unsafe { @@ -1769,6 +1776,7 @@ impl Socket { target_os = "solaris", target_os = "haiku", target_os = "espidf", + target_os = "vita", )))] pub fn recv_tclass_v6(&self) -> io::Result { unsafe { @@ -1792,6 +1800,7 @@ impl Socket { target_os = "solaris", target_os = "haiku", target_os = "espidf", + target_os = "vita", )))] pub fn set_recv_tclass_v6(&self, recv_tclass: bool) -> io::Result<()> { unsafe { @@ -1817,13 +1826,23 @@ impl Socket { /// supported Unix operating systems. #[cfg(all( feature = "all", - not(any(windows, target_os = "haiku", target_os = "openbsd")) + not(any( + windows, + target_os = "haiku", + target_os = "openbsd", + target_os = "vita" + )) ))] #[cfg_attr( docsrs, doc(cfg(all( feature = "all", - not(any(windows, target_os = "haiku", target_os = "openbsd")) + not(any( + windows, + target_os = "haiku", + target_os = "openbsd", + target_os = "vita" + )) ))) )] pub fn keepalive_time(&self) -> io::Result { diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 244117b5..c5626003 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -128,6 +128,7 @@ pub(crate) use libc::ipv6_mreq as Ipv6Mreq; target_os = "solaris", target_os = "haiku", target_os = "espidf", + target_os = "vita", )))] pub(crate) use libc::IPV6_RECVTCLASS; #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))] @@ -144,6 +145,7 @@ pub(crate) use libc::IP_HDRINCL; target_os = "haiku", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] pub(crate) use libc::IP_RECVTOS; #[cfg(not(any( @@ -183,6 +185,7 @@ pub(crate) use libc::{ target_os = "fuchsia", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] pub(crate) use libc::{ ip_mreq_source as IpMreqSource, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, @@ -255,6 +258,7 @@ use libc::TCP_KEEPALIVE as KEEPALIVE_TIME; target_os = "openbsd", target_os = "tvos", target_os = "watchos", + target_os = "vita", )))] use libc::TCP_KEEPIDLE as KEEPALIVE_TIME; @@ -335,6 +339,7 @@ type IovLen = usize; target_os = "tvos", target_os = "watchos", target_os = "espidf", + target_os = "vita", ))] type IovLen = c_int; @@ -928,12 +933,20 @@ pub(crate) fn try_clone(fd: Socket) -> io::Result { syscall!(fcntl(fd, libc::F_DUPFD_CLOEXEC, 0)) } -#[cfg(all(feature = "all", unix))] +#[cfg(all(feature = "all", unix, not(target_os = "vita")))] pub(crate) fn nonblocking(fd: Socket) -> io::Result { let file_status_flags = fcntl_get(fd, libc::F_GETFL)?; Ok((file_status_flags & libc::O_NONBLOCK) != 0) } +#[cfg(all(feature = "all", target_os = "vita"))] +pub(crate) fn nonblocking(fd: Socket) -> io::Result { + unsafe { + getsockopt::(fd, libc::SOL_SOCKET, libc::SO_NONBLOCK).map(|non_block| non_block != 0) + } +} + +#[cfg(not(target_os = "vita"))] pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> { if nonblocking { fcntl_add(fd, libc::F_GETFL, libc::F_SETFL, libc::O_NONBLOCK) @@ -942,6 +955,18 @@ pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> { } } +#[cfg(target_os = "vita")] +pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> { + unsafe { + setsockopt( + fd, + libc::SOL_SOCKET, + libc::SO_NONBLOCK, + nonblocking as libc::c_int, + ) + } +} + pub(crate) fn shutdown(fd: Socket, how: Shutdown) -> io::Result<()> { let how = match how { Shutdown::Write => libc::SHUT_WR, @@ -1118,10 +1143,16 @@ fn into_timeval(duration: Option) -> libc::timeval { } } -#[cfg(all(feature = "all", not(any(target_os = "haiku", target_os = "openbsd"))))] +#[cfg(all( + feature = "all", + not(any(target_os = "haiku", target_os = "openbsd", target_os = "vita")) +))] #[cfg_attr( docsrs, - doc(cfg(all(feature = "all", not(any(target_os = "haiku", target_os = "openbsd"))))) + doc(cfg(all( + feature = "all", + not(any(target_os = "haiku", target_os = "openbsd", target_os = "vita")) + ))) )] pub(crate) fn keepalive_time(fd: Socket) -> io::Result { unsafe { @@ -1132,7 +1163,12 @@ pub(crate) fn keepalive_time(fd: Socket) -> io::Result { #[allow(unused_variables)] pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Result<()> { - #[cfg(not(any(target_os = "haiku", target_os = "openbsd", target_os = "nto")))] + #[cfg(not(any( + target_os = "haiku", + target_os = "openbsd", + target_os = "nto", + target_os = "vita" + )))] if let Some(time) = keepalive.time { let secs = into_secs(time); unsafe { setsockopt(fd, libc::IPPROTO_TCP, KEEPALIVE_TIME, secs)? } @@ -1172,17 +1208,24 @@ pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Res Ok(()) } -#[cfg(not(any(target_os = "haiku", target_os = "openbsd", target_os = "nto")))] +#[cfg(not(any( + target_os = "haiku", + target_os = "openbsd", + target_os = "nto", + target_os = "vita" +)))] fn into_secs(duration: Duration) -> c_int { min(duration.as_secs(), c_int::MAX as u64) as c_int } /// Get the flags using `cmd`. +#[cfg(not(target_os = "vita"))] fn fcntl_get(fd: Socket, cmd: c_int) -> io::Result { syscall!(fcntl(fd, cmd)) } /// Add `flag` to the current set flags of `F_GETFD`. +#[cfg(not(target_os = "vita"))] fn fcntl_add(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> { let previous = fcntl_get(fd, get_cmd)?; let new = previous | flag; @@ -1195,6 +1238,7 @@ fn fcntl_add(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Res } /// Remove `flag` to the current set flags of `F_GETFD`. +#[cfg(not(target_os = "vita"))] fn fcntl_remove(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> { let previous = fcntl_get(fd, get_cmd)?; let new = previous & !flag; @@ -1275,6 +1319,7 @@ pub(crate) fn from_in6_addr(addr: in6_addr) -> Ipv6Addr { target_os = "solaris", target_os = "nto", target_os = "espidf", + target_os = "vita", )))] pub(crate) const fn to_mreqn( multiaddr: &Ipv4Addr, @@ -1371,12 +1416,13 @@ impl crate::Socket { ), allow(rustdoc::broken_intra_doc_links) )] - #[cfg(feature = "all")] + #[cfg(all(feature = "all", not(target_os = "vita")))] #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))] pub fn set_cloexec(&self, close_on_exec: bool) -> io::Result<()> { self._set_cloexec(close_on_exec) } + #[cfg(not(target_os = "vita"))] pub(crate) fn _set_cloexec(&self, close_on_exec: bool) -> io::Result<()> { if close_on_exec { fcntl_add( diff --git a/tests/socket.rs b/tests/socket.rs index f81f6233..2ab87705 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -13,13 +13,15 @@ ))] use std::fs::File; use std::io; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "vita")))] use std::io::IoSlice; use std::io::Read; use std::io::Write; -use std::mem::{self, MaybeUninit}; +#[cfg(not(target_os = "vita"))] +use std::mem::MaybeUninit; +use std::mem::{self}; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpStream}; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "vita")))] use std::net::{Ipv6Addr, SocketAddrV6}; #[cfg(all( feature = "all", @@ -41,6 +43,7 @@ use std::os::windows::io::AsRawSocket; #[cfg(unix)] use std::path::Path; use std::str; +#[cfg(not(target_os = "vita"))] use std::thread; use std::time::Duration; use std::{env, fs}; @@ -48,9 +51,11 @@ use std::{env, fs}; #[cfg(windows)] use windows_sys::Win32::Foundation::{GetHandleInformation, HANDLE_FLAG_INHERIT}; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "vita")))] use socket2::MaybeUninitSlice; -use socket2::{Domain, Protocol, SockAddr, Socket, TcpKeepalive, Type}; +#[cfg(not(target_os = "vita"))] +use socket2::TcpKeepalive; +use socket2::{Domain, Protocol, SockAddr, Socket, Type}; #[test] fn domain_for_address() { @@ -216,7 +221,7 @@ fn set_nonblocking() { } fn assert_common_flags(socket: &Socket, expected: bool) { - #[cfg(unix)] + #[cfg(all(unix, not(target_os = "vita")))] assert_close_on_exec(socket, expected); #[cfg(any( target_os = "ios", @@ -227,6 +232,13 @@ fn assert_common_flags(socket: &Socket, expected: bool) { assert_flag_no_sigpipe(socket, expected); #[cfg(windows)] assert_flag_no_inherit(socket, expected); + + // Vita does not have process API, so neither SO_NOSIGPIPE nor FD_CLOEXEC are supported on this platform + #[cfg(target_os = "vita")] + { + let _ = socket; + let _ = expected; + } } #[test] @@ -284,11 +296,30 @@ fn type_nonblocking() { pub fn assert_nonblocking(socket: &Socket, want: bool) { #[cfg(all(feature = "all", unix))] assert_eq!(socket.nonblocking().unwrap(), want, "non-blocking option"); - #[cfg(not(all(feature = "all", unix)))] + + #[cfg(not(any(all(feature = "all", unix), target_os = "vita")))] { let flags = unsafe { libc::fcntl(socket.as_raw_fd(), libc::F_GETFL) }; assert_eq!(flags & libc::O_NONBLOCK != 0, want, "non-blocking option"); } + + #[cfg(all(target_os = "vita", not(feature = "all")))] + { + let mut optval: libc::c_int = 0; + let mut optlen = std::mem::size_of::() as libc::socklen_t; + + let res = unsafe { + libc::getsockopt( + socket.as_raw_fd(), + libc::SOL_SOCKET, + libc::SO_NONBLOCK, + &mut optval as *mut libc::c_int as _, + &mut optlen, + ) + }; + assert_eq!(res, 0, "unable to get non-blocing option"); + assert_eq!(optval > 0, want, "non-blocking option"); + } } #[cfg(windows)] @@ -297,7 +328,7 @@ pub fn assert_nonblocking(_: &Socket, _: bool) { // No way to get this information... } -#[cfg(all(unix, feature = "all"))] +#[cfg(all(unix, feature = "all", not(target_os = "vita")))] #[test] fn set_cloexec() { let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); @@ -448,6 +479,7 @@ fn connect_timeout_unrouteable() { } #[test] +#[cfg(not(target_os = "vita"))] // Loopback has special behavior on vita fn connect_timeout_unbound() { // Bind and drop a socket to track down a "probably unassigned" port. let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); @@ -508,6 +540,11 @@ fn unix_sockets_supported() -> bool { Err(err) => panic!("socket error: {err}"), } } + + #[cfg(target_os = "vita")] + return false; + + #[cfg(not(target_os = "vita"))] true } @@ -563,6 +600,7 @@ fn vsock() { } #[test] +#[cfg(not(target_os = "vita"))] // Vita does not support OOB fn out_of_band() { let listener = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); listener.bind(&any_ipv4()).unwrap(); @@ -593,7 +631,7 @@ fn out_of_band() { } #[test] -#[cfg(not(target_os = "redox"))] // cfg of `udp_pair_unconnected()` +#[cfg(not(any(target_os = "redox", target_os = "vita")))] fn udp_peek_sender() { let (socket_a, socket_b) = udp_pair_unconnected(); @@ -608,7 +646,7 @@ fn udp_peek_sender() { } #[test] -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "vita")))] fn send_recv_vectored() { let (socket_a, socket_b) = udp_pair_connected(); @@ -655,7 +693,7 @@ fn send_recv_vectored() { } #[test] -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "vita")))] fn send_from_recv_to_vectored() { let (socket_a, socket_b) = udp_pair_unconnected(); let addr_a = socket_a.local_addr().unwrap(); @@ -704,7 +742,7 @@ fn send_from_recv_to_vectored() { } #[test] -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "vita")))] fn sendmsg() { let (socket_a, socket_b) = udp_pair_unconnected(); @@ -722,7 +760,7 @@ fn sendmsg() { } #[test] -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "vita")))] fn recv_vectored_truncated() { let (socket_a, socket_b) = udp_pair_connected(); @@ -742,7 +780,7 @@ fn recv_vectored_truncated() { } #[test] -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "vita")))] fn recv_from_vectored_truncated() { let (socket_a, socket_b) = udp_pair_unconnected(); let addr_a = socket_a.local_addr().unwrap(); @@ -768,7 +806,7 @@ fn recv_from_vectored_truncated() { } /// Create a pair of non-connected UDP sockets suitable for unit tests. -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "vita")))] fn udp_pair_unconnected() -> (Socket, Socket) { // Use ephemeral ports assigned by the OS. let unspecified_addr = SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0); @@ -796,7 +834,7 @@ fn udp_pair_unconnected() -> (Socket, Socket) { } /// Create a pair of connected UDP sockets suitable for unit tests. -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "vita")))] fn udp_pair_connected() -> (Socket, Socket) { let (socket_a, socket_b) = udp_pair_unconnected(); @@ -809,6 +847,7 @@ fn udp_pair_connected() -> (Socket, Socket) { } #[test] +#[cfg(not(target_os = "vita"))] fn tcp_keepalive() { let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); let params = TcpKeepalive::new().with_time(Duration::from_secs(200)); @@ -1171,8 +1210,11 @@ fn r#type() { let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); assert_eq!(socket.r#type().unwrap(), Type::STREAM); - let socket = Socket::new(Domain::IPV6, Type::DGRAM, None).unwrap(); - assert_eq!(socket.r#type().unwrap(), Type::DGRAM); + #[cfg(not(target_os = "vita"))] + { + let socket = Socket::new(Domain::IPV6, Type::DGRAM, None).unwrap(); + assert_eq!(socket.r#type().unwrap(), Type::DGRAM); + } // macos doesn't support seqpacket #[cfg(all( @@ -1182,6 +1224,7 @@ fn r#type() { target_os = "macos", target_os = "tvos", target_os = "watchos", + target_os = "vita", )), feature = "all", ))] @@ -1217,6 +1260,7 @@ fn any_ipv4() -> SockAddr { /// Assume the `buf`fer to be initialised. // TODO: replace with `MaybeUninit::slice_assume_init_ref` once stable. +#[cfg(not(target_os = "vita"))] // Loopback has special behavior on vita unsafe fn assume_init(buf: &[MaybeUninit]) -> &[u8] { &*(buf as *const [MaybeUninit] as *const [u8]) } @@ -1232,6 +1276,7 @@ macro_rules! test { $( #[$attr] )* fn $get_fn() { test!(__ Domain::IPV4, $get_fn, $set_fn($arg), $expected); + #[cfg(not(target_os = "vita"))] test!(__ Domain::IPV6, $get_fn, $set_fn($arg), $expected); } }; @@ -1357,18 +1402,22 @@ test!(IPv4 tos, set_tos(96)); target_os = "redox", target_os = "solaris", target_os = "windows", + target_os = "vita", )))] test!(IPv4 recv_tos, set_recv_tos(true)); #[cfg(not(windows))] // TODO: returns `WSAENOPROTOOPT` (10042) on Windows. test!(IPv4 broadcast, set_broadcast(true)); +#[cfg(not(target_os = "vita"))] test!(IPv6 unicast_hops_v6, set_unicast_hops_v6(20)); + #[cfg(not(any( windows, target_os = "dragonfly", target_os = "freebsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "vita" )))] test!(IPv6 only_v6, set_only_v6(true)); // IPv6 socket are already IPv6 only on FreeBSD and Windows. @@ -1399,6 +1448,7 @@ test!(IPv6 tclass_v6, set_tclass_v6(96)); target_os = "redox", target_os = "solaris", target_os = "windows", + target_os = "vita", )))] test!(IPv6 recv_tclass_v6, set_recv_tclass_v6(true)); @@ -1419,6 +1469,7 @@ test!( target_os = "openbsd", target_os = "redox", target_os = "solaris", + target_os = "vita", )))] fn join_leave_multicast_v4_n() { let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap(); @@ -1448,6 +1499,7 @@ fn join_leave_multicast_v4_n() { target_os = "openbsd", target_os = "redox", target_os = "fuchsia", + target_os = "vita", )))] fn join_leave_ssm_v4() { let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap();