Repository: rust-lang/socket2
Pull request #365 (2022-12-21 10:20:08)

Fix some Clippy lints
New Clippy version, new lints, let's fix them.


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index dda0056..d59569b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -34,7 +34,7 @@ rustdoc-args = ["--cfg", "docsrs"]
 features = ["all"]
 
 [target."cfg(unix)".dependencies]
-libc = "0.2.124"
+libc = "0.2.139"
 
 [target.'cfg(windows)'.dependencies.windows-sys]
 version = "0.36"
diff --git a/src/lib.rs b/src/lib.rs
index a396326..0b08a0f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -390,13 +390,19 @@ impl<'a> DerefMut for MaybeUninitSlice<'a> {
 pub struct TcpKeepalive {
     #[cfg_attr(target_os = "openbsd", allow(dead_code))]
     time: Option<Duration>,
-    #[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "solaris")))]
+    #[cfg(not(any(
+        target_os = "openbsd",
+        target_os = "redox",
+        target_os = "solaris",
+        target_os = "nto",
+    )))]
     interval: Option<Duration>,
     #[cfg(not(any(
         target_os = "openbsd",
         target_os = "redox",
         target_os = "solaris",
-        target_os = "windows"
+        target_os = "windows",
+        target_os = "nto",
     )))]
     retries: Option<u32>,
 }
@@ -406,13 +412,19 @@ impl TcpKeepalive {
     pub const fn new() -> TcpKeepalive {
         TcpKeepalive {
             time: None,
-            #[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "solaris")))]
+            #[cfg(not(any(
+                target_os = "openbsd",
+                target_os = "redox",
+                target_os = "solaris",
+                target_os = "nto",
+            )))]
             interval: None,
             #[cfg(not(any(
                 target_os = "openbsd",
                 target_os = "redox",
                 target_os = "solaris",
-                target_os = "windows"
+                target_os = "windows",
+                target_os = "nto",
             )))]
             retries: None,
         }
diff --git a/src/sockaddr.rs b/src/sockaddr.rs
index 309ed38..2de6505 100644
--- a/src/sockaddr.rs
+++ b/src/sockaddr.rs
@@ -1,3 +1,4 @@
+use std::hash::Hash;
 use std::mem::{self, size_of, MaybeUninit};
 use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
 use std::path::Path;
@@ -304,6 +305,7 @@ impl fmt::Debug for SockAddr {
             target_os = "netbsd",
             target_os = "openbsd",
             target_os = "vxworks",
+            target_os = "nto",
         ))]
         f.field("ss_len", &self.storage.ss_len);
         f.field("ss_family", &self.storage.ss_family)
@@ -312,44 +314,197 @@ impl fmt::Debug for SockAddr {
     }
 }
 
-#[test]
-fn ipv4() {
-    use std::net::Ipv4Addr;
-    let std = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
-    let addr = SockAddr::from(std);
-    assert!(addr.is_ipv4());
-    assert_eq!(addr.family(), AF_INET as sa_family_t);
-    assert_eq!(addr.domain(), Domain::IPV4);
-    assert_eq!(addr.len(), size_of::<sockaddr_in>() as socklen_t);
-    assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std)));
-    assert_eq!(addr.as_socket_ipv4(), Some(std));
-    assert!(addr.as_socket_ipv6().is_none());
-
-    let addr = SockAddr::from(SocketAddr::from(std));
-    assert_eq!(addr.family(), AF_INET as sa_family_t);
-    assert_eq!(addr.len(), size_of::<sockaddr_in>() as socklen_t);
-    assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std)));
-    assert_eq!(addr.as_socket_ipv4(), Some(std));
-    assert!(addr.as_socket_ipv6().is_none());
+impl PartialEq for SockAddr {
+    fn eq(&self, other: &Self) -> bool {
+        unsafe {
+            let these_bytes: &[u8] = any_as_u8_slice(&self.storage, self.len as usize);
+            let those_bytes: &[u8] = any_as_u8_slice(&other.storage, other.len as usize);
+            these_bytes == those_bytes
+        }
+    }
+}
+
+impl Eq for SockAddr {}
+
+impl Hash for SockAddr {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        unsafe {
+            let these_bytes: &[u8] = any_as_u8_slice(&self.storage, self.len as usize);
+            these_bytes.hash(state);
+        }
+    }
+}
+
+unsafe fn any_as_u8_slice<T: Sized>(p: &T, size: usize) -> &[u8] {
+    ::std::slice::from_raw_parts((p as *const T) as *const u8, size)
 }
 
-#[test]
-fn ipv6() {
-    use std::net::Ipv6Addr;
-    let std = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);
-    let addr = SockAddr::from(std);
-    assert!(addr.is_ipv6());
-    assert_eq!(addr.family(), AF_INET6 as sa_family_t);
-    assert_eq!(addr.domain(), Domain::IPV6);
-    assert_eq!(addr.len(), size_of::<sockaddr_in6>() as socklen_t);
-    assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std)));
-    assert!(addr.as_socket_ipv4().is_none());
-    assert_eq!(addr.as_socket_ipv6(), Some(std));
-
-    let addr = SockAddr::from(SocketAddr::from(std));
-    assert_eq!(addr.family(), AF_INET6 as sa_family_t);
-    assert_eq!(addr.len(), size_of::<sockaddr_in6>() as socklen_t);
-    assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std)));
-    assert!(addr.as_socket_ipv4().is_none());
-    assert_eq!(addr.as_socket_ipv6(), Some(std));
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn ipv4() {
+        use std::net::Ipv4Addr;
+        let std = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
+        let addr = SockAddr::from(std);
+        assert!(addr.is_ipv4());
+        assert_eq!(addr.family(), AF_INET as sa_family_t);
+        assert_eq!(addr.domain(), Domain::IPV4);
+        assert_eq!(addr.len(), size_of::<sockaddr_in>() as socklen_t);
+        assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std)));
+        assert_eq!(addr.as_socket_ipv4(), Some(std));
+        assert!(addr.as_socket_ipv6().is_none());
+
+        let addr = SockAddr::from(SocketAddr::from(std));
+        assert_eq!(addr.family(), AF_INET as sa_family_t);
+        assert_eq!(addr.len(), size_of::<sockaddr_in>() as socklen_t);
+        assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std)));
+        assert_eq!(addr.as_socket_ipv4(), Some(std));
+        assert!(addr.as_socket_ipv6().is_none());
+    }
+
+    #[test]
+    fn ipv6() {
+        use std::net::Ipv6Addr;
+        let std = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);
+        let addr = SockAddr::from(std);
+        assert!(addr.is_ipv6());
+        assert_eq!(addr.family(), AF_INET6 as sa_family_t);
+        assert_eq!(addr.domain(), Domain::IPV6);
+        assert_eq!(addr.len(), size_of::<sockaddr_in6>() as socklen_t);
+        assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std)));
+        assert!(addr.as_socket_ipv4().is_none());
+        assert_eq!(addr.as_socket_ipv6(), Some(std));
+
+        let addr = SockAddr::from(SocketAddr::from(std));
+        assert_eq!(addr.family(), AF_INET6 as sa_family_t);
+        assert_eq!(addr.len(), size_of::<sockaddr_in6>() as socklen_t);
+        assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std)));
+        assert!(addr.as_socket_ipv4().is_none());
+        assert_eq!(addr.as_socket_ipv6(), Some(std));
+    }
+
+    #[test]
+    fn ipv4_eq() {
+        use std::net::Ipv4Addr;
+
+        let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
+        let std2 = SocketAddrV4::new(Ipv4Addr::new(5, 6, 7, 8), 8765);
+
+        test_eq(
+            SockAddr::from(std1),
+            SockAddr::from(std1),
+            SockAddr::from(std2),
+        );
+    }
+
+    #[test]
+    fn ipv4_hash() {
+        use std::net::Ipv4Addr;
+
+        let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
+        let std2 = SocketAddrV4::new(Ipv4Addr::new(5, 6, 7, 8), 8765);
+
+        test_hash(
+            SockAddr::from(std1),
+            SockAddr::from(std1),
+            SockAddr::from(std2),
+        );
+    }
+
+    #[test]
+    fn ipv6_eq() {
+        use std::net::Ipv6Addr;
+
+        let std1 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);
+        let std2 = SocketAddrV6::new(Ipv6Addr::new(3, 4, 5, 6, 7, 8, 9, 0), 7654, 13, 14);
+
+        test_eq(
+            SockAddr::from(std1),
+            SockAddr::from(std1),
+            SockAddr::from(std2),
+        );
+    }
+
+    #[test]
+    fn ipv6_hash() {
+        use std::net::Ipv6Addr;
+
+        let std1 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);
+        let std2 = SocketAddrV6::new(Ipv6Addr::new(3, 4, 5, 6, 7, 8, 9, 0), 7654, 13, 14);
+
+        test_hash(
+            SockAddr::from(std1),
+            SockAddr::from(std1),
+            SockAddr::from(std2),
+        );
+    }
+
+    #[test]
+    fn ipv4_ipv6_eq() {
+        use std::net::Ipv4Addr;
+        use std::net::Ipv6Addr;
+
+        let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
+        let std2 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);
+
+        test_eq(
+            SockAddr::from(std1),
+            SockAddr::from(std1),
+            SockAddr::from(std2),
+        );
+
+        test_eq(
+            SockAddr::from(std2),
+            SockAddr::from(std2),
+            SockAddr::from(std1),
+        );
+    }
+
+    #[test]
+    fn ipv4_ipv6_hash() {
+        use std::net::Ipv4Addr;
+        use std::net::Ipv6Addr;
+
+        let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);
+        let std2 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);
+
+        test_hash(
+            SockAddr::from(std1),
+            SockAddr::from(std1),
+            SockAddr::from(std2),
+        );
+
+        test_hash(
+            SockAddr::from(std2),
+            SockAddr::from(std2),
+            SockAddr::from(std1),
+        );
+    }
+
+    #[allow(clippy::eq_op)] // allow a0 == a0 check
+    fn test_eq(a0: SockAddr, a1: SockAddr, b: SockAddr) {
+        assert!(a0 == a0);
+        assert!(a0 == a1);
+        assert!(a1 == a0);
+        assert!(a0 != b);
+        assert!(b != a0);
+    }
+
+    fn test_hash(a0: SockAddr, a1: SockAddr, b: SockAddr) {
+        assert!(calculate_hash(&a0) == calculate_hash(&a0));
+        assert!(calculate_hash(&a0) == calculate_hash(&a1));
+        // technically unequal values can have the same hash, in this case x != z and both have different hashes
+        assert!(calculate_hash(&a0) != calculate_hash(&b));
+    }
+
+    fn calculate_hash(x: &SockAddr) -> u64 {
+        use std::collections::hash_map::DefaultHasher;
+        use std::hash::Hasher;
+
+        let mut hasher = DefaultHasher::new();
+        x.hash(&mut hasher);
+        hasher.finish()
+    }
 }
diff --git a/src/socket.rs b/src/socket.rs
index 24a1af5..baedc76 100644
--- a/src/socket.rs
+++ b/src/socket.rs
@@ -11,7 +11,9 @@ use std::io::{self, Read, Write};
 #[cfg(not(target_os = "redox"))]
 use std::io::{IoSlice, IoSliceMut};
 use std::mem::MaybeUninit;
-use std::net::{self, Ipv4Addr, Ipv6Addr, Shutdown};
+#[cfg(not(target_os = "nto"))]
+use std::net::Ipv6Addr;
+use std::net::{self, Ipv4Addr, Shutdown};
 #[cfg(unix)]
 use std::os::unix::io::{FromRawFd, IntoRawFd};
 #[cfg(windows)]
@@ -1174,6 +1176,7 @@ impl Socket {
         target_os = "openbsd",
         target_os = "redox",
         target_os = "solaris",
+        target_os = "nto",
     )))]
     pub fn join_multicast_v4_n(
         &self,
@@ -1203,6 +1206,7 @@ impl Socket {
         target_os = "openbsd",
         target_os = "redox",
         target_os = "solaris",
+        target_os = "nto",
     )))]
     pub fn leave_multicast_v4_n(
         &self,
@@ -1234,6 +1238,7 @@ impl Socket {
         target_os = "openbsd",
         target_os = "redox",
         target_os = "fuchsia",
+        target_os = "nto",
     )))]
     pub fn join_ssm_v4(
         &self,
@@ -1268,6 +1273,7 @@ impl Socket {
         target_os = "openbsd",
         target_os = "redox",
         target_os = "fuchsia",
+        target_os = "nto",
     )))]
     pub fn leave_ssm_v4(
         &self,
@@ -1442,10 +1448,10 @@ impl Socket {
         target_os = "openbsd",
         target_os = "redox",
         target_os = "solaris",
+        target_os = "haiku",
+        target_os = "nto",
     )))]
     pub fn set_recv_tos(&self, recv_tos: bool) -> io::Result<()> {
-        let recv_tos = i32::from(recv_tos);
-
         unsafe {
             setsockopt(
                 self.as_raw(),
@@ -1469,6 +1475,8 @@ impl Socket {
         target_os = "openbsd",
         target_os = "redox",
         target_os = "solaris",
+        target_os = "haiku",
+        target_os = "nto",
     )))]
     pub fn recv_tos(&self) -> io::Result<bool> {
         unsafe {
@@ -1491,6 +1499,7 @@ impl Socket {
     /// This function specifies a new multicast group for this socket to join.
     /// The address must be a valid multicast address, and `interface` is the
     /// index of the interface to join/leave (or 0 to indicate any interface).
+    #[cfg(not(target_os = "nto"))]
     pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
         let mreq = sys::Ipv6Mreq {
             ipv6mr_multiaddr: sys::to_in6_addr(multiaddr),
@@ -1514,6 +1523,7 @@ impl Socket {
     /// For more information about this option, see [`join_multicast_v6`].
     ///
     /// [`join_multicast_v6`]: Socket::join_multicast_v6
+    #[cfg(not(target_os = "nto"))]
     pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
         let mreq = sys::Ipv6Mreq {
             ipv6mr_multiaddr: sys::to_in6_addr(multiaddr),
@@ -1667,6 +1677,54 @@ impl Socket {
             )
         }
     }
+
+    /// Get the value of the `IPV6_RECVTCLASS` option for this socket.
+    ///
+    /// For more information about this option, see [`set_recv_tclass_v6`].
+    ///
+    /// [`set_recv_tclass_v6`]: Socket::set_recv_tclass_v6
+    #[cfg(not(any(
+        target_os = "dragonfly",
+        target_os = "fuchsia",
+        target_os = "illumos",
+        target_os = "netbsd",
+        target_os = "openbsd",
+        target_os = "redox",
+        target_os = "solaris",
+        target_os = "haiku",
+    )))]
+    pub fn recv_tclass_v6(&self) -> io::Result<bool> {
+        unsafe {
+            getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_RECVTCLASS)
+                .map(|recv_tclass| recv_tclass > 0)
+        }
+    }
+
+    /// Set the value of the `IPV6_RECVTCLASS` option for this socket.
+    ///
+    /// If enabled, the `IPV6_TCLASS` ancillary message is passed with incoming
+    /// packets. It contains a byte which specifies the traffic class field of
+    /// the packet header.
+    #[cfg(not(any(
+        target_os = "dragonfly",
+        target_os = "fuchsia",
+        target_os = "illumos",
+        target_os = "netbsd",
+        target_os = "openbsd",
+        target_os = "redox",
+        target_os = "solaris",
+        target_os = "haiku",
+    )))]
+    pub fn set_recv_tclass_v6(&self, recv_tclass: bool) -> io::Result<()> {
+        unsafe {
+            setsockopt(
+                self.as_raw(),
+                sys::IPPROTO_IPV6,
+                sys::IPV6_RECVTCLASS,
+                recv_tclass as c_int,
+            )
+        }
+    }
 }
 
 /// Socket options for TCP sockets, get/set using `IPPROTO_TCP`.
diff --git a/src/sys/unix.rs b/src/sys/unix.rs
index 972dfe1..d8403a0 100644
--- a/src/sys/unix.rs
+++ b/src/sys/unix.rs
@@ -76,6 +76,19 @@ pub(crate) use libc::{
 #[cfg(not(target_os = "redox"))]
 pub(crate) use libc::{MSG_TRUNC, SO_OOBINLINE};
 // Used in `Socket`.
+#[cfg(not(target_os = "nto"))]
+pub(crate) use libc::ipv6_mreq as Ipv6Mreq;
+#[cfg(not(any(
+    target_os = "dragonfly",
+    target_os = "fuchsia",
+    target_os = "illumos",
+    target_os = "netbsd",
+    target_os = "openbsd",
+    target_os = "redox",
+    target_os = "solaris",
+    target_os = "haiku",
+)))]
+pub(crate) use libc::IPV6_RECVTCLASS;
 #[cfg(all(feature = "all", not(target_os = "redox")))]
 pub(crate) use libc::IP_HDRINCL;
 #[cfg(not(any(
@@ -86,6 +99,8 @@ pub(crate) use libc::IP_HDRINCL;
     target_os = "openbsd",
     target_os = "redox",
     target_os = "solaris",
+    target_os = "haiku",
+    target_os = "nto",
 )))]
 pub(crate) use libc::IP_RECVTOS;
 #[cfg(not(any(
@@ -100,11 +115,11 @@ pub(crate) use libc::SO_LINGER;
 #[cfg(target_vendor = "apple")]
 pub(crate) use libc::SO_LINGER_SEC as SO_LINGER;
 pub(crate) use libc::{
-    ip_mreq as IpMreq, ipv6_mreq as Ipv6Mreq, linger, IPPROTO_IP, IPPROTO_IPV6,
-    IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY,
-    IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL,
-    IP_TTL, MSG_OOB, MSG_PEEK, SOL_SOCKET, SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_RCVBUF,
-    SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF, SO_SNDTIMEO, SO_TYPE, TCP_NODELAY,
+    ip_mreq as IpMreq, linger, IPPROTO_IP, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF,
+    IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP,
+    IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, MSG_OOB, MSG_PEEK, SOL_SOCKET,
+    SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_RCVBUF, SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF,
+    SO_SNDTIMEO, SO_TYPE, TCP_NODELAY,
 };
 #[cfg(not(any(
     target_os = "dragonfly",
