diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 20ca0d1c..f3b7fce3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -83,6 +83,7 @@ jobs: - sparcv9-sun-solaris - x86_64-apple-darwin - x86_64-apple-ios + - x86_64-pc-cygwin - x86_64-pc-solaris # Fails with: # `rror calling dlltool 'x86_64-w64-mingw32-dlltool': No such file or diff --git a/src/lib.rs b/src/lib.rs index 4d39b05d..6f8a4a4b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -516,6 +516,7 @@ impl TcpKeepalive { target_os = "tvos", target_os = "watchos", target_os = "windows", + target_os = "cygwin", ))] pub const fn with_interval(self, interval: Duration) -> Self { Self { @@ -543,6 +544,7 @@ impl TcpKeepalive { target_os = "netbsd", target_os = "tvos", target_os = "watchos", + target_os = "cygwin", ) ))] pub const fn with_retries(self, retries: u32) -> Self { diff --git a/src/socket.rs b/src/socket.rs index 7f2a1edf..b44d8a5c 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -271,6 +271,7 @@ impl Socket { target_os = "linux", target_os = "netbsd", target_os = "openbsd", + target_os = "cygwin", ))] return self._accept4(libc::SOCK_CLOEXEC); @@ -284,6 +285,7 @@ impl Socket { target_os = "linux", target_os = "netbsd", target_os = "openbsd", + target_os = "cygwin", )))] { let (socket, addr) = self.accept_raw()?; @@ -752,6 +754,7 @@ const fn set_common_type(ty: Type) -> Type { target_os = "linux", target_os = "netbsd", target_os = "openbsd", + target_os = "cygwin", ))] let ty = ty._cloexec(); @@ -781,6 +784,7 @@ fn set_common_flags(socket: Socket) -> io::Result { target_os = "openbsd", target_os = "espidf", target_os = "vita", + target_os = "cygwin", )) ))] socket._set_cloexec(true)?; @@ -956,7 +960,7 @@ impl Socket { /// For more information about this option, see [`set_passcred`]. /// /// [`set_passcred`]: Socket::set_passcred - #[cfg(all(unix, target_os = "linux"))] + #[cfg(all(unix, any(target_os = "linux", target_os = "cygwin")))] pub fn passcred(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_PASSCRED) @@ -968,7 +972,7 @@ impl Socket { /// /// If this option is enabled, enables the receiving of the `SCM_CREDENTIALS` /// control messages. - #[cfg(all(unix, target_os = "linux"))] + #[cfg(all(unix, any(target_os = "linux", target_os = "cygwin")))] pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { unsafe { setsockopt( @@ -1254,6 +1258,7 @@ impl Socket { target_os = "nto", target_os = "espidf", target_os = "vita", + target_os = "cygwin", )))] pub fn join_multicast_v4_n( &self, @@ -1287,6 +1292,7 @@ impl Socket { target_os = "nto", target_os = "espidf", target_os = "vita", + target_os = "cygwin", )))] pub fn leave_multicast_v4_n( &self, @@ -1577,6 +1583,7 @@ impl Socket { target_os = "nto", target_os = "espidf", target_os = "vita", + target_os = "cygwin", )))] pub fn set_recv_tos_v4(&self, recv_tos: bool) -> io::Result<()> { unsafe { @@ -1608,6 +1615,7 @@ impl Socket { target_os = "nto", target_os = "espidf", target_os = "vita", + target_os = "cygwin", )))] pub fn recv_tos_v4(&self) -> io::Result { unsafe { @@ -1978,6 +1986,7 @@ impl Socket { target_os = "hurd", target_os = "espidf", target_os = "vita", + target_os = "cygwin", )) ))] pub fn recv_hoplimit_v6(&self) -> io::Result { @@ -2006,6 +2015,7 @@ impl Socket { target_os = "hurd", target_os = "espidf", target_os = "vita", + target_os = "cygwin", )) ))] pub fn set_recv_hoplimit_v6(&self, recv_hoplimit: bool) -> io::Result<()> { @@ -2063,6 +2073,7 @@ impl Socket { target_os = "netbsd", target_os = "tvos", target_os = "watchos", + target_os = "cygwin", ) ))] pub fn keepalive_interval(&self) -> io::Result { @@ -2092,6 +2103,7 @@ impl Socket { target_os = "netbsd", target_os = "tvos", target_os = "watchos", + target_os = "cygwin", ) ))] pub fn keepalive_retries(&self) -> io::Result { diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 8e24f4e5..ab64d273 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -72,6 +72,7 @@ use std::{io, slice}; target_os = "macos", target_os = "tvos", target_os = "watchos", + target_os = "cygwin", )))] use libc::ssize_t; use libc::{in6_addr, in_addr}; @@ -140,6 +141,7 @@ pub(crate) use libc::IPV6_HDRINCL; target_os = "haiku", target_os = "espidf", target_os = "vita", + target_os = "cygwin", )) ))] pub(crate) use libc::IPV6_RECVHOPLIMIT; @@ -173,6 +175,7 @@ pub(crate) use libc::IP_HDRINCL; target_os = "nto", target_os = "espidf", target_os = "vita", + target_os = "cygwin", )))] pub(crate) use libc::IP_RECVTOS; #[cfg(not(any( @@ -199,7 +202,7 @@ pub(crate) use libc::SO_LINGER; target_os = "watchos", ))] pub(crate) use libc::SO_LINGER_SEC as SO_LINGER; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "cygwin"))] pub(crate) use libc::SO_PASSCRED; pub(crate) use libc::{ ip_mreq as IpMreq, linger, IPPROTO_IP, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, @@ -271,6 +274,7 @@ pub(crate) use libc::{ target_os = "netbsd", target_os = "tvos", target_os = "watchos", + target_os = "cygwin", ) ))] pub(crate) use libc::{TCP_KEEPCNT, TCP_KEEPINTVL}; @@ -278,6 +282,8 @@ pub(crate) use libc::{TCP_KEEPCNT, TCP_KEEPINTVL}; // See this type in the Windows file. pub(crate) type Bool = c_int; +#[cfg(target_os = "cygwin")] +use libc::SO_PEERCRED; #[cfg(any( target_os = "ios", target_os = "visionos", @@ -320,6 +326,7 @@ macro_rules! syscall { target_os = "macos", target_os = "tvos", target_os = "watchos", + target_os = "cygwin", )))] const MAX_BUF_LEN: usize = ssize_t::MAX as usize; @@ -337,6 +344,7 @@ const MAX_BUF_LEN: usize = ssize_t::MAX as usize; target_os = "macos", target_os = "tvos", target_os = "watchos", + target_os = "cygwin", ))] const MAX_BUF_LEN: usize = c_int::MAX as usize - 1; @@ -383,6 +391,7 @@ type IovLen = usize; target_os = "watchos", target_os = "espidf", target_os = "vita", + target_os = "cygwin", ))] type IovLen = c_int; @@ -425,7 +434,8 @@ impl Type { target_os = "illumos", target_os = "linux", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "cygwin", ) ))] pub const fn nonblocking(self) -> Type { @@ -447,6 +457,7 @@ impl Type { target_os = "openbsd", target_os = "redox", target_os = "solaris", + target_os = "cygwin", ) ))] pub const fn cloexec(self) -> Type { @@ -465,6 +476,7 @@ impl Type { target_os = "openbsd", target_os = "redox", target_os = "solaris", + target_os = "cygwin", ))] pub(crate) const fn _cloexec(self) -> Type { Type(self.0 | libc::SOCK_CLOEXEC) @@ -578,7 +590,10 @@ impl RecvFlags { /// on the local host. /// /// On Unix this corresponds to the `MSG_DONTROUTE` flag. - #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] + #[cfg(all( + feature = "all", + any(target_os = "android", target_os = "linux", target_os = "cygwin"), + ))] pub const fn is_dontroute(self) -> bool { self.0 & libc::MSG_DONTROUTE != 0 } @@ -595,7 +610,10 @@ impl std::fmt::Debug for RecvFlags { s.field("is_truncated", &self.is_truncated()); #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] s.field("is_confirm", &self.is_confirm()); - #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] + #[cfg(all( + feature = "all", + any(target_os = "android", target_os = "linux", target_os = "cygwin"), + ))] s.field("is_dontroute", &self.is_dontroute()); s.finish() } @@ -767,7 +785,7 @@ impl SockAddr { // Abstract addresses only exist on Linux. // NOTE: although Fuchsia does define `AF_UNIX` it's not actually implemented. // See https://github.com/rust-lang/socket2/pull/403#discussion_r1123557978 - || (cfg!(not(any(target_os = "linux", target_os = "android"))) + || (cfg!(not(any(target_os = "linux", target_os = "android", target_os = "cygwin"))) && storage.sun_path[0] == 0) }) .unwrap_or_default() @@ -840,14 +858,14 @@ impl SockAddr { pub fn as_abstract_namespace(&self) -> Option<&[u8]> { // NOTE: although Fuchsia does define `AF_UNIX` it's not actually implemented. // See https://github.com/rust-lang/socket2/pull/403#discussion_r1123557978 - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin"))] { self.as_sockaddr_un().and_then(|storage| { (self.len() > offset_of_path(storage) as _ && storage.sun_path[0] == 0) .then(|| self.path_bytes(storage, true)) }) } - #[cfg(not(any(target_os = "linux", target_os = "android")))] + #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "cygwin")))] None } } @@ -1200,6 +1218,7 @@ pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Res target_os = "netbsd", target_os = "tvos", target_os = "watchos", + target_os = "cygwin", ))] { if let Some(interval) = keepalive.interval { @@ -1333,6 +1352,7 @@ pub(crate) fn from_in6_addr(addr: in6_addr) -> Ipv6Addr { target_os = "nto", target_os = "espidf", target_os = "vita", + target_os = "cygwin", )))] pub(crate) const fn to_mreqn( multiaddr: &Ipv4Addr, @@ -1414,6 +1434,7 @@ impl crate::Socket { target_os = "linux", target_os = "netbsd", target_os = "openbsd", + target_os = "cygwin", ) ))] pub fn accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> { @@ -1429,6 +1450,7 @@ impl crate::Socket { target_os = "linux", target_os = "netbsd", target_os = "openbsd", + target_os = "cygwin", ))] pub(crate) fn _accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> { // Safety: `accept4` initialises the `SockAddr` for us. @@ -1479,6 +1501,20 @@ impl crate::Socket { } } + /// Sets `SO_PEERCRED` to null on the socket. + /// It disables the initial handshake of unix domain sockets. + #[cfg(target_os = "cygwin")] + pub fn set_no_peercred(&self) -> io::Result<()> { + syscall!(setsockopt( + self.as_raw(), + SOL_SOCKET, + SO_PEERCRED, + ptr::null_mut(), + 0, + )) + .map(|_| ()) + } + /// Sets `SO_NOSIGPIPE` on the socket. #[cfg(all( feature = "all", @@ -1551,6 +1587,7 @@ impl crate::Socket { target_os = "freebsd", target_os = "fuchsia", target_os = "linux", + target_os = "cygwin", ) ))] pub fn is_listener(&self) -> io::Result { @@ -1680,7 +1717,12 @@ impl crate::Socket { /// [`set_quickack`]: crate::Socket::set_quickack #[cfg(all( feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") + any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + target_os = "cygwin", + ) ))] pub fn quickack(&self) -> io::Result { unsafe { @@ -1697,7 +1739,12 @@ impl crate::Socket { /// internal protocol processing and factors such as delayed ack timeouts occurring and data transfer. #[cfg(all( feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") + any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + target_os = "cygwin", + ) ))] pub fn set_quickack(&self, quickack: bool) -> io::Result<()> { unsafe { @@ -1956,7 +2003,7 @@ impl crate::Socket { /// [`set_reuse_port`]: crate::Socket::set_reuse_port #[cfg(all( feature = "all", - not(any(target_os = "solaris", target_os = "illumos")) + not(any(target_os = "solaris", target_os = "illumos", target_os = "cygwin")) ))] pub fn reuse_port(&self) -> io::Result { unsafe { @@ -1972,7 +2019,7 @@ impl crate::Socket { /// there's a socket already listening on this port. #[cfg(all( feature = "all", - not(any(target_os = "solaris", target_os = "illumos")) + not(any(target_os = "solaris", target_os = "illumos", target_os = "cygwin")) ))] pub fn set_reuse_port(&self, reuse: bool) -> io::Result<()> { unsafe { @@ -2272,7 +2319,12 @@ impl crate::Socket { /// approximately 49.71 days. #[cfg(all( feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") + any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + target_os = "cygwin", + ) ))] pub fn set_tcp_user_timeout(&self, timeout: Option) -> io::Result<()> { let timeout = timeout.map_or(0, |to| { @@ -2295,7 +2347,12 @@ impl crate::Socket { /// [`set_tcp_user_timeout`]: crate::Socket::set_tcp_user_timeout #[cfg(all( feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") + any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + target_os = "cygwin", + ) ))] pub fn tcp_user_timeout(&self) -> io::Result> { unsafe { @@ -2369,7 +2426,8 @@ impl crate::Socket { target_os = "linux", target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "cygwin", ) ))] pub fn tclass_v6(&self) -> io::Result { @@ -2393,7 +2451,8 @@ impl crate::Socket { target_os = "linux", target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "cygwin", ) ))] pub fn set_tclass_v6(&self, tclass: u32) -> io::Result<()> { diff --git a/tests/socket.rs b/tests/socket.rs index a2dca668..77e30d35 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -186,7 +186,10 @@ fn socket_address_unix_unnamed() { } #[test] -#[cfg(all(any(target_os = "linux", target_os = "android"), feature = "all"))] +#[cfg(all( + any(target_os = "linux", target_os = "android", target_os = "cygwin"), + feature = "all", +))] fn socket_address_unix_abstract_namespace() { let path = "\0h".repeat(108 / 2); let addr = SockAddr::unix(&path).unwrap(); @@ -568,10 +571,14 @@ fn unix() { let addr = SockAddr::unix(path).unwrap(); let listener = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap(); + #[cfg(target_os = "cygwin")] + listener.set_no_peercred().unwrap(); listener.bind(&addr).unwrap(); listener.listen(10).unwrap(); let mut a = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap(); + #[cfg(target_os = "cygwin")] + a.set_no_peercred().unwrap(); a.connect(&addr).unwrap(); let mut b = listener.accept().unwrap().0; @@ -1247,6 +1254,7 @@ fn r#type() { target_os = "tvos", target_os = "watchos", target_os = "vita", + target_os = "cygwin", )), feature = "all", ))] @@ -1356,12 +1364,21 @@ test!(out_of_band_inline, set_out_of_band_inline(true)); test!(reuse_address, set_reuse_address(true)); #[cfg(all( feature = "all", - not(any(windows, target_os = "solaris", target_os = "illumos")) + not(any( + windows, + target_os = "solaris", + target_os = "illumos", + target_os = "cygwin", + )) ))] test!(reuse_port, set_reuse_port(true)); #[cfg(all(feature = "all", target_os = "freebsd"))] test!(reuse_port_lb, set_reuse_port_lb(true)); -#[cfg(all(feature = "all", unix, not(target_os = "redox")))] +#[cfg(all( + feature = "all", + unix, + not(any(target_os = "redox", target_os = "cygwin")), +))] test!( #[cfg_attr(target_os = "linux", ignore = "Different value returned")] mss, @@ -1413,6 +1430,7 @@ test!(IPv4 ttl_v4, set_ttl_v4(40)); target_os = "solaris", target_os = "illumos", target_os = "haiku", + target_os = "cygwin", )))] test!(IPv4 tos_v4, set_tos_v4(96)); @@ -1428,10 +1446,11 @@ test!(IPv4 tos_v4, set_tos_v4(96)); target_os = "windows", target_os = "vita", target_os = "haiku", + target_os = "cygwin", )))] test!(IPv4 recv_tos_v4, set_recv_tos_v4(true)); -#[cfg(not(windows))] // TODO: returns `WSAENOPROTOOPT` (10042) on Windows. +#[cfg(not(any(windows, target_os = "cygwin")))] // TODO: returns `WSAENOPROTOOPT` (10042) on Windows. test!(IPv4 broadcast, set_broadcast(true)); #[cfg(not(target_os = "vita"))] @@ -1442,11 +1461,12 @@ test!(IPv6 unicast_hops_v6, set_unicast_hops_v6(20)); target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", - target_os = "vita" + target_os = "vita", + target_os = "cygwin", )))] test!(IPv6 only_v6, set_only_v6(true)); // IPv6 socket are already IPv6 only on FreeBSD and Windows. -#[cfg(any(windows, target_os = "freebsd"))] +#[cfg(any(windows, target_os = "freebsd", target_os = "cygwin"))] test!(IPv6 only_v6, set_only_v6(false)); #[cfg(all( @@ -1476,6 +1496,7 @@ test!(IPv6 tclass_v6, set_tclass_v6(96)); target_os = "windows", target_os = "vita", target_os = "haiku", + target_os = "cygwin", )))] test!(IPv6 recv_tclass_v6, set_recv_tclass_v6(true)); @@ -1493,6 +1514,7 @@ test!(IPv6 recv_tclass_v6, set_recv_tclass_v6(true)); target_os = "windows", target_os = "vita", target_os = "haiku", + target_os = "cygwin", )) ))] test!(IPv6 recv_hoplimit_v6, set_recv_hoplimit_v6(true)); @@ -1520,6 +1542,7 @@ test!(IPv6 multicast_all_v6, set_multicast_all_v6(false)); target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "cygwin", )))] fn join_leave_multicast_v4_n() { let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap();