From 3fc1276f04bf91d3bdb5850488acba8b6dcd5a2e Mon Sep 17 00:00:00 2001 From: Han Xu Date: Tue, 31 May 2022 06:40:12 -0700 Subject: [PATCH] Add UnboundUdpSocket in std::net to support UDP socket configurations before binding to an address. Once bound, the socket becomes a regular UdpSocket. --- library/std/src/net/mod.rs | 8 +- library/std/src/net/socket_addr.rs | 20 ++ library/std/src/net/udp.rs | 99 ++++++++- library/std/src/net/udp/tests.rs | 266 ++++++++++++++++++++++++ library/std/src/os/fd/owned.rs | 16 ++ library/std/src/os/windows/io/raw.rs | 16 ++ library/std/src/os/windows/io/socket.rs | 16 ++ library/std/src/sys/unix/net.rs | 10 +- library/std/src/sys/windows/c.rs | 2 + library/std/src/sys/windows/net.rs | 10 +- library/std/src/sys_common/net.rs | 202 +++++++++++++----- 11 files changed, 604 insertions(+), 61 deletions(-) diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 01e3db9de51c4..bd4866e5bc784 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -28,7 +28,9 @@ pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::parser::AddrParseError; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::socket_addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; +pub use self::socket_addr::{ + SocketAddr, SocketAddrFamily, SocketAddrV4, SocketAddrV6, ToSocketAddrs, +}; #[unstable(feature = "tcplistener_into_incoming", issue = "88339")] pub use self::tcp::IntoIncoming; #[stable(feature = "rust1", since = "1.0.0")] @@ -36,8 +38,12 @@ pub use self::tcp::{Incoming, TcpListener, TcpStream}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::udp::UdpSocket; +#[unstable(feature = "unbound_socket", issue = "none")] +pub use self::udp::UnboundUdpSocket; + mod display_buffer; mod ip_addr; + mod parser; mod socket_addr; mod tcp; diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index 33b0dfa03e0ed..6bd537106d3ed 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -264,6 +264,15 @@ impl SocketAddr { pub const fn is_ipv6(&self) -> bool { matches!(*self, SocketAddr::V6(_)) } + + /// Returns the `SocketAddrFamily` value of this `SocketAddr`. + #[unstable(feature = "unbound_socket", issue = "none")] + pub const fn family(&self) -> SocketAddrFamily { + match *self { + SocketAddr::V4(..) => SocketAddrFamily::InetV4, + SocketAddr::V6(..) => SocketAddrFamily::InetV6, + } + } } impl SocketAddrV4 { @@ -972,3 +981,14 @@ impl ToSocketAddrs for String { (&**self).to_socket_addrs() } } + +/// Address family values for an Internet socket address. +#[derive(Debug, Clone, Copy, PartialEq)] +#[non_exhaustive] +#[unstable(feature = "unbound_socket", issue = "none")] +pub enum SocketAddrFamily { + /// Address family of IPv4. + InetV4, + /// Address family of IPv6. + InetV6, +} diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 864e1b0f3450a..1975746482f1f 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -3,7 +3,7 @@ mod tests; use crate::fmt; use crate::io::{self, ErrorKind}; -use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; +use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrFamily, ToSocketAddrs}; use crate::sys_common::net as net_imp; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; @@ -811,3 +811,100 @@ impl fmt::Debug for UdpSocket { self.0.fmt(f) } } + +/// A UDP Socket that is not bound to a `SocketAddr` yet. +/// +/// This socket is designed to support socket configurations _before_ +/// binding to a `SocketAddr`. After configurations, this socket +/// can be bound and translated into a [`UdpSocket`]. +/// +/// # Example +/// +/// ```no_run +/// #![feature(unbound_socket)] +/// use std::net::{SocketAddr, SocketAddrFamily, UnboundUdpSocket}; +/// +/// fn main() -> std::io::Result<()> { +/// let unbound_socket = UnboundUdpSocket::new(SocketAddrFamily::InetV4)?; +/// unbound_socket.set_reuseaddr(true)?; +/// let addr = SocketAddr::from(([127, 0, 0, 1], 5500)); +/// let _udp_socket = unbound_socket.bind(&addr)?; +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "unbound_socket", issue = "none")] +pub struct UnboundUdpSocket { + inner: net_imp::UnboundUdpSocket, +} + +impl UnboundUdpSocket { + /// Creates a new unbound UDP socket with `addr_family`. + #[unstable(feature = "unbound_socket", issue = "none")] + pub fn new(addr_family: SocketAddrFamily) -> io::Result { + let inner = net_imp::UnboundUdpSocket::new(addr_family)?; + Ok(Self { inner }) + } + + /// Sets `SO_REUSEADDR` option for the socket. + /// + /// In general, this option allows a second caller to bind to a `(addr, port)` again, + /// where the `addr` could be the `unspecified` address. However it behaves with subtle + /// differences on different platforms. Please be sure to check your platform for + /// the exact expected behaviors. + /// + /// This method can only be called before `bind`, otherwise will fail. + #[unstable(feature = "unbound_socket", issue = "none")] + pub fn set_reuseaddr(&self, enable: bool) -> io::Result<()> { + self.inner.set_reuseaddr(enable) + } + + /// Sets `SO_REUSEPORT` option for the socket. + /// + /// In general, this option allows a second caller to bind to a same `(addr, port)` + /// pair again, if the first caller has enabled this option too. Please check with + /// your specific platform for the details of the behavior. + /// + /// This option is only available for UNIX-like platforms and not Windows platforms. + #[unstable(feature = "unbound_socket", issue = "none")] + pub fn set_reuseport(&self, enable: bool) -> io::Result<()> { + self.inner.set_reuseport(enable) + } + + /// Sets `SO_EXCLUSIVEADDRUSE` option for the socket. + /// + /// This option is only available in Windows. Its purpose is to prevent + /// any other caller to "reuse" the same (addr, port), even if they call + /// `set_reuseaddr(true)`. This method returns an error on non-Windows platforms. + #[unstable(feature = "unbound_socket", issue = "none")] + pub fn set_exclusiveaddruse(&self, enable: bool) -> io::Result<()> { + self.inner.set_exclusiveaddruse(enable) + } + + /// Binds to `addr`, consumes this unbound socket and returns a [`UdpSocket`]. + #[unstable(feature = "unbound_socket", issue = "none")] + pub fn bind(self, addr: &SocketAddr) -> io::Result { + let net_imp = self.inner.bind(addr)?; + Ok(UdpSocket(net_imp)) + } +} + +#[unstable(feature = "unbound_socket", issue = "none")] +impl fmt::Debug for UnboundUdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +#[unstable(feature = "unbound_socket", issue = "none")] +impl AsInner for UnboundUdpSocket { + fn as_inner(&self) -> &net_imp::UnboundUdpSocket { + &self.inner + } +} + +#[unstable(feature = "unbound_socket", issue = "none")] +impl IntoInner for UnboundUdpSocket { + fn into_inner(self) -> net_imp::UnboundUdpSocket { + self.inner + } +} diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index f82904ffbbf77..96b58e75d861d 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -363,3 +363,269 @@ fn set_nonblocking() { } }) } + +#[cfg(not(windows))] +#[test] +fn set_reuseaddr_v4_not_windows() { + let addr = next_test_ip4(); + let addr_family = addr.family(); + let port = addr.port(); + let wild_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port); + + let unbound_socket = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket.set_reuseaddr(true)); // Needed at least in Linux. + let wild_socket = t!(unbound_socket.bind(&wild_addr)); + + // Without set_reuseaddr, we cannot bind to the addr with the same port. + let unbound_socket2 = t!(UnboundUdpSocket::new(addr_family)); + let res = unbound_socket2.bind(&addr); + assert!(res.is_err()); + + // With set_reuseaddr(false), we cannot bind with the same port. + let unbound_socket3 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket3.set_reuseaddr(false)); + let res = unbound_socket3.bind(&addr); + assert!(res.is_err()); + + // With set_reuseaddr(true), we can bind to a local addr with the same port. + let unbound_socket4 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket4.set_reuseaddr(true)); + let socket = t!(unbound_socket4.bind(&addr)); + + const MSG_1: &[u8] = b"hello world"; + t!(socket.send_to(MSG_1, &addr)); + let mut buf = [0; MSG_1.len()]; + let (size, _) = t!(socket.recv_from(&mut buf)); + assert_eq!(MSG_1, &buf[..]); + assert_eq!(size, MSG_1.len()); + + // Multicast also works with set_reuseaddr. + let group_ip = Ipv4Addr::new(224, 0, 0, 251); + let any_ip = Ipv4Addr::new(0, 0, 0, 0); + let broadcast_addr = SocketAddr::new(IpAddr::V4(group_ip), port); + + let unbound_socket_mcast = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket_mcast.set_reuseaddr(true)); + let socket_mcast = t!(unbound_socket_mcast.bind(&broadcast_addr)); + t!(socket_mcast.join_multicast_v4(&group_ip, &any_ip)); + t!(wild_socket.join_multicast_v4(&group_ip, &any_ip)); + + const MSG_2: &[u8] = b"hello multicast"; + t!(wild_socket.send_to(MSG_2, &broadcast_addr)); + let mut buf = [0; MSG_2.len()]; + let (size, _) = t!(socket_mcast.recv_from(&mut buf)); + assert_eq!(MSG_2, &buf[..]); + assert_eq!(size, MSG_2.len()); +} + +#[cfg(not(windows))] +#[test] +fn set_reuseaddr_v6_not_windows() { + let addr = next_test_ip6(); + let addr_family = addr.family(); + let port = addr.port(); + let wild_addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), port); + + let unbound_socket = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket.set_reuseaddr(true)); + let _wild_socket = t!(unbound_socket.bind(&wild_addr)); + + // Without set_reuseaddr, we cannot bind to the addr with the same port. + let unbound_socket2 = t!(UnboundUdpSocket::new(addr_family)); + let res = unbound_socket2.bind(&addr); + assert!(res.is_err()); + + // With set_reuseaddr(false), we cannot bind with the same port. + let unbound_socket3 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket3.set_reuseaddr(false)); + let res = unbound_socket3.bind(&addr); + assert!(res.is_err()); + + // With set_reuseaddr(true), we can bind to a local addr with the same port. + let unbound_socket4 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket4.set_reuseaddr(true)); + let socket = t!(unbound_socket4.bind(&addr)); + + const MSG_1: &[u8] = b"hello world"; + t!(socket.send_to(MSG_1, &addr)); + let mut buf = [0; MSG_1.len()]; + let (size, _) = t!(socket.recv_from(&mut buf)); + assert_eq!(MSG_1, &buf[..]); + assert_eq!(size, MSG_1.len()); +} + +#[cfg(windows)] +#[test] +fn set_reuseaddr_v4_windows() { + let addr = next_test_ip4(); + let addr_family = addr.family(); + + // Since Windows Server 2003 and later, we cannot bind to the same specific address & port + // unless both the first socket and the second socket enables set_reuseaddr. Note that + // wild card address (0.0.0.0) is not subject to this rule. + + // The 2nd bind would fail as the 1st socket did not enable set_reuseaddr. + let unbound_socket1 = t!(UnboundUdpSocket::new(addr_family)); + let _socket1 = t!(unbound_socket1.bind(&addr)); + + let unbound_socket2 = t!(UnboundUdpSocket::new(addr_family)); + let res = unbound_socket2.bind(&addr); + assert!(res.is_err()); + + let unbound_socket2 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket2.set_reuseaddr(true)); // only the 2nd socket enables reuseaddr. + let res = unbound_socket2.bind(&addr); + assert!(res.is_err()); + + // The 2nd bind would succeed as both the 1st socket and the 2nd socket enabled + // set_reuseaddr. + let addr = next_test_ip4(); + let addr_family = addr.family(); + + let unbound_socket1 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket1.set_reuseaddr(true)); + let _socket1 = t!(unbound_socket1.bind(&addr)); + + let unbound_socket2 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket2.set_reuseaddr(true)); + let _socket2 = t!(unbound_socket2.bind(&addr)); +} + +#[cfg(windows)] +#[test] +fn set_reuseaddr_v6_windows() { + let addr = next_test_ip6(); + let addr_family = addr.family(); + + // Since Windows Server 2003 and later, we cannot bind to the same specific address & port + // unless both the first socket and the second socket enables set_reuseaddr. Note that + // wild card address (0.0.0.0) is not subject to this rule. + + // The 2nd bind would fail as the 1st socket did not enable set_reuseaddr. + let unbound_socket1 = t!(UnboundUdpSocket::new(addr_family)); + let _socket1 = t!(unbound_socket1.bind(&addr)); + + let unbound_socket2 = t!(UnboundUdpSocket::new(addr_family)); + let res = unbound_socket2.bind(&addr); + assert!(res.is_err()); + + let unbound_socket2 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket2.set_reuseaddr(true)); // only the 2nd socket enables reuseaddr. + let res = unbound_socket2.bind(&addr); + assert!(res.is_err()); + + // The 2nd bind would succeed as both the 1st socket and the 2nd socket enabled + // set_reuseaddr. + let addr = next_test_ip6(); + let addr_family = addr.family(); + + let unbound_socket1 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket1.set_reuseaddr(true)); + let _socket1 = t!(unbound_socket1.bind(&addr)); + + let unbound_socket2 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket2.set_reuseaddr(true)); + let _socket2 = t!(unbound_socket2.bind(&addr)); +} + +#[cfg(unix)] +#[test] +fn set_reuseport_v4_unix() { + set_reuseport_unix(next_test_ip4()); +} + +#[cfg(unix)] +#[test] +fn set_reuseport_v6_unix() { + set_reuseport_unix(next_test_ip6()); +} + +#[cfg(unix)] +fn set_reuseport_unix(sockaddr: SocketAddr) { + let addr_family = sockaddr.family(); + + // Bind the 1st socket. + let unbound_socket = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket.set_reuseport(true)); + let socket1 = t!(unbound_socket.bind(&sockaddr)); + + // With set_reuseport, We can bind again to the same sockaddr. + let unbound_socket2 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket2.set_reuseport(true)); + let socket2 = t!(unbound_socket2.bind(&sockaddr)); + + // Use the new socket to send. Because the recv side + // is distributed between two sockets by the OS, we cannot + // be sure which socket to recv_from, and hence not to recv + // the packet. + const MSG_1: &[u8] = b"hello world"; + t!(socket1.send_to(MSG_1, &sockaddr)); + t!(socket2.send_to(MSG_1, &sockaddr)); + + // Verify the negative case: + // Without set_reuseport, We cannot bind again to the same sockaddr. + let unbound_socket3 = t!(UnboundUdpSocket::new(addr_family)); + let res = unbound_socket3.bind(&sockaddr); + assert!(res.is_err()); + + // Make sure the 1st socket was not dropped earlier. + t!(socket1.send_to(MSG_1, &sockaddr)); +} + +#[cfg(windows)] +#[test] +fn set_exclusiveaddruse_v4_windows() { + let addr = next_test_ip4(); + let addr_family = addr.family(); + let port = addr.port(); + let wild_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port); + + let unbound_socket = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket.set_exclusiveaddruse(true)); + let _wild_socket = t!(unbound_socket.bind(&wild_addr)); + + // With set_exclusiveaddruse(true), we cannot bind to the addr with the same port, + // even the first socket is a wild adress. + let unbound_socket2 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket2.set_reuseaddr(true)); + let res = unbound_socket2.bind(&addr); + assert!(res.is_err()); +} + +#[cfg(windows)] +#[test] +fn set_exclusiveaddruse_v6_windows() { + let addr = next_test_ip6(); + let addr_family = addr.family(); + let port = addr.port(); + let wild_addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), port); + + let unbound_socket = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket.set_exclusiveaddruse(true)); + let _wild_socket = t!(unbound_socket.bind(&wild_addr)); + + // With set_exclusiveaddruse(true), we cannot bind to the addr with the same port. + let unbound_socket2 = t!(UnboundUdpSocket::new(addr_family)); + t!(unbound_socket2.set_reuseaddr(true)); + let res = unbound_socket2.bind(&addr); + assert!(res.is_err()); +} + +#[cfg(not(windows))] +#[test] +fn set_exclusiveaddruse_non_windows() { + let addr_family = SocketAddrFamily::InetV4; + let unbound_socket = t!(UnboundUdpSocket::new(addr_family)); + let res = unbound_socket.set_exclusiveaddruse(true); + assert!(res.is_err()); // Not supported. +} + +#[test] +fn bind_wrong_addr_family() { + // An UnboundUdpSocket of IPv4 cannot bind to IPv6 address. + let addr = next_test_ip6(); + let addr_family = SocketAddrFamily::InetV4; + let unbound_socket = t!(UnboundUdpSocket::new(addr_family)); + let res = unbound_socket.bind(&addr); + assert!(res.is_err()); +} diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index c16518577f7c4..1441c4da2cc5d 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -454,3 +454,19 @@ impl<'a> AsFd for io::StderrLock<'a> { unsafe { BorrowedFd::borrow_raw(2) } } } + +#[unstable(feature = "unbound_socket", issue = "none")] +impl AsFd for crate::net::UnboundUdpSocket { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().socket().as_fd() + } +} + +#[unstable(feature = "unbound_socket", issue = "none")] +impl From for OwnedFd { + #[inline] + fn from(socket: crate::net::UnboundUdpSocket) -> OwnedFd { + socket.into_inner().into_socket().into_inner().into_inner().into() + } +} diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 49e4f304f5dba..3021e6ec118c2 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -255,6 +255,14 @@ impl AsRawSocket for net::UdpSocket { } } +#[unstable(feature = "unbound_socket", issue = "none")] +impl AsRawSocket for net::UnboundUdpSocket { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.as_inner().socket().as_raw_socket() + } +} + #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawSocket for net::TcpStream { #[inline] @@ -303,3 +311,11 @@ impl IntoRawSocket for net::UdpSocket { self.into_inner().into_socket().into_inner().into_raw_socket() } } + +#[unstable(feature = "unbound_socket", issue = "none")] +impl IntoRawSocket for net::UnboundUdpSocket { + #[inline] + fn into_raw_socket(self) -> RawSocket { + self.into_inner().into_socket().into_inner().into_raw_socket() + } +} diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 72cb3406dcada..b4e4fb7b67e6a 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -336,3 +336,19 @@ impl From for crate::net::UdpSocket { unsafe { Self::from_raw_socket(owned.into_raw_socket()) } } } + +#[unstable(feature = "unbound_socket", issue = "none")] +impl AsSocket for crate::net::UnboundUdpSocket { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } + } +} + +#[unstable(feature = "unbound_socket", issue = "none")] +impl From for OwnedSocket { + #[inline] + fn from(unbound_udp_socket: crate::net::UnboundUdpSocket) -> OwnedSocket { + unsafe { OwnedSocket::from_raw_socket(unbound_udp_socket.into_raw_socket()) } + } +} diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index b84bf8f9264a4..ac4488820e05e 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -2,7 +2,7 @@ use crate::cmp; use crate::ffi::CStr; use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem; -use crate::net::{Shutdown, SocketAddr}; +use crate::net::{Shutdown, SocketAddr, SocketAddrFamily}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::str; use crate::sys::fd::FileDesc; @@ -59,10 +59,10 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> { } impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => libc::AF_INET, - SocketAddr::V6(..) => libc::AF_INET6, + pub fn new(addr: SocketAddrFamily, ty: c_int) -> io::Result { + let fam = match addr { + SocketAddrFamily::InetV4 => libc::AF_INET, + SocketAddrFamily::InetV6 => libc::AF_INET6, }; Socket::new_raw(fam, ty) } diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 917fc8e4995e6..dcc452842e097 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -226,6 +226,8 @@ pub const IP_TTL: c_int = 4; pub const IPV6_V6ONLY: c_int = 27; pub const SO_ERROR: c_int = 0x1007; pub const SO_BROADCAST: c_int = 0x0020; +pub const SO_REUSEADDR: c_int = 0x0004; +pub const SO_EXCLUSIVEADDRUSE: c_int = !SO_REUSEADDR; pub const IP_MULTICAST_LOOP: c_int = 11; pub const IPV6_MULTICAST_LOOP: c_int = 11; pub const IP_MULTICAST_TTL: c_int = 10; diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index e0701a498fad7..d36a4db0bc74f 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -3,7 +3,7 @@ use crate::cmp; use crate::io::{self, IoSlice, IoSliceMut, Read}; use crate::mem; -use crate::net::{Shutdown, SocketAddr}; +use crate::net::{Shutdown, SocketAddr, SocketAddrFamily}; use crate::os::windows::io::{ AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, }; @@ -100,10 +100,10 @@ where } impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let family = match *addr { - SocketAddr::V4(..) => c::AF_INET, - SocketAddr::V6(..) => c::AF_INET6, + pub fn new(addr_family: SocketAddrFamily, ty: c_int) -> io::Result { + let family = match addr_family { + SocketAddrFamily::InetV4 => c::AF_INET, + SocketAddrFamily::InetV6 => c::AF_INET6, }; let socket = unsafe { c::WSASocketW( diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index fad4a63331b59..0507f40ca8c71 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -6,7 +6,7 @@ use crate::convert::{TryFrom, TryInto}; use crate::fmt; use crate::io::{self, ErrorKind, IoSlice, IoSliceMut}; use crate::mem; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrFamily}; use crate::ptr; use crate::sys::common::small_c_string::run_with_cstr; use crate::sys::net::netc as c; @@ -221,22 +221,13 @@ pub struct TcpStream { impl TcpStream { pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { let addr = addr?; - - init(); - - let sock = Socket::new(addr, c::SOCK_STREAM)?; - - let (addr, len) = addr.into_inner(); - cvt_r(|| unsafe { c::connect(sock.as_raw(), addr.as_ptr(), len) })?; - Ok(TcpStream { inner: sock }) + let unbound_sock = UnboundTcpSocket::new(addr.family())?; + unbound_sock.connect(addr) } pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { - init(); - - let sock = Socket::new(addr, c::SOCK_STREAM)?; - sock.connect_timeout(addr, timeout)?; - Ok(TcpStream { inner: sock }) + let unbound_sock = UnboundTcpSocket::new(addr.family())?; + unbound_sock.connect_timeout(addr, timeout) } pub fn socket(&self) -> &Socket { @@ -387,40 +378,8 @@ pub struct TcpListener { impl TcpListener { pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { let addr = addr?; - - init(); - - let sock = Socket::new(addr, c::SOCK_STREAM)?; - - // On platforms with Berkeley-derived sockets, this allows to quickly - // rebind a socket, without needing to wait for the OS to clean up the - // previous one. - // - // On Windows, this allows rebinding sockets which are actively in use, - // which allows “socket hijacking”, so we explicitly don't set it here. - // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse - #[cfg(not(windows))] - setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; - - // Bind our new socket - let (addr, len) = addr.into_inner(); - cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; - - cfg_if::cfg_if! { - if #[cfg(target_os = "horizon")] { - // The 3DS doesn't support a big connection backlog. Sometimes - // it allows up to about 37, but other times it doesn't even - // accept 32. There may be a global limitation causing this. - let backlog = 20; - } else { - // The default for all other platforms - let backlog = 128; - } - } - - // Start listening - cvt(unsafe { c::listen(sock.as_raw(), backlog) })?; - Ok(TcpListener { inner: sock }) + let unbound_sock = UnboundTcpSocket::new(addr.family())?; + unbound_sock.listen(addr) } pub fn socket(&self) -> &Socket { @@ -507,7 +466,7 @@ impl UdpSocket { init(); - let sock = Socket::new(addr, c::SOCK_DGRAM)?; + let sock = Socket::new(addr.family(), c::SOCK_DGRAM)?; let (addr, len) = addr.into_inner(); cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; Ok(UdpSocket { inner: sock }) @@ -743,3 +702,148 @@ impl<'a> IntoInner<(SocketAddrCRepr, c::socklen_t)> for &'a SocketAddr { } } } + +/// Represents a UDP socket that is not bound to any SocketAddr yet. +pub struct UnboundUdpSocket { + inner: Socket, + addr_family: SocketAddrFamily, +} + +impl UnboundUdpSocket { + /// Creates a new UDP socket without binding. + pub fn new(addr_family: SocketAddrFamily) -> io::Result { + init(); + + let inner = Socket::new(addr_family, c::SOCK_DGRAM)?; + let new_self = Self { inner, addr_family }; + Ok(new_self) + } + + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn set_reuseaddr(&self, enable: bool) -> io::Result<()> { + // The `SO_REUSEADDR` option behaves with subtle differences on different platforms. + // Here is a good summary: + // https://stackoverflow.com/a/14388707/1783732 + setsockopt(&self.inner, c::SOL_SOCKET, c::SO_REUSEADDR, enable as c_int) + } + + cfg_if::cfg_if! { + if #[cfg(windows)] { + /// Set "SO_EXCLUSIVEADDRUSE" to true or false on Windows. + pub fn set_exclusiveaddruse(&self, enable: bool) -> io::Result<()> { + // This is a sockopt in Windows to prevent 'port hijacking': + // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse + setsockopt(&self.inner, c::SOL_SOCKET, c::SO_EXCLUSIVEADDRUSE, enable as c_int) + } + } else { + /// Returns an error on non-Windows platforms. + pub fn set_exclusiveaddruse(&self, _enable: bool) -> io::Result<()> { + Err(io::Error::from(io::ErrorKind::Unsupported)) + } + + } + } + + cfg_if::cfg_if! { + if #[cfg(unix)] { + /// Sets `SO_REUSEPORT` option to true or false on Unix platforms, including + /// BSDs, macOS and Linux. + pub fn set_reuseport(&self, enable: bool) -> io::Result<()> { + setsockopt(&self.inner, c::SOL_SOCKET, c::SO_REUSEPORT, enable as c_int) + } + } else { + /// Returns an error on non-Unix platforms. Note, it is possible + /// that some non-Windows and non-Unix platforms also support this option. + pub fn set_reuseport(&self, _enable: bool) -> io::Result<()> { + Err(io::Error::from(io::ErrorKind::Unsupported)) + } + } + } + + /// Binds the socket to an address, and returns a `UdpSocket`. + pub fn bind(self, addr: &SocketAddr) -> io::Result { + if self.addr_family != addr.family() { + return Err(io::Error::from(io::ErrorKind::InvalidInput)); + } + + let (addr, len) = addr.into_inner(); + cvt(unsafe { c::bind(self.inner.as_raw(), addr.as_ptr(), len as _) })?; + Ok(UdpSocket { inner: self.inner }) + } +} + +impl fmt::Debug for UnboundUdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let name = if cfg!(windows) { "socket" } else { "fd" }; + f.debug_struct("UnboundUdpSocket").field(name, &self.inner.as_raw()).finish() + } +} + +/// Represents a TCP socket that is not bound to a `SocketAddr` yet. +pub struct UnboundTcpSocket { + inner: Socket, +} + +impl UnboundTcpSocket { + pub fn new(addr_family: SocketAddrFamily) -> io::Result { + init(); + + let sock = Socket::new(addr_family, c::SOCK_STREAM)?; + Ok(Self { inner: sock }) + } + + #[cfg(not(windows))] + pub fn set_reuseaddr(&self, enable: bool) -> io::Result<()> { + setsockopt(&self.inner, c::SOL_SOCKET, c::SO_REUSEADDR, enable as c_int) + } + + pub fn connect(self, addr: &SocketAddr) -> io::Result { + let (addr, len) = addr.into_inner(); + cvt_r(|| unsafe { c::connect(self.inner.as_raw(), addr.as_ptr(), len) })?; + Ok(TcpStream { inner: self.inner }) + } + + pub fn connect_timeout(self, addr: &SocketAddr, timeout: Duration) -> io::Result { + self.inner.connect_timeout(addr, timeout)?; + Ok(TcpStream { inner: self.inner }) + } + + pub fn listen(self, addr: &SocketAddr) -> io::Result { + // On platforms with Berkeley-derived sockets, this allows to quickly + // rebind a socket, without needing to wait for the OS to clean up the + // previous one. + // + // On Windows, this allows rebinding sockets which are actively in use, + // which allows “socket hijacking”, so we explicitly don't set it here. + // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse + #[cfg(not(windows))] + self.set_reuseaddr(true)?; + + // Bind our socket + let (addr, len) = addr.into_inner(); + cvt(unsafe { c::bind(self.inner.as_raw(), addr.as_ptr(), len as _) })?; + + cfg_if::cfg_if! { + if #[cfg(target_os = "horizon")] { + // The 3DS doesn't support a big connection backlog. Sometimes + // it allows up to about 37, but other times it doesn't even + // accept 32. There may be a global limitation causing this. + let backlog = 20; + } else { + // The default for all other platforms + let backlog = 128; + } + } + + // Start listening + cvt(unsafe { c::listen(self.inner.as_raw(), backlog) })?; + Ok(TcpListener { inner: self.inner }) + } +}