@@ -113,6 +128,7 @@ pub(crate) use libc::{
     target_os = "openbsd",
     target_os = "redox",
     target_os = "fuchsia",
+    target_os = "nto",
 )))]
 pub(crate) use libc::{
     ip_mreq_source as IpMreqSource, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP,
@@ -125,6 +141,7 @@ pub(crate) use libc::{
     target_os = "netbsd",
     target_os = "openbsd",
     target_os = "solaris",
+    target_os = "nto",
     target_vendor = "apple"
 )))]
 pub(crate) use libc::{IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP};
@@ -159,9 +176,14 @@ pub(crate) use libc::{TCP_KEEPCNT, TCP_KEEPINTVL};
 // See this type in the Windows file.
 pub(crate) type Bool = c_int;
 
-#[cfg(target_vendor = "apple")]
+#[cfg(any(target_vendor = "apple", target_os = "nto"))]
 use libc::TCP_KEEPALIVE as KEEPALIVE_TIME;
-#[cfg(not(any(target_vendor = "apple", target_os = "haiku", target_os = "openbsd")))]
+#[cfg(not(any(
+    target_vendor = "apple",
+    target_os = "haiku",
+    target_os = "openbsd",
+    target_os = "nto",
+)))]
 use libc::TCP_KEEPIDLE as KEEPALIVE_TIME;
 
 /// Helper macro to execute a system call that returns an `io::Result`.
@@ -192,6 +214,10 @@ const MAX_BUF_LEN: usize = ssize_t::MAX as usize;
 #[cfg(target_vendor = "apple")]
 const MAX_BUF_LEN: usize = c_int::MAX as usize - 1;
 
+// TCP_CA_NAME_MAX isn't defined in user space include files(not in libc)
+#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
+const TCP_CA_NAME_MAX: usize = 16;
+
 #[cfg(any(
     all(
         target_os = "linux",
@@ -220,6 +246,7 @@ type IovLen = usize;
     target_os = "netbsd",
     target_os = "openbsd",
     target_os = "solaris",
+    target_os = "nto",
     target_vendor = "apple",
 ))]
 type IovLen = c_int;
@@ -464,7 +491,7 @@ impl<'a> MaybeUninitSlice<'a> {
     }
 }
 
-#[allow(unused_unsafe)] // TODO: replace with `unsafe_op_in_unsafe_fn` once stable.
+#[allow(unsafe_op_in_unsafe_fn)]
 pub(crate) fn unix_sockaddr(path: &Path) -> io::Result<SockAddr> {
     // SAFETY: a `sockaddr_storage` of all zeros is valid.
     let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };
@@ -519,7 +546,7 @@ impl SockAddr {
     ///
     /// This function can never fail. In a future version of this library it will be made
     /// infallible.
-    #[allow(unused_unsafe)] // TODO: replace with `unsafe_op_in_unsafe_fn` once stable.
+    #[allow(unsafe_op_in_unsafe_fn)]
     #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
     #[cfg_attr(
         docsrs,
@@ -604,7 +631,7 @@ pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Res
         }
 
         let timeout = (timeout - elapsed).as_millis();
-        let timeout = clamp(timeout, 1, c_int::MAX as u128) as c_int;
+        let timeout = timeout.clamp(1, c_int::MAX as u128) as c_int;
 
         match syscall!(poll(&mut pollfd, 1, timeout)) {
             Ok(0) => return Err(io::ErrorKind::TimedOut.into()),
@@ -630,20 +657,6 @@ pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Res
     }
 }
 
-// TODO: use clamp from std lib, stable since 1.50.
-fn clamp<T>(value: T, min: T, max: T) -> T
-where
-    T: Ord,
-{
-    if value <= min {
-        min
-    } else if value >= max {
-        max
-    } else {
-        value
-    }
-}
-
 pub(crate) fn listen(fd: Socket, backlog: c_int) -> io::Result<()> {
     syscall!(listen(fd, backlog)).map(|_| ())
 }
@@ -754,6 +767,7 @@ pub(crate) fn recv_from_vectored(
 
 /// Returns the (bytes received, sending address len, `RecvFlags`).
 #[cfg(not(target_os = "redox"))]
+#[allow(clippy::unnecessary_cast)] // For `IovLen::MAX as usize` (Already `usize` on Linux).
 fn recvmsg(
     fd: Socket,
     msg_name: *mut sockaddr_storage,
@@ -814,6 +828,7 @@ pub(crate) fn send_to_vectored(
 
 /// Returns the (bytes received, sending address len, `RecvFlags`).
 #[cfg(not(target_os = "redox"))]
+#[allow(clippy::unnecessary_cast)] // For `IovLen::MAX as usize` (Already `usize` on Linux).
 fn sendmsg(
     fd: Socket,
     msg_name: *const sockaddr_storage,
@@ -886,7 +901,7 @@ pub(crate) fn keepalive_time(fd: Socket) -> io::Result<Duration> {
 
 #[allow(unused_variables)]
 pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Result<()> {
-    #[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
+    #[cfg(not(any(target_os = "haiku", target_os = "openbsd", target_os = "nto")))]
     if let Some(time) = keepalive.time {
         let secs = into_secs(time);
         unsafe { setsockopt(fd, libc::IPPROTO_TCP, KEEPALIVE_TIME, secs)? }
@@ -913,10 +928,16 @@ pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Res
         }
     }
 
+    #[cfg(target_os = "nto")]
+    if let Some(time) = keepalive.time {
+        let secs = into_timeval(Some(time));
+        unsafe { setsockopt(fd, libc::IPPROTO_TCP, KEEPALIVE_TIME, secs)? }
+    }
+
     Ok(())
 }
 
-#[cfg(not(any(target_os = "haiku", target_os = "openbsd")))]
+#[cfg(not(any(target_os = "haiku", target_os = "openbsd", target_os = "nto")))]
 fn into_secs(duration: Duration) -> c_int {
     min(duration.as_secs(), c_int::MAX as u64) as c_int
 }
@@ -1016,6 +1037,7 @@ pub(crate) fn from_in6_addr(addr: in6_addr) -> Ipv6Addr {
     target_os = "openbsd",
     target_os = "redox",
     target_os = "solaris",
+    target_os = "nto",
 )))]
 pub(crate) const fn to_mreqn(
     multiaddr: &Ipv4Addr,
@@ -1055,7 +1077,7 @@ impl crate::Socket {
             target_os = "illumos",
             target_os = "linux",
             target_os = "netbsd",
-            target_os = "openbsd"
+            target_os = "openbsd",
         )
     ))]
     #[cfg_attr(
@@ -1070,7 +1092,7 @@ impl crate::Socket {
                 target_os = "illumos",
                 target_os = "linux",
                 target_os = "netbsd",
-                target_os = "openbsd"
+                target_os = "openbsd",
             )
         )))
     )]
@@ -1086,7 +1108,7 @@ impl crate::Socket {
         target_os = "illumos",
         target_os = "linux",
         target_os = "netbsd",
-        target_os = "openbsd"
+        target_os = "openbsd",
     ))]
     pub(crate) fn _accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> {
         // Safety: `accept4` initialises the `SockAddr` for us.
@@ -2057,6 +2079,140 @@ impl crate::Socket {
     pub fn detach_filter(&self) -> io::Result<()> {
         unsafe { setsockopt(self.as_raw(), libc::SOL_SOCKET, libc::SO_DETACH_FILTER, 0) }
     }
+
+    /// Get the value of the `IPV6_TCLASS` option for this socket.
+    ///
+    /// For more information about this option, see [`set_tclass_v6`].
+    ///
+    /// [`set_tclass_v6`]: Socket::set_tclass_v6
+    #[cfg(all(
+        feature = "all",
+        any(
+            target_os = "android",
+            target_os = "dragonfly",
+            target_os = "freebsd",
+            target_os = "fuchsia",
+            target_os = "linux",
+            target_os = "macos",
+            target_os = "netbsd",
+            target_os = "openbsd"
+        )
+    ))]
+    #[cfg_attr(
+        docsrs,
+        doc(cfg(all(
+            feature = "all",
+            any(
+                target_os = "android",
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "fuchsia",
+                target_os = "linux",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "openbsd"
+            )
+        )))
+    )]
+    pub fn tclass_v6(&self) -> io::Result<u32> {
+        unsafe {
+            getsockopt::<c_int>(self.as_raw(), IPPROTO_IPV6, libc::IPV6_TCLASS)
+                .map(|tclass| tclass as u32)
+        }
+    }
+
+    /// Set the value of the `IPV6_TCLASS` option for this socket.
+    ///
+    /// Specifies the traffic class field that is used in every packets
+    /// sent from this socket.
+    #[cfg(all(
+        feature = "all",
+        any(
+            target_os = "android",
+            target_os = "dragonfly",
+            target_os = "freebsd",
+            target_os = "fuchsia",
+            target_os = "linux",
+            target_os = "macos",
+            target_os = "netbsd",
+            target_os = "openbsd"
+        )
+    ))]
+    #[cfg_attr(
+        docsrs,
+        doc(cfg(all(
+            feature = "all",
+            any(
+                target_os = "android",
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "fuchsia",
+                target_os = "linux",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "openbsd"
+            )
+        )))
+    )]
+    pub fn set_tclass_v6(&self, tclass: u32) -> io::Result<()> {
+        unsafe {
+            setsockopt(
+                self.as_raw(),
+                IPPROTO_IPV6,
+                libc::IPV6_TCLASS,
+                tclass as c_int,
+            )
+        }
+    }
+
+    /// Get the value of the `TCP_CONGESTION` option for this socket.
+    ///
+    /// For more information about this option, see [`set_tcp_congestion`].
+    ///
+    /// [`set_tcp_congestion`]: Socket::set_tcp_congestion
+    #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
+    #[cfg_attr(
+        docsrs,
+        doc(cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux"))))
+    )]
+    pub fn tcp_congestion(&self) -> io::Result<Vec<u8>> {
+        let mut payload: [u8; TCP_CA_NAME_MAX] = [0; TCP_CA_NAME_MAX];
+        let mut len = payload.len() as libc::socklen_t;
+        syscall!(getsockopt(
+            self.as_raw(),
+            IPPROTO_TCP,
+            libc::TCP_CONGESTION,
+            payload.as_mut_ptr().cast(),
+            &mut len,
+        ))
+        .map(|_| {
+            let buf = &payload[..len as usize];
+            // TODO: use `MaybeUninit::slice_assume_init_ref` once stable.
+            unsafe { &*(buf as *const [_] as *const [u8]) }.into()
+        })
+    }
+
+    /// Set the value of the `TCP_CONGESTION` option for this socket.
+    ///
+    /// Specifies the TCP congestion control algorithm to use for this socket.
+    ///
+    /// The value must be a valid TCP congestion control algorithm name of the
+    /// platform. For example, Linux may supports "reno", "cubic".
+    #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
+    #[cfg_attr(
+        docsrs,
+        doc(cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux"))))
+    )]
+    pub fn set_tcp_congestion(&self, tcp_ca_name: &[u8]) -> io::Result<()> {
+        syscall!(setsockopt(
+            self.as_raw(),
+            IPPROTO_TCP,
+            libc::TCP_CONGESTION,
+            tcp_ca_name.as_ptr() as *const _,
+            tcp_ca_name.len() as libc::socklen_t,
+        ))
+        .map(|_| ())
+    }
 }
 
 #[cfg_attr(docsrs, doc(cfg(unix)))]
diff --git a/src/sys/windows.rs b/src/sys/windows.rs
index 03e1a60..cd06640 100644
--- a/src/sys/windows.rs
+++ b/src/sys/windows.rs
@@ -69,12 +69,13 @@ pub(crate) type socklen_t = i32;
 pub(crate) use windows_sys::Win32::Networking::WinSock::IP_HDRINCL;
 pub(crate) use windows_sys::Win32::Networking::WinSock::{
     linger, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MREQ as Ipv6Mreq,
-    IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY,
-    IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP,
-    IP_MREQ as IpMreq, IP_MREQ_SOURCE as IpMreqSource, IP_MULTICAST_IF, IP_MULTICAST_LOOP,
-    IP_MULTICAST_TTL, IP_RECVTOS, IP_TOS, IP_TTL, MSG_OOB, MSG_PEEK, SO_BROADCAST, SO_ERROR,
-    SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_RCVBUF, SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF,
-    SO_SNDTIMEO, SO_TYPE, TCP_NODELAY,
+    IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_RECVTCLASS,
+    IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP,
+    IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, IP_MREQ as IpMreq,
+    IP_MREQ_SOURCE as IpMreqSource, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL,
+    IP_RECVTOS, IP_TOS, IP_TTL, MSG_OOB, MSG_PEEK, SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_LINGER,
+    SO_OOBINLINE, SO_RCVBUF, SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF, SO_SNDTIMEO, SO_TYPE,
+    TCP_NODELAY,
 };
 pub(crate) const IPPROTO_IP: c_int = windows_sys::Win32::Networking::WinSock::IPPROTO_IP as c_int;
 pub(crate) const SOL_SOCKET: c_int = windows_sys::Win32::Networking::WinSock::SOL_SOCKET as c_int;
@@ -775,7 +776,7 @@ pub(crate) fn to_mreqn(
     }
 }
 
-#[allow(unused_unsafe)] // TODO: replace with `unsafe_op_in_unsafe_fn` once stable.
+#[allow(unsafe_op_in_unsafe_fn)]
 pub(crate) fn unix_sockaddr(path: &Path) -> io::Result<SockAddr> {
     // SAFETY: a `sockaddr_storage` of all zeros is valid.
     let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };


Test patch:
diff --git a/tests/socket.rs b/tests/socket.rs
index 54c8cd9..c3872bd 100644
--- a/tests/socket.rs
+++ b/tests/socket.rs
@@ -902,7 +902,7 @@ fn sendfile() {
         assert_eq!(n, HELLO_WORLD.data.len());
 
         let mut buf = Vec::with_capacity(HELLO_WORLD.data.len() + 1);
-        let n = receiver.recv(spare_capacity_mut(&mut buf)).unwrap();
+        let n = receiver.recv(buf.spare_capacity_mut()).unwrap();
         assert_eq!(n, HELLO_WORLD.data.len());
         unsafe { buf.set_len(n) };
         assert_eq!(buf, HELLO_WORLD.data);
@@ -923,7 +923,7 @@ fn sendfile() {
         let mut buf = Vec::with_capacity(LOREM.data.len() + 1);
         let mut total = 0;
         while total < LOREM.data.len() {
-            let n = receiver.recv(spare_capacity_mut(&mut buf)).unwrap();
+            let n = receiver.recv(buf.spare_capacity_mut()).unwrap();
             unsafe { buf.set_len(buf.len() + n) };
             total += n;
         }
@@ -932,25 +932,6 @@ fn sendfile() {
     }
 }
 
-// TODO: use `Vec::spare_capacity_mut` once stable.
-#[cfg(all(
-    feature = "all",
-    any(
-        target_os = "android",
-        target_os = "freebsd",
-        target_os = "linux",
-        target_vendor = "apple",
-    )
-))]
-fn spare_capacity_mut(buf: &mut Vec<u8>) -> &mut [MaybeUninit<u8>] {
-    unsafe {
-        std::slice::from_raw_parts_mut(
-            buf.as_mut_ptr().add(buf.len()) as *mut MaybeUninit<u8>,
-            buf.capacity() - buf.len(),
-        )
-    }
-}
-
 #[cfg(all(
     feature = "all",
     any(
@@ -1218,6 +1199,33 @@ test!(IPv6 only_v6, set_only_v6(true));
 #[cfg(any(windows, target_os = "freebsd"))]
 test!(IPv6 only_v6, set_only_v6(false));
 
+#[cfg(all(
+    feature = "all",
+    any(
+        target_os = "android",
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "linux",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    )
+))]
+test!(IPv6 tclass_v6, set_tclass_v6(96));
+
+#[cfg(not(any(
+    target_os = "dragonfly",
+    target_os = "fuchsia",
+    target_os = "illumos",
+    target_os = "netbsd",
+    target_os = "openbsd",
+    target_os = "redox",
+    target_os = "solaris",
+    target_os = "windows",
+)))]
+test!(IPv6 recv_tclass_v6, set_recv_tclass_v6(true));
+
 #[cfg(all(
     feature = "all",
     any(target_os = "android", target_os = "fuchsia", target_os = "linux")
@@ -1333,3 +1341,49 @@ fn original_dst_ipv6() {
         Err(err) => assert_eq!(err.raw_os_error(), Some(libc::EOPNOTSUPP)),
     }
 }
+
+#[test]
+#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
+fn tcp_congestion() {
+    let socket: Socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap();
+    // Get and set current tcp_ca
+    let origin_tcp_ca = socket
+        .tcp_congestion()
+        .expect("failed to get tcp congestion algorithm");
+    socket
+        .set_tcp_congestion(&origin_tcp_ca)
+        .expect("failed to set tcp congestion algorithm");
+    // Return a Err when set a non-exist tcp_ca
+    socket
+        .set_tcp_congestion(b"tcp_congestion_does_not_exist")
+        .unwrap_err();
+    let cur_tcp_ca = socket.tcp_congestion().unwrap();
+    assert_eq!(
+        cur_tcp_ca, origin_tcp_ca,
+        "expected {origin_tcp_ca:?} but get {cur_tcp_ca:?}"
+    );
+    let cur_tcp_ca = cur_tcp_ca.splitn(2, |num| *num == 0).next().unwrap();
+    const OPTIONS: [&[u8]; 2] = [
+        b"cubic",
+        #[cfg(target_os = "linux")] // or Android.
+        b"reno",
+        #[cfg(target_os = "freebsd")]
+        b"newreno",
+    ];
+    // Set a new tcp ca
+    #[cfg(target_os = "linux")]
+    let new_tcp_ca = if cur_tcp_ca == OPTIONS[0] {
+        OPTIONS[1]
+    } else {
+        OPTIONS[0]
+    };
+    #[cfg(target_os = "freebsd")]
+    let new_tcp_ca = OPTIONS[1];
+    socket.set_tcp_congestion(new_tcp_ca).unwrap();
+    // Check if new tcp ca is successfully set
+    let cur_tcp_ca = socket.tcp_congestion().unwrap();
+    assert_eq!(
+        cur_tcp_ca.splitn(2, |num| *num == 0).next().unwrap(),
+        new_tcp_ca,
+    );
+}